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_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 newqml.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 forCNOT
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.
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.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!
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
a
ofm
can 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_inverses
andsingle_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 withqjit
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 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 newQNode
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 ofqml.clifford_t_decomposition
, which should now give less extraneousqml.T
gates.qml.MPSPrep
now has a gate decomposition. This enables its use with any device. Additionally, theright_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 whenwork_wires
are not used.
Improved drawing:
qml.draw_mpl
can now split deep circuits over multiple figures via amax_length
keyword argument.qml.draw
now re-displays wire labels at the start of each partitioned chunk when using themax_length
keyword argument.qml.draw
andqml.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 ofTensorFlow/Keras 2
, like Pytorch or JAX. - Executing
qml.specs
is 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
tape
andqtape
properties of QNode have been removed. Instead, use theqml.workflow.construct_tape
function. - The
gradient_fn
keyword argument inqml.execute
has been removed. Instead, it has been replaced withdiff_method
. qml.MultiControlledX
no longer accepts strings as control values and no longer has acontrol_wires
argument.- The
qml.qsvt_legacy
function has been removed. Please use theqml.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
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.