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

July 10, 2024

PennyLane v0.37 and Catalyst v0.7 released

Isaac De Vlugt

Isaac De Vlugt

Thomas Bromley

Thomas Bromley

Josh Izaac

Josh Izaac

Diego Guala

Diego Guala

Cool off from the summer heat with these new features in PennyLane v0.37 and Catalyst v0.7!

Contents

  • A new tensor network and matrix product state device 🦾
  • Add noise models to your quantum circuits πŸ“Ί
  • Debugging quantum algorithms πŸš«πŸ›
  • Accelerate classical processing on GPUs with Catalyst and qjit πŸš—
  • Just-in-time compile workflows with the Qrack GPU simulator 🐎
  • Better control over when drawing and specs take place πŸŽ›οΈ
  • Contributions from UnitaryHACK 2024 πŸ’›
  • Improvements πŸ› οΈ
  • Deprecations and breaking changes πŸ’”
  • Contributors ✍️

A new tensor network and matrix product state device 🦾

Wide circuits, meet default.tensor 😀

Tensor networks and matrix product states in PennyLane

A new default.tensor device is now available for performing tensor network (TN) and matrix product state (MPS) simulations of quantum circuits using the quimb backend.

This device is meant to simulate very wide circuits, where the backend method used can be set when instantiating the default.tensor device with the method keyword: method = "tn" (tensor network) or method = "mps" (matrix product state).

An important feature of the MPS simulation method is that it allows for a tradeoff between simulation speed and accuracy by setting the max_bond_dim argument when instantiating the device:

import pennylane as qml import numpy as np dev = qml.device("default.tensor", max_bond_dim=5) n_layers = 10 n_wires = 10 initial_shape, weights_shape = qml.SimplifiedTwoDesign.shape(n_layers, n_wires) np.random.seed(1967) initial_layer_weights = np.random.random(initial_shape) weights = np.random.random(weights_shape) @qml.qnode(dev) def f(): qml.SimplifiedTwoDesign(initial_layer_weights, weights, range(n_wires)) return qml.expval(qml.Z(0))
>>> f() -0.13818212192515317

Learn more about default.tensor and how to configure it by visiting our how-to guide!

Add noise models to your quantum circuits πŸ“Ί

Make some πŸ’₯ NOISE πŸ’₯

Noise_(Blog)

Support for building noise models and applying them to a quantum circuit has been added via the NoiseModel class and an add_noise transform.

Under the hood, PennyLane's approach to noise models is insertion-based, meaning that noise is included by inserting additional operators (gates or channels) that describe the noise into the quantum circuit itself. Creating a NoiseModel boils down to defining Boolean conditions under which specific noisy operations are inserted. There are several ways to specify conditions for adding noisy operations:

  • qml.noise.op_eq(op): if the operator op is encountered in the circuit, add noise.

  • qml.noise.op_in(ops): if any operators in ops are encountered in the circuit, add noise.

  • qml.noise.wires_eq(wires): if an operator is applied to wires, add noise.

  • qml.noise.wires_in(wires): if an operator is applied to any wire in wires, add noise.

  • custom noise conditions: custom conditions can be defined as functions decorated with qml.BooleanFn that return a Boolean value. For example, the following function will insert noise if a qml.RY operator is encountered with an angle of rotation that is smaller than 0.5:

    @qml.BooleanFn def c0(op): return isinstance(op, qml.RY) and op.parameters[0] < 0.5

Conditions can also be combined together with &, and, |, etc. Once the conditions under which noise is to be inserted have been stated, we can specify exactly what noise is inserted with the following:

  • qml.noise.partial_wires(op): insert op on the wires that are specified by the condition that triggers adding this noise

  • custom noise operations: custom noise can be specified by defining a standard quantum function like below.

    def n0(op, **kwargs): qml.RY(op.parameters[0] * 0.05, wires=op.wires)

With that, we can create a qml.NoiseModel object whose argument must be a dictionary mapping conditions to noise:

c1 = qml.noise.op_eq(qml.X) & qml.noise.wires_in([0, 1]) n1 = qml.noise.partial_wires(qml.AmplitudeDamping, 0.4) noise_model = qml.NoiseModel({c0: n0, c1: n1})
>>> noise_model NoiseModel({ BooleanFn(c0): n0 OpEq(PauliX) | WiresIn([0, 1]): AmplitudeDamping(gamma=0.4) })

The noise model created can then be added to a QNode with qml.add_noise:

dev = qml.device("default.mixed", wires=3) @qml.qnode(dev) def circuit(): qml.Y(0) qml.CNOT([0, 1]) qml.RY(0.3, wires=2) # triggers c0 qml.X(1) # triggers c1 return qml.state()
>>> print(qml.draw(circuit)()) 0: ──Y────────╭●───── State 1: ───────────╰X──X── State 2: ──RY(0.30)──────── State >>> circuit = qml.add_noise(circuit, noise_model) >>> print(qml.draw(circuit)()) 0: ──Y────────╭●──────────────────────────────────── State 1: ───────────╰X─────────X──AmplitudeDamping(0.40)── State 2: ──RY(0.30)──RY(0.01)───────────────────────────── State

To learn more about this new functionality, check out our noise module documentation and keep your eyes peeled for an in-depth guide in PennyLane Demos!

Debugging quantum algorithms πŸš«πŸ›

The ultimate bug zapper ⚑️ (batteries not included 🀠)

Debugging quantum algorithms in PennyLane

The new PennyLane quantum debugger allows pausing simulations via the qml.breakpoint() command and provides tools for analyzing quantum circuits during execution.

This includes the monitoring of the circuit via measurements using qml.debug_state(), qml.debug_probs(), qml.debug_expval(), and qml.debug_tape(), stepping through the operations in a quantum circuit, and interactively adding operations during execution.

Including qml.breakpoint() in a circuit will cause the simulation to pause during execution and bring up the interactive console (even in a notebook). For example, consider the following code in a Python file called script.py:

@qml.qnode(qml.device('default.qubit', wires=(0,1,2))) def circuit(x): qml.Hadamard(wires=0) qml.CNOT(wires=(0,2)) qml.breakpoint() qml.RX(x, wires=1) qml.RY(x, wires=2) qml.breakpoint() return qml.sample() circuit(1.2345)

Upon executing script.py, the simulation pauses at the first breakpoint:

> /Users/your/path/to/script.py(8)circuit() -> qml.RX(x, wires=1) [pldb]

While debugging, we can look at the current quantum state with qml.debug_state() (similar to qml.state()):

[pldb] print(qml.debug_state()) [0.70710678+0.j 0. +0.j 0. +0.j 0. +0.j 1. +0.j 0.70710678+0.j 0. +0.j 0. +0.j]

Other debugger functions like qml.debug_probs() and qml.debug_expval() also function like their simulation counterparts (qml.probs and qml.expval, respectively) and are described in more detail in the debugger documentation

Stay tuned for an in-depth demonstration on using this feature with real-world examples!

Accelerate classical processing on GPUs with Catalyst and qjit πŸš—

Your GPU is on the phone. They'd like you to callback πŸ“ž

In the previous release, we added support for callbacks β€” seamlessly integrate arbitrary classical code within a quantum just-in-time (QJIT) compiled function.

This release, they have gotten even better; you can accelerate classical code within qjit-compiled functions on GPUs with catalyst.accelerate and differentiate through arbitrary callbacks!

catalyst.accelerate allows you to seamlessly mix quantum and classical GPU computation. Simply wrap your classical, jax.jit-compatible functionality, and specify the device you want it to execute on:

@catalyst.accelerate(dev=jax.devices("gpu")[0]) def gpu_fn(x): return jnp.sin(x) ** 2 dev = qml.device("lightning.qubit", wires=2) @qml.qnode(dev) def circuit(x): qml.RX(x, wires=0) return qml.expval(qml.PauliZ(0)) @qml.qjit def hybrid_fn(x): y = circuit(jnp.sin(x)) z = gpu_fn(y) # gpu_fn will be executed on a GPU! return jnp.cos(z)

For more details, see the catalyst.accelerate documentation.

In addition, catalyst.pure_callback and catalyst.debug.callback now support autodifferentiation. catalyst.debug.callback can be used in any function that will be differentiated, while catalyst.pure_callback allows you to define custom VJP rules that will be registered with the Catalyst compiler via pure_callback.fwd and pure_callback.bwd. See the catalyst.pure_callback documentation for more details on usage.

Just-in-time compile workflows with the Qrack GPU simulator 🐎

!!!qjit on Qrack!!!

Qrack, a GPU-accelerated simulator supported by the Unitary Fund that supports dynamical transitions between GPU-based and CPU-based simulation techniques and approximations for maximum execution speed now supports workflows that have been compiled with @qml.qjit and Catalyst.

Simply pip install pennylane-qrack, and use the Qrack simulator device within a qjit-compiled function:

qubits = 4 dev = qml.device("qrack.simulator", qubits, shots=8) @qml.qjit(autograph=True) @qml.qnode(dev) def circuit(): qml.Hadamard(0) for i in range(1, qubits): qml.CNOT(wires=[i - 1, i]) return qml.sample(wires=range(qubits))

By using Qrack with Catalyst and qjit, take advantage of just-in-time compilation performance improvements and features such as AutoGraph and dynamic quantum control flow, alongside Qrack-supported simulation methods such as near-Clifford simulation.

For more details, see our tutorial on Qrack and Catalyst, as well as the PennyLane-Qrack documentation.

Better control over when drawing and specs take place πŸŽ›οΈ

It is now possible to control the stage at which qml.draw, qml.draw_mpl, and qml.specs occur within a QNode's transform program.

Consider the following circuit, which has multiple transforms applied:

@qml.transforms.cancel_inverses @qml.transforms.merge_rotations @qml.qnode(qml.device("default.qubit")) def f(): qml.Hadamard(0) qml.Y(0) qml.RX(0.4, 0) qml.RX(-0.4, 0) qml.Y(0) return qml.expval(qml.X(0) + 2 * qml.Y(0))

We can specify a level value when using qml.draw().

  • level = 0: the original circuit with no transforms applied
>>> print(qml.draw(f, level = 0)()) 0: ──H──Y──RX(0.40)──RX(-0.40)──Y── <X+(2.00*Y)>
  • level = 1: apply the first transform (merge_rotations)
>>> print(qml.draw(f, level = 1)()) 0: ──H──Y──Y── <X+(2.00*Y)>
  • level = 2: apply the second transform (cancel_inverses)
>>> print(qml.draw(f, level = 2)()) 0: ──H── <X+(2.00*Y)>

Contributions from UnitaryHACK 2024 πŸ’›

As always, UnitaryHACK was a smooth, fun, and productive experience for everyone involved. Congrats to to this year's bounty hunters and Unitary Fund for another great event πŸŽ‰! Here are the contributions made to PennyLane:

  • default.clifford now supports arbitrary state-based measurements with qml.Snapshot.

  • The implementation for qml.assert_equal has been updated for Operator, Controlled, Adjoint, Pow, Exp, SProd, ControlledSequence, Prod, Sum, Tensor and Hamiltonian instances.

  • qml.from_qasm now supports the ability to convert mid-circuit measurements from OpenQASM 2 code, and it can now also take an optional argument to specify a list of measurements to be performed at the end of the circuit, just like qml.from_qiskit.

  • A new operator called qml.QutritChannel is now availaible, which allows for specifying noise on the default.qutrit.mixed device using a collection of (3Γ—3) Kraus matrices.

Improvements πŸ› οΈ

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

  • catalyst.value_and_grad returns both the result of a function and its gradient with a single forward and backwards pass. This can be more efficient and reduce overall quantum executions, compared to separately executing the function and then computing its gradient.

  • Catalyst now supports the 'dynamic one-shot' method for simulating circuits with mid-circuit measurements, which may be advantageous for circuits with many mid-circuit measurements executed for few shots. Select this option via the mcm_method="one-shot" argument of the QNode (Catalyst's existing method for simulating mid-circuit measurements remains available via mcm_method="single-branch-statistics"). For more details, see the dynamic quantum circuit documentation.

  • AutoGraph, which allows standard Python control flow to be used with @qjit-compiled dynamic quantum circuits, comes with a host of new features:

    • Single-index in-place array assignments written in standard Python syntax x[i] = y can now be captured and automatically converted into the JAX-compatible form x = x.at(i).set(y):

      @qml.qjit(autograph=True) def f(array): result = jnp.ones(array.shape, dtype=array.dtype) for i, x in enumerate(array): result[i] = result[i] + x * 3 return result
      >>> f(jnp.array([-0.1, 0.12, 0.43, 0.54])) array([0.7 , 1.36, 2.29, 2.62])
    • The decorator catalyst.disable_autograph allows one to disable AutoGraph from auto-converting specific external functions when called within a qjit-compiled function with autograph=True.

  • Catalyst now supports qjit-compiled functions that have dynamically shaped arrays in control-flow primitives. Arrays with dynamic shapes can now be used with for_loop, while_loop, and cond primitives:

    @qml.qjit def f(shape): a = jnp.ones([shape], dtype=float) @for_loop(0, 10, 2) def loop(i, a): return a + i return loop(a)
    >>> f(3) array([21., 21., 21.])
  • QChem Hamiltonians can now be converted to and from OpenFermion with qml.to_openfermion and qml.from_openfermion.

  • The QROM algorithm is now available in PennyLane with qml.QROM. This template allows you to enter classical data in the form of bitstrings. Stay tuned in the coming weeks after this release for a PennyLane Demo on using qml.QROM in the wild!

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:

  • The simplify argument in qml.Hamiltonian and qml.ops.LinearCombination has been deprecated. Instead, qml.simplify can be called on the constructed operator.

  • The default behaviour of qml.from_qasm() to remove measurements in the QASM code has been deprecated. Use measurements=[] to keep this behaviour or measurements=None to keep the measurements from the QASM code.

  • qml.load has been removed in favour of more specific functions, such as qml.from_qiskit, etc.

  • qml.from_qasm_file has been removed. The user can open files and load their content using qml.from_qasm.


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-Linaje, Ali Asadi, Utkarsh Azad, Ludmila Botelho, Gabriel Bottrill, Thomas Bromley, Jack Brown, Astral Cai, Ahmed Darwish, Isaac De Vlugt, Diksha Dhawan, Amintor Dusko, Tarik El-Khateeb, Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, Diego Guala, Austin Huang, David Ittah, Soran Jahangiri, Rohan Jain, Tzung-Han Juang, Mashhood Khan, Korbinian Kottmann, Ivana KurečiΔ‡, Christina Lee, Mehrdad Malekmohammadi, Vincent Michaud-Rioux, Sergei Mironov, Erick Ochoa, Lee James O'Riordan, Mudit Pandey, Kenya Sakka, Shuli Shu, Jay Soni, Raul Torres, Kazuki Tsuoka, Daria Van Hende, Haochen Paul 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 🀠

Thomas Bromley
Thomas Bromley

Thomas Bromley

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:Β August 14, 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