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

April 15, 2025

PennyLane v0.41 and Catalyst v0.11 released

Anton Naim Ibrahim

Anton Naim Ibrahim

Isaac De Vlugt

Isaac De Vlugt

Diego Guala

Diego Guala

Josh Izaac

Josh Izaac

PennyLane v0.41 and Catalyst v0.11 released

Seeking a quantum perspective? Let PennyLane v0.41 and Catalyst v0.11 paint the picture for you 🎨!

Contents

  • Resource-efficient decompositions πŸ”Ž
  • Qualtran integration 🧩
  • End-to-end sparse execution 🌌
  • QROM state preparation πŸ“–
  • Dynamical Lie algebras πŸ•“
  • Structured and dynamic programs with better Catalyst integration πŸ“₯
  • Improvements πŸ› οΈ
  • Deprecations and breaking changes πŸ’”
  • Contributors ✍️

Resource-efficient decompositions πŸ”Ž

Don't let your ideas decompose! πŸ₯€
A new, more efficient graph-based decomposition system is now available in PennyLane.

Resource-efficient decompositions

This experimental decomposition system offers more resource efficiency by traversing an internal graph structure that is weighted by the resources (e.g., gate counts) required to decompose down to a given set of gates.

Adding qml.decomposition.enable_graph() to the top of your program will enable this new system, unlocking the following features:

  • New decomposition rules can be globally added to operators in PennyLane with the new qml.add_decomps function. Creating a valid decomposition rule requires:

    • Defining a quantum function that represents the decomposition.
    • Adding resource requirements (gate counts) to the above quantum function by decorating it with the new qml.register_resources function, which requires a dictionary mapping operator types present in the quantum function to their number of occurrences.
    qml.decomposition.enable_graph() @qml.register_resources({qml.H: 2, qml.CZ: 1}) def my_cnot(wires, **_): qml.H(wires=wires[1]) qml.CZ(wires=wires) qml.H(wires=wires[1]) qml.add_decomps(qml.CNOT, my_cnot)

    This newly added rule for qml.CNOT can be verified as being available to use with the new qml.list_decomps function, which returns a list of every available decomposition rule to a given operator:

    >>> my_new_rule = qml.list_decomps(qml.CNOT)[-1] >>> print(my_new_rule) @qml.register_resources({qml.H: 2, qml.CZ: 1}) def my_cnot(wires, **_): qml.H(wires=wires[1]) qml.CZ(wires=wires) qml.H(wires=wires[1])

    Additional information for defining more complex decomposition rules can be found in the documentation for qml.register_resources.

  • The qml.transforms.decompose transform now offers the ability to inject new decomposition rules for operators via two new keyword arguments:

    • fixed_decomps: decomposition rules provided to this keyword argument will be used by the new system, bypassing all other decomposition rules that may exist for the relevant operators.
    • alt_decomps: decomposition rules provided to this keyword argument are alternative decomposition rules that the new system may choose if they're the most resource efficient.

    Each keyword argument must be assigned a dictionary that maps operator types to decomposition rules. Here is an example using fixed_decomps and the decomposition rule for CNOT that was created in the previous bullet point:

    qml.decomposition.enable_graph() @partial( qml.transforms.decompose, gate_set={qml.H, qml.S, qml.T, qml.CZ}, fixed_decomps={qml.CNOT: my_cnot}, ) @qml.qnode(qml.device("default.qubit")) def circuit(): qml.H(0) qml.CNOT(wires=[0, 1]) return qml.state()
    >>> print(qml.draw(circuit)()) 0: ──H─╭●───── State 1: ──H─╰Z──H── State

    This new system is something we will continue to develop in hopes of it becoming the default way of doing decompositions in PennyLane going forward! Keep your eyes peeled for more updates in the coming releases πŸ‘€!

Qualtran integration 🧩

Great art happens in collaboration 🀝. This version of PennyLane makes it easy to use Qualtran with PennyLane.

Two frames with PennyLane and Qualtran

The new PennyLane-Qualtran integration allows you to simulate many quantum subroutines available in Qualtran bloqs and to validate Qualtran circuits by simulating them in PennyLane.

The new qml.FromBloq class translates Qualtran bloqs into equivalent PennyLane operators. It requires two inputs:

  • bloq: an initialized Qualtran Bloq
  • wires: the wires the operator acts on

The following example applies a PennyLane Operator and Qualtran Bloq in the same circuit:

from qualtran.bloqs.basic_gates import CNOT dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(): qml.X(wires=0) qml.FromBloq(CNOT(), wires=[0, 1]) return qml.state()
>>> circuit() array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])

End-to-end sparse execution 🌌

Save on supplies, we don't need a giant canvas for tiny paintings. 🀏
PennyLane now supports sparse objects to save on memory and computational time! πŸ€‘

Large frame with the word sparse

Many real-world quantum operators, Hamiltonians, and initial states are sparse. PennyLane v0.41 adds functionality to use sparse computational objects in compressed-sparse-row (csr) format to represent matrices and states for savings on memory usage and computational time.

Try out the end-to-end sparse execution on default.qubit and lightning.qubit with these and other templates that now support sparse data structures: qml.StatePrep, qml.QubitUnitary, qml.BlockEncode, and qml.SWAP.

import scipy import numpy as np sparse_state = scipy.sparse.csr_array([0, 1, 0, 0]) mat = np.kron(np.identity(2**12), qml.X.compute_matrix()) sparse_mat = scipy.sparse.csr_array(mat) sparse_x = scipy.sparse.csr_array(qml.X.compute_matrix()) dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(): qml.StatePrep(sparse_state, wires=range(2)) for i in range(10): qml.H(i) qml.CNOT(wires=[i, i + 1]) qml.QubitUnitary(sparse_mat, wires=range(13)) qml.ctrl(qml.QubitUnitary(sparse_x, wires=0), control=1) return qml.state()

For a complete list of features with expanded sparse support, see the v0.41 release notes.

QROM state preparation πŸ“–

Quantum Read-Only Memory (QROM) isn't just useful for loading bitstrings 0️⃣1️⃣.
It can be leveraged to prepare entire quantum states! 🎯

QROM state preparation

  • A new state-of-the-art state preparation technique based on QROM is now available with the qml.QROMStatePreparation template.

    Using qml.QROMStatePreparation is analogous to using other state preparation techniques in PennyLane.

    state_vector = np.array([0.5, -0.5, 0.5, 0.5]) dev = qml.device("default.qubit") wires = qml.registers({"work_wires": 1, "prec_wires": 3, "state_wires": 2}) @qml.qnode(dev) def circuit(): qml.QROMStatePreparation( state_vector, wires["state_wires"], wires["prec_wires"], wires["work_wires"] ) return qml.state()
    >>> print(circuit()[:4].real) [ 0.5 -0.5 0.5 0.5]

Dynamical Lie algebras πŸ•“

Don't let time waste away 🫠... check out these dynamic features 😎
and let the new Lie algebra module πŸ€₯ tell you the truth πŸ’― about your quantum operators!

Dynamical Lie algebras

The new qml.liealg module provides a variety of Lie algebra functionality:

  • Existing functionality, including qml.lie_closure, qml.structure_constants, and qml.center <pennylane.center> have been revamped, and now live in the Lie algebra module.

  • Compute the dynamical Lie algebra from a set of generators with qml.lie_closure.
    This function accepts and outputs matrices when matrix=True.

    import pennylane as qml from pennylane import X, Y, Z, I n = 2 gens = [qml.X(0), qml.X(0) @ qml.X(1), qml.Y(1)] dla = qml.lie_closure(gens)
    >>> dla [X(0), X(0) @ X(1), Y(1), X(0) @ Z(1)]
  • Cartan decompositions, g = k + m, can be performed with qml.liealg.cartan_decomp.
    A variety of typically encountered involution functions are included in the module, such as even_odd_involution, concurrence_involution, A, AI, AII, AIII, BD, BDI, DIII, C, CI, CII.

    from pennylane.liealg import concurrence_involution k, m = qml.liealg.cartan_decomp(dla, concurrence_involution)
    >>> k, m ([Y(1)], [X(0), X(0) @ X(1), X(0) @ Z(1)])
  • The horizontal Cartan subalgebra a of m can be computed with qml.liealg.horizontal_cartan_subalgebra.

    from pennylane.liealg import horizontal_cartan_subalgebra newg, k, mtilde, a, new_adj = horizontal_cartan_subalgebra(k, m, return_adjvec=True)
    >>> newg.shape, k.shape, mtilde.shape, a.shape, new_adj.shape ((4, 4), (1, 4), (1, 4), (2, 4), (4, 4, 4))

Take some more time πŸ•“ to learn about all the Lie algebra functionality in the documentation for the qml.liealg module, and get into the theory with our demo: Introducing (dynamical) Lie algebras for quantum practitioners.

Structured and dynamic programs with better Catalyst integration πŸ“₯

Seamless integration with Catalyst incoming πŸ›¬

Program capture

We have been working on a new and experimental feature called program capture, which allows for compactly expressing structure and dynamism in hybrid workflows while also providing a smoother integration with just-in-time compilation frameworks like Catalyst (via the qjit decorator) and JAX-jit.

With this release, many of the core features of PennyLaneβ€”and more!β€”are available with program capture enabled by adding qml.capture.enable() to the top of your program. Here's a peek at what's new:

  • Classically processing mid-circuit measurement (MCM) values is now possible with program capture enabled and mcm_method="deferred", unlocking all sorts of new possibilities with hybrid programs like having MCM values as gate parameters:

    import jax import jax.numpy as jnp jax.config.update("jax_enable_x64", True) qml.capture.enable() @qml.qnode(qml.device("default.qubit", wires=3), mcm_method="deferred") def f(x): m0 = qml.measure(0) m1 = qml.measure(0) # classical processing on m0 and m1 a = jnp.sin(0.5 * jnp.pi * m0) phi = a - (m1 + 1) ** 4 qml.s_prod(x, qml.RX(phi, 0)) return qml.expval(qml.Z(0))
    >>> f(0.1) Array(0.00540302, dtype=float64)
  • Using Catalyst with PennyLane when transforms are present is now more seamless and efficient with program capture enabled.
    PennyLane transforms now automatically dispatch to the analogous Catalyst transforms if they exist.

    In the following example, we show a toy qjit'd workflow including the cancel_inverses and single_qubit_fusion transforms. With program capture enabled (qjit(experimental_capture=True)), cancel_inverses will dispatch to Catalyst's efficient implementation of the same transform (catalyst.passes.cancel_inverses). Conversely, single_qubit_fusion is only supported by PennyLane and will have to be applied by PennyLane, but the whole workflow can still be compiled and executed with qjit thanks to program capture 😎:

    import pennylane as qml dev = qml.device("lightning.qubit", wires=1) @qml.qjit(experimental_capture=True) def func(r1, r2): @qml.transforms.single_qubit_fusion @qml.transforms.cancel_inverses @qml.qnode(dev) def circuit(r1, r2): qml.Rot(*r1, wires=0) qml.Rot(*r2, wires=0) qml.RZ(r1[0], wires=0) qml.RZ(r2[0], wires=0) qml.Hadamard(wires=0) qml.Hadamard(wires=0) return qml.expval(qml.PauliZ(0)) return circuit(r1, r2)
    >>> r1 = jnp.array([0.1, 0.2, 0.3]) >>> r2 = jnp.array([0.4, 0.5, 0.6]) >>> func(r1, r2) Array(0.7872403, dtype=float64)

Check out the full release notes for a complete list of features!

Improvements πŸ› οΈ

Qnode execution:

  • QNodes now have an update method that allows for re-configuring settings like diff_method, mcm_method, and more. This allows for easier on-the-fly adjustments to workflows.
    After constructing a QNode,

    import pennylane as qml @qml.qnode(device=qml.device("default.qubit")) def circuit(): qml.H(0) qml.CNOT([0,1]) return qml.probs()

    its settings can be modified with update, which returns a new QNode object:

    >>> print(circuit.diff_method) best >>> new_circuit = circuit.update(diff_method="parameter-shift") >>> print(new_circuit.diff_method) 'parameter-shift'

Decompositions:

  • qml.ops.sk_decomposition has been improved to produce less gates for certain edge cases. This greatly impacts the performance of qml.clifford_t_decomposition, which should now give less extraneous qml.T gates.
  • qml.MPSPrep now has a gate decomposition. This enables its use with any device. Additionally, the right_canonicalize_mps function has also been added to transform an MPS into its right-canonical form.
  • The qml.QROM template has been upgraded to decompose more efficiently when work_wires are not used.

Improved drawing:

  • qml.draw_mpl can now split deep circuits over multiple figures via a max_length keyword argument.
  • qml.draw now re-displays wire labels at the start of each partitioned chunk when using the max_length keyword argument.
  • qml.draw and qml.draw_mpl can now reuse lines for different classical wires, saving whitespace without changing the represented circuit.

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 qml.qnn.KerasLayer class has been deprecated because Keras 2 is no longer actively maintained. Please consider using a different machine learning framework instead of TensorFlow/Keras 2, like Pytorch or JAX.
  • Executing qml.specs is now much more efficient with the removal of accessing num_diagonalizing_gates. The calculation of this quantity is extremely expensive, and the definition is ambiguous for non-commuting observables.
  • The tape and qtape properties of QNode have been removed. Instead, use the qml.workflow.construct_tape function.
  • The gradient_fn keyword argument in qml.execute has been removed. Instead, it has been replaced with diff_method.
  • qml.MultiControlledX no longer accepts strings as control values and no longer has a control_wires argument.
  • The qml.qsvt_legacy function has been removed. Please use the qml.qsvt function instead.

These highlights are just the first layer πŸŽ¨πŸ–ŒοΈπŸ–ΌοΈ β€” we also just released a Quantum Compilation wiki page, which includes overviews and implementation details for industry-standard quantum compilation techniques ✨!

For more details on this release, check out the full release notes for PennyLane and Catalyst.

Contributors ✍️

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

Runor Agbaire, Catalina Albornoz, Guillermo Alonso, Daniela Angulo, Ali Asadi, Utkarsh Azad, Saeed Bohloul, Astral Cai, Joey Carter, Henry Chang, Yushao Chen, Isaac De Vlugt, Diksha Dhawan, Amintor Dusko, Tarik El-Khateeb, Lillian M.A. Frederiksen, Pietropaolo Frisoni, Marcus GisslΓ©n, Zach Goldthorpe, Diego Guala, Sengthai Heng, Austin Huang, David Ittah, Josh Izaac, Soran Jahangiri, Korbinian Kottmann, Rohan Nolan Lasrado, Christina Lee, Joseph Lee, Dantong Li, Mehrdad Malekmohammadi, William Maxwell, Anton Naim Ibrahim, Rashid N H M, Luis Alfredo NuΓ±ez Meneses, Erick Ochoa Lopez, Lee J. O'Riordan, Mudit Pandey, Andrija Paurevic, Justin Pickering, Alex Preciado, Jay Soni, Shuli Shu, Raul Torres, Paul Haochen Wang, David Wierichs, Jake Zaia.

About the authors

Anton Naim Ibrahim
Anton Naim Ibrahim

Anton Naim Ibrahim

Exploring uncharted territory.

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 🀠

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.

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.

Last modified: April 15, 2025

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