Skip to content

Release 0.39.0

Latest
Compare
Choose a tag to compare
@mudit2812 mudit2812 released this 05 Nov 19:09
51797f7

New features since last release

Creating spin Hamiltonians on lattices 💞

  • Functionality for creating custom Hamiltonians on arbitrary lattices has been added. (#6226) (#6237)

    Hamiltonians beyond the available boiler-plate ones in the qml.spin module can be created with the addition of three new functions:

    • qml.spin.Lattice: a new object for instantiating customized lattices via primitive translation vectors and unit cell parameters,
    • qml.spin.generate_lattice: a utility function for creating standard Lattice objects, including 'chain', 'square', 'rectangle', 'triangle', 'honeycomb', 'kagome', 'lieb', 'cubic', 'bcc', 'fcc', and 'diamond',
    • qml.spin.spin_hamiltonian: generates a spin Hamiltonian object given a Lattice object with custom edges/nodes.

    An example is shown below for a $3 \times 3$ triangular lattice with open boundary conditions.

    lattice = qml.spin.Lattice(
        n_cells=[3, 3],
        vectors=[[1, 0], [np.cos(np.pi/3), np.sin(np.pi/3)]],
        positions=[[0, 0]],
        boundary_condition=False
    )

    We can validate this lattice against qml.spin.generate_lattice('triangle', ...) by checking the lattice_points (the $(x, y)$ coordinates of all sites in the lattice):

    >>> lp = lattice.lattice_points
    >>> triangular_lattice = qml.spin.generate_lattice('triangle', n_cells=[3, 3])
    >>> np.allclose(lp, triangular_lattice.lattice_points)
    True

    The edges of the Lattice object are nearest-neighbour by default, where we can add edges by using its add_edge method.

    Optionally, a Lattice object can have interactions and fields endowed to it by specifying values for its custom_edges and custom_nodes keyword arguments. The Hamiltonian can then be extracted with the qml.spin.spin_hamiltonian function. An example is shown below for the transverse-field Ising model Hamiltonian on a $3 \times 3$ triangular lattice. Note that the custom_edges and custom_nodes keyword arguments only need to be defined for one unit cell repetition.

    edges = [
        (0, 1), (0, 3), (1, 3)
    ]
    
    lattice = qml.spin.Lattice(
        n_cells=[3, 3],
        vectors=[[1, 0], [np.cos(np.pi/3), np.sin(np.pi/3)]],
        positions=[[0, 0]],
        boundary_condition=False,
        custom_edges=[[edge, ("ZZ", -1.0)] for edge in edges], 
        custom_nodes=[[i, ("X", -0.5)] for i in range(3*3)],
    )
    >>> tfim_ham = qml.spin.transverse_ising('triangle', [3, 3], coupling=1.0, h=0.5)
    >>> tfim_ham == qml.spin.spin_hamiltonian(lattice=lattice)
    True
  • More industry-standard spin Hamiltonians have been added in the qml.spin module. (#6174) (#6201)

    Three new industry-standard spin Hamiltonians are now available with PennyLane v0.39:

    These additions accompany qml.spin.heisenberg, qml.spin.transverse_ising, and qml.spin.fermi_hubbard, which were introduced in v0.38.

Calculating Polynomials 🔢

  • Polynomial functions can now be easily encoded into quantum circuits with qml.OutPoly. (#6320)

    A new template called qml.OutPoly is available, which provides the ability to encode a polynomial function in a quantum circuit. Given a polynomial function $f(x_1, x_2, \cdots, x_N)$, qml.OutPoly requires:

    • f : a standard Python function that represents $f(x_1, x_2, \cdots, x_N)$,
    • input_registers ($\vert x_1 \rangle$, $\vert x_2 \rangle$, ..., $\vert x_N \rangle$) : a list/tuple containing Wires objects that correspond to the embedded numeric values of $x_1, x_2, \cdots, x_N$,
    • output_wires : the Wires for which the numeric value of $f(x_1, x_2, \cdots, x_N)$ is stored.

    Here is an example of using qml.OutPoly to calculate $f(x_1, x_2) = 3x_1^2 - x_1x_2$ for $f(1, 2) = 1$.

    wires = qml.registers({"x1": 1, "x2": 2, "output": 2})
    
    def f(x1, x2):
        return 3 * x1 ** 2 - x1 * x2
    
    @qml.qnode(qml.device("default.qubit", shots = 1))
    def circuit():
        # load values of x1 and x2
        qml.BasisEmbedding(1, wires=wires["x1"])
        qml.BasisEmbedding(2, wires=wires["x2"])
    
        # apply the polynomial
        qml.OutPoly(
            f,
            input_registers = [wires["x1"], wires["x2"]],
            output_wires = wires["output"])
    
        return qml.sample(wires=wires["output"])
    >>> circuit()
    array([0, 1])

    The result, [0, 1], is the binary representation of $1$. By default, the result is calculated modulo $2^\text{len(output wires)}$ but can be overridden with the mod keyword argument.

Readout Noise 📠

  • Readout errors can now be included in qml.NoiseModel and qml.add_noise with the new qml.noise.meas_eq function. (#6321)

    Measurement/readout errors can be specified in a similar fashion to regular gate noise in PennyLane: a newly added Boolean function called qml.noise.meas_eq that accepts a measurement function (e.g., qml.expval, qml.sample, or any other function that can be returned from a QNode) that, when present in the QNode, inserts a noisy operation via qml.noise.partial_wires or a custom noise function. Readout noise in PennyLane also follows the insertion convention, where the specified noise is inserted before the measurement.

    Here is an example of adding qml.PhaseFlip noise to any qml.expval measurement:

    c0 = qml.noise.meas_eq(qml.expval)
    n0 = qml.noise.partial_wires(qml.PhaseFlip, 0.2)

    To include this in a qml.NoiseModel, use its meas_map keyword argument:

    # gate-based noise
    c1 = qml.noise.wires_in([0, 2]) 
    n1 = qml.noise.partial_wires(qml.RY, -0.42)
    
    noise_model = qml.NoiseModel({c1: n1}, meas_map={c0: n0})
    >>> noise_model
    NoiseModel({
      WiresIn([0, 2]): RY(phi=-0.42)
    },
    meas_map = {
        MeasEq(expval): PhaseFlip(p=0.2)
    })

    qml.noise.meas_eq can also be combined with other Boolean functions in qml.noise via bitwise operators for more versatility.

    To add this noise_model to a circuit, use the qml.add_noise transform as per usual. For example,

    @qml.qnode(qml.device("default.mixed", wires=3))
    def circuit():
        qml.RX(0.1967, wires=0)
        for i in range(3):
            qml.Hadamard(i)
    
        return qml.expval(qml.X(0) @ qml.X(1))
    >>> noisy_circuit = qml.add_noise(circuit, noise_model)
    >>> print(qml.draw(noisy_circuit)())
    0: ──RX(0.20)──RY(-0.42)────────H──RY(-0.42)──PhaseFlip(0.20)─┤ ╭<X@X>
    1: ──H─────────PhaseFlip(0.20)────────────────────────────────┤ ╰<X@X>
    2: ──H─────────RY(-0.42)──────────────────────────────────────┤    
    >>> print(circuit(), noisy_circuit())
    0.9807168489852615 0.35305806563469433

User-friendly decompositions 📠

  • A new transform called qml.transforms.decompose has been added to better facilitate the custom decomposition of operators in PennyLane circuits. (#6334)

    Previous to the addition of qml.transforms.decompose, decomposing operators in PennyLane had to be done by specifying a stopping_condition in qml.device.preprocess.decompose. With qml.transforms.decompose, the user-interface for specifying decompositions is much simpler and more versatile.

    Decomposing gates in a circuit can be done a few ways:

    • Specifying a gate_set comprising PennyLane Operators to decompose into:

      from functools import partial
      
      dev = qml.device('default.qubit')
      allowed_gates = {qml.Toffoli, qml.RX, qml.RZ}
      
      @partial(qml.transforms.decompose, gate_set=allowed_gates)
      @qml.qnode(dev)
      def circuit():
          qml.Hadamard(wires=[0])
          qml.Toffoli(wires=[0, 1, 2])
          return qml.expval(qml.Z(0))
      >>> print(qml.draw(circuit)())
      0: ──RZ(1.57)──RX(1.57)──RZ(1.57)─╭●─┤  <Z>
      1: ───────────────────────────────├●─┤
      2: ───────────────────────────────╰X─┤
    • Specifying a gate_set that is defined by a rule (Boolean function). For example, one can specify an arbitrary gate set to decompose into, so long as the resulting gates only act on one or two qubits:

      @partial(qml.transforms.decompose, gate_set = lambda op: len(op.wires) <= 2)
      @qml.qnode(dev)
      def circuit():
          qml.Toffoli(wires=[0, 1, 2])
          return qml.expval(qml.Z(0))
      >>> print(qml.draw(circuit)())
      0: ───────────╭●───────────╭●────╭●──T──╭●─┤  <Z>
      1: ────╭●─────│─────╭●─────│───T─╰X──T†─╰X─┤     
      2: ──H─╰X──T†─╰X──T─╰X──T†─╰X──T──H────────┤
    • Specifying a value for max_expansion. By default, decomposition occurs recursively until the desired gate set is reached, but this can be overridden to control the number of passes.

      phase = 1.0
      target_wires = [0]
      unitary = qml.RX(phase, wires=0).matrix()
      n_estimation_wires = 1
      estimation_wires = range(1, n_estimation_wires + 1)
      
      def qfunc():
          qml.QuantumPhaseEstimation(
              unitary,
              target_wires=target_wires,
              estimation_wires=estimation_wires,
          )
      
      decompose_once = qml.transforms.decompose(qfunc, max_expansion=1)
      decompose_twice = qml.transforms.decompose(qfunc, max_expansion=2)
      >>> print(qml.draw(decompose_once)())
      0: ────╭U(M0)¹───────┤  
      1: ──H─╰●───────QFT†─┤  
      M0 = 
      [[0.87758256+0.j         0.        -0.47942554j]
      [0.        -0.47942554j 0.87758256+0.j        ]]
      >>> print(qml.draw(decompose_twice)())
      0: ──RZ(1.57)──RY(0.50)─╭X──RY(-0.50)──RZ(-6.28)─╭X──RZ(4.71)─┤  
      1: ──H──────────────────╰●───────────────────────╰●──H†───────┤ 

Improvements 🛠

qjit/jit compatibility and improvements

Quantum just-in-time (qjit) compilation is the compilation of hybrid quantum-classical programs during execution of a program, rather than before execution. PennyLane provides just-in-time compilation with its @qml.qjit decorator, powered by the Catalyst compiler.

  • Several templates are now compatible with qjit:

  • The sample-based measurements now support samples that are JAX tracers, allowing compatiblity with qjit workflows. (#6211)

  • The qml.FABLE template now returns the correct value when jit is enabled. (#6263)

  • qml.metric_tensor is now jit compatible. (#6468)

  • qml.QutritBasisStatePreparation is now jit compatible. (#6308)

  • All PennyLane templates are now unit tested to ensure jit compatibility. (#6309)

Various improvements to fermionic operators

  • qml.fermi.FermiWord and qml.fermi.FermiSentence are now compatible with JAX arrays. (#6324)

    Fermionic operators interacting, in some way, with a JAX array will no longer raise an error. For example:

    >>> import jax.numpy as jnp
    >>> w = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
    >>> jnp.array([3]) * w
    FermiSentence({FermiWord({(0, 0): '+', (1, 1): '-'}): Array([3], dtype=int32)})
  • The qml.fermi.FermiWord class now has a shift_operator method to apply anti-commutator relations. (#6196)

    >>> w = qml.fermi.FermiWord({(0, 0): '+', (1, 1): '-'})
    >>> w
    FermiWord({(0, 0): '+', (1, 1): '-'})
    >>> w.shift_operator(0, 1)
    FermiSentence({FermiWord({(0, 1): '-', (1, 0): '+'}): -1})
  • The qml.fermi.FermiWord and qml.fermi.FermiSentence classes now an adjoint method. (#6166)

    >>> w = qml.fermi.FermiWord({(0, 0): '+', (1, 1): '-'})
    >>> w.adjoint()
    FermiWord({(0, 1): '+', (1, 0): '-'})
  • The to_mat methods for qml.fermi.FermiWord and qml.fermi.FermiSentence now optionally return a sparse matrix with the format keyword argument. (#6173)

    >>> w = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'}) 
    >>> w.to_mat(format="dense")
    array([[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.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
          [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])
    >>> w.to_mat(format="csr")
    <4x4 sparse matrix of type '<class 'numpy.complex128'>'
      with 1 stored elements in Compressed Sparse Row format>
  • When printed, qml.fermi.FermiWord and qml.fermi.FermiSentence now return a unique representation of the object. (#6167)

    >>> w = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
    >>> print(w)
    a⁺(0) a(1)
  • qml.qchem.excitations now optionally returns fermionic operators with a new fermionic keyword argument (defaults to False). (#6171)

    >>> singles, doubles = excitations(electrons, orbitals, fermionic=True)
    >>> print(singles)
    [FermiWord({(0, 0): '+', (1, 2): '-'}), FermiWord({(0, 1): '+', (1, 3): '-'})]

A new optimizer

  • A new optimizer called qml.MomentumQNGOptimizer has been added. It inherits from the basic qml.QNGOptimizer optimizer and requires one additional hyperparameter: the momentum coefficient, $0 \leq \rho &lt; 1$, the default value being $\rho=0.9$. For $\rho=0$, qml.MomentumQNGOptimizer reduces down to qml.QNGOptimizer. (#6240)

Other Improvements

  • default.tensor can now handle mid-circuit measurements via the deferred measurement principle. (#6408)

  • process_density_matrix was implemented in 5 StateMeasurement subclasses: ExpVal, Var, Purity, MutualInformation, and VnEntropy. This facilitates future support for mixed-state devices and expanded density matrix operations. Also, a fix was made in the ProbabilityMP class to use qml.math.sqrt instead of np.sqrt. (#6330)

  • The decomposition for qml.Qubitization has been improved to use qml.PrepSelPrep. (#6182)

  • A ReferenceQubit device (shortname: "reference.qubit") has been introduced for testing purposes and referencing for future plugin development. (#6181)

  • qml.transforms.mitigate_with_zne now gives a clearer error message when being applied on circuits, not devices, that include channel noise. (#6346)

  • A new function called get_best_diff_method has been added to qml.workflow. (#6399)

  • A new method called construct_tape has been added to qml.workflow for users to construct single tapes from a QNode. (#6419)

  • An infrastructural improvement was made to PennyLane datasets that allows us to upload datasets more easily. (#6126)

  • qml.Hadamard now has a more friendly alias: qml.H. (#6450)

  • A Boolean keyword argument called show_wire_labels has been added to draw and draw_mpl, which hides wire labels when set to False (the default is True). (#6410)

  • A new function called sample_probs has been added to the qml.devices.qubit and qml.devices.qutrit_mixed modules. This function takes probability distributions as input and returns sampled outcomes, simplifying the sampling process by separating it from other operations in the measurement chain and improving modularity. (#6354)

  • qml.labs has been added to the PennyLane documentation. (#6397) (#6369)

  • A has_sparse_matrix property has been added to Operator to indicate whether a sparse matrix is defined. (#6278) (#6310)

  • qml.matrix now works with empty objects (e.g., empty tapes, QNodes and quantum functions that do not call operations, and single operators with empty decompositions). (#6347)

    dev = qml.device("default.qubit", wires=1)
    
    @qml.qnode(dev)
    def node():
        return qml.expval(qml.Z(0))
    >>> qml.matrix(node)()
    array([[1., 0.],
           [0., 1.]])
  • PennyLane is now compatible with NumPy 2.0. (#6061) (#6258) (#6342)

  • PennyLane is now compatible with Jax 0.4.28. (#6255)

  • The diagonalize_measurements transform now uses a more efficient method of diagonalization when possible, based on the pauli_rep of the relevant observables. (#6113)

  • The QuantumScript.copy method now takes operations, measurements, shots and trainable_params as keyword arguments. If any of these are passed when copying a tape, the specified attributes will replace the copied attributes on the new tape. (#6285) (#6363)

  • The Hermitian operator now has a compute_sparse_matrix implementation. (#6225)

  • When an observable is repeated on a tape, tape.diagonalizing_gates no longer returns the diagonalizing gates for each instance of the observable. Instead, the diagonalizing gates of each observable on the tape are included just once. (#6288)

  • The number of diagonalizing gates returned in qml.specs now follows the level keyword argument regarding whether the diagonalizing gates are modified by device, instead of always counting unprocessed diagonalizing gates. (#6290)

  • A more sensible error message is raised from a RecursionError encountered when accessing properties and methods of a nested CompositeOp or SProd. (#6375)

    By default, qml.sum and qml.prod set lazy=True, which keeps its operands nested. Given the recursive nature of such structures, if there are too many levels of nesting, a RecursionError would occur when accessing many of the properties and methods.

  • The performance of the decomposition of qml.QFT has been improved. (#6434)

Capturing and representing hybrid programs

  • qml.wires.Wires now accepts JAX arrays as input. In many workflows with JAX, PennyLane may attempt to assign a JAX array to a Wires object, which will cause an error since JAX arrays are not hashable. Now, JAX arrays are valid Wires types. Furthermore, a FutureWarning is no longer raised in JAX 0.4.30+ when providing JAX tracers as input to qml.wires.Wires. (#6312)

  • A new function called qml.capture.make_plxpr has been added to take a function and create a Callable that, when called, will return a PLxPR representation of the input function. (#6326)`

  • Differentiation of hybrid programs via qml.grad and qml.jacobian can now be captured with PLxPR. When evaluating a captured qml.grad (qml.jacobian) instruction, it will dispatch to jax.grad (jax.jacobian), which differs from the Autograd implementation without capture. Pytree inputs and outputs are supported. (#6120) (#6127) (#6134)

  • Unit testing for capturing nested control flow has been improved. (#6111)

  • Some custom primitives for the capture project can now be imported via from pennylane.capture.primitives import *. (#6129)

  • All higher order primitives now use jax.core.Jaxpr as metadata instead of sometimes using jax.core.ClosedJaxpr or jax.core.Jaxpr. (#6319)

Breaking changes 💔

  • Red-herring validation in QNode.construct has been removed, which fixed a bug with qml.GlobalPhase. (#6373)

    Removing the AllWires validation in QNode.construct was addressed as a solution to the following example not being able to run:

    @qml.qnode(qml.device('default.qubit', wires=2))
    def circuit(x):
        qml.GlobalPhase(x, wires=0)
        return qml.state()
    >>> circuit(0.5)
    array([0.87758256-0.47942554j, 0.        +0.j        ,
         1.        +0.j        , 0.        +0.j        ])
  • The simplify argument in qml.Hamiltonian and qml.ops.LinearCombination has been removed. Instead, qml.simplify() can be called on the constructed operator. (#6279)

  • The functions qml.qinfo.classical_fisher and qml.qinfo.quantum_fisher have been removed and migrated to the qml.gradients module. qml.gradients.classical_fisher and qml.gradients.quantum_fisher should be used instead. (#5911)

  • Python 3.9 is no longer supported. Please update to 3.10 or newer. (#6223)

  • default.qubit.legacy, default.qubit.tf, default.qubit.torch, default.qubit.jax, and default.qubit.autograd have been removed. Please use default.qubit for all interfaces. (#6207) (#6208) (#6209) (#6210) (#6266)

  • expand_fn, max_expansion, override_shots, and device_batch_transform have been removed from the signature of qml.execute. (#6203)

  • max_expansion and expansion_strategy have been removed from the QNode. (#6203)

  • expansion_strategy has been removed from qml.draw, qml.draw_mpl, and qml.specs. max_expansion has been removed from qml.specs, as it had no impact on the output. (#6203)

  • qml.transforms.hamiltonian_expand and qml.transforms.sum_expand have been removed. Please use qml.transforms.split_non_commuting instead. (#6204)

  • The decomp_depth keyword argument to qml.device has been removed. (#6234)

  • Operator.expand has been removed. Please use qml.tape.QuantumScript(op.decomposition()) instead. (#6227)

  • The native folding method qml.transforms.fold_global for the qml.transforms.mitigate_with_zne transform no longer expands the circuit automatically. Instead, the user should apply qml.transforms.decompose to decompose a circuit into a target gate set before applying fold_global or mitigate_with_zne. (#6382)

  • The LightningVJPs class has been removed, as all lightning devices now follow the new device interface. (#6420)

Deprecations 👋

  • The expand_depth and max_expansion arguments for qml.transforms.compile and qml.transforms.decompositions.clifford_t_decomposition respectively have been deprecated. (#6404)

  • Legacy operator arithmetic has been deprecated. This includes qml.ops.Hamiltonian, qml.operation.Tensor, qml.operation.enable_new_opmath, qml.operation.disable_new_opmath, and qml.operation.convert_to_legacy_H. Note that when new operator arithmetic is enabled, qml.Hamiltonian will continue to dispatch to qml.ops.LinearCombination; this behaviour is not deprecated. For more information, check out the updated operator troubleshooting page. (#6287) (#6365)

  • qml.pauli.PauliSentence.hamiltonian and qml.pauli.PauliWord.hamiltonian have been deprecated. Instead, please use qml.pauli.PauliSentence.operation and qml.pauli.PauliWord.operation, respectively. (#6287)

  • qml.pauli.simplify() has been deprecated. Instead, please use qml.simplify(op) or op.simplify(). (#6287)

  • The qml.BasisStatePreparation template has been deprecated. Instead, use qml.BasisState. (#6021)

  • The 'ancilla' argument for qml.iterative_qpe has been deprecated. Instead, use the 'aux_wire' argument. (#6277)

  • qml.shadows.shadow_expval has been deprecated. Instead, use the qml.shadow_expval measurement process. (#6277)

  • qml.broadcast has been deprecated. Please use Python for loops instead. (#6277)

  • The qml.QubitStateVector template has been deprecated. Instead, use qml.StatePrep. (#6172)

  • The qml.qinfo module has been deprecated. Please see the respective functions in the qml.math and qml.measurements modules instead. (#5911)

  • Device, QubitDevice, and QutritDevice will no longer be accessible via top-level import in v0.40. They will still be accessible as qml.devices.LegacyDevice, qml.devices.QubitDevice, and qml.devices.QutritDevice respectively. (#6238)

  • QNode.gradient_fn has been deprecated. Please use QNode.diff_method and QNode.get_gradient_fn instead. (#6244)

Documentation 📝

  • Updated qml.spin documentation. (#6387)

  • Updated links to PennyLane.ai in the documentation to use the latest URL format, which excludes the .html prefix. (#6412)

  • Update qml.Qubitization documentation based on new decomposition. (#6276)

  • Fixed examples in the documentation of a few optimizers. (#6303) (#6315)

  • Corrected examples in the documentation of qml.jacobian. (#6283) (#6315)

  • Fixed spelling in a number of places across the documentation. (#6280)

  • Add work_wires parameter to qml.MultiControlledX docstring signature. (#6271)

  • Removed ambiguity in error raised by the PauliRot class. (#6298)

  • Renamed an incorrectly named test in test_pow_ops.py. (#6388)

  • Removed outdated Docker description from installation page. (#6487)

Bug fixes 🐛

  • The wire order for Snapshot's now matches the wire order of the device, rather than the simulation. (#6461)

  • Fixed a bug where QNSPSAOptimizer, QNGOptimizer and MomentumQNGOptimizer calculate invalid parameter updates if the metric tensor becomes singular. (#6471)

  • The default.qubit device now supports parameter broadcasting with qml.classical_shadow and qml.shadow_expval. (#6301)

  • Fixed unnecessary call of eigvals in qml.ops.op_math.decompositions.two_qubit_unitary.py that was causing an error in VJP. Raises warnings to users if this essentially nondifferentiable module is used. (#6437)

  • Patches the math module to function with autoray 0.7.0. (#6429)

  • Fixed incorrect differentiation of PrepSelPrep when using diff_method="parameter-shift". (#6423)

  • The validate_device_wires transform now raises an error if abstract wires are provided. (#6405)

  • Fixed qml.math.expand_matrix for qutrit and arbitrary qudit operators. (#6398)

  • MeasurementValue now raises an error when it is used as a boolean. (#6386)

  • default.qutrit now returns integer samples. (#6385)

  • adjoint_metric_tensor now works with circuits containing state preparation operations. (#6358)

  • quantum_fisher now respects the classical Jacobian of QNodes. (#6350)

  • qml.map_wires can now be applied to a batch of tapes. (#6295)

  • Fixed float-to-complex casting in various places across PennyLane. (#6260) (#6268)

  • Fixed a bug where zero-valued JVPs were calculated wrongly in the presence of shot vectors. (#6219)

  • Fixed qml.PrepSelPrep template to work with torch. (#6191)

  • Fixed a bug where qml.equal now correctly compares qml.PrepSelPrep operators. (#6182)

  • The qml.QSVT template now orders the projector wires first and the UA wires second, which is the expected order of the decomposition. (#6212)

  • The qml.Qubitization template now orders the control wires first and the hamiltonian wires second, which is the expected according to other templates. (#6229)

  • Fixed a bug where a circuit using the autograd interface sometimes returns nested values that are not of the autograd interface. (#6225)

  • Fixed a bug where a simple circuit with no parameters or only builtin/NumPy arrays as parameters returns autograd tensors. (#6225)

  • qml.pauli.PauliVSpace now uses a more stable SVD-based linear independence check to avoid running into LinAlgError: Singular matrix. This stabilizes the usage of qml.lie_closure. It also introduces normalization of the basis vector's internal representation _M to avoid exploding coefficients. (#6232)

  • Fixed a bug where csc_dot_product is used during measurement for Sum/Hamiltonian that contains observables that does not define a sparse matrix. (#6278) (#6310)

  • Fixed a bug where None was added to the wires in qml.PhaseAdder, qml.Adder and qml.OutAdder. (#6360)

  • Fixed a test after updating to the nightly version of Catalyst. (#6362)

  • Fixed a bug where CommutingEvolution with a trainable Hamiltonian cannot be differentiated using parameter shift. (#6372)

  • Fixed a bug where mitigate_with_zne loses the shots information of the original tape. (#6444)

  • Fixed a bug where default.tensor raises an error when applying Identity/GlobalPhase on no wires, and PauliRot/MultiRZ on a single wire. (#6448)

  • Fixes a bug where applying qml.ctrl and qml.adjoint on an operator type instead of an operator instance results in extra operators in the queue. (#6284)

Contributors ✍️

This release contains contributions from (in alphabetical order):

Guillermo Alonso, Utkarsh Azad, Oleksandr Borysenko, Astral Cai, Yushao Chen, Isaac De Vlugt, Diksha Dhawan, Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, Anthony Hayes, Austin Huang, Soran Jahangiri, Jacob Kitchen, Korbinian Kottmann, Christina Lee, William Maxwell, Erick Ochoa Lopez, Lee J. O'Riordan, Mudit Pandey, Andrija Paurevic, Alex Preciado, Ashish Kanwar Singh, David Wierichs