April 15, 2025
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.
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_decompsfunction. 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_resourcesfunction, 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.CNOTcan be verified as being available to use with the newqml.list_decompsfunction, 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.decomposetransform 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_decompsand the decomposition rule forCNOTthat 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ββ€ StateThis 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.
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 Bloqwires: 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! π€
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! π―
-
A new state-of-the-art state preparation technique based on QROM is now available with the
qml.QROMStatePreparationtemplate.Using
qml.QROMStatePreparationis 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!
The new qml.liealg module provides a variety of Lie algebra functionality:
-
Existing functionality, including
qml.lie_closure,qml.structure_constants, andqml.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 whenmatrix=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 withqml.liealg.cartan_decomp.
A variety of typically encountered involution functions are included in the module, such aseven_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
aofmcan be computed withqml.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 π¬
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_inversesandsingle_qubit_fusiontransforms. With program capture enabled (qjit(experimental_capture=True)),cancel_inverseswill dispatch to Catalyst's efficient implementation of the same transform (catalyst.passes.cancel_inverses). Conversely,single_qubit_fusionis only supported by PennyLane and will have to be applied by PennyLane, but the whole workflow can still be compiled and executed withqjitthanks 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
updatemethod that allows for re-configuring settings likediff_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 newQNodeobject:>>> print(circuit.diff_method) best >>> new_circuit = circuit.update(diff_method="parameter-shift") >>> print(new_circuit.diff_method) 'parameter-shift'
Decompositions:
qml.ops.sk_decompositionhas been improved to produce less gates for certain edge cases. This greatly impacts the performance ofqml.clifford_t_decomposition, which should now give less extraneousqml.Tgates.qml.MPSPrepnow has a gate decomposition. This enables its use with any device. Additionally, theright_canonicalize_mpsfunction has also been added to transform an MPS into its right-canonical form.- The
qml.QROMtemplate has been upgraded to decompose more efficiently whenwork_wiresare not used.
Improved drawing:
qml.draw_mplcan now split deep circuits over multiple figures via amax_lengthkeyword argument.qml.drawnow re-displays wire labels at the start of each partitioned chunk when using themax_lengthkeyword argument.qml.drawandqml.draw_mplcan 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.KerasLayerclass has been deprecated because Keras 2 is no longer actively maintained. Please consider using a different machine learning framework instead ofTensorFlow/Keras 2, like Pytorch or JAX. - Executing
qml.specsis now much more efficient with the removal of accessingnum_diagonalizing_gates. The calculation of this quantity is extremely expensive, and the definition is ambiguous for non-commuting observables. - The
tapeandqtapeproperties of QNode have been removed. Instead, use theqml.workflow.construct_tapefunction. - The
gradient_fnkeyword argument inqml.executehas been removed. Instead, it has been replaced withdiff_method. qml.MultiControlledXno longer accepts strings as control values and no longer has acontrol_wiresargument.- The
qml.qsvt_legacyfunction has been removed. Please use theqml.qsvtfunction 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
Exploring uncharted territory.
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 is a quantum scientist at Xanadu. His work is focused on supporting the development of the datasets service and PennyLane features.
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.