PennyLane
  • Why PennyLane
  • Getting Started
  • Documentation
  • Ecosystem
Install
Install
  1. Blog/
  2. Releases/
  3. PennyLane v0.38 and Catalyst v0.8 released

September 04, 2024

PennyLane v0.38 and Catalyst v0.8 released

Isaac De Vlugt

Isaac De Vlugt

Josh Izaac

Josh Izaac

Diego Guala

Diego Guala

v0.38

PennyLane v0.38 and Catalyst v0.8 are here and ready to knock your socks off ๐Ÿงฆ๐Ÿ’ฅ!

Contents

  • Registers of wires ๐Ÿงธ
  • Quantum arithmetic operations ๐Ÿงฎ
  • Converting noise models from Qiskit โ™ป๏ธ
  • Substantial tree-traversal upgrades ๐ŸŒณ
  • Differentiable callbacks with Catalyst ๐Ÿ“ž
  • Improvements ๐Ÿ› ๏ธ
  • Deprecations and breaking changes ๐Ÿ’”
  • Contributors โœ๏ธ

Registers of wires ๐Ÿงธ

Have you registered for registers ๐Ÿ—ณ๏ธ?

Registers of wires

With quantum algorithms getting larger and larger, the less you need to deal with gates and operations on the level of individual wire labels/indices the better. With this release, a new function qml.registers has been added that lets you seamlessly create registers of wires by providing dictionaries of register names and the number of wires required.

>>> wire_reg = qml.registers({"alice": 4, "bob": 3}) >>> wire_reg {'alice': Wires([0, 1, 2, 3]), 'bob': Wires([4, 5, 6])}

The resulting data structure from qml.registers is a dictionary with the same register names as keys, but the values are Wires instances.

Nesting registers within other registers can be easily done by providing a nested dictionary, where the ordering of wire labels is based on the order of appearance and nestedness:

>>> wire_reg = qml.registers({"alice": {"alice1": 1, "alice2": 2}, "bob": {"bob1": 2, "bob2": 1}}) >>> wire_reg {'alice1': Wires([0]), 'alice2': Wires([1, 2]), 'alice': Wires([0, 1, 2]), 'bob1': Wires([3, 4]), 'bob2': Wires([5]), 'bob': Wires([3, 4, 5])}

Since the values of the dictionary are Wires instances, their use within quantum circuits is very similar to that of a list of integers:

dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(): for w in wire_reg["alice"]: qml.Hadamard(w) for w in wire_reg["bob1"]: qml.RX(0.1967, wires=w) qml.CNOT(wires=[wire_reg["alice1"][0], wire_reg["bob2"][0]]) return [qml.expval(qml.Y(w)) for w in wire_reg["bob1"]] print(qml.draw(circuit)())
0: โ”€โ”€Hโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ญโ—โ”€โ”ค 1: โ”€โ”€Hโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚โ”€โ”€โ”ค 2: โ”€โ”€Hโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚โ”€โ”€โ”ค 3: โ”€โ”€RX(0.20)โ”€โ”‚โ”€โ”€โ”ค <Y> 4: โ”€โ”€RX(0.20)โ”€โ”‚โ”€โ”€โ”ค <Y> 5: โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฐXโ”€โ”ค

In tandem with qml.registers, we've improved Wires by allowing for set-based combination operators. This new feature unlocks the ability to combine Wires instances in the following ways:

  • intersection with & or intersection(),
  • symmetric difference with ^ or symmetric_difference(),
  • union with | or union(), and
  • difference with - or difference().
>>> wires1 = Wires([1, 2, 3]) >>> wires2 = Wires([2, 3, 4]) >>> wires1.intersection(wires2) # or wires1 & wires2 Wires([2, 3])

For more information on using qml.registers in the wild, stay tuned for a demo in the coming days!

Quantum arithmetic operations ๐Ÿงฎ

Doing math on a quantum computer? It adds up ๐Ÿค .

Quantum arithmetic

Several new operator templates have been added to PennyLane that let you perform quantum arithmetic operations, including:

  • qml.Adder for in-place modular addition.

  • qml.PhaseAdder for in-place modular addition in the Fourier basis.

  • qml.Multiplier for in-place multiplication.

  • qml.OutAdder for out-place modular addition.

  • qml.OutMultiplier for out-place modular multiplication.

  • qml.ModExp for modular exponentiation.

Here is a comprehensive example that performs the following calculation: (2 + 1) * 3 mod 7 = 2 (or 010 in binary). Notice how great these templates are to use with qml.registers ๐Ÿคฉ!

dev = qml.device("default.qubit", shots=1) reg = qml.registers({ "x": 2, # |x>: stores the result of 2 + 1 = 3 "y": 2, # |y>: multiples x by 3 "result": 3, # stores the result of (2 + 1) * 3 m 7 = 2 "work": 2 # for qml.OutMultiplier }) @qml.qnode(dev) def circuit(): # In-place addition qml.BasisEmbedding(2, wires=reg["x"]) qml.Adder(1, x_wires=reg["x"]) # add 1 to wires [0, 1] # Out-place multiplication qml.BasisEmbedding(3, wires=reg["y"]) qml.OutMultiplier( reg["x"], reg["y"], reg["result"], work_wires=reg["work"], mod=7 ) return qml.sample(wires=reg["result"])
>>> circuit() array([0, 1, 0])

Converting noise models from Qiskit โ™ป๏ธ

We'll listen to anyone's noise ๐Ÿฆป.

Convert Qiskit noise models

In the last few releases, we've added substantial improvements and new features to the Pennylane-Qiskit plugin. With this release, a new qml.from_qiskit_noise function allows you to convert a Qiskit noise model into a PennyLane NoiseModel.

Here is a simple example with two quantum errors that add two different depolarizing errors based on the presence of different gates in the circuit:

import pennylane as qml import qiskit_aer.noise as noise error_1 = noise.depolarizing_error(0.001, 1) # 1-qubit noise error_2 = noise.depolarizing_error(0.01, 2) # 2-qubit noise noise_model = noise.NoiseModel() noise_model.add_all_qubit_quantum_error(error_1, ['rz', 'ry']) noise_model.add_all_qubit_quantum_error(error_2, ['cx'])
>>> qml.from_qiskit_noise(noise_model) NoiseModel({ OpIn(['RZ', 'RY']): QubitChannel(num_kraus=4, num_wires=1) OpIn(['CNOT']): QubitChannel(num_kraus=16, num_wires=2) })

Under the hood, PennyLane converts each quantum error in the Qiskit noise model into an equivalent qml.QubitChannel operator with the same canonical Kraus representation. Currently, noise models in PennyLane do not support readout errors. As such, those will be skipped during conversion if they are present in the Qiskit noise model.

Make sure to install or upgrade the PennyLane-Qiskit plugin via pip install pennylane-qiskit to access this new feature! Also be on the lookout for a demo on using noise models in PennyLane in the coming weeks after the release.

Substantial tree-traversal upgrades ๐ŸŒณ

Even monkeys can fall from trees, but not with v0.38 ๐Ÿ’.

With the last release of PennyLane, we introduced the "tree-traversal" mid-circuit measurement (MCM) method. The recursive way that this method was implemented had the unintended consequence of very deep stack calls for circuits with many MCMs, resulting in stack overflows in some cases.

With this release, we've redesigned the implementation of mcm_method="tree-traversal" into an iterative approach, which lets you have circuits with many, many mid-circuit measurements ๐Ÿ˜Œ.

In addition to that internal upgrade, the tree-traversal algorithm is now compatible with analytic-mode execution (shots=None)!

dev = qml.device("default.qubit") n_qubits = 5 @qml.qnode(dev, mcm_method="tree-traversal") def circuit(): for w in range(n_qubits): qml.Hadamard(w) for w in range(n_qubits - 1): qml.CNOT(wires=[w, w+1]) for w in range(n_qubits): m = qml.measure(w) qml.cond(m == 1, qml.RX)(0.1967 * (w + 1), w) return [qml.expval(qml.Z(w)) for w in range(n_qubits)]
>>> circuit() [tensor(0.00964158, requires_grad=True), tensor(0.03819446, requires_grad=True), tensor(0.08455748, requires_grad=True), tensor(0.14694258, requires_grad=True), tensor(0.2229438, requires_grad=True)]

Differentiable callbacks with Catalyst ๐Ÿ“ž

Your GPU is on the phone... again! They'd like you to differentiate and callback ๐Ÿ“ž

Differentiable GPU callbacks

In the previous release of Catalyst, we added support for accelerating classical code within qjit-compiled functions on GPUs with catalyst.accelerate. With this release, you can now differentiate through catalyst.accelerate!

import jax import jax.numpy as jnp @qml.qjit @catalyst.grad def f(x): expm = catalyst.accelerate(jax.scipy.linalg.expm, dev=jax.devices("gpu")[0]) return jnp.sum(expm(jnp.sin(x)) ** 2)
>>> x = jnp.array([[0.1, 0.2], [0.3, 0.4]]) >>> f(x) Array([[2.80120452, 1.67518663], [1.61605839, 4.42856163]], dtype=float64)

For more details, see the catalyst.accelerate documentation.

Improvements ๐Ÿ› ๏ธ

In addition to the new features listed above, this release contains a wide array of improvements and optimizations:

  • Molecules and Hamiltonians can now be constructed for all the elements present in the periodic table. This new feature is made possible by integrating with the basis-set-exchange package. If loading basis sets from basis-set-exchange is needed for your molecule, make sure that you pip install basis-set-exchange and set load_data=True.

    symbols = ['Ti', 'Ti'] geometry = np.array([[0.0, 0.0, -1.1967], [0.0, 0.0, 1.1967]], requires_grad=True) mol = qml.qchem.Molecule(symbols, geometry, load_data=True)
    >>> mol.n_electrons 44
  • A new template called qml.PrepSelPrep has been added that implements a block-encoding of a linear combination of unitaries. This operator acts as a nice wrapper for having to perform qml.StatePrep, qml.Select, and qml.adjoint(qml.StatePrep) in succession, which is quite common in many quantum algorithms. Here is an example showing the equivalence between using qml.PrepSelPrep and qml.StatePrep, qml.Select, and qml.adjoint(qml.StatePrep):

    coeffs = [0.3, 0.1] alphas = (np.sqrt(coeffs) / np.linalg.norm(np.sqrt(coeffs))) unitaries = [qml.X(2), qml.Z(2)] lcu = qml.dot(coeffs, unitaries) control = [0, 1] def my_prep_sel_prep(alphas, unitaries): qml.StatePrep(alphas, wires=control, pad_with=0) qml.Select(unitaries, control=control) qml.adjoint(qml.StatePrep)(alphas, wires=control, pad_with=0) @qml.qnode(qml.device("default.qubit")) def circuit(lcu, control, alphas, unitaries): qml.PrepSelPrep(lcu, control) qml.adjoint(my_prep_sel_prep)(alphas, unitaries) return qml.state()
    >>> import numpy as np >>> np.round(circuit(lcu, control, alphas, unitaries), decimals=2) [ 1.+0.j -0.+0.j -0.+0.j -0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
  • qml.pauli.group_observables now uses rustworkx colouring algorithms to solve the minimum clique cover problem, resulting in orders of magnitude performance improvements.

  • qml.StatePrep and qml.PauliRot are now much more performant with lightning.qubit.

Deprecations and breaking changes ๐Ÿ’”

As new things are added, outdated features are removed. To keep track of things in the deprecation pipeline, check out the deprecations page.

Here's a summary of what has changed in this release:

  • qml.from_qasm no longer removes measurements from the QASM code. Use measurements=[] to remove measurements from the original circuit.

  • qml.qinfo.classical_fisher and qml.qinfo.quantum_fisher have been deprecated. Instead, use qml.gradients.classical_fisher and qml.gradients.quantum_fisher.

  • The legacy devices default.qubit.{autograd,torch,tf,jax,legacy} have been deprecated. Instead, use default.qubit, as it now supports backpropagation through these backends.

  • The max_expansion argument and the expansion_strategy attribute in qml.QNode have been deprecated.

  • The expansion_strategy argument has been deprecated in all of qml.draw, qml.draw_mpl, and qml.specs. The level argument should be used instead.


These highlights are just scratching the surface โ€” check out the full release notes for PennyLane and Catalyst for more details.

Contributors โœ๏ธ

As always, this release would not have been possible without the hard work of our development team and contributors:

Tarun Kumar Allamsetty, Guillermo Alonso, Ali Asadi, Utkarsh Azad, Tonmoy T. Bhattacharya, Gabriel Bottrill, Thomas Bromley, Jack Brown, Ahmed Darwish, Astral Cai, Joey Carter, Alessandro Cosentino, Yushao Chen, Ahmed Darwish, Isaac De Vlugt, Diksha Dhawan, Amintor Dusko, Tarik El-Khateeb, Maja Franz, Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, Diego Guala, Austin Huang, Renke Huang, David Ittah, Josh Izaac, Soran Jahangiri, Tzung-Han Juang, Korbinian Kottmann, Ivana Kureฤiฤ‡, Christina Lee, Jorge Martinez de Lejarza, William Maxwell, Vincent Michaud-Rioux, Anurav Modak, Erick Ochoa Lopez, Lee J. O'Riordan, Mudit Pandey, Andrija Paurevic, Erik Schultheis, Kunwar Maheep Singh, Mehrdad Malekmohammadi, Romain Moyard, Shuli Shu, Nate Stemen, Raul Torres, Paul Haochen Wang, David Wierichs.

About the authors

Isaac De Vlugt
Isaac De Vlugt

Isaac De Vlugt

My job is to help manage the PennyLane and Catalyst feature roadmap... and spam lots of emojis in the chat ๐Ÿค 

Josh Izaac
Josh Izaac

Josh Izaac

Josh is a theoretical physicist, software tinkerer, and occasional baker. At Xanadu, he contributes to the development and growth of Xanaduโ€™s open-source quantum software products.

Diego Guala
Diego Guala

Diego Guala

Diego is a quantum scientist at Xanadu. His work is focused on supporting the development of the datasets service and PennyLane features.

Last modified:ย September 04, 2024

Related Blog Posts

PennyLane

PennyLane is an open-source software framework for quantum machine learning, quantum chemistry, and quantum computing, with the ability to run on all hardware. Built with โค๏ธ by Xanadu.

Stay updated with our newsletter

For researchers

  • Research
  • Features
  • Demos
  • Compilation
  • Datasets
  • Performance
  • Learn
  • Videos
  • Documentation
  • Teach

For learners

  • Learn
  • Codebook
  • Teach
  • Videos
  • Challenges
  • Demos
  • Compilation
  • Glossary

For developers

  • Features
  • Documentation
  • API
  • GitHub
  • Datasets
  • Demos
  • Compilation
  • Performance
  • Devices
  • Catalyst

ยฉ Copyright 2025 | Xanadu | All rights reserved

TensorFlow, the TensorFlow logo and any related marks are trademarks of Google Inc.

Privacy Policy|Terms of Service|Cookie Policy|Code of Conduct