New features since last release
A brand new resource estimation module 📖
A new toolkit dedicated to resource estimation is now available in the pennylane.estimator
module! The functionality therein is designed to rapidly and flexibly estimate the quantum resources required to execute programs written at different levels of abstraction. This new module includes the following features:
-
A new
pennylane.estimator.estimate.estimate
function allows users to estimate the quantum resources required to execute a circuit or operation with respect to a given gate set and configuration. (#8203) (#8205) (#8275) (#8227) (#8279) (#8288) (#8311) (#8313) (#8360)The
pennylane.estimator.estimate.estimate
function can be used on circuits written at different levels of detail to get high-level estimates of gate counts and additional wires fast. For workflows that are already defined in detail, like executable QNodes, thepennylane.estimator.estimate.estimate
function works as follows:import pennylane as qml import pennylane.estimator as qre dev = qml.device("null.qubit") @qml.qnode(dev) def circ(): for w in range(2): qml.Hadamard(wires=w) qml.CNOT(wires=[0,1]) qml.RX(1.23*np.pi, wires=0) qml.RY(1.23*np.pi, wires=1) qml.QFT(wires=[0, 1, 2]) return qml.state()
>>> res = qre.estimate(circ)() >>> print(res) --- Resources: --- Total wires: 3 algorithmic wires: 3 allocated wires: 0 zero state: 0 any state: 0 Total gates : 408 'T': 396, 'CNOT': 9, 'Hadamard': 3
If exact argument values and other details to operators are unknown or not available,
pennylane.estimator.estimate.estimate
can also be used on new lightweight representations of PennyLane operations that require minimal information to obtain high-level estimates. As part of this release, many operations in PennyLane now have a corresponding lightweight version that inherits from a new class calledpennylane.estimator.resource_operator.ResourceOperator
, which can be found in thepennylane.estimator
module.For example, the lightweight representation of
QFT
isqre.QFT
. By simply specifying the number of wires it acts on, we can obtain resource estimates:>>> qft = qre.QFT(num_wires=3) >>> res = qre.estimate(qft) >>> print(res) --- Resources: --- Total wires: 3 algorithmic wires: 3 allocated wires: 0 zero state: 0 any state: 0 Total gates : 408 'T': 396, 'CNOT': 9, 'Hadamard': 3
One can create a circuit comprising these operations with similar syntax as defining a QNode, but with far less detail. Here is an example of a circuit with 50 (logical) algorithmic qubits, which includes a
pennylane.estimator.templates.QROMStatePreparation
acting on 48 qubits. Defining this state preparation for execution would require a state vector of length $2^{48}$ (seeqml.QROMStatePreparation
), but we are able to estimate the required resources with only metadata, bypassing this computational barrier. Even at this scale, the resource estimate is computed in a fraction of a second!def my_circuit(): qre.QROMStatePreparation(num_state_qubits=48) for w in range(2): qre.Hadamard(wires=w) qre.QROM(num_bitstrings=32, size_bitstring=8, restored=False) qre.CNOT(wires=[0,1]) qre.RX(wires=0) qre.RY(wires=1) qre.QFT(num_wires=30) return
>>> res = qre.estimate(my_circuit)() >>> print(res) --- Resources: --- Total wires: 129 algorithmic wires: 50 allocated wires: 79 zero state: 71 any state: 8 Total gates : 2.702E+16 'Toffoli': 1.126E+15, 'T': 5.751E+4, 'CNOT': 2.027E+16, 'X': 2.252E+15, 'Z': 32, 'S': 64, 'Hadamard': 3.378E+15
Here is a summary of the lightweight operations made available in this release. A complete list can be found in the
pennylane.estimator
module.-
pennylane.estimator.ops.Identity
,pennylane.estimator.ops.GlobalPhase
, and various non-parametric operators and single-qubit parametric operators. (#8240) (#8242) (#8302) - Various controlled single and multi qubit operators. (#8243)
-
pennylane.estimator.ops.Controlled
, andpennylane.estimator.ops.Adjoint
as symbolic operators. (#8252) (#8349) -
pennylane.estimator.ops.Pow
,pennylane.estimator.ops.Prod
,pennylane.estimator.ops.ChangeOpBasis
, and parametric multi-qubit operators. (#8255) - Templates including
pennylane.estimator.templates.SemiAdder
,pennylane.estimator.templates.QFT
,pennylane.estimator.templates.AQFT
,pennylane.estimator.templates.BasisRotation
,pennylane.estimator.templates.Select
,pennylane.estimator.templates.QROM
,pennylane.estimator.templates.SelectPauliRot
,pennylane.estimator.templates.QubitUnitary
,pennylane.estimator.templates.ControlledSequence
,pennylane.estimator.templates.QPE
,pennylane.estimator.templates.IterativeQPE
,pennylane.estimator.templates.MPSPrep
,pennylane.estimator.templates.QROMStatePreparation
,pennylane.estimator.templates.UniformStatePrep
,pennylane.estimator.templates.AliasSampling
,pennylane.estimator.templates.IntegerComparator
,pennylane.estimator.templates.SingleQubitComparator
,pennylane.estimator.templates.TwoQubitComparator
,pennylane.estimator.templates.RegisterComparator
,pennylane.estimator.templates.SelectTHC
,pennylane.estimator.templates.PrepTHC
, andpennylane.estimator.templates.QubitizeTHC
. (#8300) (#8305) (#8309)
For defining your own customized lightweight resource operations that integrate with features in the
pennylane.estimator
module, check out the documentation forpennylane.estimator.resource_operator.ResourceOperator
. -
-
Users can define customized configurations to be used during resource estimation using the new
pennylane.estimator.resource_config.ResourceConfig
class. This enables the seamless analysis of tradeoffs between resources required and quantities like individual gate precisions or different gate decompositions. (#8259)In the following example, a
pennylane.estimator.resource_config.ResourceConfig
is used to modify the default precision of single qubit rotations, andT
counts are compared between different configurations.def my_circuit(): qre.RX(wires=0) qre.RY(wires=1) qre.RZ(wires=2) return my_rc = qre.ResourceConfig() res1 = qre.estimate(my_circuit, config=my_rc)() my_rc.set_single_qubit_rot_precision(1e-2) res2 = qre.estimate(my_circuit, config=my_rc)()
>>> t1 = res1.gate_counts['T'] >>> t2 = res2.gate_counts['T'] >>> print(t1, t2) 132 51
-
Hamiltonians are often both expensive to compute and to analyze, but the amount of information required to estimate the resources of Hamiltonian simulation can be surprisingly small in comparison. The
pennylane.estimator.compact_hamiltonian.CDFHamiltonian
,pennylane.estimator.compact_hamiltonian.THCHamiltonian
,pennylane.estimator.compact_hamiltonian.VibronicHamiltonian
, andpennylane.estimator.compact_hamiltonian.VibrationalHamiltonian
classes were added to store the metadata of the Hamiltonian of a quantum system pertaining to resource estimation. In addition, several resource templates were added that are related to the Suzuki-Trotter method for Hamiltonian simulation, includingpennylane.estimator.templates.TrotterProduct
,pennylane.estimator.templates.TrotterCDF
,pennylane.estimator.templates.TrotterTHC
,pennylane.estimator.templates.TrotterVibronic
, andpennylane.estimator.templates.TrotterVibrational
. (#8303)Here's a simple example of resource estimation for the simulation of a
pennylane.estimator.compact_hamiltonian.CDFHamiltonian
, where we only need to specify two integer arguments (num_orbitals
andnum_fragments
) to get resource estimates:>>> cdf_ham = qre.CDFHamiltonian(num_orbitals=4, num_fragments=4) >>> res = qre.estimate(qre.TrotterCDF(cdf_ham, num_steps=1, order=2)) >>> print(res) --- Resources: --- Total wires: 8 algorithmic wires: 8 allocated wires: 0 zero state: 0 any state: 0 Total gates : 2.238E+4 'T': 2.075E+4, 'CNOT': 448, 'Z': 336, 'S': 504, 'Hadamard': 336
-
In addition to the
pennylane.estimator.resource_operator.ResourceOperator
class mentioned above, the scalability of the resource estimation functionality in this release is owed to the following new internal classes:-
pennylane.estimator.resources_base.Resources
: A container for counts and other metadata of quantum resources. (#8205) -
pennylane.estimator.resource_operator.GateCount
: A class to represent a gate and its number of occurrences in a circuit or decomposition. -
pennylane.estimator.resource_operator.CompressedResourceOp
: A lightweight class corresponding to an operator type alongside its parameters. (#8227) - The
pennylane.estimator.wires_manager.WireResourceManager
,pennylane.estimator.wires_manager.Allocate
, andpennylane.estimator.wires_manager.Deallocate
classes, which were added to manage and track wire usage during resource estimation and withinpennylane.estimator.resource_operator.ResourceOperator
definitions. (#8203)
-
The resource estimation tools in the pennylane.estimator
module were originally prototyped in the pennylane.labs
module. Check it out too for the latest cutting-edge research functionality!
Dynamic wire allocation 🎁
-
Wires can now be dynamically allocated and deallocated in quantum functions with the
pennylane.allocate
andpennylane.deallocate
functions. These features unlock many important applications that rely on smart and efficient handling of wires, such as decompositions of gates that require auxiliary wires and logical patterns in subroutines that benefit from having dynamic wire management.(#7718) (#8151) (#8163) (#8179) (#8198) (#8381)
The
pennylane.allocate
function can accept three arguments that dictate how dynamically allocated wires are handled:num_wires
: the number of wires to dynamically allocate.state = "zero"/"any"
: the initial state that the dynamically allocated wires are requested to be in. Currently, supported values are"zero"
(initialize in the all-zero state) or"any"
(any arbitrary state).restored = True/False
: a user-guarantee that the allocated wires will be restored to their original state (True
) or not (False
) when those wires are deallocated.
The recommended way to safely allocate and deallocate wires is to use
pennylane.allocate
as a context manager:import pennylane as qml @qml.qnode(qml.device("default.qubit")) def circuit(): qml.H(0) qml.H(1) with qml.allocate(2, state="zero", restored=False) as new_wires: qml.H(new_wires[0]) qml.H(new_wires[1]) return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)()) 0: ──H───────────────────────┤ <Z> 1: ──H───────────────────────┤ <DynamicWire>: ─╭Allocate──H─╭Deallocate─┤ <DynamicWire>: ─╰Allocate──H─╰Deallocate─┤
As illustrated, using
pennylane.allocate
as a context manager ensures that allocation and safe deallocation are controlled within a localized scope. Equivalenty,pennylane.allocate
can be used in-line along withpennylane.deallocate
for manual handling:new_wires = qml.allocate(2, state="zero", restored=False) qml.H(new_wires[0]) qml.H(new_wires[1]) qml.deallocate(new_wires)
For more complex dynamic allocation in circuits, PennyLane will resolve the dynamic allocation calls in the most resource-efficient manner before sending the program to the device. Consider the following circuit, which contains two dynamic allocations within a
for
loop.@qml.qnode(qml.device("default.qubit"), mcm_method="tree-traversal") def circuit(): qml.H(0) for i in range(2): with qml.allocate(1, state="zero", restored=True) as new_qubit1: with qml.allocate(1, state="any", restored=False) as new_qubit2: m0 = qml.measure(new_qubit1[0], reset=True) qml.cond(m0 == 1, qml.Z)(new_qubit2[0]) qml.CNOT((0, new_qubit2[0])) return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)()) 0: ──H─────────────────────╭●───────────────────────╭●─────────────┤ <Z> <DynamicWire>: ──Allocate──┤↗│ │0⟩────│──────────Deallocate────│──────────────┤ <DynamicWire>: ──Allocate───║────────Z─╰X─────────Deallocate────│──────────────┤ <DynamicWire>: ─────────────║────────║──Allocate──┤↗│ │0⟩──────│───Deallocate─┤ <DynamicWire>: ─────────────║────────║──Allocate───║──────────Z─╰X──Deallocate─┤ ╚════════╝ ╚══════════╝
The user-level circuit drawing shows four separate allocations and deallocations (two per loop iteration). However, the circuit that the device receives gets automatically compiled to only use
two additional wires (wires labelled1
and2
in the diagram below). This is due to the fact thatnew_qubit1
andnew_qubit2
can both be reused after they've been deallocated in the first iteration of thefor
loop:>>> print(qml.draw(circuit, level="device")()) 0: ──H───────────╭●──────────────╭●─┤ <Z> 1: ──┤↗│ │0⟩────│───┤↗│ │0⟩────│──┤ 2: ───║────────Z─╰X───║────────Z─╰X─┤ ╚════════╝ ╚════════╝
Additionally,
pennylane.allocate
andpennylane.deallocate
work withpennylane.qjit
with some restrictions.
Resource tracking with Catalyst 🧾
-
Users can now use the
pennylane.specs
function to track the exact resources of programs compiled withpennylane.qjit
! This new feature is currently only supported when usinglevel="device"
. (#8202)from functools import partial gateset = {qml.H, qml.S, qml.CNOT, qml.T, qml.RX, qml.RY, qml.RZ} @qml.qjit @partial(qml.transforms.decompose, gate_set=gateset) @qml.qnode(qml.device("null.qubit", wires=100)) def circuit(): qml.QFT(wires=range(100)) qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) qml.OutAdder( x_wires=range(10), y_wires=range(10,20), output_wires=range(20,31) ) return qml.expval(qml.Z(0) @ qml.Z(1)) circ_specs = qml.specs(circuit, level="device")()
>>> print(circ_specs['resources']) num_wires: 100 num_gates: 138134 depth: 90142 shots: Shots(total=None) gate_types: {'CNOT': 55313, 'RZ': 82698, 'Hadamard': 123} gate_sizes: {2: 55313, 1: 82821}
-
The
pennylane.specs
function now accepts acompute_depth
keyword argument, which is set toTrue
by default. Since depth computation is usually the most expensive resource to calculate, making it optional can increase the performance ofpennylane.specs
when depth is not a desired resource to calculate. (#7998) (#8042)
ZX Calculus transforms 🍪
-
A new set of transforms enable ZX calculus-based circuit optimization. These transforms make it easy to implement advanced compilation techniques that use the ZX calculus graphical language to reduce T-gate counts of Clifford + T circuits, optimize phase polynomials, and reduce the number of gates in non-Clifford circuits. (#8025) (#8029) (#8088) (#7747) (#8201)
These transforms include:
-
pennylane.transforms.zx.optimize_t_count
: reduces the number ofT
gates in a Clifford + T circuit by applying a sequence of passes that combine ZX-based commutation and cancellation rules and the Third Order Duplicate and Destroy (TODD) algorithm. -
pennylane.transforms.zx.todd
: reduces the number ofT
gates in a Clifford + T circuit by using the TODD algorithm. -
pennylane.transforms.zx.reduce_non_clifford
: reduces the number of non-Clifford gates in a circuit by applying a combination of phase gadgetization strategies and Clifford gate simplification rules. -
pennylane.transforms.zx.push_hadamards
: reduces the number of large phase-polynomial blocks in a phase-polynomial + Hadamard circuit by pushing Hadamard gates as far as possible to one side.
As an example, consider the following circuit:
import pennylane as qml dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(): qml.T(0) qml.CNOT([0, 1]) qml.S(0) qml.T(0) qml.T(1) qml.CNOT([0, 2]) qml.T(1) return qml.state()
>>> print(qml.draw(circuit)()) 0: ──T─╭●──S──T─╭●────┤ State 1: ────╰X──T────│───T─┤ State 2: ─────────────╰X────┤ State
We can apply the holistic
pennylane.transforms.zx.optimize_t_count
compilation pass to reduce the number ofT
gates. In this case, allT
gates can be removed!>>> print(qml.draw(qml.transforms.zx.optimize_t_count(circuit))()) 0: ──Z─╭●────╭●─┤ State 1: ────╰X──S─│──┤ State 2: ──────────╰X─┤ State
-
Change operator bases 🍴
-
Users can now benefit from an optimization of the controlled compute-uncompute pattern with the new
pennylane.change_op_basis
function andpennylane.ops.op_math.ChangeOpBasis
class. Operators arranged in a compute-uncompute pattern (U V U†
, which is equivalent to changing the basis in whichV
is expressed) can be efficiently controlled, as the only the central (target) operatorV
needs to be controlled, and notU
orU†
. (#8023) (#8070)These new features leverage the graph-based decomposition system, enabled with
pennylane.decomposition.enable_graph()
. To illustrate their use, consider the following example. The compute-uncompute pattern is composed of aQFT
, followed by aPhaseAdder
, and finally an inverseQFT
.from functools import partial qml.decomposition.enable_graph() dev = qml.device("default.qubit") @partial(qml.transforms.decompose, max_expansion=1) @qml.qnode(dev) def circuit(): qml.H(0) qml.CNOT([1,2]) qml.ctrl( qml.change_op_basis(qml.QFT([1,2]), qml.PhaseAdder(1, x_wires=[1,2])), control=0 ) return qml.state()
When this circuit is decomposed, the
QFT
andAdjoint(QFT)
are not controlled, resulting in a much more resource-efficient decomposition:>>> print(qml.draw(circuit)()) 0: ──H──────╭●────────────────┤ State 1: ─╭●─╭QFT─├PhaseAdder─╭QFT†─┤ State 2: ─╰X─╰QFT─╰PhaseAdder─╰QFT†─┤ State
-
The decompositions for several templates have been updated to use
pennylane.ops.op_math.ChangeOpBasis
, which makes their decompositions more resource efficient by eliminating unnecessary controlled operations. The templates includepennylane.Adder
,pennylane.Multiplier
,pennylane.OutAdder
,pennylane.OutMultiplier
,pennylane.PrepSelPrep
. (#8207)Here, the optimization is demonstrated when
pennylane.Adder
is controlled:qml.decomposition.enable_graph() dev = qml.device("default.qubit") @partial(qml.transforms.decompose, max_expansion=2) @qml.qnode(dev) def circuit(): qml.ctrl(qml.Adder(10, x_wires=[1,2,3,4]), control=0) return qml.state()
>>> print(qml.draw(circuit)()) 0: ──────╭●────────────────┤ State 1: ─╭QFT─├PhaseAdder─╭QFT†─┤ State 2: ─├QFT─├PhaseAdder─├QFT†─┤ State 3: ─├QFT─├PhaseAdder─├QFT†─┤ State 4: ─╰QFT─╰PhaseAdder─╰QFT†─┤ State
Quantum optimizers compatible with QJIT 🫖
-
Leveraging
pennylane.qjit
to optimize hybrid workflows with the momentum quantum natural gradient optimizer is now possible withpennylane.MomentumQNGOptimizerQJIT
. This provides better scaling than its non-JIT-compatible counterpart. (#7606)The v0.42 release saw the addition of the
pennylane.QNGOptimizerQJIT
optimizer, which is aqml.qjit
-compatible analogue topennylane.QNGOptimizer
. In this release, we've added thepennylane.MomentumQNGOptimizerQJIT
optimizer, which is theqml.qjit
-compatible analogue topennylane.MomentumQNGOptimizer
. Both optimizers have an Optax-like interface:import jax.numpy as jnp dev = qml.device("lightning.qubit", wires=2) @qml.qnode(dev) def circuit(params): qml.RX(params[0], wires=0) qml.RY(params[1], wires=1) return qml.expval(qml.Z(0) + qml.X(1)) opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.1, momentum=0.2) @qml.qjit def update_step_qjit(i, args): params, state = args return opt.step(circuit, params, state) @qml.qjit def optimization_qjit(params, iters): state = opt.init(params) args = (params, state) params, state = qml.for_loop(iters)(update_step_qjit)(args) return params
Quantum just-in-time compilation works exceptionally well with repeatedly executing the same function in a
for
loop. As you can see, $10^5$ iterations takes seconds:>>> import time >>> params = jnp.array([0.1, 0.2]) >>> iters = 100_000 >>> start = time.process_time() >>> optimization_qjit(params=params, iters=iters) Array([ 3.14159265, -1.57079633], dtype=float64) >>> time.process_time() - start 21.319525
Improvements 🛠
Resource-efficient decompositions
-
With
pennylane.decomposition.enable_graph()
, dynamically allocated wires withpennylane.allocate
are now supported in decomposition rules. This provides a smoother overall experience when decomposing operators in a way that requires auxiliary/work wires. (#7861) (#8228)Support for
pennylane.allocate
unlocks the following features:-
The
pennylane.transforms.decompose
transform now accepts amax_work_wires
argument that allows the user to specify the number of work wires available for dynamic allocation during decomposition. (#7963) (#7980) (#8103) (#8236) -
Decomposition rules were added for the
pennylane.MultiControlledX
that dynamically allocate work wires if none were explicitly specified via thework_wires
argument. (#8024)
-
-
Several templates now have decompositions that can be accessed within the graph-based decomposition system (
pennylane.decomposition.enable_graph
), allowing workflows that include these templates to be decomposed in a resource-efficient and performant manner. (#7779) (#7908) (#7941) (#7943) (#8075) (#8002)The included templates are:
pennylane.Adder
,pennylane.ControlledSequence
,pennylane.ModExp
,pennylane.MottonenStatePreparation
,pennylane.MPSPrep
,pennylane.Multiplier
,pennylane.OutAdder
,pennylane.OutMultiplier
,pennylane.OutPoly
,pennylane.PrepSelPrep
,pennylane.ops.Prod
,pennylane.Reflection
,pennylane.StatePrep
,pennylane.TrotterProduct
,pennylane.QROM
,pennylane.GroverOperator
,pennylane.UCCSD
,pennylane.StronglyEntanglingLayers
,pennylane.GQSP
,pennylane.FermionicSingleExcitation
,pennylane.FermionicDoubleExcitation
,pennylane.QROM
,pennylane.ArbitraryStatePreparation
,pennylane.CosineWindow
,pennylane.AmplitudeAmplification
,pennylane.Permute
,pennylane.AQFT
,pennylane.FlipSign
,pennylane.FABLE
,pennylane.Qubitization
, andpennylane.Superposition
. -
Two additions were made to
pennylane.Select
, significantly improving its decomposition:-
A new keyword argument
partial
has been added, which allows for simplifications in the decomposition ofpennylane.Select
under the assumption that the state of the control wires has no overlap with computational basis states that are not used bypennylane.Select
. -
A new decomposition rule has been added to
pennylane.Select
. It achieves cost reductions by adding onework_wire
. This decomposition is useful to perform efficientpennylane.QROM
decompositions.
-
-
The decomposition of
pennylane.BasisRotation
has been optimized to skip redundant phase shift gates with angle $\pm \pi$ for real-valued (orthogonal) rotation matrices. This uses the fact that either one or zeropennylane.PhaseShift
gates are required in case the matrix has a determinant equal to $\pm 1$. (#7765) -
The
pennylane.transforms.decompose
transform is now able to decompose classically controlled operations (i.e., operations nested insidecond
). (#8145)from functools import partial dev = qml.device('default.qubit') @partial(qml.transforms.decompose, gate_set={qml.RY, qml.RZ, qml.measurements.MidMeasureMP}) @qml.qnode(dev) def circuit(): m0 = qml.measure(0) qml.cond(m0 == 0, qml.Rot)(qml.numpy.pi / 2, qml.numpy.pi / 2, qml.numpy.pi / 2, wires=1) return qml.expval(qml.X(0))
>>> print(qml.draw(circuit, level=0)()) 0: ──┤↗├──────────────────────┤ <X> 1: ───║───Rot(1.57,1.57,1.57)─┤ ╚═══╝ >>> print(qml.draw(circuit, level=1)()) 0: ──┤↗├───────────────────────────────┤ <X> 1: ───║───RZ(1.57)──RY(1.57)──RZ(1.57)─┤ ╚═══╩═════════╩═════════╝
-
Various decompositions of
pennylane.MultiControlledX
now utilizepennylane.TemporaryAND
in place ofpennylane.Toffoli
gates, leading to more resource-efficient decompositions. (#8172) -
Controlled(Identity)
is now directly decomposed to a single Identity operator instead of going through a numeric decomposition algorithm. (#8388) -
The internal assignment of basis states in
pennylane.Superposition
was improved, resulting in its decomposition being more performant and efficient. (#7880) -
pennylane.decomposition.has_decomp
andpennylane.decomposition.list_decomps
now take operator instances as arguments instead of types. (#8286)>>> qml.decomposition.has_decomp(qml.MultiControlledX) True >>> qml.decomposition.list_decomps(qml.Select) [<pennylane.decomposition.decomposition_rule.DecompositionRule at 0x126f99ed0>, <pennylane.decomposition.decomposition_rule.DecompositionRule at 0x127002fd0>, <pennylane.decomposition.decomposition_rule.DecompositionRule at 0x127034bd0>]
-
With the graph-based decomposition system enabled (
pennylane.decomposition.enable_graph()
), if a decomposition cannot be found for an operator in the circuit in terms of the target gates, it no longer raises an error. Instead, a warning is raised, andop.decomposition()
(the current default method for decomposing gates) is used as a fallback, while the rest of the circuit is still decomposed with the new graph-based system. Additionally, a special warning message is raised if the circuit contains aGlobalPhase
, reminding the user thatGlobalPhase
is not assumed to have a decomposition under the new system. (#8156) -
pennylane.transforms.decompose
andpennylane.preprocess.decompose
now have a unified internal implementation to promote feature parity in preparation for the graph-based decomposition system to be the default decomposition method in PennyLane. (#8193) -
A new class called
pennylane.decomposition.decomposition_graph.DecompGraphSolution
has been added to store the solution of a decomposition graph. An instance of this class is returned from thesolve
method of thepennylane.decomposition.decomposition_graph.DecompositionGraph
class. (#8031)
OpenQASM-PennyLane interoperability
-
The
pennylane.from_qasm3
function can now convert OpenQASM 3.0 circuits that contain subroutines, constants, all remaining stdlib gates, qubit registers, and built-in mathematical functions. (#7651) (#7653) (#7676) (#7677) (#7679) (#7690) (#7767) -
pennylane.to_openqasm
now supports mid-circuit measurements and conditionals of unprocessed measurement values. (#8210)
Setting shots
-
The number of
shots
can now be specified directly in QNodes as a standard keyword argument. (#8073)@qml.qnode(qml.device("default.qubit"), shots=1000) def circuit(): qml.H(0) return qml.expval(qml.Z(0))
>>> circuit.shots Shots(total_shots=1000, shot_vector=(ShotCopies(1000 shots x 1),)) >>> circuit() np.float64(-0.004)
Setting the
shots
value in a QNode is equivalent to decorating withpennylane.workflow.set_shots
. However, decorating withpennylane.workflow.set_shots
overrides QNodeshots
:>>> new_circ = qml.set_shots(circuit, shots=123) >>> new_circ.shots Shots(total_shots=123, shot_vector=(ShotCopies(123 shots x 1),))
-
The
pennylanepennylane.set_shots
transform can now be directly applied to a QNode without the need forfunctools.partial
, providing a more user-friendly syntax and negating having to import thefunctools
package. (#7876) (#7919)@qml.set_shots(shots=1000) # or @qml.set_shots(1000) @qml.qnode(dev) def circuit(): qml.H(0) return qml.expval(qml.Z(0))
>>> circuit() 0.002
Clifford + T decomposition
-
The
pennylane.clifford_t_decomposition
transform withmethod="gridsynth"
is now compatible with quantum just-in-time compilation via thepennylane.qjit
decorator. (#7711)@qml.qjit @partial(qml.transforms.clifford_t_decomposition, method="gridsynth") @qml.qnode(qml.device("lightning.qubit", wires=1)) def circuit(): qml.RX(np.pi/3, wires=0) qml.RY(np.pi/4, wires=0) return qml.state()
>>> circuit() Array([0.80011651+0.19132132j, 0.33140586-0.4619306j ], dtype=complex128)
-
The
pennylane.clifford_t_decomposition
transform can now decompose circuits with mid-circuit measurements, including Catalyst's measurement operations. It also now handlesRZ
andPhaseShift
operations where angles are odd multiples of $\pm \tfrac{\pi}{4}$ more efficiently when usingmethod="gridsynth"
. (#7793) (#7942) -
The
pennylane.ops.rs_decomposition
method now gives decompositions with exact global phase information. (#7793) -
Users can now specify a relative threshold value for the permissible operator norm error (
epsilon
) that triggers rebuilding of the cache in thepennylane.clifford_t_decomposition
, via newcache_eps_rtol
keyword argument. (#8056)
Transforms
-
New transforms called
pennylane.transforms.match_relative_phase_toffoli
andpennylane.transforms.match_controlled_iX_gate
have been added, which compile certain patterns to efficient Clifford + T equivalents. (#7748)@qml.qnode(qml.device("default.qubit", wires=4)) def circuit(): qml.CCZ(wires=[0, 1, 3]) qml.ctrl(qml.S(wires=[1]), control=[0]) qml.ctrl(qml.S(wires=[2]), control=[0, 1]) qml.MultiControlledX(wires=[0, 1, 2, 3]) return qml.expval(qml.Z(0))
>>> new_circuit = qml.transforms.match_relative_phase_toffoli(circuit) >>> print(qml.draw(new_circuit, level=1)()) 0: ─────────────────╭●───────────╭●───────────────────────────┤ <Z> 1: ─────────────────│─────╭●─────│─────╭●─────────────────────┤ 2: ───────╭●────────│─────│──────│─────│────────────╭●────────┤ 3: ──H──T─╰X──T†──H─╰X──T─╰X──T†─╰X──T─╰X──T†──H──T─╰X──T†──H─┤
-
New intermediate representations (IRs) called
pennylanetransforms.parity_matrix
andpennylanetransforms.phase_polynomial
are available in PennyLane. These IRs are used in compilation passes to optimizeCNOT
and phase polynomial circuits, respectively. Additionally, thepennylanetransforms.rowcol
has been added, which uses the parity matrix as its IR forCNOT
routing under constraint connectivity. (#8171) (#8443)The example below showcases the use of
pennylanetransforms.parity_matrix
, which acts on circuits containing onlyCNOT
gates.dev = qml.device('default.qubit', wires=1) @qml.qnode(dev) def circuit(): qml.CNOT((3, 2)) qml.CNOT((0, 2)) qml.CNOT((2, 1)) qml.CNOT((3, 2)) qml.CNOT((3, 0)) qml.CNOT((0, 2)) return qml.state()
Upon transforming the above circuit with
pennylanetransforms.parity_matrix
, the output is the parity matrix.>>> P = qml.transforms.parity_matrix(circuit, wire_order=range(4))() >>> print(P) array([[1, 0, 0, 1], [1, 1, 1, 1], [0, 0, 1, 1], [0, 0, 0, 1]])
The
pennylanetransforms.phase_polynomial
transform functions similarly, operating on circuits containining onlyCNOT
andRZ
gates and returning the parity matrix, the parity table, and corresponding angles for each parity.@qml.qnode(dev) def circuit(): qml.CNOT((1, 0)) qml.RZ(1, 0) qml.CNOT((2, 0)) qml.RZ(2, 0) qml.CNOT((0, 1)) qml.CNOT((3, 1)) qml.RZ(3, 1) return qml.state()
>>> pmat, ptab, angles = qml.transforms.phase_polynomial(circuit, wire_order=range(4))() >>> pmat [[1 1 1 0] [1 0 1 1] [0 0 1 0] [0 0 0 1]] >>> ptab [[1 1 1] [1 1 0] [0 1 1] [0 0 1]] >>> angles [1 2 3]
-
A new transform called
pennylane.transforms.rz_phase_gradient
has been added, which lets you realize arbitrary anglepennylane.RZ
rotations with a phase gradient resource state and semi-in-place addition (pennylane.SemiAdder
). This can be a crucial subroutine in FTQC when sufficient auxiliary wires are available, as it saves onT
gates compared to other discretization schemes. (#8213) -
A new keyword argument called
shot_dist
has been added to thepennylane.transforms.split_non_commuting
transform. This allows for more customization and efficiency when calculating expectation values across the non-commuting groups of observables that make up aHamiltonian
/LinearCombination
. (#7988)Given a QNode that returns a sample-based measurement (e.g.,
expval
) of aHamiltonian
/LinearCombination
with finiteshots
, the current default behaviour ofpennylane.transforms.split_non_commuting
will performshots
executions for each group of commuting terms. With theshot_dist
argument, this behaviour can be changed:"uniform"
: evenly distributes the number ofshots
across all groups of commuting terms"weighted"
: distributes the number ofshots
according to weights proportional to the L1 norm of the coefficients in each group"weighted_random"
: same as"weighted"
, but the numbers ofshots
are sampled from a multinomial distribution- or a user-defined function implementing a custom shot distribution strategy
To show an example about how this works, let's start by defining a simple Hamiltonian:
ham = qml.Hamiltonian( coeffs=[10, 0.1, 20, 100, 0.2], observables=[ qml.X(0) @ qml.Y(1), qml.Z(0) @ qml.Z(2), qml.Y(1), qml.X(1) @ qml.X(2), qml.Z(0) @ qml.Z(1) @ qml.Z(2) ] )
This Hamiltonian can be split into 3 non-commuting groups of mutually commuting terms. With
shot_dist = "weighted"
, for example, the number of shots will be divided according to the L1 norm of each group's coefficients:from functools import partial from pennylane.transforms import split_non_commuting dev = qml.device("default.qubit") @partial(split_non_commuting, shot_dist="weighted") @qml.qnode(dev, shots=10000) def circuit(): return qml.expval(ham) with qml.Tracker(dev) as tracker: circuit()
>>> print(tracker.history["shots"]) [2303, 23, 7674]
-
The
pennylane.noise.fold_global
transform has been refactored to collect operators into a list directly rather than relying on queuing. (#8296)
Choi matrix functionality
-
A new function called
pennylane.math.choi_matrix
is available, which computes the Choi matrix of a quantum channel. This is a useful tool in quantum information science and to check circuit identities involving non-unitary operations. (#7951)>>> import numpy as np >>> Ks = [np.sqrt(0.3) * qml.CNOT((0, 1)), np.sqrt(1-0.3) * qml.X(0)] >>> Ks = [qml.matrix(op, wire_order=range(2)) for op in Ks] >>> Lambda = qml.math.choi_matrix(Ks) >>> np.trace(Lambda), np.trace(Lambda @ Lambda) (np.float64(1.0), np.float64(0.58))
Other improvements
-
pennylane.snapshots
can now be used withmcm_method="one-shot"
andmcm_method="tree-traversal"
. (#8140)This improvement is particularly useful for extracting the state in finite-shot workflows:
@qml.qnode(qml.device("default.qubit"), mcm_method="one-shot", shots=1) def circuit(): qml.RY(1.23, 0) m0 = qml.measure(0) qml.cond(m0 == 0, qml.H)(0) qml.Snapshot("state", measurement=qml.state()) return qml.expval(qml.X(0))
>>> qml.snapshots(circuit)() {'state': array([0.+0.j, 1.+0.j]), 'execution_results': np.float64(-1.0)}
Here, the state is projected onto the corresponding state resulting from the MCM.
-
The printing and drawing of
pennylane.TemporaryAND
, also known asqml.Elbow
, and its adjoint have been improved to be more legible and consistent with how it's depicted in circuits in the literature. (#8017) (#8432)import pennylane as qml @qml.draw @qml.qnode(qml.device("lightning.qubit", wires=4)) def node(): qml.TemporaryAND([0, 1, 2], control_values=[1, 0]) qml.CNOT([2, 3]) qml.adjoint(qml.TemporaryAND([0, 1, 2], control_values=[1, 0])) return qml.expval(qml.Z(3))
print(node()) 0: ─╭●─────●╮─┤ 1: ─├○─────○┤─┤ 2: ─╰──╭●───╯─┤ 3: ────╰X─────┤ <Z>
-
With
pennylane.decomposition.enable_graph
, theUserWarning
that is raised when a decomposition cannot be found for an operator in the circuit is now more generic, not making any assumptions about how the unresolved operations will be applied or used in the decompose transformation. (#8361) -
The
pennylane.sample
function can now receive an optionaldtype
parameter which sets the type and precision of the samples returned by this measurement process. (#8189) (#8271) -
DefaultQubit
will now default to the tree-traversal MCM method whenmcm_method="device"
. (#7885) -
DefaultQubit
now determines themcm_method
inDevice.setup_execution_config
, making it easier to tell whichmcm_method
will be used. This also allowsdefer_measurements
anddynamic_one_shot
to be applied at different locations in the preprocessing program. (#8184) -
The default implementation of
Device.setup_execution_config
now choses"device"
as the defaultmcm_method
if it is available, as specified by the device TOML file. (#7968) -
ExecutionConfig
andMCMConfig
frompennylane.devices
are now frozen dataclasses whose fields should be updated withdataclass.replace
. (#7697) (#8046) -
An error is no longer raised when non-integer wire labels are used in QNodes using
mcm_method="deferred"
. (#7934)@qml.qnode(qml.device("default.qubit"), mcm_method="deferred") def circuit(): m = qml.measure("a") qml.cond(m == 0, qml.X)("aux") return qml.expval(qml.Z("a"))
>>> print(qml.draw(circuit)()) a: ──┤↗├────┤ <Z> aux: ───║───X─┤ ╚═══╝
-
qml.transforms.core.TransformContainer
now holds onto aTransformDispatcher
,args
, andkwargs
, instead of the transform's defining function and unpacked properties. It can still be constructed via the old signature, as well. (#8306) -
The JAX version is now included in
pennylane.about
. (#8277) -
A warning is now raised when circuits are executed without Catalyst and with
qml.capture.enable()
present. (#8291) -
The QNode primitive in the experimental program capture module now captures the unprocessed
ExecutionConfig
, instead of one processed by the device. This allows for better integration with Catalyst. (#8258) -
qml.counts
can now be captured with program capture. Circuits returningcounts
still cannot be interpreted or executed with program capture. (#8229) -
Templates are now compatible with program capture. (#8211)
-
PennyLane
autograph
supports standard Python for index assignment (arr[i] = x
) and updating array elements (arr[i] += x
) instead ofjax.numpy
form (i.e.,arr = arr.at[i].set(x)
andarr.at[i].add(x)
). (#8027) (#8076)import jax.numpy as jnp qml.capture.enable() @qml.qnode(qml.device("default.qubit", wires=3)) def circuit(val): angles = jnp.zeros(3) angles[0:3] += val for i, angle in enumerate(angles): qml.RX(angle, i) return qml.expval(qml.Z(0)), qml.expval(qml.Z(1)), qml.expval(qml.Z(2))
>>> circuit(jnp.pi) (Array(-1, dtype=float32), Array(-1, dtype=float32), Array(-1, dtype=float32))
-
Logical operations (
and
,or
andnot
) are now supported with PennyLaneautograph
. (#8006)qml.capture.enable() @qml.qnode(qml.device("default.qubit", wires=1)) def circuit(param): if param >= 0 and param <= 1: qml.H(0) return qml.state()
>>> circuit(0.5) Array([0.70710677+0.j, 0.70710677+0.j], dtype=complex64)
-
With program capture, the
true_fn
can now be a subclass ofOperator
when nofalse_fn
is provided. For example,qml.cond(condition, qml.X)(0)
is now valid code. (#8060) (#8101) -
With program capture, an error is now raised if the conditional predicate in, say, an
if
statement is not a scalar. (#8066) -
Program capture can now handle dynamic shots, shot vectors, and shots set with
pennylanepennylane.set_shots
. (#7652) -
The error message raised when using unified-compiler transforms with
pennylane.qjit
has been updated with suggested fixes. (#7916) -
Two new
draw
andgenerate_mlir_graph
functions have been introduced in theqml.compiler.python_compiler.visualization
module to visualize circuits with the new unified compiler framework when xDSL and/or Catalyst compilation passes are applied. (#8040) (#8180) (#8091) -
The
catalyst
,qec
, and ``stablehlo` xDSL dialects have been added to the unified compiler framework, containing data structures that support core compiler functionality and quantum error correction and extending the existing StableHLO dialect with missing upstream operations. (#7901) (#7985) (#8036) (#8084) (#8113) -
The
Quantum
xDSL dialect now has more strict constraints for operands and results. (#8083) -
A callback mechanism has been added to
qml.compiler.python_compiler
submodule to inspect the intermediate representation of the program between multiple compilation passes. (#7964) -
A
QuantumParser
class has been added to theqml.compiler.python_compiler
submodule that automatically loads relevant dialects. (#7888) -
Two new operations have been added to the
Quantum
dialect of the unified compiler: -
A compilation pass written called
qml.compiler.python_compiler.transforms.MeasurementsFromSamplesPass
has been added for integration with the unified compiler framework. This pass replaces all terminal measurements in a program with a singlepennylane.sample
measurement, and adds postprocessing instructions to recover the original measurement. (#7620) -
A combine-global-phase pass has been added to the unified compiler framework. Note that the current implementation can only combine all the global phase operations at the last global phase operation in the same region. In other words, global phase operations inside a control flow region can't be combined with those in their parent region. (#7675)
-
The matrix factorization using
pennylane.math.decomposition.givens_decomposition
has been optimized to factor out the redundant sign in the diagonal phase matrix for the real-valued (orthogonal) rotation matrices. For example, in case the determinant of a matrix is $-1$, only a single element of the phase matrix is required. (#7765) -
A new device preprocess transform,
pennylane.devices.preprocess.no_analytic
, is available for hardware devices and hardware-like simulators. It validates that all executions are shot-based. (#8037) -
PennyLane is now compatible with
quimb == 1.11.2
after a bug affectingdefault.tensor
was fixed. (#7931) -
A new
pennylane.transforms.resolve_dynamic_wires
transform can allocate concrete wire values for dynamic wire allocation. (#7678) (#8184) (#8406)
Labs: a place for unified and rapid prototyping of research software 🧪
Labs Resource Estimation
-
State-of-the-art resource estimates have been added to existing templates:
pennylanepennylane.labs.resource_estimation.ResourceSelectPauliRot
,pennylanepennylane.labs.resource_estimation.ResourceQubitUnitary
,pennylanepennylane.labs.resource_estimation.ResourceSingleQubitComparator
,pennylanepennylane.labs.resource_estimation.ResourceTwoQubitComparator
,pennylanepennylane.labs.resource_estimation.ResourceIntegerComparator
,pennylanepennylane.labs.resource_estimation.ResourceRegisterComparator
,pennylanepennylane.labs.resource_estimation.ResourceUniformStatePrep
,pennylanepennylane.labs.resource_estimation.ResourceAliasSampling
,pennylanepennylane.labs.resource_estimation.ResourceQFT
,pennylanepennylane.labs.resource_estimation.ResourceAQFT
, andpennylanepennylane.labs.resource_estimation.ResourceTrotterProduct
. (#7786) (#7857) (#7883) (#7920) (#7910) -
Users can now do resource estimation on QPE and iterative QPE with
pennylanepennylane.labs.resource_estimation.ResourceQPE
andpennylanepennylane.labs.resource_estimation.ResourceIterativeQPE
, respectively. Additionally, apennylanepennylane.labs.resource_estimation.ResourceControlledSequence
template has been added that allows estimating resources on controlled sequences of resource operators. (#8053) -
estimate_resources
has been renamed toestimate
to make the function name concise and clearer thanlabs.resource_estimation.estimate_resources
. (#8232) -
A new
ResourceConfig
class has been added to help track the configuration for errors, precisions and custom decompositions for the resource estimation pipeline. (#8195) -
The symbolic
ResourceOperators
have been updated to use hyperparameters from theconfig
dictionary. (#8181) -
An internal
dequeue()
method has been added to theResourceOperator
class to simplify the instantiation of resource operators which require resource operators as input. (#7974) -
ResourceOperator
instances can now be compared with==
. (#8155) -
A mapper function called
pennylanepennylane.labs.resource_estimation.map_to_resource_op
has been added to map PennyLane operators toResourceOperator
equivalents. (#8146) (#8162) -
Several Labs test files have been renamed to prevent conflict with names in mainline PennyLane tests. (#8264)
-
A queueing issue in the
ResourceOperator
tests has been fixed. (#8204)
Labs Trotter Error Estimation
-
Parallelization support for
effective_hamiltonian
has been added to improve performance. (#8081) (#8257) -
New
SparseFragment
andSparseState
classes have been created to allow the use of sparse matrices for Hamiltonian Fragments when estimating Trotter error. (#7971) -
The
pennylanepennylane.labs.trotter_error.perturbation_error
function has been updated to sum over expectation values instead of states. (#8226) -
The docstring in
perturbation_error
has been updated to use the correct positional argument name. (#8174)
Labs Removals
- The module
qml.labs.zxopt
has been removed. Its functionalities are now available in the submodulepennylane.transforms.zx
. The same functions are available, but their signature may have changed. - Instead ofqml.labs.zxopt.full_optimize
, usepennylane.transforms.zx.optimize_t_count
- Instead ofqml.labs.zxopt.full_reduce
, usepennylane.transforms.zx.reduce_non_clifford
- Instead ofqml.labs.zxopt.todd
, usepennylane.transforms.zx.todd
- Instead ofqml.labs.zxopt.basic_optimization
, usepennylane.transforms.zx.push_hadamards
(#8177)
Breaking changes 💔
-
autoray
has been pinned to v0.8.0 for PennyLane v0.43.0 to prevent potential bugs due to breaking changes in autoray releases. (#8412)Previous to this change, the
autoray
package was upper-bounded inpyproject.toml
to unblock CI failures due to breaking changes inv0.8.0
. Then, it was unpinned by fixing source code that was broken by the release. (#8110) (#8147) (#8159) (#8160) -
Using
postselect_mode="fill-shots"
withmcm_method="one-shot"
or"tree-traversal"
has been disallowed withdefault.qubit
, as it produces incorrect results where the correlation between measurements is not preserved. (#8411) -
qml.workflow.construct_batch.expand_fn_transform
has been deleted as it was no longer getting used. (#8344) -
get_canonical_interface_name
has been removed in favour of overridingEnum._missing_
inInterface
. (#8223)If you would like to get the canonical interface you can simply use the
Enum
:>>> from pennylane.math.interface_utils import Interface >>> Interface("torch") <Interface.TORCH: 'torch'> >>> Interface("jax-jit") <Interface.JAX_JIT: 'jax-jit'>
-
pennylane.PrepSelPrep
has been made more reliable by deriving the attributescoeffs
and `ops
from the property ``lcu`` instead of storing them independently. In addition, it is now more consistent with other PennyLane operators, dequeuing its input ``lcu``. (#8169) -
MidMeasureMP
now inherits fromOperator
instead ofMeasurementProcess
, which resolves problems caused by it always acting like an operator. (#8166) -
With the deprecation of the
shots
kwarg inqml.device
,DefaultQubit.eval_jaxpr
does not useself.shots
from the device anymore; instead, it takesshots
as a keyword argument, and the QNode primitive should process theshots
and calleval_jaxpr
accordingly. (#8161) -
The methods
pauli.PauliWord.operation
andpauli.PauliSentence.operation
no longer queue any operators. This improves the consistency of the queuing behaviour for the operators. (#8136) -
qml.sample
no longer has singleton dimensions squeezed out for single shots or single wires. This cuts down on the complexity of post-processing due to having to handle single shot and single wire cases separately. The return shape will now always be(shots, num_wires)
. (#7944) (#8118)For a simple qnode:
@qml.qnode(qml.device('default.qubit')) def circuit(): return qml.sample(wires=0)
Before the change, we had:
>>> qml.set_shots(circuit, shots=1)() 0
and now we have:
>>> qml.set_shots(circuit, shots=1)() array([[0]])
Previous behavior can be recovered by squeezing the output:
>>> qml.math.squeeze(qml.set_shots(circuit, shots=1)()) array(0)
-
Functions involving an execution configuration will now default to
None
instead ofpennylane.devices.DefaultExecutionConfig
and have to be handled accordingly. This prevents the potential mutation of a global object. (#7697)This means that functions like,
def some_func(..., execution_config = DefaultExecutionConfig): ...
should be written as follows,
def some_func(..., execution_config: ExecutionConfig | None = None): if execution_config is None: execution_config = ExecutionConfig()
-
The
pennylane.HilbertSchmidt
andpennylane.LocalHilbertSchmidt
templates have been updated and their UI has been remarkably simplified. They now accept an operation or a list of operations as unitaries. (#7933)In past versions of PennyLane, these templates required providing the
U
andV
unitaries as aqml.tape.QuantumTape
and a quantum function, respectively, along with separate parameters and wires.With this release, each template has been improved to accept one or more operators as unitaries. The wires and parameters of the approximate unitary
V
are inferred from the inputs, according to the order provided.>>> U = qml.Hadamard(0) >>> V = qml.RZ(0.1, wires=1) >>> qml.HilbertSchmidt(V, U) HilbertSchmidt(0.1, wires=[0, 1])
-
Support for Python 3.10 has been removed and support for Python 3.13 has been added. (#7935)
-
To make the codebase more organized and easier to maintain, custom exceptions were moved into
exceptions.py
, and a documentation page for them was added in the internals. (#7856) -
The boolean functions provided in
qml.operation
have been deprecated. See the deprecations page for equivalent code to use instead. These includenot_tape
,has_gen
,has_grad_method
,has_multipar
,has_nopar
,has_unitary_gen
,is_measurement
,defines_diagonalizing_gates
, andgen_is_multi_term_hamiltonian
. (#7924) -
To prevent code duplication, access to
lie_closure
,structure_constants
andcenter
viaqml.pauli
has been removed. The functions now live in theliealg
module and top level import and usage is advised. (#7928) (#7994)import pennylane.liealg from pennylane.liealg import lie_closure, structure_constants, center
-
qml.operation.Observable
and the correspondingObservable.compare
have been removed, as PennyLane now depends on the more generalOperator
interface instead. TheOperator.is_hermitian
property can instead be used to check whether or not it is highly likely that the operator instance is Hermitian. (#7927) -
qml.operation.WiresEnum
,qml.operation.AllWires
, andqml.operation.AnyWires
have been removed. To indicate that an operator can act on any number of wires,Operator.num_wires = None
should be used instead. This is the default and does not need to be overwritten unless the operator developer wants to validate that the correct number of wires is passed. (#7911) -
The
qml.QNode.get_gradient_fn
function has been removed. Instead, useqml.workflow.get_best_diff_method
to obtain the differentiation method. (#7907) -
Top-level access to
DeviceError
,PennyLaneDeprecationWarning
,QuantumFunctionError
andExperimentalWarning
has been removed. Please import these objects from the newpennylane.exceptions
module. (#7874) -
To improve code reliability,
qml.cut_circuit_mc
no longer accepts ashots
keyword argument. The shots should instead be set on the tape itself. (#7882) -
pennylane.tape.tape.expand_tape
has been moved to its own file, and made available atqml.tape
. (#8296)
Deprecations 👋
-
PennyLane and Lightning will no longer ship wheels for Intel MacOS platforms for v0.44 and newer. Additionally, MacOS ARM wheels will require a minimum OS version of 14.0 for continued use with v0.44 and newer. This change is needed to account for MacOS officially deprecating support for Intel CPUs in the OS (see their blog post for more details).
-
Setting shots on a device through the
shots
keyword argument (e.g.,qml.device("default.qubit", wires=2, shots=1000)
) and in QNode calls (e.g.,qml.QNode(circuit, dev)(shots=1000)
) has been deprecated. Please use thepennylanepennylane.set_shots
transform to set the number of shots for a QNode instead. This is done to reduce confusion and code complexity by having a centralized way to set shots. (#7979) (#8161) (#7906)dev = qml.device("default.qubit", wires=2) @qml.set_shots(1000) @qml.qnode(dev) def circuit(x): qml.RX(x, wires=0) return qml.expval(qml.Z(0))
-
Support for using TensorFlow with PennyLane has been deprecated and will be dropped in Pennylane v0.44. Future versions of PennyLane are not guaranteed to work with TensorFlow. Instead, we recommend using the JAX or PyTorch interfaces for machine learning applications to benefit from enhanced support and features. Please consult the following demos for more usage information: Turning quantum nodes into Torch Layers and How to optimize a QML model using JAX and Optax. (#7989) (#8106)
-
pennylane.devices.DefaultExecutionConfig
has been deprecated and will be removed in v0.44. Instead, useqml.devices.ExecutionConfig()
to create a default execution configuration. This helps prevent unintended changes in a workflow's behaviour that could be caused by using a global, mutable object. (#7987) -
Specifying the
work_wire_type
argument inqml.ctrl
and other controlled operators as"clean"
or"dirty"
has been deprecated. Use"zeroed"
to indicate that the work wires are initially in the $|0\rangle$ state, and"borrowed"
to indicate that the work wires can be in any arbitrary state instead. In both cases, the work wires are restored to their original state upon completing the decomposition. This is done to standardize how work wires are called in PennyLane. (#7993) -
Providing the Trotter number kwarg
num_steps
topennylane.evolve
,pennylane.exp
,pennylane.ops.Evolution
, andpennylane.ops.Exp
has been deprecated and will be removed in a future release. Instead, usepennylane.TrotterProduct
for approximate methods, providing then
parameter to perform the Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps. This change resolves the ambiguity that arises when usingnum_steps
on devices that support analytic evolution (e.g.,default.qubit
). (#7954) (#7977)As a concrete example, consider the following case:
coeffs = [0.5, -0.6] ops = [qml.X(0), qml.X(0) @ qml.Y(1)] H_flat = qml.dot(coeffs, ops)
Instead of computing the Suzuki-Trotter product approximation as:
>>> qml.evolve(H_flat, num_steps=2).decomposition() [RX(0.5, wires=[0]), PauliRot(-0.6, XY, wires=[0, 1]), RX(0.5, wires=[0]), PauliRot(-0.6, XY, wires=[0, 1])]
The same result can be obtained using
pennylane.TrotterProduct
as follows:>>> decomp_ops = qml.adjoint(qml.TrotterProduct(H_flat, time=1.0, n=2)).decomposition() >>> [simp_op for op in decomp_ops for simp_op in map(qml.simplify, op.decomposition())] [RX(0.5, wires=[0]), PauliRot(-0.6, XY, wires=[0, 1]), RX(0.5, wires=[0]), PauliRot(-0.6, XY, wires=[0, 1])]
-
MeasurementProcess.expand
has been deprecated. The relevant method can be replaced withqml.tape.QuantumScript(mp.obs.diagonalizing_gates(), [type(mp)(eigvals=mp.obs.eigvals(), wires=mp.obs.wires)])
. This improves the code design by removing an unused method with undesired dependencies (i.e. circular dependency). (#7953) -
QuantumScript.shape
andQuantumScript.numeric_type
have been deprecated and will be removed in version v0.44. Instead, the corresponding.shape
or.numeric_type
of theMeasurementProcess
class should be used. (#7950) -
Some unnecessary methods of the
qml.CircuitGraph
class have been deprecated and will be removed in version v0.44: (#7904)-
print_contents
in favor ofprint(obj)
-observables_in_order
in favor ofobservables
-operations_in_order
in favor ofoperations
-ancestors_in_order
in favor ofancestors(obj, sort=True)
-descendants_in_order
in favor ofdescendants(obj, sort=True)
-
-
The
QuantumScript.to_openqasm
method has been deprecated and will be removed in version v0.44. Instead, theqml.to_openqasm
function should be used. This change makes the code cleaner by separating out methods that interface with external libraries from PennyLane's internal functionality. (#7909) -
The
level=None
argument in thepennylane.workflow.get_transform_program
,pennylane.workflow.construct_batch
,qml.draw
,qml.draw_mpl
, andqml.specs
transforms has been deprecated and will be removed in v0.44. Please uselevel='device'
instead to apply the noise model at the device level. This reduces ambiguity by making it clear that the default is to apply all transforms to the QNode. (#7886) (#8364) -
qml.qnn.cost.SquaredErrorLoss
has been deprecated and will be removed in version v0.44. Instead, this hybrid workflow can be accomplished with a function likeloss = lambda *args: (circuit(*args) - target)**2
. (#7527) -
Access to
add_noise
,insert
and noise mitigation transforms from thetransforms
module has been deprecated. Instead, these functions should be imported from thenoise
module, which is a more appropriate location for them. (#7854) -
The
qml.QNode.add_transform
method has been deprecated and will be removed in v0.44. Instead, please useQNode.transform_program.push_back(transform_container=transform_container)
. (#7855) (#8266)
Internal changes ⚙️
-
GitHub actions and workflows (
interface-unit-tests.yml
,tests-labs.yml
,unit-test.yml
,upload-nightly-release.yml
andupload.yml
) have been updated to useubuntu-24.04
runners. (#8371) -
measurements
is now a "core" module in thetach
specification. (#7945) -
Enforce various modules to follow modular architecture via
tach
. (#7847) -
CI workflows to test documentation using
sybil
have been added. (#8324) (#8328) (#8329) (#8330) (#8331) (#8386) -
The
templates/subroutines
now hasarithmetic
,qchem
, andtime_evolution
submodules. (#8333) -
test_horizontal_cartan_subalgebra.py
now uses our fixtureseed
for reproducibility and CI stability. (#8304) -
The
qml.compiler.python_compiler
submodule has been restructured to be more cohesive. (#8273) -
default.tensor
now supports graph decomposition (qml.decomposition.enable_graph()
) during preprocessing. (#8253) -
Legacy interface names from tests have been removed (e.g.,
interface="jax-python"
orinterface="pytorch"
) (#8249) -
qml.devices.preprocess.decompose
now works in graph decomposition mode when a gateset is provided.default.qubit
andnull.qubit
can now use graph decomposition mode. (#8225) (#8265) (#8260) -
Usage of the
pytest.mark.capture
marker from tests in thetests/python_compiler
directory has been removed. (#8234) -
pylint
has been updated to v3.3.8 in our CI andrequirements-dev.txt
(#8216) -
Links in the
README.md
have been updated. (#8165) -
The
autograph
guide to now reflects new capabilities. (#8132) -
strict=True
is now used tozip
usage in source code. (#8164) (#8182) (#8183) -
The
autograph
keyword argument has been removed from theQNode
constructor. To enable autograph conversion, use theqjit
decorator together with theqml.capture.disable_autograph
context manager. -
The ability to disable
autograph
conversion has been added by using the newqml.capture.disable_autograph
decorator or context manager. Additionally, theautograph
keyword argument has been removed from theQNode
constructor. To enable autograph conversion, use theqjit
decorator together with theqml.capture.disable_autograph
context manager. (#8102) (#8104) -
Roundtrip testing and module verification to the Python compiler is now done in
run_filecheck
andrun_filecheck_qjit
fixtures. (#8049) -
Various type hints have been improved internally. (#8086) (#8284)
-
The
cond
primitive with program capture no longer stores missing false branches asNone
, instead storing them as jaxprs with no output. (#8080) -
Unnecessary execution tests along with accuracy validation in
tests/ops/functions/test_map_wires.py
were removed due to stochastic failures. (#8032) -
A new
all-tests-passed
gatekeeper job has been added tointerface-unit-tests.yml
to ensure all test jobs complete successfully before triggering downstream actions. This reduces the need to maintain a long list of required checks in GitHub settings. Also added the previously missingcapture-jax-tests
job to the list of required test jobs, ensuring this test suite is properly enforced in CI. (#7996) -
DefaultQubitLegacy
(test suite only) has been equipped with seeded sampling. This allows for reproducible sampling results of legacy classical shadow across CI. (#7903) -
DefaultQubitLegacy
(test suite only) no longer provides a customized classical shadow implementation. (#7895) -
Capture does not block
wires=0
anymore. This allows Catalyst to work with zero-wire devices. Note thatwires=None
is still not allowed. (#7978) -
The readability of
dynamic_one_shot
postprocessing has been improved to allow for further modification. (#7962) (#8041) -
PennyLane's top-level
__init__.py
file has been updated with imports to improve Python language server support for finding PennyLane submodules. (#7959) -
Type hints in the
measurements
module have been improved. (#7938) -
The codebase has been refactored to adopt modern type hint syntax for Python 3.11+. (#7860) (#7982)
-
Pre-commit hooks have been updated to add gitleaks for security purposes. (#7922)
-
A new fixture called
run_filecheck_qjit
has been added, which can be used to runFileCheck
on integration tests for theqml.compiler.python_compiler
submodule. (#7888) -
The minimum supported
pytest
version has been updated to8.4.1
. (#7853) -
The
pennylane.io
module is now a tertiary module. (#7877) -
Tests for the
split_to_single_terms
transformation are now seeded. (#7851) -
The
rc_sync.yml
file has been updated to work with the latestpyproject.toml
changes. (#7808) (#7818) -
LinearCombination
instances can now be created with_primitive.impl
when capture is enabled and tracing is active. (#7893) -
The
TensorLike
type is now compatible with static type checkers. (#7905) -
The supported version of xDSL has been updated to
0.49
. (#7923) (#7932) (#8120) -
The JAX version used in tests to has been updated to
0.6.2
(#7925) -
An
xdsl_extras
module has been added to the unified compiler framework to house additional utilities and functionality not available upstream in xDSL. (#8067) (#8120) -
Two new xDSL passes have been added to the unified compiler framework:
decompose-graph-state
, which decomposesmbqc.graph_state_prep
operations into their corresponding set of quantum operations for execution on state simulators, andnull-decompose-graph-state
, which replacesmbqc.graph_state_prep
operations with single quantum-register allocation operations for execution on null devices. (#8090) -
The
mbqc
xDSL dialect has been added to the unified compiler framework, which is used to represent measurement-based quantum-computing instructions in the xDSL framework. (#7815) (#8059) -
A compilation pass written with xDSL called
qml.compiler.python_compiler.transforms.ConvertToMBQCFormalismPass
has been added for the experimental unified compiler framework. This pass converts all gates in the MBQC gate set (Hadamard
,S
,RZ
,RotXZX
andCNOT
) to the textbook MBQC formalism. (#7870) (#8254) -
A
dialects
submodule has been added toqml.compiler.python_compiler
which now houses all the xDSL dialects we create. Additionally, theMBQCDialect
andQuantumDialect
dialects have been renamed toMBQC
andQuantum
. (#7897) -
The measurement-plane attribute of the unified compiler
mbqc
dialect now uses the "opaque syntax" format when printing in the generic IR format. This enables usage of this attribute when IR needs to be passed from xDSL to Catalyst. (#7957) -
A
diagonalize_mcms
option has been added to theftqc.decomposition.convert_to_mbqc_formalism
tape transform that, when set, maps arbitrary-basis mid-circuit measurements into corresponding diagonalizing gates and Z-basis mid-circuit measurements. (#8105) -
The
mbqc.graph_state_prep
operation is now integrated into theconvert_to_mbqc_formalism
pass. (#8153) (#8301) (#8314) (#8362) -
A
graph_state_utils
submodule has been added topython_compiler.transforms.mbqc
for common utilities for MBQC workflows. (#8219) (#8273) -
Support for
pubchempy
has been updated to1.0.5
in the unit tests forqml.qchem.mol_data
. (#8224) -
A nightly RC builds script has been added to
.github/workflows
. (#8148) -
The test files for
pennylane.estimator
were renamed to avoid a dual definition error with thepennylane.labs
module. (#8261)
Documentation 📝
-
The program capture sharp bits page has been updated to include a warning about the experimental nature of the feature. (#8448)
-
The installation page has been updated to include currently supported Python versions and installation instructions. (#8369)
-
The documentation of
qml.probs
andqml.Hermitian
has been updated with a warning to avoid using them together as the output might be different than expected. Furthermore, a warning is raised if a user attempts to useqml.probs
with a Hermitian observable. (#8235) -
"
>>>
" and "...
" have been removed from ".. code-block::
" directives in docstrings to facilitate docstring testing and fit best practices. (#8319) -
Three more examples of the deprecated usage of
qml.device(..., shots=...)
have been updated in the documentation. (#8298) -
The documentation of
qml.device
has been updated to reflect the usage ofpennylanepennylane.set_shots
. (#8294) -
The "Simplifying Operators" section in the Compiling circuits page has been pushed further down the page to show more relevant sections first. (#8233)
-
ancilla
has been renamed toauxiliary
in internal documentation. (#8005) -
Small typos in the docstring for
qml.noise.partial_wires
have been corrected. (#8052) -
The theoretical background section of
pennylane.BasisRotation
has been extended to explain the underlying Lie group/algebra homomorphism between the (dense) rotation matrix and the performed operations on the target qubits. (#7765) -
The code examples in the documentation of
pennylane.specs
have been updated to replace keyword arguments withgradient_kwargs
in the QNode definition. (#8003) -
The documentation for
Operator.pow
andOperator.adjoint
have been updated to clarify optional developer-facing use cases. (#7999) -
The docstring of the
is_hermitian
operator property has been updated to better describe its behaviour. (#7946) -
The docstrings of all optimizers have been improved for consistency and legibility. (#7891)
-
The code example in the documentation for
pennylane.transforms.split_non_commuting
has been updated to give the correct output. (#7892) -
The $\LaTeX$ rendering in the documentation for
qml.TrotterProduct
andqml.trotterize
has been corrected. (#8014) -
The docstring of
ClassicalShadow.entropy
has been updated to trim out the outdated part of an explanation about the different choices of thealpha
parameter. (#8100) -
A warning has been added to the interfaces documentation under the Pytorch section to explain that all Pytorch floating-point inputs are promoted to
torch.float64
. (#8124) -
The Dynamic Quantum Circuits page has been updated to include the latest device-dependent mid-circuit measurement method defaults. (#8149) (#8444)
-
A syntax rendering issue in the DefaultQubit documentation has been fixed to correctly display the
max_workers
parameter. (#8289)
Bug fixes 🐛
-
Fixed a bug in
default.qubit
where the device wasn't properly validating themcm_method
keyword argument. (#8343) -
An error is now raised if postselection is requested for a zero-probability mid-circuit measurement outcome with finite shots and
pennylanepennylane.devices.DefaultQubit
whenmcm_method="deferred"
andpostselect_mode="fill-shots"
, as this previously led to invalid results. (#8389) -
Applying a transform to a
QNode
with capture enabled now returns aQNode
. This allows autograph to transform the user function when transforms are applied to theQNode
. (#8307) -
qml.compiler.python_compiler.transforms.MergeRotationsPass
now takes theadjoint
property of merged operations correctly into account. (#8429) -
Promoting NumPy data to autograd no longer occurs in
qml.qchem.molecular_hamiltonian
. (#8410) -
Fixed compatibility with JAX and PyTorch input parameters in
pennylane.SpecialUnitary
when large numbers of wires are used. (#8209) -
With
qml.capture.enable()
, AutoGraph will now be correctly applied to functions containing control flow that are then wrapped inpennylanepennylane.adjoint
orpennylanepennylane.ctrl
. (#8215) -
Fixed a bug that was causing parameter broadcasting on
default.mixed
with diagonal gates in the computational basis to raise an error. (#8251) -
qml.ctrl(qml.Barrier(), control_wires)
now just returns the originalBarrier
operation, but placed in the circuit where thectrl
happens. (#8238) -
JIT compilation of
pennylanepennylane.MottonenStatePrep
can now accept statically defined state-vector arrays. (#8222) -
Pauli arithmetic operations (e.g.,
op.simplify()
) can now handle abstract/runtime coefficients when participating in a jitted function. (#8190) -
Operators queued with
pennylane.apply
no longer get dequeued by subsequent dequeuing operations (e.g.,pennylane.adjoint
). (#8078) -
Fixed a bug in the decomposition rules of
pennylane.Select
with the graph-based decomposition system that broke the decompositions if the targetops
of theSelect
operator were parametrized. This enables the graph-based decomposition system withSelect
being provided parametrized targetops
. (#8186) -
Exp
andEvolution
now have improved decompositions, allowing them to handle more situations more robustly. In particular, the generator is simplified prior to decomposition. Now, more time evolution operators can be supported on devices that do not natively support them. (#8133) -
A scalar product of a norm one scalar and an operator now decomposes into a
GlobalPhase
and the operator. For example,-1 * qml.X(0)
now decomposes into[qml.GlobalPhase(-np.pi), qml.X(0)]
. This improves the decomposition ofSelect
when there are complicated targetops
. (#8133) -
Fixed a bug that made the queueing behaviour of
qml.PauliWord.operation
andqml.PauliSentence.operation
depndent on the global state of a program due to a caching issue. (#8135) -
A more informative error is raised when extremely deep circuits are attempted to be drawn. (#8139)
-
An error is now raised if sequences of classically processed mid-circuit measurements are used as input to
pennylane.counts
orpennylane.probs
(e.g.,qml.counts([2*qml.measure(0), qml.measure(1)])
) (#8109) -
Simplifying operators raised to integer powers no longer causes recursion errors. (#8044)
-
Fixed a GPU selection issue in
qml.math
with PyTorch when multiple GPUs are present. (#8008) -
The
pennylane.for_loop
function with capture enabled can now properly handle cases whenstart == stop
. (#8026) -
Plxpr primitives now only return dynamically shaped arrays if their outputs actually have dynamic shapes. (#8004)
-
Fixed an issue with the tree-traversal MCM method and non-sequential wire orders that produced incorrect results. (#7991)
-
Fixed a bug in
pennylane.matrix
where an operator's constituent gates in its decomposition were incorrectly queued, causing extraneous gates to appear in the circuit. (#7976) -
An error is now raised if an
end
statement is found in a measurement conditioned branch in a QASM string being imported into PennyLane. (#7872) -
Fixed issue related to
pennylane.transforms.to_zx
adding the support forToffoli
andCCZ
gates conversion into their ZX-graph representation. (#7899) -
Fixed
qml.workflow.get_best_diff_method
to correctly align withexecute
andconstruct_batch
logic in the workflow module for internal consistency. (#7898) -
Issues were resolved with AutoGraph transforming internal PennyLane library code in addition to user-level code, which was causing downstream errors in Catalyst. (#7889)
-
Fixed a bug that caused calls to
QNode.update
(e.g.,circuit.update(...)(shots=10)
) to update the shots value as ifset_shots
had been applied, causing unnecessary warnings to appear. (#7881) -
Fixed attributes and types in the quantum dialect in the unified compiler framework that now allows for types to be inferred correctly when parsing. (#7825)
-
Fixed a bug in
SemiAdder
that was causing failures when inputs were defined with a single wire. (#7940) (#8437) -
Fixed a bug where
qml.prod
,qml.matrix
, andqml.cond
applied on a quantum function was not dequeueing operators passed as arguments to the function. (#8094) (#8119) (#8078) -
Fixed a bug where a copy of
ShadowExpvalMP
was incorrect for a multi-term composite observable. (#8078) -
Fixed a bug where
pennylane.transforms.cancel_inverses
,pennylane.transforms.merge_rotations
,pennylane.transforms.single_qubit_fusion
,pennylane.transforms.commute_controlled
, andpennylane.transforms.clifford_t_decomposition
were giving incorrect results when acting on (#8297) (8297) -
When using
mcm_method="tree-traversal"
withqml.samples
, the data type of the returned values is nowint
. This change ensures consistency with the output of other MCM methods. (#8274) -
The labels for operators that have multiple matrix-valued parameters (e.g. those from
pennylane.operation.Operator
) can now also be drawn correctly (e.g. withqml.draw
). (#8432) -
Fixed a bug with
pennylane.estimator.resource_mapping._map_to_resource_op()
where it was incorrectly mapping thepennylane.TrotterProduct
template. (#8425) -
Fixed bugs in the
pennylane.estimator
module pertaining to tracking resource operator names, as well as the handling of decompositions and measurement operators by the mapper used by thepennylane.estimator.estimate.estimate
function. (#8384) -
Fixed a bug where
pennylane.ops.rs_decomposition
logic to streamline queuing conditions were applied incorrectly. (#8441)
Contributors ✍️
This release contains contributions from (in alphabetical order):
Runor Agbaire, Guillermo Alonso, Ali Asadi, Utkarsh Azad, Astral Cai, Joey Carter, Yushao Chen, Isaac De Vlugt, Diksha Dhawan, Gabriela Sanchez Diaz, Marcus Edwards, Lillian Frederiksen, Pietropaolo Frisoni, Simone Gasperini, Diego Guala, Sengthai Heng Austin Huang, David Ittah, Soran Jahangiri, Korbinian Kottmann, Elton Law, Mehrdad Malekmohammadi, Pablo Antonio Moreno Casares, Anton Naim Ibrahim, Erick Ochoa, Lee James O'Riordan, Mudit Pandey, Andrija Paurevic, Justin Pickering, Alex Preciado, Shuli Shu, Jay Soni, Paul Haochen Wang, David Wierichs, Jake Zaia.