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.estimatefunction 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.estimatefunction 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.estimatefunction 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.estimatecan 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.estimatormodule.For example, the lightweight representation of
QFTisqre.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.QROMStatePreparationacting 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.estimatormodule.-
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.Adjointas 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.estimatormodule, 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.ResourceConfigclass. 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.ResourceConfigis used to modify the default precision of single qubit rotations, andTcounts 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.VibrationalHamiltonianclasses 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_orbitalsandnum_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.ResourceOperatorclass 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.Deallocateclasses, which were added to manage and track wire usage during resource estimation and withinpennylane.estimator.resource_operator.ResourceOperatordefinitions. (#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.allocateandpennylane.deallocatefunctions. 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.allocatefunction 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.allocateas 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.allocateas a context manager ensures that allocation and safe deallocation are controlled within a localized scope. Equivalenty,pennylane.allocatecan be used in-line along withpennylane.deallocatefor 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
forloop.@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 labelled1and2in the diagram below). This is due to the fact thatnew_qubit1andnew_qubit2can both be reused after they've been deallocated in the first iteration of theforloop:>>> print(qml.draw(circuit, level="device")()) 0: ──H───────────╭●──────────────╭●─┤ <Z> 1: ──┤↗│ │0⟩────│───┤↗│ │0⟩────│──┤ 2: ───║────────Z─╰X───║────────Z─╰X─┤ ╚════════╝ ╚════════╝Additionally,
pennylane.allocateandpennylane.deallocatework withpennylane.qjitwith some restrictions.
Resource tracking with Catalyst 🧾
-
Users can now use the
pennylane.specsfunction 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.specsfunction now accepts acompute_depthkeyword argument, which is set toTrueby default. Since depth computation is usually the most expensive resource to calculate, making it optional can increase the performance ofpennylane.specswhen 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 ofTgates 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 ofTgates 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────┤ StateWe can apply the holistic
pennylane.transforms.zx.optimize_t_countcompilation pass to reduce the number ofTgates. In this case, allTgates 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_basisfunction andpennylane.ops.op_math.ChangeOpBasisclass. Operators arranged in a compute-uncompute pattern (U V U†, which is equivalent to changing the basis in whichVis expressed) can be efficiently controlled, as the only the central (target) operatorVneeds to be controlled, and notUorU†. (#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
QFTandAdjoint(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.Adderis 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.qjitto 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.QNGOptimizerQJIToptimizer, which is aqml.qjit-compatible analogue topennylane.QNGOptimizer. In this release, we've added thepennylane.MomentumQNGOptimizerQJIToptimizer, 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
forloop. 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.allocateare 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.allocateunlocks the following features:-
The
pennylane.transforms.decomposetransform now accepts amax_work_wiresargument 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.MultiControlledXthat dynamically allocate work wires if none were explicitly specified via thework_wiresargument. (#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
partialhas been added, which allows for simplifications in the decomposition ofpennylane.Selectunder 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.QROMdecompositions.
-
-
The decomposition of
pennylane.BasisRotationhas 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.PhaseShiftgates are required in case the matrix has a determinant equal to$\pm 1$ . (#7765) -
The
pennylane.transforms.decomposetransform 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.MultiControlledXnow utilizepennylane.TemporaryANDin place ofpennylane.Toffoligates, 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.Superpositionwas improved, resulting in its decomposition being more performant and efficient. (#7880) -
pennylane.decomposition.has_decompandpennylane.decomposition.list_decompsnow 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 thatGlobalPhaseis not assumed to have a decomposition under the new system. (#8156) -
pennylane.transforms.decomposeandpennylane.preprocess.decomposenow 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.DecompGraphSolutionhas been added to store the solution of a decomposition graph. An instance of this class is returned from thesolvemethod of thepennylane.decomposition.decomposition_graph.DecompositionGraphclass. (#8031)
OpenQASM-PennyLane interoperability
-
The
pennylane.from_qasm3function 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_openqasmnow supports mid-circuit measurements and conditionals of unprocessed measurement values. (#8210)
Setting shots
-
The number of
shotscan 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
shotsvalue in a QNode is equivalent to decorating withpennylane.workflow.set_shots. However, decorating withpennylane.workflow.set_shotsoverrides 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_shotstransform can now be directly applied to a QNode without the need forfunctools.partial, providing a more user-friendly syntax and negating having to import thefunctoolspackage. (#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_decompositiontransform withmethod="gridsynth"is now compatible with quantum just-in-time compilation via thepennylane.qjitdecorator. (#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_decompositiontransform can now decompose circuits with mid-circuit measurements, including Catalyst's measurement operations. It also now handlesRZandPhaseShiftoperations where angles are odd multiples of$\pm \tfrac{\pi}{4}$ more efficiently when usingmethod="gridsynth". (#7793) (#7942) -
The
pennylane.ops.rs_decompositionmethod 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_rtolkeyword argument. (#8056)
Transforms
-
New transforms called
pennylane.transforms.match_relative_phase_toffoliandpennylane.transforms.match_controlled_iX_gatehave 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_matrixandpennylanetransforms.phase_polynomialare available in PennyLane. These IRs are used in compilation passes to optimizeCNOTand phase polynomial circuits, respectively. Additionally, thepennylanetransforms.rowcolhas been added, which uses the parity matrix as its IR forCNOTrouting under constraint connectivity. (#8171) (#8443)The example below showcases the use of
pennylanetransforms.parity_matrix, which acts on circuits containing onlyCNOTgates.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_polynomialtransform functions similarly, operating on circuits containining onlyCNOTandRZgates 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_gradienthas been added, which lets you realize arbitrary anglepennylane.RZrotations 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 onTgates compared to other discretization schemes. (#8213) -
A new keyword argument called
shot_disthas been added to thepennylane.transforms.split_non_commutingtransform. 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/LinearCombinationwith finiteshots, the current default behaviour ofpennylane.transforms.split_non_commutingwill performshotsexecutions for each group of commuting terms. With theshot_distargument, this behaviour can be changed:"uniform": evenly distributes the number ofshotsacross all groups of commuting terms"weighted": distributes the number ofshotsaccording to weights proportional to the L1 norm of the coefficients in each group"weighted_random": same as"weighted", but the numbers ofshotsare 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_globaltransform 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_matrixis 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.snapshotscan 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, theUserWarningthat 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.samplefunction can now receive an optionaldtypeparameter which sets the type and precision of the samples returned by this measurement process. (#8189) (#8271) -
DefaultQubitwill now default to the tree-traversal MCM method whenmcm_method="device". (#7885) -
DefaultQubitnow determines themcm_methodinDevice.setup_execution_config, making it easier to tell whichmcm_methodwill be used. This also allowsdefer_measurementsanddynamic_one_shotto be applied at different locations in the preprocessing program. (#8184) -
The default implementation of
Device.setup_execution_confignow choses"device"as the defaultmcm_methodif it is available, as specified by the device TOML file. (#7968) -
ExecutionConfigandMCMConfigfrompennylane.devicesare 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.TransformContainernow 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.countscan now be captured with program capture. Circuits returningcountsstill cannot be interpreted or executed with program capture. (#8229) -
Templates are now compatible with program capture. (#8211)
-
PennyLane
autographsupports standard Python for index assignment (arr[i] = x) and updating array elements (arr[i] += x) instead ofjax.numpyform (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,orandnot) 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_fncan now be a subclass ofOperatorwhen nofalse_fnis 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
ifstatement 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.qjithas been updated with suggested fixes. (#7916) -
Two new
drawandgenerate_mlir_graphfunctions have been introduced in theqml.compiler.python_compiler.visualizationmodule 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
QuantumxDSL dialect now has more strict constraints for operands and results. (#8083) -
A callback mechanism has been added to
qml.compiler.python_compilersubmodule to inspect the intermediate representation of the program between multiple compilation passes. (#7964) -
A
QuantumParserclass has been added to theqml.compiler.python_compilersubmodule that automatically loads relevant dialects. (#7888) -
Two new operations have been added to the
Quantumdialect of the unified compiler: -
A compilation pass written called
qml.compiler.python_compiler.transforms.MeasurementsFromSamplesPasshas been added for integration with the unified compiler framework. This pass replaces all terminal measurements in a program with a singlepennylane.samplemeasurement, 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_decompositionhas 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.2after a bug affectingdefault.tensorwas fixed. (#7931) -
A new
pennylane.transforms.resolve_dynamic_wirestransform 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.ResourceQPEandpennylanepennylane.labs.resource_estimation.ResourceIterativeQPE, respectively. Additionally, apennylanepennylane.labs.resource_estimation.ResourceControlledSequencetemplate has been added that allows estimating resources on controlled sequences of resource operators. (#8053) -
estimate_resourceshas been renamed toestimateto make the function name concise and clearer thanlabs.resource_estimation.estimate_resources. (#8232) -
A new
ResourceConfigclass has been added to help track the configuration for errors, precisions and custom decompositions for the resource estimation pipeline. (#8195) -
The symbolic
ResourceOperatorshave been updated to use hyperparameters from theconfigdictionary. (#8181) -
An internal
dequeue()method has been added to theResourceOperatorclass to simplify the instantiation of resource operators which require resource operators as input. (#7974) -
ResourceOperatorinstances can now be compared with==. (#8155) -
A mapper function called
pennylanepennylane.labs.resource_estimation.map_to_resource_ophas been added to map PennyLane operators toResourceOperatorequivalents. (#8146) (#8162) -
Several Labs test files have been renamed to prevent conflict with names in mainline PennyLane tests. (#8264)
-
A queueing issue in the
ResourceOperatortests has been fixed. (#8204)
Labs Trotter Error Estimation
-
Parallelization support for
effective_hamiltonianhas been added to improve performance. (#8081) (#8257) -
New
SparseFragmentandSparseStateclasses have been created to allow the use of sparse matrices for Hamiltonian Fragments when estimating Trotter error. (#7971) -
The
pennylanepennylane.labs.trotter_error.perturbation_errorfunction has been updated to sum over expectation values instead of states. (#8226) -
The docstring in
perturbation_errorhas been updated to use the correct positional argument name. (#8174)
Labs Removals
- The module
qml.labs.zxopthas 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 💔
-
autorayhas 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
autoraypackage was upper-bounded inpyproject.tomlto 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_transformhas been deleted as it was no longer getting used. (#8344) -
get_canonical_interface_namehas 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.PrepSelPrephas been made more reliable by deriving the attributescoeffsand `opsfrom the property ``lcu`` instead of storing them independently. In addition, it is now more consistent with other PennyLane operators, dequeuing its input ``lcu``. (#8169) -
MidMeasureMPnow inherits fromOperatorinstead ofMeasurementProcess, which resolves problems caused by it always acting like an operator. (#8166) -
With the deprecation of the
shotskwarg inqml.device,DefaultQubit.eval_jaxprdoes not useself.shotsfrom the device anymore; instead, it takesshotsas a keyword argument, and the QNode primitive should process theshotsand calleval_jaxpraccordingly. (#8161) -
The methods
pauli.PauliWord.operationandpauli.PauliSentence.operationno longer queue any operators. This improves the consistency of the queuing behaviour for the operators. (#8136) -
qml.sampleno 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
Noneinstead ofpennylane.devices.DefaultExecutionConfigand 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.HilbertSchmidtandpennylane.LocalHilbertSchmidttemplates 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
UandVunitaries as aqml.tape.QuantumTapeand 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
Vare 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.operationhave 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_constantsandcenterviaqml.paulihas been removed. The functions now live in theliealgmodule and top level import and usage is advised. (#7928) (#7994)import pennylane.liealg from pennylane.liealg import lie_closure, structure_constants, center
-
qml.operation.Observableand the correspondingObservable.comparehave been removed, as PennyLane now depends on the more generalOperatorinterface instead. TheOperator.is_hermitianproperty 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.AnyWireshave been removed. To indicate that an operator can act on any number of wires,Operator.num_wires = Noneshould 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_fnfunction has been removed. Instead, useqml.workflow.get_best_diff_methodto obtain the differentiation method. (#7907) -
Top-level access to
DeviceError,PennyLaneDeprecationWarning,QuantumFunctionErrorandExperimentalWarninghas been removed. Please import these objects from the newpennylane.exceptionsmodule. (#7874) -
To improve code reliability,
qml.cut_circuit_mcno longer accepts ashotskeyword argument. The shots should instead be set on the tape itself. (#7882) -
pennylane.tape.tape.expand_tapehas 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
shotskeyword 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_shotstransform 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.DefaultExecutionConfighas 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_typeargument inqml.ctrland 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_stepstopennylane.evolve,pennylane.exp,pennylane.ops.Evolution, andpennylane.ops.Exphas been deprecated and will be removed in a future release. Instead, usepennylane.TrotterProductfor approximate methods, providing thenparameter 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_stepson 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.TrotterProductas 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.expandhas 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.shapeandQuantumScript.numeric_typehave been deprecated and will be removed in version v0.44. Instead, the corresponding.shapeor.numeric_typeof theMeasurementProcessclass should be used. (#7950) -
Some unnecessary methods of the
qml.CircuitGraphclass have been deprecated and will be removed in version v0.44: (#7904)-
print_contentsin favor ofprint(obj)-observables_in_orderin favor ofobservables-operations_in_orderin favor ofoperations-ancestors_in_orderin favor ofancestors(obj, sort=True)-descendants_in_orderin favor ofdescendants(obj, sort=True)
-
-
The
QuantumScript.to_openqasmmethod has been deprecated and will be removed in version v0.44. Instead, theqml.to_openqasmfunction 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=Noneargument in thepennylane.workflow.get_transform_program,pennylane.workflow.construct_batch,qml.draw,qml.draw_mpl, andqml.specstransforms 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.SquaredErrorLosshas 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,insertand noise mitigation transforms from thetransformsmodule has been deprecated. Instead, these functions should be imported from thenoisemodule, which is a more appropriate location for them. (#7854) -
The
qml.QNode.add_transformmethod 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.ymlandupload.yml) have been updated to useubuntu-24.04runners. (#8371) -
measurementsis now a "core" module in thetachspecification. (#7945) -
Enforce various modules to follow modular architecture via
tach. (#7847) -
CI workflows to test documentation using
sybilhave been added. (#8324) (#8328) (#8329) (#8330) (#8331) (#8386) -
The
templates/subroutinesnow hasarithmetic,qchem, andtime_evolutionsubmodules. (#8333) -
test_horizontal_cartan_subalgebra.pynow uses our fixtureseedfor reproducibility and CI stability. (#8304) -
The
qml.compiler.python_compilersubmodule has been restructured to be more cohesive. (#8273) -
default.tensornow 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.decomposenow works in graph decomposition mode when a gateset is provided.default.qubitandnull.qubitcan now use graph decomposition mode. (#8225) (#8265) (#8260) -
Usage of the
pytest.mark.capturemarker from tests in thetests/python_compilerdirectory has been removed. (#8234) -
pylinthas been updated to v3.3.8 in our CI andrequirements-dev.txt(#8216) -
Links in the
README.mdhave been updated. (#8165) -
The
autographguide to now reflects new capabilities. (#8132) -
strict=Trueis now used tozipusage in source code. (#8164) (#8182) (#8183) -
The
autographkeyword argument has been removed from theQNodeconstructor. To enable autograph conversion, use theqjitdecorator together with theqml.capture.disable_autographcontext manager. -
The ability to disable
autographconversion has been added by using the newqml.capture.disable_autographdecorator or context manager. Additionally, theautographkeyword argument has been removed from theQNodeconstructor. To enable autograph conversion, use theqjitdecorator together with theqml.capture.disable_autographcontext manager. (#8102) (#8104) -
Roundtrip testing and module verification to the Python compiler is now done in
run_filecheckandrun_filecheck_qjitfixtures. (#8049) -
Various type hints have been improved internally. (#8086) (#8284)
-
The
condprimitive 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.pywere removed due to stochastic failures. (#8032) -
A new
all-tests-passedgatekeeper job has been added tointerface-unit-tests.ymlto 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-testsjob 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=0anymore. This allows Catalyst to work with zero-wire devices. Note thatwires=Noneis still not allowed. (#7978) -
The readability of
dynamic_one_shotpostprocessing has been improved to allow for further modification. (#7962) (#8041) -
PennyLane's top-level
__init__.pyfile has been updated with imports to improve Python language server support for finding PennyLane submodules. (#7959) -
Type hints in the
measurementsmodule 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_qjithas been added, which can be used to runFileCheckon integration tests for theqml.compiler.python_compilersubmodule. (#7888) -
The minimum supported
pytestversion has been updated to8.4.1. (#7853) -
The
pennylane.iomodule is now a tertiary module. (#7877) -
Tests for the
split_to_single_termstransformation are now seeded. (#7851) -
The
rc_sync.ymlfile has been updated to work with the latestpyproject.tomlchanges. (#7808) (#7818) -
LinearCombinationinstances can now be created with_primitive.implwhen capture is enabled and tracing is active. (#7893) -
The
TensorLiketype 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_extrasmodule 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_prepoperations into their corresponding set of quantum operations for execution on state simulators, andnull-decompose-graph-state, which replacesmbqc.graph_state_prepoperations with single quantum-register allocation operations for execution on null devices. (#8090) -
The
mbqcxDSL 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.ConvertToMBQCFormalismPasshas been added for the experimental unified compiler framework. This pass converts all gates in the MBQC gate set (Hadamard,S,RZ,RotXZXandCNOT) to the textbook MBQC formalism. (#7870) (#8254) -
A
dialectssubmodule has been added toqml.compiler.python_compilerwhich now houses all the xDSL dialects we create. Additionally, theMBQCDialectandQuantumDialectdialects have been renamed toMBQCandQuantum. (#7897) -
The measurement-plane attribute of the unified compiler
mbqcdialect 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_mcmsoption has been added to theftqc.decomposition.convert_to_mbqc_formalismtape 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_prepoperation is now integrated into theconvert_to_mbqc_formalismpass. (#8153) (#8301) (#8314) (#8362) -
A
graph_state_utilssubmodule has been added topython_compiler.transforms.mbqcfor common utilities for MBQC workflows. (#8219) (#8273) -
Support for
pubchempyhas been updated to1.0.5in 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.estimatorwere renamed to avoid a dual definition error with thepennylane.labsmodule. (#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.probsandqml.Hermitianhas 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.probswith 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.devicehas 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)
-
ancillahas been renamed toauxiliaryin internal documentation. (#8005) -
Small typos in the docstring for
qml.noise.partial_wireshave been corrected. (#8052) -
The theoretical background section of
pennylane.BasisRotationhas 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.specshave been updated to replace keyword arguments withgradient_kwargsin the QNode definition. (#8003) -
The documentation for
Operator.powandOperator.adjointhave been updated to clarify optional developer-facing use cases. (#7999) -
The docstring of the
is_hermitianoperator 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_commutinghas been updated to give the correct output. (#7892) -
The
$\LaTeX$ rendering in the documentation forqml.TrotterProductandqml.trotterizehas been corrected. (#8014) -
The docstring of
ClassicalShadow.entropyhas been updated to trim out the outdated part of an explanation about the different choices of thealphaparameter. (#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_workersparameter. (#8289)
Bug fixes 🐛
-
Fixed a bug in
default.qubitwhere the device wasn't properly validating themcm_methodkeyword 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.DefaultQubitwhenmcm_method="deferred"andpostselect_mode="fill-shots", as this previously led to invalid results. (#8389) -
Applying a transform to a
QNodewith 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.MergeRotationsPassnow takes theadjointproperty 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.SpecialUnitarywhen 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.adjointorpennylanepennylane.ctrl. (#8215) -
Fixed a bug that was causing parameter broadcasting on
default.mixedwith diagonal gates in the computational basis to raise an error. (#8251) -
qml.ctrl(qml.Barrier(), control_wires)now just returns the originalBarrieroperation, but placed in the circuit where thectrlhappens. (#8238) -
JIT compilation of
pennylanepennylane.MottonenStatePrepcan 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.applyno longer get dequeued by subsequent dequeuing operations (e.g.,pennylane.adjoint). (#8078) -
Fixed a bug in the decomposition rules of
pennylane.Selectwith the graph-based decomposition system that broke the decompositions if the targetopsof theSelectoperator were parametrized. This enables the graph-based decomposition system withSelectbeing provided parametrized targetops. (#8186) -
ExpandEvolutionnow 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
GlobalPhaseand the operator. For example,-1 * qml.X(0)now decomposes into[qml.GlobalPhase(-np.pi), qml.X(0)]. This improves the decomposition ofSelectwhen there are complicated targetops. (#8133) -
Fixed a bug that made the queueing behaviour of
qml.PauliWord.operationandqml.PauliSentence.operationdepndent 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.countsorpennylane.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.mathwith PyTorch when multiple GPUs are present. (#8008) -
The
pennylane.for_loopfunction 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.matrixwhere 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
endstatement is found in a measurement conditioned branch in a QASM string being imported into PennyLane. (#7872) -
Fixed issue related to
pennylane.transforms.to_zxadding the support forToffoliandCCZgates conversion into their ZX-graph representation. (#7899) -
Fixed
qml.workflow.get_best_diff_methodto correctly align withexecuteandconstruct_batchlogic 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_shotshad 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
SemiAdderthat was causing failures when inputs were defined with a single wire. (#7940) (#8437) -
Fixed a bug where
qml.prod,qml.matrix, andqml.condapplied on a quantum function was not dequeueing operators passed as arguments to the function. (#8094) (#8119) (#8078) -
Fixed a bug where a copy of
ShadowExpvalMPwas 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_decompositionwere 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.TrotterProducttemplate. (#8425) -
Fixed bugs in the
pennylane.estimatormodule pertaining to tracking resource operator names, as well as the handling of decompositions and measurement operators by the mapper used by thepennylane.estimator.estimate.estimatefunction. (#8384) -
Fixed a bug where
pennylane.ops.rs_decompositionlogic 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.