Pennylane

Latest version: v0.39.0

Safety actively analyzes 682404 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 4 of 11

0.27.0

<h3>New features since last release</h3>

<h4>An all-new data module 💾</h4>

* The `qml.data` module is now available, allowing users to download, load, and create quantum datasets. [(3156)](https://github.com/PennyLaneAI/pennylane/pull/3156)

Datasets are hosted on Xanadu Cloud and can be downloaded by using `qml.data.load()`:

pycon
>>> H2_datasets = qml.data.load(
... data_name="qchem", molname="H2", basis="STO-3G", bondlength=1.1
... )
>>> H2data = H2_datasets[0]
>>> H2data
<Dataset = description: qchem/H2/STO-3G/1.1, attributes: ['molecule', 'hamiltonian', ...]>


- Datasets available to be downloaded can be listed with `qml.data.list_datasets()`.

- To download or load only specific properties of a dataset, we can specify the desired properties in `qml.data.load` with the `attributes` keyword argument:

pycon
>>> H2_hamiltonian = qml.data.load(
... data_name="qchem", molname="H2", basis="STO-3G", bondlength=1.1,
... attributes=["molecule", "hamiltonian"]
... )[0]
>>> H2_hamiltonian.hamiltonian
<Hamiltonian: terms=15, wires=[0, 1, 2, 3]>


The available attributes can be found using `qml.data.list_attributes()`:

- To select data interactively, we can use `qml.data.load_interactive()`:

pycon
>>> qml.data.load_interactive()
Please select a data name:
1) qspin
2) qchem
Choice [1-2]: 1
Please select a sysname:
...
Please select a periodicity:
...
Please select a lattice:
...
Please select a layout:
...
Please select attributes:
...
Force download files? (Default is no) [y/N]: N
Folder to download to? (Default is pwd, will download to /datasets subdirectory):

Please confirm your choices:
dataset: qspin/Ising/open/rectangular/4x4
attributes: ['parameters', 'ground_states']
force: False
dest folder: datasets
Would you like to continue? (Default is yes) [Y/n]:
<Dataset = description: qspin/Ising/open/rectangular/4x4, attributes: ['parameters', 'ground_states']>


- Once a dataset is loaded, its properties can be accessed as follows:

pycon
>>> dev = qml.device("default.qubit",wires=4)
>>> qml.qnode(dev)
... def circuit():
... qml.BasisState(H2data.hf_state, wires = [0, 1, 2, 3])
... for op in H2data.vqe_gates:
... qml.apply(op)
... return qml.expval(H2data.hamiltonian)
>>> print(circuit())
-1.0791430411076344


It's also possible to create custom datasets with `qml.data.Dataset`:

pycon
>>> example_hamiltonian = qml.Hamiltonian(coeffs=[1,0.5], observables=[qml.PauliZ(wires=0),qml.PauliX(wires=1)])
>>> example_energies, _ = np.linalg.eigh(qml.matrix(example_hamiltonian))
>>> example_dataset = qml.data.Dataset(
... data_name = 'Example', hamiltonian=example_hamiltonian, energies=example_energies
... )
>>> example_dataset.data_name
'Example'
>>> example_dataset.hamiltonian
(0.5) [X1]
+ (1) [Z0]
>>> example_dataset.energies
array([-1.5, -0.5, 0.5, 1.5])


Custom datasets can be saved and read with the `qml.data.Dataset.write()` and `qml.data.Dataset.read()` methods, respectively.

pycon
>>> example_dataset.write('./path/to/dataset.dat')
>>> read_dataset = qml.data.Dataset()
>>> read_dataset.read('./path/to/dataset.dat')
>>> read_dataset.data_name
'Example'
>>> read_dataset.hamiltonian
(0.5) [X1]
+ (1) [Z0]
>>> read_dataset.energies
array([-1.5, -0.5, 0.5, 1.5])


We will continue to work on adding more datasets and features for `qml.data` in future releases.

<h4>Adaptive optimization 🏃🏋️🏊</h4>

* Optimizing quantum circuits can now be done *adaptively* with `qml.AdaptiveOptimizer`. [(3192)](https://github.com/PennyLaneAI/pennylane/pull/3192)

The `qml.AdaptiveOptimizer` takes an initial circuit and a collection of operators as input and adds a selected gate to the circuit at each optimization step. The process of growing the circuit can be repeated until the circuit gradients converge to zero within a given threshold. The adaptive optimizer can be used to implement algorithms such as ADAPT-VQE as shown in the following example.

Firstly, we define some preliminary variables needed for VQE:

python
symbols = ["H", "H", "H"]
geometry = np.array([[0.01076341, 0.04449877, 0.0],
[0.98729513, 1.63059094, 0.0],
[1.87262415, -0.00815842, 0.0]], requires_grad=False)
H, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, charge = 1)


The collection of gates to grow the circuit is built to contain all single and double excitations:

python
n_electrons = 2
singles, doubles = qml.qchem.excitations(n_electrons, qubits)
singles_excitations = [qml.SingleExcitation(0.0, x) for x in singles]
doubles_excitations = [qml.DoubleExcitation(0.0, x) for x in doubles]
operator_pool = doubles_excitations + singles_excitations


Next, an initial circuit that prepares a Hartree-Fock state and returns the expectation value of the Hamiltonian is defined:

python
hf_state = qml.qchem.hf_state(n_electrons, qubits)
dev = qml.device("default.qubit", wires=qubits)
qml.qnode(dev)
def circuit():
qml.BasisState(hf_state, wires=range(qubits))
return qml.expval(H)


Finally, the optimizer is instantiated and then the circuit is created and optimized adaptively:

python
opt = qml.optimize.AdaptiveOptimizer()
for i in range(len(operator_pool)):
circuit, energy, gradient = opt.step_and_cost(circuit, operator_pool, drain_pool=True)
print('Energy:', energy)
print(qml.draw(circuit)())
print('Largest Gradient:', gradient)
print()
if gradient < 1e-3:
break


pycon
Energy: -1.246549938420637
0: ─╭BasisState(M0)─╭G²(0.20)─┤ ╭<𝓗>
1: ─├BasisState(M0)─├G²(0.20)─┤ ├<𝓗>
2: ─├BasisState(M0)─│─────────┤ ├<𝓗>
3: ─├BasisState(M0)─│─────────┤ ├<𝓗>
4: ─├BasisState(M0)─├G²(0.20)─┤ ├<𝓗>
5: ─╰BasisState(M0)─╰G²(0.20)─┤ ╰<𝓗>
Largest Gradient: 0.14399872776755085

Energy: -1.2613740231529604
0: ─╭BasisState(M0)─╭G²(0.20)─╭G²(0.19)─┤ ╭<𝓗>
1: ─├BasisState(M0)─├G²(0.20)─├G²(0.19)─┤ ├<𝓗>
2: ─├BasisState(M0)─│─────────├G²(0.19)─┤ ├<𝓗>
3: ─├BasisState(M0)─│─────────╰G²(0.19)─┤ ├<𝓗>
4: ─├BasisState(M0)─├G²(0.20)───────────┤ ├<𝓗>
5: ─╰BasisState(M0)─╰G²(0.20)───────────┤ ╰<𝓗>
Largest Gradient: 0.1349349562423238

Energy: -1.2743971719780331
0: ─╭BasisState(M0)─╭G²(0.20)─╭G²(0.19)──────────┤ ╭<𝓗>
1: ─├BasisState(M0)─├G²(0.20)─├G²(0.19)─╭G(0.00)─┤ ├<𝓗>
2: ─├BasisState(M0)─│─────────├G²(0.19)─│────────┤ ├<𝓗>
3: ─├BasisState(M0)─│─────────╰G²(0.19)─╰G(0.00)─┤ ├<𝓗>
4: ─├BasisState(M0)─├G²(0.20)────────────────────┤ ├<𝓗>
5: ─╰BasisState(M0)─╰G²(0.20)────────────────────┤ ╰<𝓗>
Largest Gradient: 0.00040841755397108586


For a detailed breakdown of its implementation, check out the [Adaptive circuits for quantum chemistry demo](https://pennylane.ai/qml/demos/tutorial_adaptive_circuits.html).

<h4>Automatic interface detection 🧩</h4>

* QNodes now accept an `auto` interface argument which automatically detects the machine learning library to use. [(3132)](https://github.com/PennyLaneAI/pennylane/pull/3132)

python
from pennylane import numpy as np
import torch
import tensorflow as tf
from jax import numpy as jnp

dev = qml.device("default.qubit", wires=2)
qml.qnode(dev, interface="auto")
def circuit(weight):
qml.RX(weight[0], wires=0)
qml.RY(weight[1], wires=1)
return qml.expval(qml.PauliZ(0))

interface_tensors = [[0, 1], np.array([0, 1]), torch.Tensor([0, 1]), tf.Variable([0, 1], dtype=float), jnp.array([0, 1])]
for tensor in interface_tensors:
res = circuit(weight=tensor)
print(f"Result value: {res:.2f}; Result type: {type(res)}")


pycon
Result value: 1.00; Result type: <class 'pennylane.numpy.tensor.tensor'>
Result value: 1.00; Result type: <class 'pennylane.numpy.tensor.tensor'>
Result value: 1.00; Result type: <class 'torch.Tensor'>
Result value: 1.00; Result type: <class 'tensorflow.python.framework.ops.EagerTensor'>
Result value: 1.00; Result type: <class 'jaxlib.xla_extension.DeviceArray'>


<h4>Upgraded JAX-JIT gradient support 🏎</h4>

* JAX-JIT support for computing the gradient of QNodes that return a single vector of probabilities or multiple expectation values is now available. [(3244)](https://github.com/PennyLaneAI/pennylane/pull/3244) [(#3261)](https://github.com/PennyLaneAI/pennylane/pull/3261)

python
import jax
from jax import numpy as jnp
from jax.config import config
config.update("jax_enable_x64", True)

dev = qml.device("lightning.qubit", wires=2)

jax.jit
qml.qnode(dev, diff_method="parameter-shift", interface="jax")
def circuit(x, y):
qml.RY(x, wires=0)
qml.RY(y, wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

x = jnp.array(1.0)
y = jnp.array(2.0)


pycon
>>> jax.jacobian(circuit, argnums=[0, 1])(x, y)
(DeviceArray([-0.84147098, 0.35017549], dtype=float64, weak_type=True),
DeviceArray([ 4.47445479e-18, -4.91295496e-01], dtype=float64, weak_type=True))


Note that this change depends on `jax.pure_callback`, which requires `jax>=0.3.17`.

<h4>Construct Pauli words and sentences 🔤</h4>

* We've reorganized and grouped everything in PennyLane responsible for manipulating Pauli operators into a `pauli` module. The `grouping` module has been deprecated as a result, and logic was moved from `pennylane/grouping` to `pennylane/pauli/grouping`. [(3179)](https://github.com/PennyLaneAI/pennylane/pull/3179)

* `qml.pauli.PauliWord` and `qml.pauli.PauliSentence` can be used to represent tensor products and linear combinations of Pauli operators, respectively. These provide a more performant method to compute sums and products of Pauli operators. [(3195)](https://github.com/PennyLaneAI/pennylane/pull/3195)

- `qml.pauli.PauliWord` represents tensor products of Pauli operators. We can efficiently multiply and extract the matrix of these operators using this representation.

pycon
>>> pw1 = qml.pauli.PauliWord({0:"X", 1:"Z"})
>>> pw2 = qml.pauli.PauliWord({0:"Y", 1:"Z"})
>>> pw1, pw2
(X(0) Z(1), Y(0) Z(1))
>>> pw1 * pw2
(Z(0), 1j)
>>> pw1.to_mat(wire_order=[0,1])
array([[ 0, 0, 1, 0],
[ 0, 0, 0, -1],
[ 1, 0, 0, 0],
[ 0, -1, 0, 0]])


- `qml.pauli.PauliSentence` represents linear combinations of Pauli words. We can efficiently add, multiply and extract the matrix of these operators in this representation.

pycon
>>> ps1 = qml.pauli.PauliSentence({pw1: 1.2, pw2: 0.5j})
>>> ps2 = qml.pauli.PauliSentence({pw1: -1.2})
>>> ps1
1.2 * X(0) Z(1)
+ 0.5j * Y(0) Z(1)
>>> ps1 + ps2
0.0 * X(0) Z(1)
+ 0.5j * Y(0) Z(1)
>>> ps1 * ps2
-1.44 * I
+ (-0.6+0j) * Z(0)
>>> (ps1 + ps2).to_mat(wire_order=[0,1])
array([[ 0. +0.j, 0. +0.j, 0.5+0.j, 0. +0.j],
[ 0. +0.j, 0. +0.j, 0. +0.j, -0.5+0.j],
[-0.5+0.j, 0. +0.j, 0. +0.j, 0. +0.j],
[ 0. +0.j, 0.5+0.j, 0. +0.j, 0. +0.j]])


<h4>(Experimental) More support for multi-measurement and gradient output types 🧪</h4>

* `qml.enable_return()` now supports QNodes returning multiple measurements, including shots vectors, and gradient output types. [(2886)](https://github.com/PennyLaneAI/pennylane/pull/2886) [(#3052)](https://github.com/PennyLaneAI/pennylane/pull/3052) [(#3041)](https://github.com/PennyLaneAI/pennylane/pull/3041) [(#3090)](https://github.com/PennyLaneAI/pennylane/pull/3090) [(#3069)](https://github.com/PennyLaneAI/pennylane/pull/3069) [(#3137)](https://github.com/PennyLaneAI/pennylane/pull/3137) [(#3127)](https://github.com/PennyLaneAI/pennylane/pull/3127) [(#3099)](https://github.com/PennyLaneAI/pennylane/pull/3099) [(#3098)](https://github.com/PennyLaneAI/pennylane/pull/3098) [(#3095)](https://github.com/PennyLaneAI/pennylane/pull/3095) [(#3091)](https://github.com/PennyLaneAI/pennylane/pull/3091) [(#3176)](https://github.com/PennyLaneAI/pennylane/pull/3176) [(#3170)](https://github.com/PennyLaneAI/pennylane/pull/3170) [(#3194)](https://github.com/PennyLaneAI/pennylane/pull/3194) [(#3267)](https://github.com/PennyLaneAI/pennylane/pull/3267) [(#3234)](https://github.com/PennyLaneAI/pennylane/pull/3234) [(#3232)](https://github.com/PennyLaneAI/pennylane/pull/3232) [(#3223)](https://github.com/PennyLaneAI/pennylane/pull/3223) [(#3222)](https://github.com/PennyLaneAI/pennylane/pull/3222) [(#3315)](https://github.com/PennyLaneAI/pennylane/pull/3315)

In v0.25, we introduced `qml.enable_return()`, which separates measurements into their own tensors. The motivation of this change is the deprecation of ragged `ndarray` creation in NumPy.

With this release, we're continuing to elevate this feature by adding support for:

- Execution (`qml.execute`)
- Jacobian vector product (JVP) computation
- Gradient transforms (`qml.gradients.param_shift`, `qml.gradients.finite_diff`, `qml.gradients.hessian_transform`, `qml.gradients.param_shift_hessian`).

- Interfaces (Autograd, TensorFlow, and JAX, although without JIT)

With this added support, the JAX interface can handle multiple shots (shots vectors), measurements, and gradient output types with `qml.enable_return()`:

python
import jax

qml.enable_return()
dev = qml.device("default.qubit", wires=2, shots=(1, 10000))

params = jax.numpy.array([0.1, 0.2])

qml.qnode(dev, interface="jax", diff_method="parameter-shift", max_diff=2)
def circuit(x):
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
return qml.var(qml.PauliZ(0) qml.PauliX(1)), qml.probs(wires=[0])


pycon
>>> jax.hessian(circuit)(params)
((DeviceArray([[ 0., 0.],
[ 2., -3.]], dtype=float32),
DeviceArray([[[-0.5, 0. ],
[ 0. , 0. ]],
[[ 0.5, 0. ],
[ 0. , 0. ]]], dtype=float32)),
(DeviceArray([[ 0.07677898, 0.0563341 ],
[ 0.07238522, -1.830669 ]], dtype=float32),
DeviceArray([[[-4.9707499e-01, 2.9999996e-04],
[-6.2500127e-04, 1.2500001e-04]],
[[ 4.9707499e-01, -2.9999996e-04],
[ 6.2500127e-04, -1.2500001e-04]]], dtype=float32)))


For more details, please [refer to the documentation](https://docs.pennylane.ai/en/stable/code/api/pennylane.enable_return.html?highlight=enable_return#pennylane.enable_return).

<h4>New basis rotation and tapering features in qml.qchem 🤓</h4>

* Grouped coefficients, observables, and basis rotation transformation matrices needed to construct a qubit Hamiltonian in the rotated basis of molecular orbitals are now calculable via `qml.qchem.basis_rotation()`. ([3011](https://github.com/PennyLaneAI/pennylane/pull/3011))

pycon
>>> symbols = ['H', 'H']
>>> geometry = np.array([[0.0, 0.0, 0.0], [1.398397361, 0.0, 0.0]], requires_grad = False)
>>> mol = qml.qchem.Molecule(symbols, geometry)
>>> core, one, two = qml.qchem.electron_integrals(mol)()
>>> coeffs, ops, unitaries = qml.qchem.basis_rotation(one, two, tol_factor=1.0e-5)
>>> unitaries
[tensor([[-1.00000000e+00, -5.46483514e-13],
[ 5.46483514e-13, -1.00000000e+00]], requires_grad=True),
tensor([[-1.00000000e+00, 3.17585063e-14],
[-3.17585063e-14, -1.00000000e+00]], requires_grad=True),
tensor([[-0.70710678, -0.70710678],
[-0.70710678, 0.70710678]], requires_grad=True),
tensor([[ 2.58789009e-11, 1.00000000e+00],
[-1.00000000e+00, 2.58789009e-11]], requires_grad=True)]


* Any gate operation can now be tapered according to :math:`\mathbb{Z}_2` symmetries of the Hamiltonian via `qml.qchem.taper_operation`. [(3002)](https://github.com/PennyLaneAI/pennylane/pull/3002) [(#3121)](https://github.com/PennyLaneAI/pennylane/pull/3121)

pycon
>>> symbols = ['He', 'H']
>>> geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.4589]])
>>> mol = qml.qchem.Molecule(symbols, geometry, charge=1)
>>> H, n_qubits = qml.qchem.molecular_hamiltonian(symbols, geometry)
>>> generators = qml.qchem.symmetry_generators(H)
>>> paulixops = qml.qchem.paulix_ops(generators, n_qubits)
>>> paulix_sector = qml.qchem.optimal_sector(H, generators, mol.n_electrons)
>>> tap_op = qml.qchem.taper_operation(qml.SingleExcitation, generators, paulixops,
... paulix_sector, wire_order=H.wires, op_wires=[0, 2])
>>> tap_op(3.14159)
[Exp(1.5707949999999993j PauliY)]


Moreover, the obtained tapered operation can be used directly within a QNode.

pycon
>>> dev = qml.device('default.qubit', wires=[0, 1])
>>> qml.qnode(dev)
... def circuit(params):
... tap_op(params[0])
... return qml.expval(qml.PauliZ(0) qml.PauliZ(1))
>>> drawer = qml.draw(circuit, show_all_wires=True)
>>> print(drawer(params=[3.14159]))
0: ──Exp(0.00+1.57j Y)─┤ ╭<ZZ>
1: ────────────────────┤ ╰<ZZ>


* Functionality has been added to estimate the number of measurements required to compute an expectation value with a target error and estimate the error in computing an expectation value with a given number of measurements. [(3000)](https://github.com/PennyLaneAI/pennylane/pull/3000)

<h4>New functions, operations, and observables 🤩</h4>

* Wires of operators or entire QNodes can now be mapped to other wires via `qml.map_wires()`. [(3143)](https://github.com/PennyLaneAI/pennylane/pull/3143) [(#3145)](https://github.com/PennyLaneAI/pennylane/pull/3145)

The `qml.map_wires()` function requires a dictionary representing a wire map. Use it with

- arbitrary operators:

pycon
>>> op = qml.RX(0.54, wires=0) + qml.PauliX(1) + (qml.PauliZ(2) qml.RY(1.23, wires=3))
>>> op
(RX(0.54, wires=[0]) + PauliX(wires=[1])) + (PauliZ(wires=[2]) RY(1.23, wires=[3]))
>>> wire_map = {0: 10, 1: 11, 2: 12, 3: 13}
>>> qml.map_wires(op, wire_map)
(RX(0.54, wires=[10]) + PauliX(wires=[11])) + (PauliZ(wires=[12]) RY(1.23, wires=[13]))


A `map_wires` method has also been added to operators, which returns a copy
of the operator with its wires changed according to the given wire map.

- entire QNodes:

python
dev = qml.device("default.qubit", wires=["A", "B", "C", "D"])
wire_map = {0: "A", 1: "B", 2: "C", 3: "D"}

qml.qnode(dev)
def circuit():
qml.RX(0.54, wires=0)
qml.PauliX(1)
qml.PauliZ(2)
qml.RY(1.23, wires=3)
return qml.probs(wires=0)


pycon
>>> mapped_circuit = qml.map_wires(circuit, wire_map)
>>> mapped_circuit()
tensor([0.92885434, 0.07114566], requires_grad=True)
>>> print(qml.draw(mapped_circuit)())
A: ──RX(0.54)─┤ Probs
B: ──X────────┤
C: ──Z────────┤
D: ──RY(1.23)─┤


* The `qml.IntegerComparator` arithmetic operation is now available. [(3113)](https://github.com/PennyLaneAI/pennylane/pull/3113)

Given a basis state :math:`\vert n \rangle`, where :math:`n` is a positive integer, and a fixed positive integer :math:`L`, `qml.IntegerComparator` flips a target qubit if :math:`n \geq L`. Alternatively, the flipping condition can be :math:`n < L` as demonstrated below:

python
dev = qml.device("default.qubit", wires=2)

qml.qnode(dev)
def circuit():
qml.BasisState(np.array([0, 1]), wires=range(2))
qml.broadcast(qml.Hadamard, wires=range(2), pattern='single')
qml.IntegerComparator(2, geq=False, wires=[0, 1])
return qml.state()


pycon
>>> circuit()
[-0.5+0.j 0.5+0.j -0.5+0.j 0.5+0.j]


* The `qml.GellMann` qutrit observable, the ternary generalization of the Pauli observables, is now available. [(3035)](https://github.com/PennyLaneAI/pennylane/pull/3035)

When using `qml.GellMann`, the `index` keyword argument determines which of the 8 Gell-Mann matrices is used.

python
dev = qml.device("default.qutrit", wires=2)

qml.qnode(dev)
def circuit():
qml.TClock(wires=0)
qml.TShift(wires=1)
qml.TAdd(wires=[0, 1])
return qml.expval(qml.GellMann(wires=0, index=8) + qml.GellMann(wires=1, index=3))


pycon
>>> circuit()
-0.42264973081037416


* Controlled qutrit operations can now be performed with `qml.ControlledQutritUnitary`. ([2844](https://github.com/PennyLaneAI/pennylane/pull/2844))

The control wires and values that define the operation are defined analogously to the qubit operation.

python
dev = qml.device("default.qutrit", wires=3)

qml.qnode(dev)
def circuit(U):
qml.TShift(wires=0)
qml.TAdd(wires=[0, 1])
qml.ControlledQutritUnitary(U, control_wires=[0, 1], control_values='12', wires=2)
return qml.state()


pycon
>>> U = np.array([[1, 1, 0], [1, -1, 0], [0, 0, np.sqrt(2)]]) / np.sqrt(2)
>>> circuit(U)
tensor([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, 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, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,
0.+0.j, 0.+0.j, 0.+0.j], requires_grad=True)


<h3>Improvements</h3>

* PennyLane now supports Python 3.11! [(3297)](https://github.com/PennyLaneAI/pennylane/pull/3297)

* `qml.sample` and `qml.counts` work more efficiently and track if computational basis samples are being generated when they are called without specifying an observable. [(3207)](https://github.com/PennyLaneAI/pennylane/pull/3207)

* The parameters of a basis set containing a different number of Gaussian functions are now easier to differentiate. [(3213)](https://github.com/PennyLaneAI/pennylane/pull/3213)

* Printing a `qml.MultiControlledX` operator now shows the `control_values` keyword argument. [(3113)](https://github.com/PennyLaneAI/pennylane/pull/3113)

* `qml.simplify` and transforms like `qml.matrix`, `batch_transform`, `hamiltonian_expand`, and `split_non_commuting` now work with `QuantumScript` as well as `QuantumTape`. [(3209)](https://github.com/PennyLaneAI/pennylane/pull/3209)

* A redundant flipping of the initial state in the UCCSD and kUpCCGSD templates has been removed. [(3148)](https://github.com/PennyLaneAI/pennylane/pull/3148)

* `qml.adjoint` now supports batching if the base operation supports batching. [(3168)](https://github.com/PennyLaneAI/pennylane/pull/3168)

* `qml.OrbitalRotation` is now decomposed into two `qml.SingleExcitation` operations for faster execution and more efficient parameter-shift gradient calculations on devices that natively support `qml.SingleExcitation`. [(3171)](https://github.com/PennyLaneAI/pennylane/pull/3171)

* The `Exp` class decomposes into a `PauliRot` class if the coefficient is imaginary and the base operator is a Pauli Word. [(3249)](https://github.com/PennyLaneAI/pennylane/pull/3249)

* Added the operator attributes `has_decomposition` and `has_adjoint` that indicate whether a corresponding `decomposition` or `adjoint` method is available. [(2986)](https://github.com/PennyLaneAI/pennylane/pull/2986)

* Structural improvements are made to `QueuingManager`, formerly `QueuingContext`, and `AnnotatedQueue`. [(2794)](https://github.com/PennyLaneAI/pennylane/pull/2794) [(#3061)](https://github.com/PennyLaneAI/pennylane/pull/3061) [(#3085)](https://github.com/PennyLaneAI/pennylane/pull/3085)

- `QueuingContext` is renamed to `QueuingManager`.
- `QueuingManager` should now be the global communication point for putting queuable objects into the active queue.
- `QueuingManager` is no longer an abstract base class.
- `AnnotatedQueue` and its children no longer inherit from `QueuingManager`.
- `QueuingManager` is no longer a context manager.
- Recording queues should start and stop recording via the `QueuingManager.add_active_queue` and `QueuingContext.remove_active_queue` class methods instead of directly manipulating the `_active_contexts` property.
- `AnnotatedQueue` and its children no longer provide global information about actively recording queues. This information is now only available through `QueuingManager`.
- `AnnotatedQueue` and its children no longer have the private `_append`, `_remove`, `_update_info`, `_safe_update_info`, and `_get_info` methods. The public analogues should be used instead.
- `QueuingManager.safe_update_info` and `AnnotatedQueue.safe_update_info` are deprecated. Their functionality is moved to `update_info`.

* `qml.Identity` now accepts multiple wires. [(3049)](https://github.com/PennyLaneAI/pennylane/pull/3049)

pycon
>>> id_op = qml.Identity([0, 1])
>>> id_op.matrix()
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
>>> id_op.sparse_matrix()
<4x4 sparse matrix of type '<class 'numpy.float64'>'
with 4 stored elements in Compressed Sparse Row format>
>>> id_op.eigvals()
array([1., 1., 1., 1.])


* Added `unitary_check` keyword argument to the constructor of the `QubitUnitary` class which indicates whether the user wants to check for unitarity of the input matrix or not. Its default value is `false`. [(3063)](https://github.com/PennyLaneAI/pennylane/pull/3063)

* Modified the representation of `WireCut` by using `qml.draw_mpl`. [(3067)](https://github.com/PennyLaneAI/pennylane/pull/3067)

* Improved the performance of `qml.math.expand_matrix` function for dense and sparse matrices. [(3060)](https://github.com/PennyLaneAI/pennylane/pull/3060) [(#3064)](https://github.com/PennyLaneAI/pennylane/pull/3064)

* Added support for sums and products of operator classes with scalar tensors of any interface (NumPy, JAX, Tensorflow, PyTorch...). [(3149)](https://github.com/PennyLaneAI/pennylane/pull/3149)

pycon
>>> s_prod = torch.tensor(4) * qml.RX(1.23, 0)
>>> s_prod
4*(RX(1.23, wires=[0]))
>>> s_prod.scalar
tensor(4)


* Added `overlapping_ops` property to the `Composite` class to improve the performance of the `eigvals`, `diagonalizing_gates` and `Prod.matrix` methods. [(3084)](https://github.com/PennyLaneAI/pennylane/pull/3084)

* Added the `map_wires` method to the operators, which returns a copy of the operator with its wires changed according to the given wire map. [(3143)](https://github.com/PennyLaneAI/pennylane/pull/3143)

pycon
>>> op = qml.Toffoli([0, 1, 2])
>>> wire_map = {0: 2, 2: 0}
>>> op.map_wires(wire_map=wire_map)
Toffoli(wires=[2, 1, 0])


* Calling `compute_matrix` and `compute_sparse_matrix` of simple non-parametric operations is now faster and more memory-efficient with the addition of caching. [(3134)](https://github.com/PennyLaneAI/pennylane/pull/3134)

* Added details to the output of `Exp.label()`. [(3126)](https://github.com/PennyLaneAI/pennylane/pull/3126)

* `qml.math.unwrap` no longer creates ragged arrays. Lists remain lists. [(3163)](https://github.com/PennyLaneAI/pennylane/pull/3163)

* New `null.qubit` device. The `null.qubit`performs no operations or memory allocations. [(2589)](https://github.com/PennyLaneAI/pennylane/pull/2589)

* `default.qubit` favours decomposition and avoids matrix construction for `QFT` and `GroverOperator` at larger qubit numbers. [(3193)](https://github.com/PennyLaneAI/pennylane/pull/3193)

* `qml.ControlledQubitUnitary` now has a `control_values` property. [(3206)](https://github.com/PennyLaneAI/pennylane/pull/3206)

* Added a new `qml.tape.QuantumScript` class that contains all the non-queuing behavior of `QuantumTape`. Now, `QuantumTape` inherits from `QuantumScript` as well as `AnnotatedQueue`. [(3097)](https://github.com/PennyLaneAI/pennylane/pull/3097)

* Extended the `qml.equal` function to MeasurementProcesses [(3189)](https://github.com/PennyLaneAI/pennylane/pull/3189)

* `qml.drawer.draw.draw_mpl` now accepts a `style` kwarg to select a style for plotting, rather than calling `qml.drawer.use_style(style)` before plotting. Setting a style for `draw_mpl` does not change the global configuration for matplotlib plotting. If no `style` is passed, the function defaults to plotting with the `black_white` style. [(3247)](https://github.com/PennyLaneAI/pennylane/pull/3247)

<h3>Breaking changes</h3>

* `QuantumTape._par_info` is now a list of dictionaries, instead of a dictionary whose keys are integers starting from zero. [(3185)](https://github.com/PennyLaneAI/pennylane/pull/3185)

* `QueuingContext` has been renamed to `QueuingManager`. [(3061)](https://github.com/PennyLaneAI/pennylane/pull/3061)

* Deprecation patches for the return types enum's location and `qml.utils.expand` are removed. [(3092)](https://github.com/PennyLaneAI/pennylane/pull/3092)

* `_multi_dispatch` functionality has been moved inside the `get_interface` function. This function can now be called with one or multiple tensors as arguments. [(3136)](https://github.com/PennyLaneAI/pennylane/pull/3136)

pycon
>>> torch_scalar = torch.tensor(1)
>>> torch_tensor = torch.Tensor([2, 3, 4])
>>> numpy_tensor = np.array([5, 6, 7])
>>> qml.math.get_interface(torch_scalar)
'torch'
>>> qml.math.get_interface(numpy_tensor)
'numpy'


`_multi_dispatch` previously had only one argument which contained a list of the tensors to be dispatched:

pycon
>>> qml.math._multi_dispatch([torch_scalar, torch_tensor, numpy_tensor])
'torch'


To differentiate whether the user wants to get the interface of a single tensor or multiple tensors, `get_interface` now accepts a different argument per tensor to be dispatched:

pycon
>>> qml.math.get_interface(*[torch_scalar, torch_tensor, numpy_tensor])
'torch'
>>> qml.math.get_interface(torch_scalar, torch_tensor, numpy_tensor)
'torch'


* `Operator.compute_terms` is removed. On a specific instance of an operator, `op.terms()` can be used instead. There is no longer a static method for this. [(3215)](https://github.com/PennyLaneAI/pennylane/pull/3215)

<h3>Deprecations</h3>

* `QueuingManager.safe_update_info` and `AnnotatedQueue.safe_update_info` are deprecated. Instead, `update_info` no longer raises errors if the object isn't in the queue. [(3085)](https://github.com/PennyLaneAI/pennylane/pull/3085)

* `qml.tape.stop_recording` and `QuantumTape.stop_recording` have been moved to `qml.QueuingManager.stop_recording`. The old functions will still be available until v0.29. [(3068)](https://github.com/PennyLaneAI/pennylane/pull/3068)

* `qml.tape.get_active_tape` has been deprecated. Use `qml.QueuingManager.active_context()` instead. [(3068)](https://github.com/PennyLaneAI/pennylane/pull/3068)

* `Operator.compute_terms` has been removed. On a specific instance of an operator, use `op.terms()` instead. There is no longer a static method for this. [(3215)](https://github.com/PennyLaneAI/pennylane/pull/3215)

* `qml.tape.QuantumTape.inv()` has been deprecated. Use `qml.tape.QuantumTape.adjoint` instead. [(3237)](https://github.com/PennyLaneAI/pennylane/pull/3237)

* `qml.transforms.qcut.remap_tape_wires` has been deprecated. Use `qml.map_wires` instead. [(3186)](https://github.com/PennyLaneAI/pennylane/pull/3186)

* The grouping module `qml.grouping` has been deprecated. Use `qml.pauli` or `qml.pauli.grouping` instead. The module will still be available until v0.28. [(3262)](https://github.com/PennyLaneAI/pennylane/pull/3262)

<h3>Documentation</h3>

* The code block in the usage details of the UCCSD template has been updated. [(3140)](https://github.com/PennyLaneAI/pennylane/pull/3140)

* Added a "Deprecations" page to the developer documentation. [(3093)](https://github.com/PennyLaneAI/pennylane/pull/3093)

* The example of the `qml.FlipSign` template has been updated. [(3219)](https://github.com/PennyLaneAI/pennylane/pull/3219)

<h3>Bug fixes</h3>

* `qml.SparseHamiltonian` now validates the size of the input matrix. [(3278)](https://github.com/PennyLaneAI/pennylane/pull/3278)

* Users no longer see unintuitive errors when inputing sequences to `qml.Hermitian`. [(3181)](https://github.com/PennyLaneAI/pennylane/pull/3181)

* The evaluation of QNodes that return either `vn_entropy` or `mutual_info` raises an informative error message when using devices that define a vector of shots. [(3180)](https://github.com/PennyLaneAI/pennylane/pull/3180)

* Fixed a bug that made `qml.AmplitudeEmbedding` incompatible with JITting. [(3166)](https://github.com/PennyLaneAI/pennylane/pull/3166)

* Fixed the `qml.transforms.transpile` transform to work correctly for all two-qubit operations. [(3104)](https://github.com/PennyLaneAI/pennylane/pull/3104)

* Fixed a bug with the control values of a controlled version of a `ControlledQubitUnitary`. [(3119)](https://github.com/PennyLaneAI/pennylane/pull/3119)

* Fixed a bug where `qml.math.fidelity(non_trainable_state, trainable_state)` failed unexpectedly. [(3160)](https://github.com/PennyLaneAI/pennylane/pull/3160)

* Fixed a bug where `qml.QueuingManager.stop_recording` did not clean up if yielded code raises an exception. [(3182)](https://github.com/PennyLaneAI/pennylane/pull/3182)

* Returning `qml.sample()` or `qml.counts()` with other measurements of non-commuting observables now raises a QuantumFunctionError (e.g., `return qml.expval(PauliX(wires=0)), qml.sample()` now raises an error). [(2924)](https://github.com/PennyLaneAI/pennylane/pull/2924)

* Fixed a bug where `op.eigvals()` would return an incorrect result if the operator was a non-hermitian composite operator. [(3204)](https://github.com/PennyLaneAI/pennylane/pull/3204)

* Fixed a bug where `qml.BasisStatePreparation` and `qml.BasisEmbedding` were not jit-compilable with JAX. [(3239)](https://github.com/PennyLaneAI/pennylane/pull/3239)

* Fixed a bug where `qml.MottonenStatePreparation` was not jit-compilable with JAX. [(3260)](https://github.com/PennyLaneAI/pennylane/pull/3260)

* Fixed a bug where `qml.expval(qml.Hamiltonian())` would not raise an error if the Hamiltonian involved some wires that are not present on the device. [(3266)](https://github.com/PennyLaneAI/pennylane/pull/3266)

* Fixed a bug where `qml.tape.QuantumTape.shape()` did not account for the batch dimension of the tape [(3269)](https://github.com/PennyLaneAI/pennylane/pull/3269)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Kamal Mohamed Ali, Guillermo Alonso-Linaje, Juan Miguel Arrazola, Utkarsh Azad, Thomas Bromley, Albert Mitjans Coma, Isaac De Vlugt, Olivia Di Matteo, Amintor Dusko, Lillian M. A. Frederiksen, Diego Guala, Josh Izaac, Soran Jahangiri, Edward Jiang, Korbinian Kottmann, Christina Lee, Romain Moyard, Lee J. O'Riordan, Mudit Pandey, Matthew Silverman, Jay Soni, Antal Száva, David Wierichs.


v0.26.0-postfix-1
A minor postfix release to update the documentation styling.

0.26.0

<h3>New features since last release</h3>

<h4>Classical shadows 👤</h4>

* PennyLane now provides built-in support for implementing the classical-shadows measurement protocol. [(2820)](https://github.com/PennyLaneAI/pennylane/pull/2820) [(#2821)](https://github.com/PennyLaneAI/pennylane/pull/2821) [(#2871)](https://github.com/PennyLaneAI/pennylane/pull/2871) [(#2968)](https://github.com/PennyLaneAI/pennylane/pull/2968) [(#2959)](https://github.com/PennyLaneAI/pennylane/pull/2959) [(#2968)](https://github.com/PennyLaneAI/pennylane/pull/2968)

The classical-shadow measurement protocol is described in detail in the paper [Predicting Many Properties of a Quantum System from Very Few Measurements](https://arxiv.org/abs/2002.08953). As part of the support for classical shadows in this release, two new finite-shot and fully-differentiable measurements are available:

- QNodes returning the new measurement `qml.classical_shadow()` will return two entities; `bits` (0 or 1 if the 1 or -1 eigenvalue is sampled, respectively) and `recipes` (the randomized Pauli measurements that are performed for each qubit, labelled by integer):

python
dev = qml.device("default.qubit", wires=2, shots=3)

qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
return qml.classical_shadow(wires=[0, 1])


pycon
>>> bits, recipes = circuit()
>>> bits
tensor([[0, 0],
[1, 0],
[0, 1]], dtype=uint8, requires_grad=True)
>>> recipes
tensor([[2, 2],
[0, 2],
[0, 2]], dtype=uint8, requires_grad=True)


- QNodes returning `qml.shadow_expval()` yield the expectation value estimation using classical shadows:

python
dev = qml.device("default.qubit", wires=range(2), shots=10000)

qml.qnode(dev)
def circuit(x, H):
qml.Hadamard(0)
qml.CNOT((0,1))
qml.RX(x, wires=0)
return qml.shadow_expval(H)

x = np.array(0.5, requires_grad=True)
H = qml.Hamiltonian(
[1., 1.],
[qml.PauliZ(0) qml.PauliZ(1), qml.PauliX(0) qml.PauliX(1)]
)


pycon
>>> circuit(x, H)
tensor(1.8486, requires_grad=True)
>>> qml.grad(circuit)(x, H)
-0.4797000000000001


Fully-differentiable QNode transforms for both new classical-shadows measurements are also available via `qml.shadows.shadow_state` and `qml.shadows.shadow_expval`, respectively.

For convenient post-processing, we've also added the ability to calculate general Renyi entropies by way of the `ClassicalShadow` class' `entropy` method, which requires the wires of the subsystem of interest and the Renyi entropy order:

pycon
>>> shadow = qml.ClassicalShadow(bits, recipes)
>>> vN_entropy = shadow.entropy(wires=[0, 1], alpha=1)


<h4>Qutrits: quantum circuits for tertiary degrees of freedom ☘️</h4>

* An entirely new framework for quantum computing is now simulatable with the addition of qutrit functionalities. [(2699)](https://github.com/PennyLaneAI/pennylane/pull/2699) [(#2781)](https://github.com/PennyLaneAI/pennylane/pull/2781) [(#2782)](https://github.com/PennyLaneAI/pennylane/pull/2782) [(#2783)](https://github.com/PennyLaneAI/pennylane/pull/2783) [(#2784)](https://github.com/PennyLaneAI/pennylane/pull/2784) [(#2841)](https://github.com/PennyLaneAI/pennylane/pull/2841) [(#2843)](https://github.com/PennyLaneAI/pennylane/pull/2843)

[Qutrits](https://en.wikipedia.org/wiki/Qutrit) are like qubits, but instead live in a *three*-dimensional Hilbert space; they are not binary degrees of freedom, they are *tertiary*. The advent of qutrits allows for all sorts of interesting theoretical, practical, and algorithmic capabilities that have yet to be discovered.

To facilitate qutrit circuits requires a new device: `default.qutrit`. The `default.qutrit` device is a Python-based simulator, akin to `default.qubit`, and is defined as per usual:

pycon
>>> dev = qml.device("default.qutrit", wires=1)


The following operations are supported on `default.qutrit` devices:

- The qutrit shift operator, `qml.TShift`, and the ternary clock operator, `qml.TClock`, as defined in this paper by [Yeh et al. (2022)](https://arxiv.org/abs/2204.00552),
which are the qutrit analogs of the Pauli X and Pauli Z operations, respectively.
- The `qml.TAdd` and `qml.TSWAP` operations which are the qutrit analogs of the CNOT and SWAP operations, respectively.
- Custom unitary operations via `qml.QutritUnitary`.
- `qml.state` and `qml.probs` measurements.
- Measuring user-specified Hermitian matrix observables via `qml.THermitian`.

A comprehensive example of these features is given below:

python
dev = qml.device("default.qutrit", wires=1)

U = np.array([
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
]
) / np.sqrt(3)

obs = np.array([
[1, 1, 0],
[1, -1, 0],
[0, 0, np.sqrt(2)]
]
) / np.sqrt(2)

qml.qnode(dev)
def qutrit_state(U, obs):
qml.TShift(0)
qml.TClock(0)
qml.QutritUnitary(U, wires=0)
return qml.state()

qml.qnode(dev)
def qutrit_expval(U, obs):
qml.TShift(0)
qml.TClock(0)
qml.QutritUnitary(U, wires=0)
return qml.expval(qml.THermitian(obs, wires=0))


pycon
>>> qutrit_state(U, obs)
tensor([-0.28867513+0.5j, -0.28867513+0.5j, -0.28867513+0.5j], requires_grad=True)
>>> qutrit_expval(U, obs)
tensor(0.80473785, requires_grad=True)


We will continue to add more and more support for qutrits in future releases.

<h4>Simplifying just got... simpler 😌</h4>

* The `qml.simplify()` function has several intuitive improvements with this release. [(2978)](https://github.com/PennyLaneAI/pennylane/pull/2978) [(#2982)](https://github.com/PennyLaneAI/pennylane/pull/2982) [(#2922)](https://github.com/PennyLaneAI/pennylane/pull/2922) [(#3012)](https://github.com/PennyLaneAI/pennylane/pull/3012)

`qml.simplify` can now perform the following:

- simplify parametrized operations
- simplify the adjoint and power of specific operators
- group like terms in a sum
- resolve products of Pauli operators
- combine rotation angles of identical rotation gates

Here is an example of `qml.simplify` in action with parameterized rotation gates. In this case, the angles of rotation are simplified to be modulo $4\pi$.

pycon
>>> op1 = qml.RX(30.0, wires=0)
>>> qml.simplify(op1)
RX(4.867258771281655, wires=[0])
>>> op2 = qml.RX(4 * np.pi, wires=0)
>>> qml.simplify(op2)
Identity(wires=[0])


All of these simplification features can be applied directly to quantum functions, QNodes, and tapes via decorating with `qml.simplify`, as well:

python
dev = qml.device("default.qubit", wires=2)
qml.simplify
qml.qnode(dev)
def circuit():
qml.adjoint(qml.prod(qml.RX(1, 0) ** 1, qml.RY(1, 0), qml.RZ(1, 0)))
return qml.probs(wires=0)


pycon
>>> circuit()
>>> list(circuit.tape)
[RZ(11.566370614359172, wires=[0]) RY(11.566370614359172, wires=[0]) RX(11.566370614359172, wires=[0]),
probs(wires=[0])]


<h4>QNSPSA optimizer 💪</h4>

* A new optimizer called `qml.QNSPSAOptimizer` is available that implements the quantum natural simultaneous perturbation stochastic approximation (QNSPSA) method based on [Simultaneous Perturbation Stochastic Approximation of the Quantum Fisher Information](https://quantum-journal.org/papers/q-2021-10-20-567/). [(#2818)](https://github.com/PennyLaneAI/pennylane/pull/2818)

`qml.QNSPSAOptimizer` is a second-order [SPSA algorithm](https://en.wikipedia.org/wiki/Simultaneous_perturbation_stochastic_approximation), which combines the convergence power of the quantum-aware Quantum Natural Gradient (QNG) optimization method with the reduced quantum evaluations of SPSA methods.

While the QNSPSA optimizer requires additional circuit executions (10 executions per step) compared to standard SPSA optimization (3 executions per step), these additional evaluations are used to provide a stochastic estimation of a second-order metric tensor, which often helps the optimizer to achieve faster convergence.

Use `qml.QNSPSAOptimizer` like you would any other optimizer:

python
max_iterations = 50
opt = qml.QNSPSAOptimizer()

for _ in range(max_iterations):
params, cost = opt.step_and_cost(cost, params)


Check out [our demo](https://pennylane.ai/qml/demos/qnspsa.html) on the QNSPSA optimizer for more information.

<h4>Operator and parameter broadcasting supplements 📈</h4>

* Operator methods for exponentiation and raising to a power have been added. [(2799)](https://github.com/PennyLaneAI/pennylane/pull/2799) [(#3029)](https://github.com/PennyLaneAI/pennylane/pull/3029)

- The `qml.exp` function can be used to create observables or generic rotation gates:

pycon
>>> x = 1.234
>>> t = qml.PauliX(0) qml.PauliX(1) + qml.PauliY(0) qml.PauliY(1)
>>> isingxy = qml.exp(t, 0.25j * x)
>>> isingxy.matrix()
array([[1. +0.j , 0. +0.j ,
1. +0.j , 0. +0.j ],
[0. +0.j , 0.8156179+0.j ,
1. +0.57859091j, 0. +0.j ],
[0. +0.j , 0. +0.57859091j,
0.8156179+0.j , 0. +0.j ],
[0. +0.j , 0. +0.j ,
1. +0.j , 1. +0.j ]])


- The `qml.pow` function raises a given operator to a power:

pycon
>>> op = qml.pow(qml.PauliX(0), 2)
>>> op.matrix()
array([[1, 0], [0, 1]])


* An operator called `qml.PSWAP` is now available. [(2667)](https://github.com/PennyLaneAI/pennylane/pull/2667)

The `qml.PSWAP` gate -- or phase-SWAP gate -- was previously available within the PennyLane-Braket plugin only. Enjoy it natively in PennyLane with v0.26.

* Check whether or not an operator is hermitian or unitary with `qml.is_hermitian` and `qml.is_unitary`. [(2960)](https://github.com/PennyLaneAI/pennylane/pull/2960)

pycon
>>> op1 = qml.PauliX(wires=0)
>>> qml.is_hermitian(op1)
True
>>> op2 = qml.PauliX(0) + qml.RX(np.pi/3, 0)
>>> qml.is_unitary(op2)
False


* Embedding templates now support parameter broadcasting. [(2810)](https://github.com/PennyLaneAI/pennylane/pull/2810)

Embedding templates like `AmplitudeEmbedding` or `IQPEmbedding` now support parameter broadcasting with a leading broadcasting dimension in their variational parameters. `AmplitudeEmbedding`, for example, would usually use a one-dimensional input vector of features. With broadcasting, we can now compute

pycon
>>> features = np.array([
... [0.5, 0.5, 0., 0., 0.5, 0., 0.5, 0.],
... [1., 0., 0., 0., 0., 0., 0., 0.],
... [0.5, 0.5, 0., 0., 0., 0., 0.5, 0.5],
... ])
>>> op = qml.AmplitudeEmbedding(features, wires=[1, 5, 2])
>>> op.batch_size
3


An exception is `BasisEmbedding`, which is not broadcastable.

<h3>Improvements</h3>

* The `qml.math.expand_matrix()` method now allows the sparse matrix representation of an operator to be extended to a larger hilbert space. [(2998)](https://github.com/PennyLaneAI/pennylane/pull/2998)

pycon
>>> from scipy import sparse
>>> mat = sparse.csr_matrix([[0, 1], [1, 0]])
>>> qml.math.expand_matrix(mat, wires=[1], wire_order=[0,1]).toarray()
array([[0., 1., 0., 0.],
[1., 0., 0., 0.],
[0., 0., 0., 1.],
[0., 0., 1., 0.]])


* `qml.ctrl` now uses `Controlled` instead of `ControlledOperation`. The new `Controlled` class wraps individual `Operator`'s instead of a tape. It provides improved representations and integration. [(2990)](https://github.com/PennyLaneAI/pennylane/pull/2990)

* `qml.matrix` can now compute the matrix of tapes and QNodes that contain multiple broadcasted operations or non-broadcasted operations after broadcasted ones. [(3025)](https://github.com/PennyLaneAI/pennylane/pull/3025)

A common scenario in which this becomes relevant is the decomposition of broadcasted operations: the decomposition in general will contain one or multiple broadcasted operations as well as operations with no or fixed parameters that are not broadcasted.

* Lists of operators are now internally sorted by their respective wires while also taking into account their commutativity property.[(2995)](https://github.com/PennyLaneAI/pennylane/pull/2995)

* Some methods of the `QuantumTape` class have been simplified and reordered to improve both readability and performance. [(2963)](https://github.com/PennyLaneAI/pennylane/pull/2963)

* The `qml.qchem.molecular_hamiltonian` function is modified to support observable grouping. [(2997)](https://github.com/PennyLaneAI/pennylane/pull/2997)

* `qml.ops.op_math.Controlled` now has basic decomposition functionality. [(2938)](https://github.com/PennyLaneAI/pennylane/pull/2938)

* Automatic circuit cutting has been improved by making better partition imbalance derivations. Now it is more likely to generate optimal cuts for larger circuits. [(2517)](https://github.com/PennyLaneAI/pennylane/pull/2517)

* By default, `qml.counts` only returns the outcomes observed in sampling. Optionally, specifying `qml.counts(all_outcomes=True)` will return a dictionary containing all possible outcomes. [(2889)](https://github.com/PennyLaneAI/pennylane/pull/2889)

pycon
>>> dev = qml.device("default.qubit", wires=2, shots=1000)
>>>
>>> qml.qnode(dev)
>>> def circuit():
... qml.Hadamard(wires=0)
... qml.CNOT(wires=[0, 1])
... return qml.counts(all_outcomes=True)
>>> result = circuit()
>>> result
{'00': 495, '01': 0, '10': 0, '11': 505}


* Internal use of in-place inversion is eliminated in preparation for its deprecation. [(2965)](https://github.com/PennyLaneAI/pennylane/pull/2965)

* `Controlled` operators now work with `qml.is_commuting`. [(2994)](https://github.com/PennyLaneAI/pennylane/pull/2994)

* `qml.prod` and `qml.op_sum` now support the `sparse_matrix()` method. [(3006)](https://github.com/PennyLaneAI/pennylane/pull/3006)

pycon
>>> xy = qml.prod(qml.PauliX(1), qml.PauliY(1))
>>> op = qml.op_sum(xy, qml.Identity(0))
>>>
>>> sparse_mat = op.sparse_matrix(wire_order=[0,1])
>>> type(sparse_mat)
<class 'scipy.sparse.csr.csr_matrix'>
>>> sparse_mat.toarray()
[[1.+1.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 1.-1.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 1.+1.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 1.-1.j]]


* Provided `sparse_matrix()` support for single qubit observables. [(2964)](https://github.com/PennyLaneAI/pennylane/pull/2964)

* `qml.Barrier` with `only_visual=True` now simplifies via `op.simplify()` to the identity operator or a product of identity operators.[(3016)](https://github.com/PennyLaneAI/pennylane/pull/3016)

* More accurate and intuitive outputs for printing some operators have been added. [(3013)](https://github.com/PennyLaneAI/pennylane/pull/3013)

* Results for the matrix of the sum or product of operators are stored in a more efficient manner. [(3022)](https://github.com/PennyLaneAI/pennylane/pull/3022)

* The computation of the (sparse) matrix for the sum or product of operators is now more efficient. [(3030)](https://github.com/PennyLaneAI/pennylane/pull/3030)

* When the factors of `qml.prod` don't share any wires, the matrix and sparse matrix are computed using a kronecker product for improved efficiency. [(3040)](https://github.com/PennyLaneAI/pennylane/pull/3040)

* `qml.grouping.is_pauli_word` now returns `False` for operators that don't inherit from `qml.Observable` instead of raising an error. [(3039)](https://github.com/PennyLaneAI/pennylane/pull/3039)

* Added functionality to iterate over operators created from `qml.op_sum` and `qml.prod`. [(3028)](https://github.com/PennyLaneAI/pennylane/pull/3028)

pycon
>>> op = qml.op_sum(qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2))
>>> len(op)
3
>>> op[1]
PauliY(wires=[1])
>>> [o.name for o in op]
['PauliX', 'PauliY', 'PauliZ']


<h3>Deprecations</h3>

* In-place inversion is now deprecated. This includes `op.inv()` and `op.inverse=value`. Please use `qml.adjoint` or `qml.pow` instead. Support for these methods will remain till v0.28. [(2988)](https://github.com/PennyLaneAI/pennylane/pull/2988)

Don't use:

pycon
>>> v1 = qml.PauliX(0).inv()
>>> v2 = qml.PauliX(0)
>>> v2.inverse = True


Instead use:

pycon
>>> qml.adjoint(qml.PauliX(0))
Adjoint(PauliX(wires=[0]))
>>> qml.pow(qml.PauliX(0), -1)
PauliX(wires=[0])**-1
>>> qml.pow(qml.PauliX(0), -1, lazy=False)
PauliX(wires=[0])
>>> qml.PauliX(0) ** -1
PauliX(wires=[0])**-1


`qml.adjoint` takes the conjugate transpose of an operator, while `qml.pow(op, -1)` indicates matrix inversion. For unitary operators, `adjoint` will be more efficient than `qml.pow(op, -1)`, even though they represent the same thing.

* The `supports_reversible_diff` device capability is unused and has been removed. [(2993)](https://github.com/PennyLaneAI/pennylane/pull/2993)

<h3>Breaking changes</h3>

* Measuring an operator that might not be hermitian now raises a warning instead of an error. To definitively determine whether or not an operator is hermitian, use `qml.is_hermitian`. [(2960)](https://github.com/PennyLaneAI/pennylane/pull/2960)

* The `ControlledOperation` class has been removed. This was a developer-only class, so the change should not be evident to any users. It is replaced by `Controlled`. [(2990)](https://github.com/PennyLaneAI/pennylane/pull/2990)

* The default `execute` method for the `QubitDevice` base class now calls `self.statistics` with an additional keyword argument `circuit`, which represents the quantum tape being executed. Any device that overrides `statistics` should edit the signature of the method to include the new `circuit` keyword argument. [(2820)](https://github.com/PennyLaneAI/pennylane/pull/2820)

* The `expand_matrix()` has been moved from `pennylane.operation` to `pennylane.math.matrix_manipulation`. [(3008)](https://github.com/PennyLaneAI/pennylane/pull/3008)

* `qml.grouping.utils.is_commuting` has been removed, and its Pauli word logic is now part of `qml.is_commuting`. [(3033)](https://github.com/PennyLaneAI/pennylane/pull/3033)

* `qml.is_commuting` has been moved from `pennylane.transforms.commutation_dag` to `pennylane.ops.functions`. [(2991)](https://github.com/PennyLaneAI/pennylane/pull/2991)

<h3>Documentation</h3>

* Updated the Fourier transform docs to use `circuit_spectrum` instead of `spectrum`, which has been deprecated. [(3018)](https://github.com/PennyLaneAI/pennylane/pull/3018)

* Corrected the docstrings for diagonalizing gates for all relevant operations. The docstrings used to say that the diagonalizing gates implemented $U$, the unitary such that $O = U \Sigma U^{\dagger}$, where $O$ is the original observable and $\Sigma$ a diagonal matrix. However, the diagonalizing gates actually implement $U^{\dagger}$, since $\langle \psi | O | \psi \rangle = \langle \psi | U \Sigma U^{\dagger} | \psi \rangle$, making $U^{\dagger} | \psi \rangle$ the actual state being measured in the Z-basis. [(2981)](https://github.com/PennyLaneAI/pennylane/pull/2981)

<h3>Bug fixes</h3>

* Fixed a bug with `qml.ops.Exp` operators when the coefficient is autograd but the diagonalizing gates don't act on all wires. [(3057)](https://github.com/PennyLaneAI/pennylane/pull/3057)

* Fixed a bug where the tape transform `single_qubit_fusion` computed wrong rotation angles for specific combinations of rotations. [(3024)](https://github.com/PennyLaneAI/pennylane/pull/3024)

* Jax gradients now work with a QNode when the quantum function was transformed by `qml.simplify`. [(3017)](https://github.com/PennyLaneAI/pennylane/pull/3017)

* Operators that have `num_wires = AnyWires` or `num_wires = AnyWires` now raise an error, with certain exceptions, when instantiated with `wires=[]`. [(2979)](https://github.com/PennyLaneAI/pennylane/pull/2979)

* Fixed a bug where printing `qml.Hamiltonian` with complex coefficients raises `TypeError` in some cases. [(3005)](https://github.com/PennyLaneAI/pennylane/pull/3004)

* Added a more descriptive error message when measuring non-commuting observables at the end of a circuit with `probs`, `samples`, `counts` and `allcounts`. [(3065)](https://github.com/PennyLaneAI/pennylane/pull/3065)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Juan Miguel Arrazola, Utkarsh Azad, Tom Bromley, Olivia Di Matteo, Isaac De Vlugt, Yiheng Duan, Lillian Marie Austin Frederiksen, Josh Izaac, Soran Jahangiri, Edward Jiang, Ankit Khandelwal, Korbinian Kottmann, Meenu Kumari, Christina Lee, Albert Mitjans Coma, Romain Moyard, Rashid N H M, Zeyue Niu, Mudit Pandey, Matthew Silverman, Jay Soni, Antal Száva, Cody Wang, David Wierichs.

0.25.1

<h3>Bug fixes</h3>

* Fixed Torch device discrepencies for certain parametrized operations by updating `qml.math.array` and `qml.math.eye` to preserve the Torch device used. [(2967)](https://github.com/PennyLaneAI/pennylane/pull/2967)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Romain Moyard, Rashid N H M, Lee James O'Riordan, Antal Száva.

0.25.0

<h3>New features since last release</h3>

<h4>Estimate computational resource requirements 🧠</h4>

* Functionality for estimating molecular simulation computations has been added with `qml.resource`. [(2646)](https://github.com/PennyLaneAI/pennylane/pull/2646) [(#2653)](https://github.com/PennyLaneAI/pennylane/pull/2653) [(#2665)](https://github.com/PennyLaneAI/pennylane/pull/2665) [(#2694)](https://github.com/PennyLaneAI/pennylane/pull/2694) [(#2720)](https://github.com/PennyLaneAI/pennylane/pull/2720) [(#2723)](https://github.com/PennyLaneAI/pennylane/pull/2723) [(#2746)](https://github.com/PennyLaneAI/pennylane/pull/2746) [(#2796)](https://github.com/PennyLaneAI/pennylane/pull/2796) [(#2797)](https://github.com/PennyLaneAI/pennylane/pull/2797) [(#2874)](https://github.com/PennyLaneAI/pennylane/pull/2874) [(#2944)](https://github.com/PennyLaneAI/pennylane/pull/2944) [(#2644)](https://github.com/PennyLaneAI/pennylane/pull/2644)

The new [resource](https://pennylane.readthedocs.io/en/stable/code/qml_resource.html) module allows you to estimate the number of non-[Clifford gates](https://en.wikipedia.org/wiki/Clifford_gates) and logical qubits needed to implement [quantum phase estimation](https://codebook.xanadu.ai/P.1) algorithms for simulating materials and molecules. This includes support for quantum algorithms using [first](https://en.wikipedia.org/wiki/First_quantization) and [second](https://en.wikipedia.org/wiki/Second_quantization) quantization with specific bases:

- [First quantization](https://en.wikipedia.org/wiki/First_quantization) using a plane-wave basis via the `FirstQuantization` class:

pycon
>>> n = 100000 number of plane waves
>>> eta = 156 number of electrons
>>> omega = 1145.166 unit cell volume in atomic units
>>> algo = FirstQuantization(n, eta, omega)
>>> print(algo.gates, algo.qubits)
1.10e+13, 4416


- [Second quantization](https://en.wikipedia.org/wiki/Second_quantization) with a double-factorized Hamiltonian via the `DoubleFactorization` class:

python
symbols = ["O", "H", "H"]
geometry = np.array(
[
[0.00000000, 0.00000000, 0.28377432],
[0.00000000, 1.45278171, -1.00662237],
[0.00000000, -1.45278171, -1.00662237],
],
requires_grad=False,
)

mol = qml.qchem.Molecule(symbols, geometry, basis_name="sto-3g")
core, one, two = qml.qchem.electron_integrals(mol)()

algo = DoubleFactorization(one, two)


pycon
>>> print(algo.gates, algo.qubits)
103969925, 290


The methods of the `FirstQuantization` and the `DoubleFactorization` classes, such as `qubit_cost` (number of logical qubits) and `gate_cost` (number of non-Clifford gates), can be also accessed as static methods:

pycon
>>> qml.resource.FirstQuantization.qubit_cost(100000, 156, 169.69608, 0.01)
4377
>>> qml.resource.FirstQuantization.gate_cost(100000, 156, 169.69608, 0.01)
3676557345574


<h4>Differentiable error mitigation ⚙️</h4>

* Differentiable zero-noise-extrapolation (ZNE) error mitigation is now available. [(2757)](https://github.com/PennyLaneAI/pennylane/pull/2757)

Elevate any variational quantum algorithm to a *mitigated* algorithm with improved results on noisy hardware while maintaining differentiability throughout.

In order to do so, use the `qml.transforms.mitigate_with_zne` transform on your QNode and provide the PennyLane proprietary `qml.transforms.fold_global` folding function and `qml.transforms.poly_extrapolate` extrapolation function. Here is an example for a noisy simulation device where we mitigate a QNode and are still able to compute the gradient:

python
Describe noise
noise_gate = qml.DepolarizingChannel
noise_strength = 0.1

Load devices
dev_ideal = qml.device("default.mixed", wires=1)
dev_noisy = qml.transforms.insert(noise_gate, noise_strength)(dev_ideal)

scale_factors = [1, 2, 3]
mitigate_with_zne(
scale_factors,
qml.transforms.fold_global,
qml.transforms.poly_extrapolate,
extrapolate_kwargs={'order': 2}
)
qml.qnode(dev_noisy)
def qnode_mitigated(theta):
qml.RY(theta, wires=0)
return qml.expval(qml.PauliX(0))


pycon
>>> theta = np.array(0.5, requires_grad=True)
>>> qml.grad(qnode_mitigated)(theta)
0.5712737447327619


<h4>More native support for parameter broadcasting 📡</h4>

* `default.qubit` now natively supports parameter broadcasting, providing increased performance when executing the same circuit at various parameter positions compared to manually looping over parameters, or directly using the `qml.transforms.broadcast_expand` transform. [(2627)](https://github.com/PennyLaneAI/pennylane/pull/2627)

python
dev = qml.device("default.qubit", wires=1)

qml.qnode(dev)
def circuit(x):
qml.RX(x, wires=0)
return qml.expval(qml.PauliZ(0))


pycon
>>> circuit(np.array([0.1, 0.3, 0.2]))
tensor([0.99500417, 0.95533649, 0.98006658], requires_grad=True)

Currently, not all templates have been updated to support broadcasting.

* Parameter-shift gradients now allow for parameter broadcasting internally, which can result in a significant speedup when computing gradients of circuits with many parameters. [(2749)](https://github.com/PennyLaneAI/pennylane/pull/2749)

The gradient transform `qml.gradients.param_shift` now accepts the keyword argument `broadcast`. If set to `True`, broadcasting is used to compute the derivative:

python
dev = qml.device("default.qubit", wires=2)

qml.qnode(dev)
def circuit(x, y):
qml.RX(x, wires=0)
qml.RY(y, wires=1)
return qml.expval(qml.PauliZ(0) qml.PauliZ(1))


pycon
>>> x = np.array([np.pi/3, np.pi/2], requires_grad=True)
>>> y = np.array([np.pi/6, np.pi/5], requires_grad=True)
>>> qml.gradients.param_shift(circuit, broadcast=True)(x, y)
(tensor([[-0.7795085, 0. ],
[ 0. , -0.7795085]], requires_grad=True),
tensor([[-0.125, 0. ],
[0. , -0.125]], requires_grad=True))


The following example highlights how to make use of broadcasting gradients at the QNode level. Internally, broadcasting is used to compute the parameter-shift rule when required, which may result in performance improvements.

python
qml.qnode(dev, diff_method="parameter-shift", broadcast=True)
def circuit(x, y):
qml.RX(x, wires=0)
qml.RY(y, wires=1)
return qml.expval(qml.PauliZ(0) qml.PauliZ(1))


pycon
>>> x = np.array(0.1, requires_grad=True)
>>> y = np.array(0.4, requires_grad=True)
>>> qml.grad(circuit)(x, y)
(array(-0.09195267), array(-0.38747287))


Here, only 2 circuits are created internally, rather than 4 with `broadcast=False`.

To illustrate the speedup, for a constant-depth circuit with Pauli rotations and controlled Pauli rotations, the time required to compute `qml.gradients.param_shift(circuit, broadcast=False)(params)` ("No broadcasting") and `qml.gradients.param_shift(circuit, broadcast=True)(params)` ("Broadcasting") as a function of the number of qubits is given [here](https://pennylane.readthedocs.io/en/stable/_images/param_shift_broadcast_speedup.png).

* Operations for quantum chemistry now support parameter broadcasting. [(2726)](https://github.com/PennyLaneAI/pennylane/pull/2726)

pycon
>>> op = qml.SingleExcitation(np.array([0.3, 1.2, -0.7]), wires=[0, 1])
>>> op.matrix().shape
(3, 4, 4)


<h4>Intuitive operator arithmetic 🧮</h4>

* New functionality for representing the sum, product, and scalar-product of operators is available. [(2475)](https://github.com/PennyLaneAI/pennylane/pull/2475) [(#2625)](https://github.com/PennyLaneAI/pennylane/pull/2625) [(#2622)](https://github.com/PennyLaneAI/pennylane/pull/2622) [(#2721)](https://github.com/PennyLaneAI/pennylane/pull/2721)

The following functionalities have been added to facilitate creating new operators whose matrix, terms, and eigenvalues can be accessed as per usual, while maintaining differentiability. Operators created from these new features can be used within QNodes as operations or as observables (where physically applicable).

- Summing any number of operators via `qml.op_sum` results in a "summed" operator:

pycon
>>> ops_to_sum = [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(0)]
>>> summed_ops = qml.op_sum(*ops_to_sum)
>>> summed_ops
PauliX(wires=[0]) + PauliY(wires=[1]) + PauliZ(wires=[0])
>>> qml.matrix(summed_ops)
array([[ 1.+0.j, 0.-1.j, 1.+0.j, 0.+0.j],
[ 0.+1.j, 1.+0.j, 0.+0.j, 1.+0.j],
[ 1.+0.j, 0.+0.j, -1.+0.j, 0.-1.j],
[ 0.+0.j, 1.+0.j, 0.+1.j, -1.+0.j]])
>>> summed_ops.terms()
([1.0, 1.0, 1.0], (PauliX(wires=[0]), PauliY(wires=[1]), PauliZ(wires=[0])))


- Multiplying any number of operators via `qml.prod` results in a "product" operator, where the matrix product or tensor product is used correspondingly:

pycon
>>> theta = 1.23
>>> prod_op = qml.prod(qml.PauliZ(0), qml.RX(theta, 1))
>>> prod_op
PauliZ(wires=[0]) RX(1.23, wires=[1])
>>> qml.eigvals(prod_op)
[-1.39373197 -0.23981492 0.23981492 1.39373197]


- Taking the product of a coefficient and an operator via `qml.s_prod` produces a "scalar-product" operator:

pycon
>>> sprod_op = qml.s_prod(2.0, qml.PauliX(0))
>>> sprod_op
2.0*(PauliX(wires=[0]))
>>> sprod_op.matrix()
array([[ 0., 2.],
[ 2., 0.]])
>>> sprod_op.terms()
([2.0], [PauliX(wires=[0])])


Each of these new functionalities can be used within QNodes as operators or observables, where applicable, while also maintaining differentiability. For example:

python
dev = qml.device("default.qubit", wires=2)

qml.qnode(dev)
def circuit(angles):
qml.prod(qml.PauliZ(0), qml.RY(angles[0], 1))
qml.op_sum(qml.PauliX(1), qml.RY(angles[1], 0))

return qml.expval(qml.op_sum(qml.PauliX(0), qml.PauliZ(1)))


pycon
>>> angles = np.array([1.23, 4.56], requires_grad=True)
>>> circuit(angles)
tensor(0.33423773, requires_grad=True)
>>> qml.grad(circuit)(angles)
array([-0.9424888, 0. ])


* All PennyLane operators can now be added, subtracted, multiplied, scaled, and raised to powers using `+`, `-`, ``, `*`, `**`, respectively. [(2849)](https://github.com/PennyLaneAI/pennylane/pull/2849) [(#2825)](https://github.com/PennyLaneAI/pennylane/pull/2825) [(#2891)](https://github.com/PennyLaneAI/pennylane/pull/2891)

- You can now add scalars to operators, where the interpretation is that the scalar is a properly-sized identity matrix;

pycon
>>> sum_op = 5 + qml.PauliX(0)
>>> sum_op.matrix()
array([[5., 1.],
[1., 5.]])


- The `+` and `-` operators can be used to combine all Pennylane operators:

pycon
>>> sum_op = qml.RX(phi=1.23, wires=0) + qml.RZ(phi=3.14, wires=0) - qml.RY(phi=0.12, wires=0)
>>> sum_op
RX(1.23, wires=[0]) + RZ(3.14, wires=[0]) + -1*(RY(0.12, wires=[0]))
>>> qml.matrix(sum_op)
array([[-0.18063077-0.99999968j, 0.05996401-0.57695852j],
[-0.05996401-0.57695852j, -0.18063077+0.99999968j]])

Note that the behavior of `+` and `-` with *observables* is different; it still creates a Hamiltonian.

- The `*` and `` operators can be used to scale and compose all PennyLane operators.

pycon
>>> prod_op = 2*qml.RX(1, wires=0) qml.RY(2, wires=0)
>>> prod_op
2*(RX(1, wires=[0])) RY(2, wires=[0])
>>> qml.matrix(prod_op)
array([[ 0.94831976-0.80684536j, -1.47692053-0.51806945j],
[ 1.47692053-0.51806945j, 0.94831976+0.80684536j]])


- The `**` operator can be used to raise PennyLane operators to a power.

pycon
>>> exp_op = qml.RZ(1.0, wires=0) ** 2
>>> exp_op
RZ**2(1.0, wires=[0])
>>> qml.matrix(exp_op)
array([[0.54030231-0.84147098j, 0. +0.j ],
[0. +0.j , 0.54030231+0.84147098j]])


* A new class called `Controlled` is available in `qml.ops.op_math` to represent a controlled version of any operator. This will eventually be integrated into `qml.ctrl` to provide a performance increase and more feature coverage. [(2634)](https://github.com/PennyLaneAI/pennylane/pull/2634)

* Arithmetic operations can now be simplified using `qml.simplify`. [(2835)](https://github.com/PennyLaneAI/pennylane/pull/2835) [(#2854)](https://github.com/PennyLaneAI/pennylane/pull/2854)

pycon
>>> op = qml.adjoint(qml.adjoint(qml.RX(x, wires=0)))
>>> op
Adjoint(Adjoint(RX))(tensor([1.04719755, 1.57079633], requires_grad=True), wires=[0])
>>> qml.simplify(op)
RX(tensor([1.04719755, 1.57079633], requires_grad=True), wires=[0])


* A new function called `qml.equal` can be used to compare the equality of parametric operators. [(2651)](https://github.com/PennyLaneAI/pennylane/pull/2651)

pycon
>>> qml.equal(qml.RX(1.23, 0), qml.RX(1.23, 0))
True
>>> qml.equal(qml.RY(4.56, 0), qml.RY(7.89, 0))
False


<h4>Marvelous mixed state features 🙌</h4>

* The `default.mixed` device now supports [backpropagation](https://pennylane.readthedocs.io/en/stable/introduction/unsupported_gradients.html#backpropagation) with the `"jax"` interface, which can result in significant speedups. [(2754)](https://github.com/PennyLaneAI/pennylane/pull/2754) [(#2776)](https://github.com/PennyLaneAI/pennylane/pull/2776)

python
dev = qml.device("default.mixed", wires=2)

qml.qnode(dev, diff_method="backprop", interface="jax")
def circuit(angles):
qml.RX(angles[0], wires=0)
qml.RY(angles[1], wires=1)
return qml.expval(qml.PauliZ(0) + qml.PauliZ(1))


pycon
>>> angles = np.array([np.pi/6, np.pi/5], requires_grad=True)
>>> qml.grad(circuit)(angles)
array([-0.8660254 , -0.25881905])


Additionally, quantum channels now support Jax and TensorFlow tensors. This allows quantum channels to be used inside QNodes decorated by `tf.function`, `jax.jit`, or `jax.vmap`.

* The `default.mixed` device now supports readout error. [(2786)](https://github.com/PennyLaneAI/pennylane/pull/2786)

A new keyword argument called `readout_prob` can be specified when creating a `default.mixed` device. Any circuits running on a `default.mixed` device with a finite `readout_prob` (upper-bounded by 1) will alter the measurements performed at the end of the circuit similarly to how a `qml.BitFlip` channel would affect circuit measurements:

pycon
>>> dev = qml.device("default.mixed", wires=2, readout_prob=0.1)
>>> qml.qnode(dev)
... def circuit():
... return qml.expval(qml.PauliZ(0))
>>> circuit()
array(0.8)


<h4>Relative entropy is now available in qml.qinfo 💥</h4>

* The quantum information module now supports computation of [relative entropy](https://en.wikipedia.org/wiki/Quantum_relative_entropy).
[(2772)](https://github.com/PennyLaneAI/pennylane/pull/2772)

We've enabled two cases for calculating the relative entropy:

- A QNode transform via `qml.qinfo.relative_entropy`:

python
dev = qml.device('default.qubit', wires=2)

qml.qnode(dev)
def circuit(param):
qml.RY(param, wires=0)
qml.CNOT(wires=[0, 1])
return qml.state()


pycon
>>> relative_entropy_circuit = qml.qinfo.relative_entropy(circuit, circuit, wires0=[0], wires1=[0])
>>> x, y = np.array(0.4), np.array(0.6)
>>> relative_entropy_circuit((x,), (y,))
0.017750012490703237


- Support in `qml.math` for flexible post-processing:

pycon
>>> rho = np.array([[0.3, 0], [0, 0.7]])
>>> sigma = np.array([[0.5, 0], [0, 0.5]])
>>> qml.math.relative_entropy(rho, sigma)
tensor(0.08228288, requires_grad=True)


<h4>New measurements, operators, and more! ✨</h4>

* A new measurement called `qml.counts` is available. [(2686)](https://github.com/PennyLaneAI/pennylane/pull/2686) [(#2839)](https://github.com/PennyLaneAI/pennylane/pull/2839) [(#2876)](https://github.com/PennyLaneAI/pennylane/pull/2876)

QNodes with `shots != None` that return `qml.counts` will yield a dictionary whose keys are bitstrings representing computational basis states that were measured, and whose values are the corresponding counts (i.e., how many times that computational basis state was measured):

python
dev = qml.device("default.qubit", wires=2, shots=1000)

qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
return qml.counts()


pycon
>>> circuit()
{'00': 495, '11': 505}


`qml.counts` can also accept observables, where the resulting dictionary is ordered by the eigenvalues of the observable.

python
dev = qml.device("default.qubit", wires=2, shots=1000)

qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
return qml.counts(qml.PauliZ(0)), qml.counts(qml.PauliZ(1))


pycon
>>> circuit()
({-1: 470, 1: 530}, {-1: 470, 1: 530})


* A new experimental return type for QNodes with multiple measurements has been added. [(2814)](https://github.com/PennyLaneAI/pennylane/pull/2814) [(#2815)](https://github.com/PennyLaneAI/pennylane/pull/2815) [(#2860)](https://github.com/PennyLaneAI/pennylane/pull/2860)

QNodes returning a list or tuple of different measurements return an intuitive data structure via `qml.enable_return()`, where the individual measurements are separated into their own tensors:

python
qml.enable_return()
dev = qml.device("default.qubit", wires=2)

qml.qnode(dev)
def circuit(x):
qml.Hadamard(wires=[0])
qml.CRX(x, wires=[0, 1])
return (qml.probs(wires=[0]), qml.vn_entropy(wires=[0]), qml.probs(wires=0), qml.expval(wires=1))

pycon
>>> circuit(0.5)
(tensor([0.5, 0.5], requires_grad=True), tensor(0.08014815, requires_grad=True), tensor([0.5, 0.5], requires_grad=True), tensor(0.93879128, requires_grad=True))


In addition, QNodes that utilize this new return type support backpropagation. This new return type can be disabled thereafter via `qml.disable_return()`.

* An operator called `qml.FlipSign` is now available. [(2780)](https://github.com/PennyLaneAI/pennylane/pull/2780)

Mathematically, `qml.FlipSign` functions as follows: $\text{FlipSign}(n) \vert m \rangle = (-1)^\delta_{n,m} \vert m \rangle$, where $\vert m \rangle$ is an arbitrary qubit state and $n$ is a qubit configuration:

python
basis_state = [0, 1]

dev = qml.device("default.qubit", wires=2)

qml.qnode(dev)
def circuit():
for wire in list(range(2)):
qml.Hadamard(wires = wire)
qml.FlipSign(basis_state, wires = list(range(2)))
return qml.state()


pycon
>>> circuit()
tensor([ 0.5+0.j -0.5+0.j 0.5+0.j 0.5+0.j], requires_grad=True)


* The [simultaneous perturbation stochastic approximation (SPSA) optimizer](https://www.jhuapl.edu/SPSA/PDF-SPSA/Spall_An_Overview.PDF) is available via `qml.SPSAOptimizer`. [(#2661)](https://github.com/PennyLaneAI/pennylane/pull/2661)

The SPSA optimizer is suitable for cost functions whose evaluation may involve noise. Use the SPSA optimizer like you would any other optimizer:

python
max_iterations = 50
opt = qml.SPSAOptimizer(maxiter=max_iterations)

for _ in range(max_iterations):
params, cost = opt.step_and_cost(cost, params)


<h4>More drawing styles 🎨</h4>

* New PennyLane-inspired `sketch` and `sketch_dark` styles are now available for drawing circuit diagram graphics. [(2709)](https://github.com/PennyLaneAI/pennylane/pull/2709)

<h3>Improvements 📈</h3>

* `default.qubit` now natively executes any operation that defines a matrix except for trainable `Pow` operations. [(2836)](https://github.com/PennyLaneAI/pennylane/pull/2836)

* Added `expm` to the `qml.math` module for matrix exponentiation. [(2890)](https://github.com/PennyLaneAI/pennylane/pull/2890)

* When adjoint differentiation is requested, circuits are now decomposed so that all trainable operations have a generator. [(2836)](https://github.com/PennyLaneAI/pennylane/pull/2836)

* A warning is now emitted for `qml.state`, `qml.density_matrix`, `qml.vn_entropy`, and `qml.mutual_info` when using a device with finite shots or a shot list since these measurements are always analytic. [(2918)](https://github.com/PennyLaneAI/pennylane/pull/2918)

* The efficiency of the Hartree-Fock workflow has been improved by removing repetitive steps. [(2850)](https://github.com/PennyLaneAI/pennylane/pull/2850)

* The coefficients of the non-differentiable molecular Hamiltonians generated with openfermion now have `requires_grad = False` by default. [(2865)](https://github.com/PennyLaneAI/pennylane/pull/2865)

* Upgraded performance of the `compute_matrix` method of broadcastable parametric operations. [(2759)](https://github.com/PennyLaneAI/pennylane/pull/2759)

* Jacobians are now cached with the Autograd interface when using the parameter-shift rule. [(2645)](https://github.com/PennyLaneAI/pennylane/pull/2645)

* The `qml.state` and `qml.density_matrix` measurements now support custom wire labels. [(2779)](https://github.com/PennyLaneAI/pennylane/pull/2779)

* Add trivial behaviour logic to `qml.operation.expand_matrix`. [(2785)](https://github.com/PennyLaneAI/pennylane/issues/2785)

* Added an `are_pauli_words_qwc` function which checks if certain Pauli words are pairwise qubit-wise commuting. This new function improves performance when measuring hamiltonians with many commuting terms. [(2789)](https://github.com/PennyLaneAI/pennylane/pull/2798)

* Adjoint differentiation now uses the adjoint symbolic wrapper instead of in-place inversion. [(2855)](https://github.com/PennyLaneAI/pennylane/pull/2855)

<h3>Breaking changes 💔</h3>

* The deprecated `qml.hf` module is removed. Users with code that calls `qml.hf` can simply replace `qml.hf` with `qml.qchem` in most cases, or refer to the [qchem documentation](https://pennylane.readthedocs.io/en/stable/code/qml_qchem.html) and [demos](https://pennylane.ai/qml/demos_quantum-chemistry.html) for more information. [(#2795)](https://github.com/PennyLaneAI/pennylane/pull/2795)

* `default.qubit` now uses `stopping_condition` to specify support for anything with a matrix. To override this behavior in inheriting devices and to support only a specific subset of operations, developers need to override `stopping_condition`. [(2836)](https://github.com/PennyLaneAI/pennylane/pull/2836)

* Custom devices inheriting from `DefaultQubit` or `QubitDevice` can break due to the introduction of parameter broadcasting. [(2627)](https://github.com/PennyLaneAI/pennylane/pull/2627)

A custom device should only break if all three following statements hold simultaneously:

1. The custom device inherits from `DefaultQubit`, not `QubitDevice`.
2. The device implements custom methods in the simulation pipeline that are incompatible with broadcasting (for example `expval`, `apply_operation` or `analytic_probability`).
3. The custom device maintains the flag `"supports_broadcasting": True` in its `capabilities` dictionary *or* it overwrites `Device.batch_transform` without applying `broadcast_expand` (or both).

The `capabilities["supports_broadcasting"]` is set to `True` for `DefaultQubit`. Typically, the easiest fix will be to change `capabilities["supports_broadcasting"]` flag to `False` for the child device and/or to include a call to `broadcast_expand` in `CustomDevice.batch_transform`, similar to how `Device.batch_transform` calls it.

Separately from the above, custom devices that inherit from `QubitDevice` and implement a custom `_gather` method need to allow for the kwarg `axis` to be passed to this `_gather` method.

* The argument `argnum` of the function `qml.batch_input` has been redefined: now it indicates the indices of the batched parameters, which need to be non-trainable, in the quantum tape. Consequently, its default value (set to 0) has been removed. [(2873)](https://github.com/PennyLaneAI/pennylane/pull/2873)

Before this breaking change, one could call `qml.batch_input` without any arguments when using batched inputs as the first argument of the quantum circuit.

python
dev = qml.device("default.qubit", wires=2, shots=None)

qml.batch_input() argnum = 0
qml.qnode(dev, diff_method="parameter-shift", interface="tf")
def circuit(inputs, weights): argument `inputs` is batched
qml.RY(weights[0], wires=0)
qml.AngleEmbedding(inputs, wires=range(2), rotation="Y")
qml.RY(weights[1], wires=1)
return qml.expval(qml.PauliZ(1))


With this breaking change, users must set a value to `argnum` specifying the index of the batched inputs with respect to all quantum tape parameters. In this example the quantum tape parameters are `[ weights[0], inputs, weights[1] ]`, thus `argnum` should be set to 1, specifying that `inputs` is batched:

python
dev = qml.device("default.qubit", wires=2, shots=None)

qml.batch_input(argnum=1)
qml.qnode(dev, diff_method="parameter-shift", interface="tf")
def circuit(inputs, weights):
qml.RY(weights[0], wires=0)
qml.AngleEmbedding(inputs, wires=range(2), rotation="Y")
qml.RY(weights[1], wires=1)
return qml.expval(qml.PauliZ(1))


* PennyLane now depends on newer versions (>=2.7) of the `semantic_version` package, which provides an updated API that is incompatible which versions of the package prior to 2.7. If you run into issues relating to this package, please reinstall PennyLane. [(2744)](https://github.com/PennyLaneAI/pennylane/pull/2744) [(#2767)](https://github.com/PennyLaneAI/pennylane/pull/2767)

<h3>Documentation 📕</h3>

* Added a dedicated docstring for the `QubitDevice.sample` method. [(2812)](https://github.com/PennyLaneAI/pennylane/pull/2812)

* Optimization examples of using JAXopt and Optax with the JAX interface have been added. [(2769)](https://github.com/PennyLaneAI/pennylane/pull/2769)

* Updated IsingXY gate docstring. [(2858)](https://github.com/PennyLaneAI/pennylane/pull/2858)

<h3>Bug fixes 🐞</h3>

* Fixes `qml.equal` so that operators with different inverse properties are not equal. [(2947)](https://github.com/PennyLaneAI/pennylane/pull/2947)

* Cleans up interactions between operator arithmetic and batching by testing supported cases and adding errors when batching is not supported. [(2900)](https://github.com/PennyLaneAI/pennylane/pull/2900)

* Fixed a bug where the parameter-shift rule wasn't defined for `qml.kUpCCGSD`. [(2913)](https://github.com/PennyLaneAI/pennylane/pull/2913)

* Reworked the Hermiticity check in `qml.Hermitian` by using `qml.math` calls because calling `.conj()` on an `EagerTensor` from TensorFlow raised an error. [(2895)](https://github.com/PennyLaneAI/pennylane/pull/2895)

* Fixed a bug where the parameter-shift gradient breaks when using both custom `grad_recipe`s that contain unshifted terms and recipes that do not contain any unshifted terms. [(2834)](https://github.com/PennyLaneAI/pennylane/pull/2834)

* Fixed mixed CPU-GPU data-locality issues for the Torch interface. [(2830)](https://github.com/PennyLaneAI/pennylane/pull/2830)

* Fixed a bug where the parameter-shift Hessian of circuits with untrainable parameters might be computed with respect to the wrong parameters or might raise an error. [(2822)](https://github.com/PennyLaneAI/pennylane/pull/2822)

* Fixed a bug where the custom implementation of the `states_to_binary` device method was not used. [(2809)](https://github.com/PennyLaneAI/pennylane/pull/2809)

* `qml.grouping.group_observables` now works when individual wire labels are iterable. [(2752)](https://github.com/PennyLaneAI/pennylane/pull/2752)

* The adjoint of an adjoint now has a correct `expand` result. [(2766)](https://github.com/PennyLaneAI/pennylane/pull/2766)

* Fixed the ability to return custom objects as the expectation value of a QNode with the Autograd interface. [(2808)](https://github.com/PennyLaneAI/pennylane/pull/2808)

* The WireCut operator now raises an error when instantiating it with an empty list. [(2826)](https://github.com/PennyLaneAI/pennylane/pull/2826)

* Hamiltonians with grouped observables are now allowed to be measured on devices which were transformed using `qml.transform.insert()`. [(2857)](https://github.com/PennyLaneAI/pennylane/pull/2857)

* Fixed a bug where `qml.batch_input` raised an error when using a batched operator that was not located at the beginning of the circuit. In addition, now `qml.batch_input` raises an error when using trainable batched inputs, which avoids an unwanted behaviour with duplicated parameters. [(2873)](https://github.com/PennyLaneAI/pennylane/pull/2873)

* Calling `qml.equal` with nested operators now raises a `NotImplementedError`. [(2877)](https://github.com/PennyLaneAI/pennylane/pull/2877)

* Fixed a bug where a non-sensible error message was raised when using `qml.counts` with `shots=False`. [(2928)](https://github.com/PennyLaneAI/pennylane/pull/2928)

* Fixed a bug where no error was raised and a wrong value was returned when using `qml.counts` with another non-commuting observable. [(2928)](https://github.com/PennyLaneAI/pennylane/pull/2928)

* Operator Arithmetic now allows `Hamiltonian` objects to be used and produces correct matrices. [(2957)](https://github.com/PennyLaneAI/pennylane/pull/2957)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Juan Miguel Arrazola, Utkarsh Azad, Samuel Banning, Prajwal Borkar, Isaac De Vlugt, Olivia Di Matteo, Kristiyan Dilov, David Ittah, Josh Izaac, Soran Jahangiri, Edward Jiang, Ankit Khandelwal, Korbinian Kottmann, Meenu Kumari, Christina Lee, Sergio Martínez-Losa, Albert Mitjans Coma, Ixchel Meza Chavez, Romain Moyard, Lee James O'Riordan, Mudit Pandey, Bogdan Reznychenko, Shuli Shu, Jay Soni, Modjtaba Shokrian-Zini, Antal Száva, David Wierichs, Moritz Willmann.

0.24.0

<h3>New features since last release</h3>

<h4>All new quantum information quantities 📏</h4>

* Functionality for computing quantum information quantities for QNodes has been added. [(2554)](https://github.com/PennyLaneAI/pennylane/pull/2554) [(#2569)](https://github.com/PennyLaneAI/pennylane/pull/2569) [(#2598)](https://github.com/PennyLaneAI/pennylane/pull/2598) [(#2617)](https://github.com/PennyLaneAI/pennylane/pull/2617) [(#2631)](https://github.com/PennyLaneAI/pennylane/pull/2631) [(#2640)](https://github.com/PennyLaneAI/pennylane/pull/2640) [(#2663)](https://github.com/PennyLaneAI/pennylane/pull/2663) [(#2684)](https://github.com/PennyLaneAI/pennylane/pull/2684) [(#2688)](https://github.com/PennyLaneAI/pennylane/pull/2688) [(#2695)](https://github.com/PennyLaneAI/pennylane/pull/2695) [(#2710)](https://github.com/PennyLaneAI/pennylane/pull/2710) [(#2712)](https://github.com/PennyLaneAI/pennylane/pull/2712)

This includes two new QNode measurements:

- The [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy) via `qml.vn_entropy`:

pycon
>>> dev = qml.device("default.qubit", wires=2)
>>> qml.qnode(dev)
... def circuit_entropy(x):
... qml.IsingXX(x, wires=[0,1])
... return qml.vn_entropy(wires=[0], log_base=2)
>>> circuit_entropy(np.pi/2)
1.0


- The [mutual information](https://en.wikipedia.org/wiki/Quantum_mutual_information) via `qml.mutual_info`:

pycon
>>> dev = qml.device("default.qubit", wires=2)
>>> qml.qnode(dev)
... def circuit(x):
... qml.IsingXX(x, wires=[0,1])
... return qml.mutual_info(wires0=[0], wires1=[1], log_base=2)
>>> circuit(np.pi/2)
2.0


New differentiable transforms are also available in the `qml.qinfo` module:

- The classical and quantum [Fisher information](https://en.wikipedia.org/wiki/Fisher_information) via `qml.qinfo.classical_fisher`, `qml.qinfo.quantum_fisher`, respectively:

python3
dev = qml.device("default.qubit", wires=3)

qml.qnode(dev)
def circ(params):
qml.RY(params[0], wires=1)
qml.CNOT(wires=(1,0))
qml.RY(params[1], wires=1)
qml.RZ(params[2], wires=1)
return qml.expval(qml.PauliX(0) qml.PauliX(1) - 0.5 * qml.PauliZ(1))

params = np.array([0.5, 1., 0.2], requires_grad=True)
cfim = qml.qinfo.classical_fisher(circ)(params)
qfim = qml.qinfo.quantum_fisher(circ)(params)


These quantities are typically employed in variational optimization schemes to tilt the gradient in a more favourable direction --- producing what is known as the [natural gradient](https://pennylane.ai/qml/demos/tutorial_quantum_natural_gradient.html). For example:

pycon
>>> grad = qml.grad(circ)(params)
>>> cfim grad natural gradient
[ 5.94225615e-01 -2.61509542e-02 -1.18674655e-18]
>>> qfim grad quantum natural gradient
[ 0.59422561 -0.02615095 -0.03989212]


- The fidelity between two arbitrary states via `qml.qinfo.fidelity`:

python
dev = qml.device('default.qubit', wires=1)

qml.qnode(dev)
def circuit_rx(x):
qml.RX(x[0], wires=0)
qml.RZ(x[1], wires=0)
return qml.state()

qml.qnode(dev)
def circuit_ry(y):
qml.RY(y, wires=0)
return qml.state()


pycon
>>> x = np.array([0.1, 0.3], requires_grad=True)
>>> y = np.array(0.2, requires_grad=True)
>>> fid_func = qml.qinfo.fidelity(circuit_rx, circuit_ry, wires0=[0], wires1=[0])
>>> fid_func(x, y)
0.9905158135644924
>>> df = qml.grad(fid_func)
>>> df(x, y)
(array([-0.04768725, -0.29183666]), array(-0.09489803))


- [Reduced density matrices](https://en.wikipedia.org/wiki/Quantum_entanglement#Reduced_density_matrices) of arbitrary states via `qml.qinfo.reduced_dm`:

python
dev = qml.device("default.qubit", wires=2)
qml.qnode(dev)
def circuit(x):
qml.IsingXX(x, wires=[0,1])
return qml.state()


pycon
>>> qml.qinfo.reduced_dm(circuit, wires=[0])(np.pi/2)
[[0.5+0.j 0.+0.j]
[0.+0.j 0.5+0.j]]


- Similar transforms, `qml.qinfo.vn_entropy` and `qml.qinfo.mutual_info` exist
for transforming QNodes.

Currently, all quantum information measurements and transforms are differentiable, but only support statevector devices, with hardware support to come in a future release (with the exception of `qml.qinfo.classical_fisher` and `qml.qinfo.quantum_fisher`, which are both hardware compatible).

For more information, check out the new [qinfo module](https://pennylane.readthedocs.io/en/stable/code/qml_qinfo.html) and [measurements page](https://pennylane.readthedocs.io/en/stable/introduction/measurements.html).

* In addition to the QNode transforms and measurements above, functions for computing and differentiating quantum information metrics with numerical statevectors and density matrices have been added to the `qml.math` module. This enables flexible custom post-processing.

Added functions include:

- `qml.math.reduced_dm`
- `qml.math.vn_entropy`
- `qml.math.mutual_info`
- `qml.math.fidelity`

For example:

pycon
>>> x = torch.tensor([1.0, 0.0, 0.0, 1.0], requires_grad=True)
>>> en = qml.math.vn_entropy(x / np.sqrt(2.), indices=[0])
>>> en
tensor(0.6931, dtype=torch.float64, grad_fn=<DivBackward0>)
>>> en.backward()
>>> x.grad
tensor([-0.3069, 0.0000, 0.0000, -0.3069])


<h4>Faster mixed-state training with backpropagation 📉</h4>

* The `default.mixed` device now supports differentiation via backpropagation with the Autograd, TensorFlow, and PyTorch (CPU) interfaces, leading to significantly more performant optimization and training. [(2615)](https://github.com/PennyLaneAI/pennylane/pull/2615) [(#2670)](https://github.com/PennyLaneAI/pennylane/pull/2670) [(#2680)](https://github.com/PennyLaneAI/pennylane/pull/2680)

As a result, the default differentiation method for the device is now `"backprop"`. To continue using the old default `"parameter-shift"`, explicitly specify this differentiation method in the QNode:

python
dev = qml.device("default.mixed", wires=2)

qml.qnode(dev, interface="autograd", diff_method="backprop")
def circuit(x):
qml.RY(x, wires=0)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliZ(wires=1))

pycon
>>> x = np.array(0.5, requires_grad=True)
>>> circuit(x)
array(0.87758256)
>>> qml.grad(circuit)(x)
-0.479425538604203


<h4>Support for quantum parameter broadcasting 📡</h4>

* Quantum operators, functions, and tapes now support broadcasting across parameter dimensions, making it more convenient for developers to execute their PennyLane programs with multiple sets of parameters. [(2575)](https://github.com/PennyLaneAI/pennylane/pull/2575) [(#2609)](https://github.com/PennyLaneAI/pennylane/pull/2609)

Parameter broadcasting refers to passing tensor parameters with additional leading dimensions to quantum operators; additional dimensions will flow through the computation, and produce additional dimensions at the output.

For example, instantiating a rotation gate with a one-dimensional array leads to a broadcasted `Operation`:

pycon
>>> x = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> op = qml.RX(x, 0)
>>> op.batch_size
3


Its matrix correspondingly is augmented by a leading dimension of size `batch_size`:

pycon
>>> np.round(qml.matrix(op), 4)
tensor([[[0.9988+0.j , 0. -0.05j ],
[0. -0.05j , 0.9988+0.j ]],
[[0.995 +0.j , 0. -0.0998j],
[0. -0.0998j, 0.995 +0.j ]],
[[0.9888+0.j , 0. -0.1494j],
[0. -0.1494j, 0.9888+0.j ]]], requires_grad=True)
>>> qml.matrix(op).shape
(3, 2, 2)


This can be extended to quantum functions, where we may mix-and-match operations with batched parameters and those without. However, the `batch_size` of each batched `Operator` within the quantum function must be the same:

pycon
>>> dev = qml.device('default.qubit', wires=1)
>>> qml.qnode(dev)
... def circuit_rx(x, z):
... qml.RX(x, wires=0)
... qml.RZ(z, wires=0)
... qml.RY(0.3, wires=0)
... return qml.probs(wires=0)
>>> circuit_rx([0.1, 0.2], [0.3, 0.4])
tensor([[0.97092256, 0.02907744],
[0.95671515, 0.04328485]], requires_grad=True)


Parameter broadcasting is supported on all devices, hardware and simulator. Note that if not natively supported by the underlying device, parameter broadcasting may result in additional quantum device evaluations.

* A new transform, `qml.transforms.broadcast_expand`, has been added, which automates the process of transforming quantum functions (and tapes) to multiple quantum evaluations with no parameter broadcasting. [(2590)](https://github.com/PennyLaneAI/pennylane/pull/2590)

pycon
>>> dev = qml.device('default.qubit', wires=1)
>>> qml.transforms.broadcast_expand()
>>> qml.qnode(dev)
... def circuit_rx(x, z):
... qml.RX(x, wires=0)
... qml.RZ(z, wires=0)
... qml.RY(0.3, wires=0)
... return qml.probs(wires=0)
>>> print(qml.draw(circuit_rx)([0.1, 0.2], [0.3, 0.4]))
0: ──RX(0.10)──RZ(0.30)──RY(0.30)─┤ Probs
\
0: ──RX(0.20)──RZ(0.40)──RY(0.30)─┤ Probs


Under-the-hood, this transform is used for devices that don't natively support parameter broadcasting.

* To specify that a device natively supports broadcasted tapes, the new flag `Device.capabilities()["supports_broadcasting"]` should be set to `True`.

* To support parameter broadcasting for new or custom operations, the following new `Operator` class attributes must be specified:

- `Operator.ndim_params` specifies expected number of dimensions for each parameter

Once set, `Operator.batch_size` and `QuantumTape.batch_size` will dynamically compute the parameter broadcasting axis dimension, if present.

<h4>Improved JAX JIT support 🏎</h4>

* JAX just-in-time (JIT) compilation now supports vector-valued QNodes, enabling new types of workflows and significant performance boosts. [(2034)](https://github.com/PennyLaneAI/pennylane/pull/2034)

Vector-valued QNodes include those with:
* `qml.probs`;
* `qml.state`;
* `qml.sample` or
* multiple `qml.expval` / `qml.var` measurements.

Consider a QNode that returns basis-state probabilities:

python
dev = qml.device('default.qubit', wires=2)
x = jnp.array(0.543)
y = jnp.array(-0.654)

jax.jit
qml.qnode(dev, diff_method="parameter-shift", interface="jax")
def circuit(x, y):
qml.RX(x, wires=[0])
qml.RY(y, wires=[1])
qml.CNOT(wires=[0, 1])
return qml.probs(wires=[1])

pycon
>>> circuit(x, y)
DeviceArray([0.8397495 , 0.16025047], dtype=float32)


Note that computing the jacobian of vector-valued QNode is not supported with JAX JIT. The output of vector-valued QNodes can, however, be used in the definition of scalar-valued cost functions whose gradients can be computed.

For example, one can define a cost function that outputs the first element of the probability vector:

python
def cost(x, y):
return circuit(x, y)[0]


pycon
>>> jax.grad(cost, argnums=[0])(x, y)
(DeviceArray(-0.2050439, dtype=float32),)


<h4>More drawing styles 🎨</h4>

* New `solarized_light` and `solarized_dark` styles are available for drawing circuit diagram graphics. [(2662)](https://github.com/PennyLaneAI/pennylane/pull/2662)

<h4>New operations & transforms 🤖</h4>

* The `qml.IsingXY` gate is now available (see [1912.04424](https://arxiv.org/abs/1912.04424)). [(#2649)](https://github.com/PennyLaneAI/pennylane/pull/2649)

* The `qml.ECR` (echoed cross-resonance) operation is now available (see [2105.01063](https://arxiv.org/pdf/2105.01063.pdf)). This gate is a maximally-entangling gate and is equivalent to a CNOT gate up to single-qubit pre-rotations. [(#2613)](https://github.com/PennyLaneAI/pennylane/pull/2613)

* The adjoint transform `adjoint` can now accept either a single instantiated operator or a quantum function. It returns an entity of the same type / call signature as what it was given: [(2222)](https://github.com/PennyLaneAI/pennylane/pull/2222) [(#2672)](https://github.com/PennyLaneAI/pennylane/pull/2672)

pycon
>>> qml.adjoint(qml.PauliX(0))
Adjoint(PauliX)(wires=[0])
>>> qml.adjoint(qml.RX)(1.23, wires=0)
Adjoint(RX)(1.23, wires=[0])


Now, `adjoint` wraps operators in a symbolic operator class `qml.ops.op_math.Adjoint`. This class should not be constructed directly; the `adjoint` constructor should always be used instead. The class behaves just like any other `Operator`:

pycon
>>> op = qml.adjoint(qml.S(0))
>>> qml.matrix(op)
array([[1.-0.j, 0.-0.j],
[0.-0.j, 0.-1.j]])
>>> qml.eigvals(op)
array([1.-0.j, 0.-1.j])


* A new symbolic operator class `qml.ops.op_math.Pow` represents an operator raised to a power. When `decomposition()` is called, a list of new operators equal to this one raised to the given power is given: [(2621)](https://github.com/PennyLaneAI/pennylane/pull/2621)

pycon
>>> op = qml.ops.op_math.Pow(qml.PauliX(0), 0.5)
>>> op.decomposition()
[SX(wires=[0])]
>>> qml.matrix(op)
array([[0.5+0.5j, 0.5-0.5j],
[0.5-0.5j, 0.5+0.5j]])


* A new transform `qml.batch_partial` is available which behaves similarly to `functools.partial`, but supports batching in the unevaluated parameters. [(2585)](https://github.com/PennyLaneAI/pennylane/pull/2585)

This is useful for executing a circuit with a batch dimension in some of its parameters:

python
dev = qml.device("default.qubit", wires=1)

qml.qnode(dev)
def circuit(x, y):
qml.RX(x, wires=0)
qml.RY(y, wires=0)
return qml.expval(qml.PauliZ(wires=0))


pycon
>>> batched_partial_circuit = qml.batch_partial(circuit, x=np.array(np.pi / 4))
>>> y = np.array([0.2, 0.3, 0.4])
>>> batched_partial_circuit(y=y)
tensor([0.69301172, 0.67552491, 0.65128847], requires_grad=True)


* A new transform `qml.split_non_commuting` is available, which splits a quantum function or tape into multiple functions/tapes determined by groups of commuting observables: [(2587)](https://github.com/PennyLaneAI/pennylane/pull/2587)

python
dev = qml.device("default.qubit", wires=1)

qml.transforms.split_non_commuting
qml.qnode(dev)
def circuit(x):
qml.RX(x,wires=0)
return [qml.expval(qml.PauliX(0)), qml.expval(qml.PauliZ(0))]


pycon
>>> print(qml.draw(circuit)(0.5))
0: ──RX(0.50)─┤ <X>
\
0: ──RX(0.50)─┤ <Z>


<h3>Improvements</h3>

* Expectation values of multiple non-commuting observables from within a single QNode are now supported: [(2587)](https://github.com/PennyLaneAI/pennylane/pull/2587)


>>> dev = qml.device('default.qubit', wires=1)
>>> qml.qnode(dev)
... def circuit_rx(x, z):
... qml.RX(x, wires=0)
... qml.RZ(z, wires=0)
... return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliY(0))
>>> circuit_rx(0.1, 0.3)
tensor([ 0.02950279, -0.09537451], requires_grad=True)


* Selecting which parts of parameter-shift Hessians are computed is now possible. [(2538)](https://github.com/PennyLaneAI/pennylane/pull/2538)

The `argnum` keyword argument for `qml.gradients.param_shift_hessian` is now allowed to be a two-dimensional Boolean `array_like`. Only the indicated entries of the Hessian will then be computed.

A particularly useful example is the computation of the diagonal of the Hessian:

python
dev = qml.device("default.qubit", wires=1)

qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=0)
qml.RX(x[2], wires=0)
return qml.expval(qml.PauliZ(0))

argnum = qml.math.eye(3, dtype=bool)
x = np.array([0.2, -0.9, 1.1], requires_grad=True)


pycon
>>> qml.gradients.param_shift_hessian(circuit, argnum=argnum)(x)
tensor([[-0.09928388, 0. , 0. ],
[ 0. , -0.27633945, 0. ],
[ 0. , 0. , -0.09928388]], requires_grad=True)


* Commuting Pauli operators are now measured faster. [(2425)](https://github.com/PennyLaneAI/pennylane/pull/2425)

The logic that checks for qubit-wise commuting (QWC) observables has been improved, resulting in a performance boost that is noticable when many commuting Pauli operators of the same type are measured.

* It is now possible to add `Observable` objects to the integer `0`, for example `qml.PauliX(wires=[0]) + 0`. [(2603)](https://github.com/PennyLaneAI/pennylane/pull/2603)

* Wires can now be passed as the final argument to an `Operator`, instead of requiring the wires to be explicitly specified with keyword `wires`. This functionality already existed for `Observable`s, but now extends to all `Operator`s: [(2432)](https://github.com/PennyLaneAI/pennylane/pull/2432)

pycon
>>> qml.S(0)
S(wires=[0])
>>> qml.CNOT((0,1))
CNOT(wires=[0, 1])


* The `qml.taper` function can now be used to consistently taper any additional observables such as dipole moment, particle number, and spin operators using the symmetries obtained from the Hamiltonian. [(2510)](https://github.com/PennyLaneAI/pennylane/pull/2510)

* Sparse Hamiltonians' representation has changed from Coordinate (COO) to Compressed Sparse Row (CSR) format. [(2561)](https://github.com/PennyLaneAI/pennylane/pull/2561)

The CSR representation is more performant for arithmetic operations and matrix-vector products. This change decreases the `expval()` calculation time for `qml.SparseHamiltonian`, specially for large workflows. In addition, the CSR format consumes less memory for `qml.SparseHamiltonian` storage.

* IPython now displays the `str` representation of a `Hamiltonian`, rather than the `repr`. This displays more information about the object.[(2648)](https://github.com/PennyLaneAI/pennylane/pull/2648)

* The `qml.qchem` tests have been restructured. [(2593)](https://github.com/PennyLaneAI/pennylane/pull/2593) [(#2545)](https://github.com/PennyLaneAI/pennylane/pull/2545)

- OpenFermion-dependent tests are now localized and collected in `tests.qchem.of_tests`. The new module `test_structure` is created to collect the tests of the `qchem.structure` module in one place and remove their dependency to OpenFermion.

- Test classes have been created to group the integrals and matrices unit tests.

* An `operations_only` argument is introduced to the `tape.get_parameters` method. [(2543)](https://github.com/PennyLaneAI/pennylane/pull/2543)

* The `gradients` module now uses faster subroutines and uniform formats of gradient rules. [(2452)](https://github.com/XanaduAI/pennylane/pull/2452)

* Instead of checking types, objects are now processed in the `QuantumTape` based on a new `_queue_category` property. This is a temporary fix that will disappear in the future. [(2408)](https://github.com/PennyLaneAI/pennylane/pull/2408)

* The `QNode` class now contains a new method `best_method_str` that returns the best differentiation method for a provided device and interface, in human-readable format. [(2533)](https://github.com/PennyLaneAI/pennylane/pull/2533)

* Using `Operation.inv()` in a queuing environment no longer updates the queue's metadata, but merely updates the operation in place. [(2596)](https://github.com/PennyLaneAI/pennylane/pull/2596)

* A new method `safe_update_info` is added to `qml.QueuingContext`. This method is substituted for `qml.QueuingContext.update_info` in a variety of places. [(2612)](https://github.com/PennyLaneAI/pennylane/pull/2612) [(#2675)](https://github.com/PennyLaneAI/pennylane/pull/2675)

* `BasisEmbedding` can accept an int as argument instead of a list of bits. [(2601)](https://github.com/PennyLaneAI/pennylane/pull/2601)

For example, `qml.BasisEmbedding(4, wires = range(4))` is now equivalent to `qml.BasisEmbedding([0,1,0,0], wires = range(4))` (as `4==0b100`).

* Introduced a new `is_hermitian` property to `Operator`s to determine if an operator can be used in a measurement process. [(2629)](https://github.com/PennyLaneAI/pennylane/pull/2629)

* Added separate `requirements_dev.txt` for separation of concerns for code development and just using PennyLane. [(2635)](https://github.com/PennyLaneAI/pennylane/pull/2635)

* The performance of building sparse Hamiltonians has been improved by accumulating the sparse representation of coefficient-operator pairs in a temporary storage and by eliminating unnecessary `kron` operations on identity matrices. [(2630)](https://github.com/PennyLaneAI/pennylane/pull/2630)

* Control values are now displayed distinctly in text and matplotlib drawings of circuits. [(2668)](https://github.com/PennyLaneAI/pennylane/pull/2668)

* The `TorchLayer` `init_method` argument now accepts either a `torch.nn.init` function or a dictionary which should specify a `torch.nn.init`/`torch.Tensor` for each different weight. [(2678)](https://github.com/PennyLaneAI/pennylane/pull/2678)

* The unused keyword argument `do_queue` for `Operation.adjoint` is now fully removed. [(2583)](https://github.com/PennyLaneAI/pennylane/pull/2583)

* Several non-decomposable `Adjoint` operators are added to the device test suite. [(2658)](https://github.com/PennyLaneAI/pennylane/pull/2658)

* The developer-facing `pow` method has been added to `Operator` with concrete implementations for many classes. [(2225)](https://github.com/PennyLaneAI/pennylane/pull/2225)

* The `ctrl` transform and `ControlledOperation` have been moved to the new `qml.ops.op_math` submodule. The developer-facing `ControlledOperation` class is no longer imported top-level. [(2656)](https://github.com/PennyLaneAI/pennylane/pull/2656)

<h3>Deprecations</h3>

* `qml.ExpvalCost` has been deprecated, and usage will now raise a warning. [(2571)](https://github.com/PennyLaneAI/pennylane/pull/2571)

Instead, it is recommended to simply pass Hamiltonians to the `qml.expval` function inside QNodes:

python
qml.qnode(dev)
def ansatz(params):
some_qfunc(params)
return qml.expval(Hamiltonian)


<h3>Breaking changes</h3>

* When using `qml.TorchLayer`, weights with negative shapes will now raise an error, while weights with `size = 0` will result in creating empty Tensor objects. [(2678)](https://github.com/PennyLaneAI/pennylane/pull/2678)

* PennyLane no longer supports TensorFlow `<=2.3`. [(2683)](https://github.com/PennyLaneAI/pennylane/pull/2683)

* The `qml.queuing.Queue` class has been removed. [(2599)](https://github.com/PennyLaneAI/pennylane/pull/2599)

* The `qml.utils.expand` function is now removed; `qml.operation.expand_matrix` should be used instead. [(2654)](https://github.com/PennyLaneAI/pennylane/pull/2654)

* The module `qml.gradients.param_shift_hessian` has been renamed to `qml.gradients.parameter_shift_hessian` in order to distinguish it from the identically named function. Note that the `param_shift_hessian` function is unaffected by this change and can be invoked in the same manner as before via the `qml.gradients` module. [(2528)](https://github.com/PennyLaneAI/pennylane/pull/2528)

* The properties `eigval` and `matrix` from the `Operator` class were replaced with the methods `eigval()` and `matrix(wire_order=None)`. [(2498)](https://github.com/PennyLaneAI/pennylane/pull/2498)

* `Operator.decomposition()` is now an instance method, and no longer accepts parameters. [(2498)](https://github.com/PennyLaneAI/pennylane/pull/2498)

* Adds tests, adds no-coverage directives, and removes inaccessible logic to improve code coverage. [(2537)](https://github.com/PennyLaneAI/pennylane/pull/2537)

* The base classes `QubitDevice` and `DefaultQubit` now accept data-types for a statevector. This enables a derived class (device) in a plugin to choose correct data-types: [(2448)](https://github.com/PennyLaneAI/pennylane/pull/2448)

pycon
>>> dev = qml.device("default.qubit", wires=4, r_dtype=np.float32, c_dtype=np.complex64)
>>> dev.R_DTYPE
<class 'numpy.float32'>
>>> dev.C_DTYPE
<class 'numpy.complex64'>


<h3>Bug fixes</h3>

* Fixed a bug where returning `qml.density_matrix` using the PyTorch interface would return a density matrix with wrong shape. [(2643)](https://github.com/PennyLaneAI/pennylane/pull/2643)

* Fixed a bug to make `param_shift_hessian` work with QNodes in which gates marked as trainable do not have any impact on the QNode output. [(2584)](https://github.com/PennyLaneAI/pennylane/pull/2584)

* QNodes can now interpret variations on the interface name, like `"tensorflow"` or `"jax-jit"`, when requesting backpropagation. [(2591)](https://github.com/PennyLaneAI/pennylane/pull/2591)

* Fixed a bug for `diff_method="adjoint"` where incorrect gradients were computed for QNodes with parametrized observables (e.g., `qml.Hermitian`). [(2543)](https://github.com/PennyLaneAI/pennylane/pull/2543)

* Fixed a bug where `QNGOptimizer` did not work with operators whose generator was a Hamiltonian. [(2524)](https://github.com/PennyLaneAI/pennylane/pull/2524)

* Fixed a bug with the decomposition of `qml.CommutingEvolution`. [(2542)](https://github.com/PennyLaneAI/pennylane/pull/2542)

* Fixed a bug enabling PennyLane to work with the latest version of Autoray. [(2549)](https://github.com/PennyLaneAI/pennylane/pull/2549)

* Fixed a bug which caused different behaviour for `Hamiltonian Observable` and `Observable Hamiltonian`. [(2570)](https://github.com/PennyLaneAI/pennylane/pull/2570)

* Fixed a bug in `DiagonalQubitUnitary._controlled` where an invalid operation was queued instead of the controlled version of the diagonal unitary. [(2525)](https://github.com/PennyLaneAI/pennylane/pull/2525)

* Updated the gradients fix to only apply to the `strawberryfields.gbs` device, since the original logic was breaking some devices. [(2485)](https://github.com/PennyLaneAI/pennylane/pull/2485) [(#2595)](https://github.com/PennyLaneAI/pennylane/pull/2595)

* Fixed a bug in `qml.transforms.insert` where operations were not inserted after gates within a template. [(2704)](https://github.com/PennyLaneAI/pennylane/pull/2704)

* `Hamiltonian.wires` is now properly updated after in place operations. [(2738)](https://github.com/PennyLaneAI/pennylane/pull/2738)

<h3>Documentation</h3>

* The centralized [Xanadu Sphinx Theme](https://github.com/XanaduAI/xanadu-sphinx-theme) is now used to style the Sphinx documentation. [(#2450)](https://github.com/PennyLaneAI/pennylane/pull/2450)

* Added a reference to `qml.utils.sparse_hamiltonian` in `qml.SparseHamiltonian` to clarify how to construct sparse Hamiltonians in PennyLane. [(2572)](https://github.com/PennyLaneAI/pennylane/pull/2572)

* Added a new section in the [Gradients and Training](https://pennylane.readthedocs.io/en/stable/introduction/interfaces.html) page that summarizes the supported device configurations and provides justification. In addition, [code examples](https://pennylane.readthedocs.io/en/stable/introduction/unsupported.html) were added for some selected configurations. [(#2540)](https://github.com/PennyLaneAI/pennylane/pull/2540)

* Added a note for the [Depolarization Channel (https://pennylane.readthedocs.io/en/stable/code/api/pennylane.DepolarizingChannel.html) that specifies how the channel behaves for the different values of depolarization probability `p`. [(#2669)](https://github.com/PennyLaneAI/pennylane/pull/2669)

* The quickstart documentation has been improved. [(2530)](https://github.com/PennyLaneAI/pennylane/pull/2530) [(#2534)](https://github.com/PennyLaneAI/pennylane/pull/2534) [(#2564](https://github.com/PennyLaneAI/pennylane/pull/2564) [(#2565](https://github.com/PennyLaneAI/pennylane/pull/2565) [(#2566)](https://github.com/PennyLaneAI/pennylane/pull/2566) [(#2607)](https://github.com/PennyLaneAI/pennylane/pull/2607) [(#2608)](https://github.com/PennyLaneAI/pennylane/pull/2608)

* The quantum chemistry quickstart documentation has been improved. [(2500)](https://github.com/PennyLaneAI/pennylane/pull/2500)

* Testing documentation has been improved. [(2536)](https://github.com/PennyLaneAI/pennylane/pull/2536)

* Documentation for the `pre-commit` package has been added. [(2567)](https://github.com/PennyLaneAI/pennylane/pull/2567)

* Documentation for draw control wires change has been updated. [(2682)](https://github.com/PennyLaneAI/pennylane/pull/2682)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Guillermo Alonso-Linaje, Mikhail Andrenkov, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Samuel Banning, Avani Bhardwaj, Thomas Bromley, Albert Mitjans Coma, Isaac De Vlugt, Amintor Dusko, Trent Fridey, Christian Gogolin, Qi Hu, Katharine Hyatt, David Ittah, Josh Izaac, Soran Jahangiri, Edward Jiang, Nathan Killoran, Korbinian Kottmann, Ankit Khandelwal, Christina Lee, Chae-Yeun Park, Mason Moreland, Romain Moyard, Maria Schuld, Jay Soni, Antal Száva, tal66, David Wierichs, Roeland Wiersema, WingCode.

0.23.1

<h3>Bug fixes</h3>

* Fixed a bug enabling PennyLane to work with the latest version of Autoray. [(2548)](https://github.com/PennyLaneAI/pennylane/pull/2548)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Josh Izaac.

Page 4 of 11

© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.