Release 0.40.0
New features since last release
Efficient state preparation methods 🦾
- 
State preparation tailored for matrix product states (MPS) is now supported with :class:
qml.MPSPrep <pennylane.MPSPrep>on thelightning.tensordevice. (#6431)Given a list of
$n$ tensors that represents an MPS,$[A^{(0)}, ..., A^{(n-1)}]$ , :class:qml.MPSPrep <pennylane.MPSPrep>lets you directly inject the MPS into a QNode as the initial state of the circuit without any need for pre-processing. The first and last tensors in the list must be rank-2, while all intermediate tensors should be rank-3.import pennylane as qml import numpy as np mps = [ np.array([[0.0, 0.107], [0.994, 0.0]]), np.array( [ [[0.0, 0.0, 0.0, -0.0], [1.0, 0.0, 0.0, -0.0]], [[0.0, 1.0, 0.0, -0.0], [0.0, 0.0, 0.0, -0.0]], ] ), np.array( [ [[-1.0, 0.0], [0.0, 0.0]], [[0.0, 0.0], [0.0, 1.0]], [[0.0, -1.0], [0.0, 0.0]], [[0.0, 0.0], [1.0, 0.0]], ] ), np.array([[-1.0, -0.0], [-0.0, -1.0]]), ] dev = qml.device("lightning.tensor", wires = [0, 1, 2, 3]) @qml.qnode(dev) def circuit(): qml.MPSPrep(mps, wires = [0,1,2]) return qml.state()
>>> print(circuit()) [ 0. +0.j 0. +0.j 0. +0.j -0.1066+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.9943+0.j 0. +0.j 0. +0.j 0. +0.j]At this time, :class:
qml.MPSPrep <pennylane.MPSPrep>is only supported on thelightning.tensordevice. - 
Custom-made state preparation for linear combinations of quantum states is now available with :class:
qml.Superposition <pennylane.Superposition>. (#6670)Given a list of
$m$ coefficients$c_i$ and basic states$|b_i\rangle$ , :class:qml.Superposition <pennylane.Superposition>prepares$|\phi\rangle = \sum_i^m c_i |b_i\rangle$ . Here is a simple example showing how to use :class:qml.Superposition <pennylane.Superposition>to prepare$\tfrac{1}{\sqrt{2}} |00\rangle + \tfrac{1}{\sqrt{2}} |10\rangle$ .coeffs = np.array([0.70710678, 0.70710678]) basis = np.array([[0, 0], [1, 0]]) @qml.qnode(qml.device('default.qubit')) def circuit(): qml.Superposition(coeffs, basis, wires=[0, 1], work_wire=[2]) return qml.state()
>>> circuit() Array([0.7071068 +0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0.70710677+0.j, 0. +0.j, 0. +0.j, 0. +0.j], dtype=complex64)Note that specification of one
work_wireis required. 
Enhanced QSVT functionality 🤩
- 
New functionality to calculate and convert phase angles for QSP and QSVT has been added with :func:
qml.poly_to_angles <pennylane.poly_to_angles>and :func:qml.transform_angles <pennylane.transform_angles>. (#6483)The :func:
qml.poly_to_angles <pennylane.poly_to_angles>function calculates phase angles directly given polynomial coefficients and the routine in which the angles will be used ("QSVT","QSP", or"GQSP"):>>> poly = [0, 1.0, 0, -1/2, 0, 1/3] >>> qsvt_angles = qml.poly_to_angles(poly, "QSVT") >>> print(qsvt_angles) [-5.49778714 1.57079633 1.57079633 0.5833829 1.61095884 0.74753829]
The :func:
qml.transform_angles <pennylane.transform_angles>function can be used to convert angles from one routine to another:>>> qsp_angles = np.array([0.2, 0.3, 0.5]) >>> qsvt_angles = qml.transform_angles(qsp_angles, "QSP", "QSVT") >>> print(qsvt_angles) [-6.86858347 1.87079633 -0.28539816]
 - 
The :func:
qml.qsvt <pennylane.qsvt>function has been improved to be more user-friendly, with enhanced capabilities. (#6520) (#6693)Block encoding and phase angle computation are now handled automatically, given a matrix to encode, polynomial coefficients, and a block encoding method (
"prepselprep","qubitization","embedding", or"fable", all implemented with their corresponding operators in PennyLane).# P(x) = -x + 0.5 x^3 + 0.5 x^5 poly = np.array([0, -1, 0, 0.5, 0, 0.5]) hamiltonian = qml.dot([0.3, 0.7], [qml.Z(1), qml.X(1) @ qml.Z(2)]) dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(): qml.qsvt(hamiltonian, poly, encoding_wires=[0], block_encoding="prepselprep") return qml.state() matrix = qml.matrix(circuit, wire_order=[0, 1, 2])()
>>> print(matrix[:4, :4].real) [[-0.1625 0. -0.3793 0. ] [ 0. -0.1625 0. 0.3793] [-0.3793 0. 0.1625 0. ] [ 0. 0.3793 0. 0.1625]]
The old functionality can still be accessed with :func:
qml.qsvt_legacy <pennylane.qsvt_legacy>. - 
A new :class:
qml.GQSP <pennylane.GQSP>template has been added to perform Generalized Quantum Signal Processing (GQSP). (#6565) Similar to QSVT, GQSP is an algorithm that polynomially transforms an input unitary operator, but with fewer restrictions on the chosen polynomial.You can also use :func:
qml.poly_to_angles <pennylane.poly_to_angles>to obtain angles for GQSP!# P(x) = 0.1 + 0.2j x + 0.3 x^2 poly = [0.1, 0.2j, 0.3] angles = qml.poly_to_angles(poly, "GQSP") @qml.prod # transforms the qfunc into an Operator def unitary(wires): qml.RX(0.3, wires) dev = qml.device("default.qubit") @qml.qnode(dev) def circuit(angles): qml.GQSP(unitary(wires = 1), angles, control = 0) return qml.state() matrix = qml.matrix(circuit, wire_order=[0, 1])(angles) ``` ```pycon >>> print(np.round(matrix,3)[:2, :2]) [[0.387+0.198j 0.03 -0.089j] [0.03 -0.089j 0.387+0.198j]]
 
Generalized Trotter products 🐖
- 
Trotter products that work on exponentiated operators directly instead of full system hamiltonians can now be encoded into circuits with the addition of :class:
qml.TrotterizedQfunc <pennylane.TrotterizedQfunc>and :func:qml.trotterize <pennylane.trotterize>. This allows for custom specification of the first-order expansion of the Suzuki-Trotter product formula and extrapolating it to the$n^{\text{th}}$ order. (#6627)If the first-order of the Suzuki-Trotter product formula for a given problem is known, :class:
qml.TrotterizedQfunc <pennylane.TrotterizedQfunc>and :func:qml.trotterize <pennylane.trotterize>let you implement the$n^{\text{th}}$ -order product formula while only specifying the first-order term as a quantum function.def my_custom_first_order_expansion(time, theta, phi, wires, flip): qml.RX(time * theta, wires[0]) qml.RY(time * phi, wires[1]) if flip: qml.CNOT(wires=wires[:2])
:func:
qml.trotterize <pennylane.trotterize>requires the quantum function representing the first-order product formula, the number of Trotter steps, and the desired order. It returns a function with the same call signature as the first-order product formula quantum function:@qml.qnode(qml.device("default.qubit")) def my_circuit(time, theta, phi, num_trotter_steps): qml.trotterize( first_order_expansion, n=num_trotter_steps, order=2, )(time, theta, phi, wires=['a', 'b'], flip=True) return qml.state()
Alternatively, :class:
qml.TrotterizedQfunc <pennylane.TrotterizedQfunc>can be used as follows:@qml.qnode(qml.device("default.qubit")) def my_circuit(time, theta, phi, num_trotter_steps): qml.TrotterizedQfunc( time, theta, phi, qfunc=my_custom_first_order_expansion, n=num_trotter_steps, order=2, wires=['a', 'b'], flip=True, ) return qml.state()
>>> time = 0.1 >>> theta, phi = (0.12, -3.45) >>> print(qml.draw(my_circuit, level="device")(time, theta, phi, num_trotter_steps=1)) a: ──RX(0.01)──╭●─╭●──RX(0.01)──┤ State b: ──RY(-0.17)─╰X─╰X──RY(-0.17)─┤ State
Both methods produce the same results, but offer different interfaces based on the application or overall preference.
 
Bosonic operators 🎈
A new module, :mod:qml.bose <pennylane.bose>, has been added to PennyLane that includes support  for constructing and manipulating Bosonic operators and converting between Bosonic operators and  qubit operators.
- 
Bosonic operators analogous to
qml.FermiWordandqml.FermiSentenceare now available with :class:qml.BoseWord <pennylane.BoseWord>and :class:qml.BoseSentence <pennylane.BoseSentence>. (#6518):class:
qml.BoseWord <pennylane.BoseWord>and :class:qml.BoseSentence <pennylane.BoseSentence>operate similarly to their fermionic counterparts. To create a Bose word, a dictionary is required as input, where the keys are tuples of boson indices and values are'+/-'(denoting the bosonic creation/annihilation operators). For example, the$b^{\dagger}_0 b_1$ can be constructed as follows.>>> w = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'}) >>> print(w) b⁺(0) b(1)
Multiple Bose words can then be combined to form a Bose sentence:
>>> w1 = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'}) >>> w2 = qml.BoseWord({(0, 1) : '+', (1, 2) : '-'}) >>> s = qml.BoseSentence({w1 : 1.2, w2: 3.1}) >>> print(s) 1.2 * b⁺(0) b(1) + 3.1 * b⁺(1) b(2)
 - 
Functionality for converting bosonic operators to qubit operators is available with :func:
qml.unary_mapping <pennylane.unary_mapping>, :func:qml.binary_mapping <pennylane.binary_mapping>, and :func:qml.christiansen_mapping <pennylane.christiansen_mapping>. (#6623) (#6576) (#6564)All three mappings follow the same syntax, where a :class:
qml.BoseWord <pennylane.BoseWord>or :class:qml.BoseSentence <pennylane.BoseSentence>is required as input.>>> w = qml.BoseWord({(0, 0): "+"}) >>> qml.binary_mapping(w, n_states=4) 0.6830127018922193 * X(0) + -0.1830127018922193 * X(0) @ Z(1) + -0.6830127018922193j * Y(0) + 0.1830127018922193j * Y(0) @ Z(1) + 0.3535533905932738 * X(0) @ X(1) + -0.3535533905932738j * X(0) @ Y(1) + 0.3535533905932738j * Y(0) @ X(1) + (0.3535533905932738+0j) * Y(0) @ Y(1)
Additional fine-tuning is available within each function, such as the maximum number of allowed bosonic states and a tolerance for discarding imaginary parts of the coefficients.
 
Construct vibrational Hamiltonians 🔨
- 
Several new features are available in the :mod:
qml.qchem <pennylane.qchem>module to help with the construction of vibrational Hamiltonians. This includes:- The :class:
~.qchem.VibrationalPESclass to store potential energy surface information. (#6652) 
pes_onemode = np.array([[0.309, 0.115, 0.038, 0.008, 0.000, 0.006, 0.020, 0.041, 0.070]]) pes_twomode = np.zeros((1, 1, 9, 9)) dipole_onemode = np.zeros((1, 9, 3)) gauss_weights = np.array([3.96e-05, 4.94e-03, 8.85e-02, 4.33e-01, 7.20e-01, 4.33e-01, 8.85e-02, 4.94e-03, 3.96e-05]) grid = np.array([-3.19, -2.27, -1.47, -0.72, 0.0, 0.72, 1.47, 2.27, 3.19]) pes_object = qml.qchem.VibrationalPES( freqs=np.array([0.025]), grid=grid, uloc=np.array([[1.0]]), gauss_weights=gauss_weights, pes_data=[pes_onemode, pes_twomode], dipole_data=[dipole_onemode], localized=False, dipole_level=1, )
- The :func:
~.qchem.taylor_hamiltonianfunction to build a Taylor Hamiltonian from a :class:~.VibrationalPESobject. (#6523) 
>>> qml.qchem.taylor_hamiltonian(pes_object, 4, 2) ( 0.016867926879358452 * I(0) + -0.007078617919572303 * Z(0) + 0.0008679410939323631 * X(0) )
- The :func:
~.qchem.taylor_bosonicfunction to build a Taylor Hamiltonian in terms of Bosonic operators. (#6523) 
>>> coeffs_arr = qml.qchem.taylor_coeffs(pes_object) >>> bose_op = qml.qchem.taylor_bosonic(coeffs_arr, pes_object.freqs, is_local=pes_object.localized, uloc=pes_object.uloc) >>> type(bose_op) pennylane.bose.bosonic.BoseSentence
Additional functionality is also available to optimize molecular geometries and convert between representations:
- 
Convert Christiansen Hamiltonian integrals in the harmonic oscillator basis to integrals in the vibrational self-consistent field (VSCF) basis with the :func:
~.qchem.vscf_integralsfunction. (#6688) - 
Find the lowest energy configuration of molecules with :func:
~.qchem.optimize_geometry. (#6453) (#6666) - 
Separate normal mode frequencies and localize them with :func:
~.qchem.localize_normal_modes. (#6453) 
 - The :class:
 
Labs: a place for unified and rapid prototyping of research software 🧑🔬
The new :mod:qml.labs <pennylane.labs> module will house experimental research software 🔬. Features here may be useful for state-of-the-art research, beta testing, or getting a sneak peek into potential new features before they are added to PennyLane.
The experimental nature of this module means features may not integrate well with other PennyLane staples like differentiability, JAX, or JIT compatibility. There may also be unexpected sharp bits 🔪 and errors ❌.
.. warning:: This module is experimental! Breaking changes and removals will happen without warning. Please use these features carefully and let us know your thoughts. Your feedback will inform how these features become a part of mainline PennyLane.
Resource estimation
- 
Resource estimation functionality in Labs is focused on being light-weight and flexible. The Labs :mod:
qml.labs.resource_estimation <pennylane.labs.resource_estimation>module involves modifications to core PennyLane that reduce the memory requirements and computational time of resource estimation. These include new or modified base classes and one new function:- :class:
~.labs.resource_estimation.Resources- This class is simplified inlabs, removing the arguments:gate_sizes,depth, andshots. (#6428) - :class:
~.labs.resource_estimation.ResourceOperator- Replaces :class:~.resource.ResourceOperation, expanded to include decompositions. (#6428) - :class:
~.labs.resource_estimation.CompressedResourceOp- A new class with the minimum information to estimate resources: the operator type and the parameters needed to decompose it. (#6428) - :class:
~.labs.resource_estimation.ResourceOperator- versions of many existing PennyLane operations, like Pauli operators, :class:~.labs.resource_estimation.ResourceHadamard, and :class:~.labs.resource_estimation.ResourceCNOT. (#6447) (#6579) (#6538) (#6592) - :func:
~.labs.resource_estimation.get_resources()- The new entry point to efficiently obtain the resources of quantum circuits. (#6500) 
Using new Resource versions of existing operations and :func:
~.labs.resource_estimation.get_resources, we can estimate resources quickly:import pennylane.labs.resource_estimation as re def my_circuit(): for w in range(2): re.ResourceHadamard(w) re.ResourceCNOT([0, 1]) re.ResourceRX(1.23, 0) re.ResourceRY(-4.56, 1) re.ResourceQFT(wires=[0, 1, 2]) return qml.expval(re.ResourceHadamard(2))
>>> res = re.get_resources(my_circuit)() >>> print(res) wires: 3 gates: 202 gate_types: {'Hadamard': 5, 'CNOT': 10, 'T': 187}
We can also set custom gate sets for decompositions:
>>> gate_set={"Hadamard","CNOT","RZ", "RX", "RY", "SWAP"} >>> res = re.get_resources(my_circuit, gate_set=gate_set)() >>> print(res) wires: 3 gates: 24 gate_types: {'Hadamard': 5, 'CNOT': 7, 'RX': 1, 'RY': 1, 'SWAP': 1, 'RZ': 9}
Alternatively, it is possible to manually substitute associated resources:
>>> new_resources = re.substitute(res, "SWAP", re.Resources(2, 3, {"CNOT":3})) >>> print(new_resources) {'Hadamard': 5, 'CNOT': 10, 'RX': 1, 'RY': 1, 'RZ': 9}
 - :class:
 
Experimental functionality for handling dynamical Lie algebras (DLAs)
- 
Use the :mod:
qml.labs.dla <pennylane.labs.dla>module to perform the KAK decomposition:- :func:
~.labs.dla.cartan_decomp: obtain a Cartan decomposition of an input Lie algebra via an involution. (#6392) - We provide a variety of involutions like :func:
~.labs.dla.concurrence_involution, :func:~.labs.dla.even_odd_involutionand canonical Cartan involutions. (#6392) (#6396) - :func:
~.labs.dla.cartan_subalgebra: compute a horizontal Cartan subalgebra. (#6403) - :func:
~.labs.dla.variational_kak_adj: compute a variational KAK decomposition of a Hermitian operator using a Cartan decomposition and the adjoint representation of a horizontal Cartan subalgebra. (#6446) 
To use this functionality we start with a set of Hermitian operators.
>>> n = 3 >>> gens = [qml.X(i) @ qml.X(i + 1) for i in range(n - 1)] >>> gens += [qml.Z(i) for i in range(n)] >>> H = qml.sum(*gens)
We then generate its Lie algebra by computing the Lie closure.
>>> g = qml.lie_closure(gens) >>> g = [op.pauli_rep for op in g] >>> print(g) [1 * X(0) @ X(1), 1 * X(1) @ X(2), 1.0 * Z(0), ...]
We then choose an involution (e.g. :func:
~.labs.dla.concurrence_involution) that defines a Cartan decompositiong = k + m.kis the vertical subalgebra, andmits horizontal complement (not a subalgebra).>>> from pennylane.labs.dla import concurrence_involution, cartan_decomp >>> involution = concurrence_involution >>> k, m = cartan_decomp(g, involution=involution)
The next step is just re-ordering the basis elements in
gand computing itsstructure_constants.>>> g = k + m >>> adj = qml.structure_constants(g)
We can then compute a (horizontal) Cartan subalgebra
a, that is, a maximal Abelian subalgebra ofm.>>> from pennylane.labs.dla import cartan_subalgebra >>> g, k, mtilde, a, adj = cartan_subalgebra(g, k, m, adj)
Having determined both subalgebras
kanda, we can compute the KAK decomposition variationally like in 2104.00728, see our demo on KAK decomposition in practice.>>> from pennylane.labs.dla import variational_kak_adj >>> dims = (len(k), len(mtilde), len(a)) >>> adjvec_a, theta_opt = variational_kak_adj(H, g, dims, adj, opt_kwargs={"n_epochs": 3000})
 - :func:
 - 
We also provide some additional features that are useful for handling dynamical Lie algebras.
- :func:
~.labs.dla.recursive_cartan_decomp: perform consecutive recursive Cartan decompositions. (#6396) - :func:
~.labs.dla.lie_closure_dense: extension ofqml.lie_closureusing dense matrices. (#6371) (#6695) - :func:
~.labs.dla.structure_constants_dense: extension ofqml.structure_constantsusing dense matrices. (#6396) (#6376) 
 - :func:
 
Vibrational Hamiltonians
- 
New functionality in labs helps with the construction of vibrational Hamiltonians.
>>> symbols = ['H', 'F'] >>> geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]]) >>> mol = qml.qchem.Molecule(symbols, geometry) >>> pes = vibrational_pes(mol)
- Use the 
qml.labs.vibrational.christiansen_hamiltonianfunction and potential energy surfaces to generate Hamiltonians in the Christiansen form. (#6560) 
 - Use the 
 
Improvements 🛠
QChem improvements
- 
The
qml.qchem.factorizefunction now supports new methods for double factorization: Cholesky decomposition (cholesky=True) and compressed double factorization (compressed=True). (#6573) (#6611) - 
A new function for performing the block-invariant symmetry shift on electronic integrals has been added with
qml.qchem.symmetry_shift. (#6574) - 
The differentiable Hartree-Fock workflow is now compatible with JAX. (#6096) (#6707)
 
Transform for combining GlobalPhase instances
- A new transform called 
qml.transforms.combine_global_phaseshas been added. It combines allqml.GlobalPhasegates in a circuit into a single one applied at the end. This can be useful for circuits that include a lot ofqml.GlobalPhasegates that are introduced directly during circuit creation, decompositions that includeqml.GlobalPhasegates, etc. (#6686) 
Better drawing functionality
- 
qml.draw_mplnow has awire_optionskeyword argument, which allows for global- and per-wire customization with options likecolor,linestyle, andlinewidth. (#6486)Here is an example that would make all wires cyan and bold except for wires 2 and 6, which are dashed and a different colour.
@qml.qnode(qml.device("default.qubit")) def circuit(x): for w in range(5): qml.Hadamard(w) return qml.expval(qml.PauliZ(0) @ qml.PauliY(1)) wire_options = {"color": "cyan", "linewidth": 5, 2: {"linestyle": "--", "color": "red"}, 6: {"linestyle": "--", "color": "orange"} } print(qml.draw_mpl(circuit, wire_options=wire_options)(0.52))
 
New device capabilities 💾
- 
Two new methods,
setup_execution_configandpreprocess_transforms, have been added to theDeviceclass. Device developers are encouraged to override these two methods separately instead of thepreprocessmethod. For now, to avoid ambiguity, a device is allowed to override either these two methods orpreprocess, but not both. In the long term, we will slowly phase out the use ofpreprocessin favour of these two methods for better separation of concerns. (#6617) - 
Developers of plugin devices now have the option of providing a TOML-formatted configuration file to declare the capabilities of the device. See Device Capabilities for details.
 - 
An internal module called
qml.devices.capabilitieshas been added that defines a newDeviceCapabilitesdata class, as well as functions that load and parse the TOML-formatted configuration files. (#6407)>>> from pennylane.devices.capabilities import DeviceCapabilities >>> capabilities = DeviceCapabilities.from_toml_file("my_device.toml") >>> isinstance(capabilities, DeviceCapabilities) True
 - 
Devices that extend
qml.devices.Devicenow have an optional class attribute calledcapabilities, which is an instance of theDeviceCapabilitiesdata class constructed from the configuration file if it exists. Otherwise, it is set toNone. (#6433)from pennylane.devices import Device class MyDevice(Device): config_filepath = "path/to/config.toml" ...
>>> isinstance(MyDevice.capabilities, DeviceCapabilities) True - 
Default implementations of
Device.setup_execution_configandDevice.preprocess_transformshave been added to the device API for devices that provide a TOML configuration file and, thus, have acapabilitiesproperty. (#6632) (#6653) 
Capturing and representing hybrid programs
- 
Support has been added for
if/elsestatements andforandwhileloops in circuits executed withqml.capture.enabled, via Autograph. Autograph conversion is now used by default inmake_plxpr, but can be skipped withautograph=False. (#6406) (#6413) (#6426) (#6645) (#6685) - 
qml.transformnow accepts aplxpr_transformargument. This argument must be a function that can transform plxpr. Note that executing a transformed function will currently raise aNotImplementedError. To see more details, check out the :func:documentation of qml.transform <pennylane.transform>. (#6633) (#6722) - 
Users can now apply transforms with program capture enabled. Transformed functions cannot be executed by default. To apply the transforms (and to be able to execute the function), it must be decorated with the new
qml.capture.expand_plxpr_transformsfunction, which accepts a callable as input and returns a new function for which all present transforms have been applied. (#6722)from functools import partial qml.capture.enable() wire_map = {0: 3, 1: 6, 2: 9} @partial(qml.map_wires, wire_map=wire_map) def circuit(x, y): qml.RX(x, 0) qml.CNOT([0, 1]) qml.CRY(y, [1, 2]) return qml.expval(qml.Z(2))
>>> qml.capture.make_plxpr(circuit)(1.2, 3.4) { lambda ; a:f32[] b:f32[]. let c:AbstractMeasurement(n_wires=None) = _map_wires_transform_transform[ args_slice=slice(0, 2, None) consts_slice=slice(2, 2, None) inner_jaxpr={ lambda ; d:f32[] e:f32[]. let _:AbstractOperator() = RX[n_wires=1] d 0 _:AbstractOperator() = CNOT[n_wires=2] 0 1 _:AbstractOperator() = CRY[n_wires=2] e 1 2 f:AbstractOperator() = PauliZ[n_wires=1] 2 g:AbstractMeasurement(n_wires=None) = expval_obs f in (g,) } targs_slice=slice(2, None, None) tkwargs={'wire_map': {0: 3, 1: 6, 2: 9}, 'queue': False} ] a b in (c,) } >>> transformed_circuit = qml.capture.expand_plxpr_transforms(circuit) >>> jax.make_jaxpr(transformed_circuit)(1.2, 3.4) { lambda ; a:f32[] b:f32[]. let _:AbstractOperator() = RX[n_wires=1] a 3 _:AbstractOperator() = CNOT[n_wires=2] 3 6 _:AbstractOperator() = CRY[n_wires=2] b 6 9 c:AbstractOperator() = PauliZ[n_wires=1] 9 d:AbstractMeasurement(n_wires=None) = expval_obs c in (d,) }
 - 
The
qml.iterative_qpefunction can now be compactly captured into plxpr. (#6680) - 
Three new plxpr interpreters have been added that allow for functions and plxpr to be natively transformed with the same API as the corresponding existing transforms in PennyLane when program capture is enabled:
- 
qml.capture.transforms.CancelInterpreter:this class cancels operators appearing consecutively that are adjoints of each other following the same API asqml.transforms.cancel_inverses. (#6692) - 
qml.capture.transforms.DecomposeInterpreter: this class decomposes pennylane operators following the same API asqml.transforms.decompose. (#6691) - 
qml.capture.transforms.MapWiresInterpreter: this class maps wires to new values following the same API asqml.map_wires. (#6697) 
 - 
 - 
A
qml.tape.plxpr_to_tapefunction is now available that converts plxpr to a tape. (#6343) - 
Execution with capture enabled now follows a new execution pipeline and natively passes the captured plxpr to the device. Since it no longer falls back to the old pipeline, execution only works with a reduced feature set. (#6655) (#6596)
 - 
PennyLane transforms can now be captured as primitives with experimental program capture enabled. (#6633)
 - 
jax.vmapcan be captured withqml.capture.make_plxprand is compatible with quantum circuits. (#6349) (#6422) (#6668) - 
A
qml.capture.PlxprInterpreterbase class has been added for easy transformation and execution of plxpr. (#6141) - 
A
DefaultQubitInterpreterclass has been added to provide plxpr execution using python based tools, and theDefaultQubit.eval_jaxprmethod has been implemented. (#6594) (#6328) - 
An optional method,
eval_jaxpr, has been added to the device API for native execution of plxpr programs. (#6580) - 
qml.capture.qnode_callhas been made private and moved to theworkflowmodule. (#6620) 
Other Improvements
- 
qml.math.gradandqml.math.jacobianhave been added to differentiate a function with inputs of any interface in a JAX-like manner. (#6741) - 
qml.GroverOperatornow has awork_wiresproperty. (#6738) - 
The
Wiresobject's usage across Pennylane source code has been tidied up for internal consistency. (#6689) - 
qml.equalnow supportsqml.PauliWordandqml.PauliSentenceinstances. (#6703) - 
Redundant commutator computations from
qml.lie_closurehave been removed. (#6724) - 
A comprehensive error is now raised when using
qml.fourier.qnode_spectrumwith standard Numpy arguments andinterface="auto". (#6622) - 
Pauli string representations for the gates
{X, Y, Z, S, T, SX, SWAP, ISWAP, ECR, SISWAP}have been added, and a shape error in the matrix conversion ofqml.PauliSentences withlistorarrayinputs has been fixed. (#6562) (#6587) - 
qml.QNodeandqml.executenow forbid certain keyword arguments from being passed positionally. (#6610) - 
The string representations for the
qml.S,qml.T, andqml.SXhave been shortened. (#6542) - 
Internal class functions and dunder methods have been added to allow for multiplying Resources objects in series and in parallel. (#6567)
 - 
The
diagonalize_measurementstransform no longer raises an error for unknown observables. Instead, they are left un-diagonalized, with the expectation that observable validation will catch any un-diagonalized observables that are also unsupported by the device. (#6653) - 
A
qml.wires.Wiresobject can now be converted to a JAX array, if all wire labels are supported as JAX array elements. (#6699) - 
A developer focused
runfunction has been added to theqml.workflowmodule for a cleaner and standardized approach to executing tapes on an ML interface. (#6657) - 
Internal changes have been made to standardize execution interfaces, which resolves ambiguities in how the
interfacevalue is handled during execution. (#6643) - 
All interface handling logic has been moved to
interface_utils.pyin theqml.mathmodule. (#6649) - 
qml.executecan now be used withdiff_method="best". Classical cotransform information is now handled lazily by the workflow. Gradient method validation and program setup are now handled inside ofqml.execute, instead of inQNode. (#6716) - 
PyTree support for measurements in a circuit has been added. (#6378)
@qml.qnode(qml.device("default.qubit")) def circuit(): qml.Hadamard(0) qml.CNOT([0,1]) return {"Probabilities": qml.probs(), "State": qml.state()}
>>> circuit() {'Probabilities': array([0.5, 0. , 0. , 0.5]), 'State': array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])} - 
The
_cache_transformtransform has been moved to its own file located inpennylane/workflow/_cache_transform.py. (#6624) - 
The
qml.BasisRotationtemplate is now JIT compatible. (#6019) (#6779) - 
The Jaxpr primitives for
for_loop,while_loopandcondnow store slices instead of numbers of arguments. This helps with keeping track of what order the arguments come in. (#6521) - 
The
ExecutionConfig.gradient_methodfunction has been expanded to storeTransformDispatchertype. (#6455) - 
The string representation of
Resourcesinstances has been improved to match the attribute names. (#6581) - 
The documentation for the
dynamic_one_shottransform has been improved, and a warning is raised when a user-applieddynamic_one_shottransform is ignored in favour of the existing transform in a device's preprocessing transform program. (#6701) - 
A
qml.devices.qubit_mixedmodule has been added for mixed-state qubit device support. This module introduces anapply_operationhelper function that features:- Two density matrix contraction methods using 
einsumandtensordot - Optimized handling of special cases including: Diagonal operators, Identity operators, CX (controlled-X), Multi-controlled X gates, Grover operators (#6379)
 
 - Two density matrix contraction methods using 
 - 
A function called
create_initial_statehas been added to allow for initializing a circuit with a density matrix usingqml.StatePreporqml.QubitDensityMatrix. (#6503) - 
Several additions have been made to eventually migrate the
"default.mixed"device to the new device API:- A 
preprocessmethod has been added to theQubitMixeddevice class to preprocess the quantum circuit before execution. (#6601) - A new class called 
DefaultMixedNewAPIhas been added to theqml.devices.qubit_mixedmodule, which will replace the legacyDefaultMixed. (#6607) - A new submodule called 
devices.qubit_mixed.measurehas been added, featuring ameasurefunction for measuring qubits in mixed-state devices. (#6637) - A new submodule called 
devices.qubit_mixed.simulatehas been added, featuring asimulatefunction for simulating mixed states in analytic mode. (#6618) - A new submodule called 
devices.qubit_mixed.samplinghas been added, featuring functionssample_state,measure_with_samplesandsample_probsfor sampling qubits in mixed-state devices. (#6639) - The finite-shot branch of 
devices.qubit_mixed.simulatehas been added, which allows for accepting stochastic arguments such asshots,rngandprng_key. (#6665) - Support for 
qml.Snapshothas been added. (#6659) 
 - A 
 - 
Reporting of test warnings as failures has been added. (#6217)
 - 
A warning message in the Gradients and training documentation has been added that pertains to
ComplexWarnings. (#6543) - 
A new figure was added to the landing page of the PennyLane website. (#6696)
 
Breaking changes 💔
- 
The default graph coloring method of
qml.dot,qml.sum, andqml.pauli.optimize_measurementsfor grouping observables was changed from"rlf"to"lf". Internally,qml.pauli.group_observableshas been replaced withqml.pauli.compute_partition_indicesin several places to improve efficiency. (#6706) - 
qml.fourier.qnode_spectrumno longer automatically converts pure Numpy parameters to the Autograd framework. As the function uses automatic differentiation for validation, parameters from such a framework have to be used. (#6622) - 
qml.math.jax_argnums_to_tape_trainablehas been moved and made private to avoid an unnecessary QNode dependency in theqml.mathmodule. (#6609) - 
Gradient transforms are now applied after the user's transform program. This ensures user transforms work as expected on initial structures (e.g., embeddings or entangling layers), guarantees that gradient transforms only process compatible operations, aligns transform order with user expectations, and avoids confusion. (#6590)
 - 
Legacy operator arithmetic has been removed. This includes
qml.ops.Hamiltonian,qml.operation.Tensor,qml.operation.enable_new_opmath,qml.operation.disable_new_opmath, andqml.operation.convert_to_legacy_H. Note thatqml.Hamiltonianwill continue to dispatch toqml.ops.LinearCombination. For more information, check out the updated operator troubleshooting page. (#6548) (#6602) (#6589) - 
The developer-facing
qml.utilsmodule has been removed. (#6588):Specifically, the following 4 sets of functions have been either moved or removed:
qml.utils._flatten,qml.utils.unflattenhas been moved and renamed toqml.optimize.qng._flatten_npandqml.optimize.qng._unflatten_nprespectively.qml.utils._inv_dictandqml._get_default_argshave been removed.qml.utils.pauli_eigshas been moved toqml.pauli.utils.qml.utils.expand_vectorhas been moved toqml.math.expand_vector.
 - 
The
qml.qinfomodule has been removed. Please use the corresponding functions in theqml.mathandqml.measurementsmodules instead. (#6584) - 
Top level access to
Device,QubitDevice, andQutritDevicehave been removed. Instead, they are available asqml.devices.LegacyDevice,qml.devices.QubitDevice, andqml.devices.QutritDevice, respectively. (#6537) - 
The
'ancilla'argument forqml.iterative_qpehas been removed. Instead, use the'aux_wire'argument. (#6532) - 
The
qml.BasisStatePreparationtemplate has been removed. Instead, useqml.BasisState. (#6528) - 
The
qml.workflow.set_shotshelper function has been removed. We no longer interact with the legacy device interface in our code. Instead, shots should be specified on the tape, and the device should use these shots. (#6534) - 
QNode.gradient_fnhas been removed. Please useQNode.diff_methodinstead.QNode.get_gradient_fncan also be used to process the differentiation method. (#6535) - 
The
qml.QubitStateVectortemplate has been removed. Instead, useqml.StatePrep. (#6525) - 
qml.broadcasthas been removed. Users should useforloops instead. (#6527) - 
The
max_expansionargument forqml.transforms.clifford_t_decompositionhas been removed. (#6571) - 
The
expand_depthargument forqml.compilehas been removed. (#6531) - 
The
qml.shadows.shadow_expvaltransform has been removed. Instead, please use theqml.shadow_expvalmeasurement process. (#6530) (#6561) - 
The developer-facing
qml.drawer.MPLDrawerargumentn_wireshas been replaced withwire_map, which contains more complete information about wire labels and order. This allows the new functionality to specifywire_optionsfor specific wires when using string wire labels or non-sequential wire ordering. (#6805) 
Deprecations 👋
- 
The
tapeandqtapeproperties ofQNodehave been deprecated. Instead, use theqml.workflow.construct_tapefunction. (#6583) (#6650) - 
The
max_expansionargument inqml.devices.preprocess.decomposeis deprecated and will be removed in v0.41. (#6400) - 
The
decomp_depthargument inqml.transforms.set_decompositionis deprecated and will be removed in v0.41. (#6400) - 
The
output_dimproperty ofqml.tape.QuantumScripthas been deprecated. Instead, use methodshapeofQuantumScriptorMeasurementProcessto get the same information. (#6577) - 
The
QNode.get_best_methodandQNode.best_method_strmethods have been deprecated. Instead, use theqml.workflow.get_best_diff_methodfunction. (#6418) - 
The
qml.executegradient_fnkeyword argument has been renamed todiff_methodto better align with the termionology used by the QNode.gradient_fnwill be removed in v0.41. (#6549) - 
The old
qml.qsvtfunctionality is moved toqml.qsvt_legacyand is now deprecated. It will be removed in v0.41. (#6520) 
Documentation 📝
- 
The docstrings for
qml.qchem.Moleculeandqml.qchem.molecular_hamiltonianhave been updated to include a note that says that they are not compatible withqjitorjit. (#6702) - 
The documentation of
TrotterProducthas been updated to include the impact of the operands in the Hamiltonian on the structure of the created circuit. (#6629) - 
The documentation of
QSVThas been updated to include examples for different block encodings. (#6673) - 
The link to
qml.ops.one_qubit_transformwas fixed in theQubitUnitarydocstring. (#6745) 
Bug fixes 🐛
- 
Validation has been added to ensure that the device vjp is only used when the device actually supports it. (#6755)
 - 
qml.countsnow returns all outcomes when theall_outcomesargument isTrueand mid-circuit measurements are present. (#6732) - 
qml.ControlledQubitUnitarynow has consistent behaviour with program capture enabled. (#6719) - 
The
Wiresobject now throws aTypeErrorifwires=None. (#6713) (#6720) - 
The
qml.Hermitianclass no longer checks that the provided matrix is hermitian. The reason for this removal is to allow for faster execution and avoid incompatibilities withjax.jit. (#6642) - 
Subclasses of
qml.ops.Controlledno longer bind the primitives of their base operators when program capture is enabled. (#6672) - 
The
qml.HilbertSchmidtandqml.LocalHilbertSchmidttemplates now apply the complex conjugate of the unitaries instead of the adjoint, providing the correct result. (#6604) - 
QNode return behaviour is now consistent for lists and tuples. (#6568)
 - 
QNodes now accept arguments with types defined in libraries that are not necessarily in the list of supported interfaces, such as the
Graphclass defined innetworkx. (#6600) - 
qml.math.get_deep_interfacenow works properly for Autograd arrays. (#6557) - 
Printing instances of
qml.Identitynow returns the correct wires list. (#6506) 
Contributors ✍️
This release contains contributions from (in alphabetical order):
Guillermo Alonso, Shiwen An, Utkarsh Azad, Astral Cai, Yushao Chen, Isaac De Vlugt, Diksha Dhawan, Lasse Dierich, Lillian Frederiksen, Pietropaolo Frisoni, Simone Gasperini, Diego Guala, Austin Huang, Korbinian Kottmann, Christina Lee, Alan Martin, William Maxwell, Anton Naim Ibrahim, Andrija Paurevic, Justin Pickering, Jay Soni, David Wierichs.