Pennylane

Latest version: v0.39.0

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

Scan your dependencies

Page 6 of 11

0.19.1

<h3>Bug fixes</h3>

* Fixes several bugs when using parametric operations with the `default.qubit.torch` device on GPU. The device takes the `torch_device` argument once again to allow running non-parametric QNodes on the GPU. [(1927)](https://github.com/PennyLaneAI/pennylane/pull/1927)

* Fixes a bug where using JAX's jit function on certain QNodes that contain the `qml.QubitStateVector` operation raised an error with earlier JAX versions (e.g., `jax==0.2.10` and `jaxlib==0.1.64`). [(1924)](https://github.com/PennyLaneAI/pennylane/pull/1924)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Josh Izaac, Christina Lee, Romain Moyard, Lee James O'Riordan, Antal Száva.

0.19.0

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

<h4>Differentiable Hartree-Fock solver</h4>

* A differentiable Hartree-Fock (HF) solver has been added. It can be used to construct molecular Hamiltonians that can be differentiated with respect to nuclear coordinates and basis-set parameters. [(1610)](https://github.com/PennyLaneAI/pennylane/pull/1610)

The HF solver computes the integrals over basis functions, constructs the relevant matrices, and performs self-consistent-field iterations to obtain a set of optimized molecular orbital coefficients. These coefficients and the computed integrals over basis functions are used to construct the one- and two-body electron integrals in the molecular orbital basis which can be used to generate a differentiable second-quantized Hamiltonian in the fermionic and qubit basis.

The following code shows the construction of the Hamiltonian for the hydrogen molecule where the geometry of the molecule is differentiable.

python
symbols = ["H", "H"]
geometry = np.array([[0.0000000000, 0.0000000000, -0.6943528941],
[0.0000000000, 0.0000000000, 0.6943528941]], requires_grad=True)

mol = qml.hf.Molecule(symbols, geometry)
args_mol = [geometry]

hamiltonian = qml.hf.generate_hamiltonian(mol)(*args_mol)

pycon
>>> hamiltonian.coeffs
tensor([-0.09041082+0.j, 0.17220382+0.j, 0.17220382+0.j,
0.16893367+0.j, 0.04523101+0.j, -0.04523101+0.j,
-0.04523101+0.j, 0.04523101+0.j, -0.22581352+0.j,
0.12092003+0.j, -0.22581352+0.j, 0.16615103+0.j,
0.16615103+0.j, 0.12092003+0.j, 0.17464937+0.j], requires_grad=True)


The generated Hamiltonian can be used in a circuit where the atomic coordinates and circuit parameters are optimized simultaneously.

python
symbols = ["H", "H"]
geometry = np.array([[0.0000000000, 0.0000000000, 0.0],
[0.0000000000, 0.0000000000, 2.0]], requires_grad=True)

mol = qml.hf.Molecule(symbols, geometry)

dev = qml.device("default.qubit", wires=4)
params = [np.array([0.0], requires_grad=True)]

def generate_circuit(mol):
qml.qnode(dev)
def circuit(*args):
qml.BasisState(np.array([1, 1, 0, 0]), wires=[0, 1, 2, 3])
qml.DoubleExcitation(*args[0][0], wires=[0, 1, 2, 3])
return qml.expval(qml.hf.generate_hamiltonian(mol)(*args[1:]))
return circuit

for n in range(25):

mol = qml.hf.Molecule(symbols, geometry)
args = [params, geometry] initial values of the differentiable parameters

g_params = qml.grad(generate_circuit(mol), argnum = 0)(*args)
params = params - 0.5 * g_params[0]

forces = qml.grad(generate_circuit(mol), argnum = 1)(*args)
geometry = geometry - 0.5 * forces

print(f'Step: {n}, Energy: {generate_circuit(mol)(*args)}, Maximum Force: {forces.max()}')

In addition, the new Hartree-Fock solver can further be used to optimize the basis set parameters. For details, please refer to the [differentiable Hartree-Fock solver documentation](https://pennylane.readthedocs.io/en/latest/code/qml_hf.html#using-the-differentiable-hf-solver).

<h4>Integration with Mitiq</h4>

* Error mitigation using the zero-noise extrapolation method is now available through the `transforms.mitigate_with_zne` transform. This transform can integrate with the [Mitiq](https://mitiq.readthedocs.io/en/stable/) package for unitary folding and extrapolation functionality. [(#1813)](https://github.com/PennyLaneAI/pennylane/pull/1813)

Consider the following noisy device:

python
noise_strength = 0.05
dev = qml.device("default.mixed", wires=2)
dev = qml.transforms.insert(qml.AmplitudeDamping, noise_strength)(dev)


We can mitigate the effects of this noise for circuits run on this device by using the added
transform:

python
from mitiq.zne.scaling import fold_global
from mitiq.zne.inference import RichardsonFactory

n_wires = 2
n_layers = 2

shapes = qml.SimplifiedTwoDesign.shape(n_wires, n_layers)
np.random.seed(0)
w1, w2 = [np.random.random(s) for s in shapes]

qml.transforms.mitigate_with_zne([1, 2, 3], fold_global, RichardsonFactory.extrapolate)
qml.beta.qnode(dev)
def circuit(w1, w2):
qml.SimplifiedTwoDesign(w1, w2, wires=range(2))
return qml.expval(qml.PauliZ(0))


Now, when we execute `circuit`, errors will be automatically mitigated:

pycon
>>> circuit(w1, w2)
0.19113067083636542


<h4>Powerful new transforms</h4>

* The unitary matrix corresponding to a quantum circuit can now be generated using the new `get_unitary_matrix()` transform. [(1609)](https://github.com/PennyLaneAI/pennylane/pull/1609) [(#1786)](https://github.com/PennyLaneAI/pennylane/pull/1786)

This transform is fully differentiable across all supported PennyLane autodiff frameworks.

python
def circuit(theta):
qml.RX(theta, wires=1)
qml.PauliZ(wires=0)
qml.CNOT(wires=[0, 1])


pycon
>>> theta = torch.tensor(0.3, requires_grad=True)
>>> matrix = qml.transforms.get_unitary_matrix(circuit)(theta)
>>> print(matrix)
tensor([[ 0.9888+0.0000j, 0.0000+0.0000j, 0.0000-0.1494j, 0.0000+0.0000j],
[ 0.0000+0.0000j, 0.0000+0.1494j, 0.0000+0.0000j, -0.9888+0.0000j],
[ 0.0000-0.1494j, 0.0000+0.0000j, 0.9888+0.0000j, 0.0000+0.0000j],
[ 0.0000+0.0000j, -0.9888+0.0000j, 0.0000+0.0000j, 0.0000+0.1494j]],
grad_fn=<MmBackward>)
>>> loss = torch.real(torch.trace(matrix))
>>> loss.backward()
>>> theta.grad
tensor(-0.1494)


* Arbitrary two-qubit unitaries can now be decomposed into elementary gates. This functionality has been incorporated into the `qml.transforms.unitary_to_rot` transform, and is available separately as `qml.transforms.two_qubit_decomposition`. [(1552)](https://github.com/PennyLaneAI/pennylane/pull/1552)

As an example, consider the following randomly-generated matrix and circuit that uses it:

python
U = np.array([
[-0.03053706-0.03662692j, 0.01313778+0.38162226j, 0.4101526 -0.81893687j, -0.03864617+0.10743148j],
[-0.17171136-0.24851809j, 0.06046239+0.1929145j, -0.04813084-0.01748555j, -0.29544883-0.88202604j],
[ 0.39634931-0.78959795j, -0.25521689-0.17045233j, -0.1391033 -0.09670952j, -0.25043606+0.18393466j],
[ 0.29599198-0.19573188j, 0.55605806+0.64025769j, 0.06140516+0.35499559j, 0.02674726+0.1563311j ]
])

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

qml.qnode(dev)
qml.transforms.unitary_to_rot
def circuit(x, y):
qml.QubitUnitary(U, wires=[0, 1])
return qml.expval(qml.PauliZ(wires=0))


If we run the circuit, we can see the new decomposition:

pycon
>>> circuit(0.3, 0.4)
tensor(-0.81295986, requires_grad=True)
>>> print(qml.draw(circuit)(0.3, 0.4))
0: ──Rot(2.78, 0.242, -2.28)──╭X──RZ(0.176)───╭C─────────────╭X──Rot(-3.87, 0.321, -2.09)──┤ ⟨Z⟩
1: ──Rot(4.64, 2.69, -1.56)───╰C──RY(-0.883)──╰X──RY(-1.47)──╰C──Rot(1.68, 0.337, 0.587)───┤


* A new transform, `qml.batch_params`, has been added, that makes QNodes handle a batch dimension in trainable parameters. [(1710)](https://github.com/PennyLaneAI/pennylane/pull/1710) [(#1761)](https://github.com/PennyLaneAI/pennylane/pull/1761)

This transform will create multiple circuits, one per batch dimension. As a result, it is both simulator and hardware compatible.

python
qml.batch_params
qml.beta.qnode(dev)
def circuit(x, weights):
qml.RX(x, wires=0)
qml.RY(0.2, wires=1)
qml.templates.StronglyEntanglingLayers(weights, wires=[0, 1, 2])
return qml.expval(qml.Hadamard(0))


The `qml.batch_params` decorator allows us to pass arguments `x` and `weights` that have a batch dimension. For example,

pycon
>>> batch_size = 3
>>> x = np.linspace(0.1, 0.5, batch_size)
>>> weights = np.random.random((batch_size, 10, 3, 3))


If we evaluate the QNode with these inputs, we will get an output of shape ``(batch_size,)``:

pycon
>>> circuit(x, weights)
tensor([0.08569816, 0.12619101, 0.21122004], requires_grad=True)


* The `insert` transform has now been added, providing a way to insert single-qubit operations into a quantum circuit. The transform can apply to quantum functions, tapes, and devices. [(1795)](https://github.com/PennyLaneAI/pennylane/pull/1795)

The following QNode can be transformed to add noise to the circuit:

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

qml.qnode(dev)
qml.transforms.insert(qml.AmplitudeDamping, 0.2, position="end")
def f(w, x, y, z):
qml.RX(w, wires=0)
qml.RY(x, wires=1)
qml.CNOT(wires=[0, 1])
qml.RY(y, wires=0)
qml.RX(z, wires=1)
return qml.expval(qml.PauliZ(0) qml.PauliZ(1))


Executions of this circuit will differ from the noise-free value:

pycon
>>> f(0.9, 0.4, 0.5, 0.6)
tensor(0.754847, requires_grad=True)
>>> print(qml.draw(f)(0.9, 0.4, 0.5, 0.6))
0: ──RX(0.9)──╭C──RY(0.5)──AmplitudeDamping(0.2)──╭┤ ⟨Z ⊗ Z⟩
1: ──RY(0.4)──╰X──RX(0.6)──AmplitudeDamping(0.2)──╰┤ ⟨Z ⊗ Z⟩


* Common tape expansion functions are now available in `qml.transforms`, alongside a new `create_expand_fn` function for easily creating expansion functions from stopping criteria. [(1734)](https://github.com/PennyLaneAI/pennylane/pull/1734) [(#1760)](https://github.com/PennyLaneAI/pennylane/pull/1760)

`create_expand_fn` takes the default depth to which the expansion function should expand a tape, a stopping criterion, an optional device, and a docstring to be set for the created function. The stopping criterion must take a queuable object and return a boolean.

For example, to create an expansion function that decomposes all trainable, multi-parameter operations:

python
>>> stop_at = ~(qml.operation.has_multipar & qml.operation.is_trainable)
>>> expand_fn = qml.transforms.create_expand_fn(depth=5, stop_at=stop_at)


The created expansion function can be used within a custom transform. Devices can also be provided, producing expansion functions that decompose tapes to support the native gate set of the device.

<h4>Batch execution of circuits</h4>

* A new, experimental QNode has been added, that adds support for batch execution of circuits, custom quantum gradient support, and arbitrary order derivatives. This QNode is available via `qml.beta.QNode`, and `qml.beta.qnode`. [(1642)](https://github.com/PennyLaneAI/pennylane/pull/1642) [(#1646)](https://github.com/PennyLaneAI/pennylane/pull/1646) [(#1651)](https://github.com/PennyLaneAI/pennylane/pull/1651) [(#1804)](https://github.com/PennyLaneAI/pennylane/pull/1804)

It differs from the standard QNode in several ways:

- Custom gradient transforms can be specified as the differentiation method:

python
qml.gradients.gradient_transform
def my_gradient_transform(tape):
...
return tapes, processing_fn

qml.beta.qnode(dev, diff_method=my_gradient_transform)
def circuit():


- Arbitrary :math:`n`-th order derivatives are supported on hardware using gradient transforms such as the parameter-shift rule. To specify that an :math:`n`-th order derivative of a QNode will be computed, the `max_diff` argument should be set. By default, this is set to 1 (first-order derivatives only).

- Internally, if multiple circuits are generated for execution simultaneously, they will be packaged into a single job for execution on the device. This can lead to significant performance improvement when executing the QNode on remote quantum hardware.

- When decomposing the circuit, the default decomposition strategy will prioritize decompositions that result in the smallest number of parametrized operations required to satisfy the differentiation method. Additional decompositions required to satisfy the native gate set of the quantum device will be performed later, by the device at execution time. While this may lead to a slight increase in classical processing, it significantly reduces the number of circuit evaluations needed to compute gradients of complex unitaries.

In an upcoming release, this QNode will replace the existing one. If you come across any bugs while using this QNode, please let us know via a [bug report](https://github.com/PennyLaneAI/pennylane/issues/new?assignees=&labels=bug+%3Abug%3A&template=bug_report.yml&title=%5BBUG%5D) on our GitHub bug tracker.

Currently, this beta QNode does not support the following features:

- Non-mutability via the `mutable` keyword argument
- The `reversible` QNode differentiation method
- The ability to specify a `dtype` when using PyTorch and TensorFlow.

It is also not tested with the `qml.qnn` module.

<h4>New operations and templates</h4>

* Added a new operation `OrbitalRotation`, which implements the spin-adapted spatial orbital rotation gate. [(1665)](https://github.com/PennyLaneAI/pennylane/pull/1665)

An example circuit that uses `OrbitalRotation` operation is:

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

qml.qnode(dev)
def circuit(phi):
qml.BasisState(np.array([1, 1, 0, 0]), wires=[0, 1, 2, 3])
qml.OrbitalRotation(phi, wires=[0, 1, 2, 3])
return qml.state()


If we run this circuit, we will get the following output

pycon
>>> circuit(0.1)
array([ 0. +0.j, 0. +0.j, 0. +0.j,
0.00249792+0.j, 0. +0.j, 0. +0.j,
-0.04991671+0.j, 0. +0.j, 0. +0.j,
-0.04991671+0.j, 0. +0.j, 0. +0.j,
0.99750208+0.j, 0. +0.j, 0. +0.j,
0. +0.j])


* Added a new template `GateFabric`, which implements a local, expressive, quantum-number-preserving ansatz proposed by Anselmetti *et al.* in [arXiv:2104.05692](https://arxiv.org/abs/2104.05695). [(#1687)](https://github.com/PennyLaneAI/pennylane/pull/1687)

An example of a circuit using `GateFabric` template is:

python
coordinates = np.array([0.0, 0.0, -0.6614, 0.0, 0.0, 0.6614])
H, qubits = qml.qchem.molecular_hamiltonian(["H", "H"], coordinates)
ref_state = qml.qchem.hf_state(electrons=2, orbitals=qubits)

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

qml.qnode(dev)
def ansatz(weights):
qml.templates.GateFabric(weights, wires=[0,1,2,3],
init_state=ref_state, include_pi=True)
return qml.expval(H)


For more details, see the [GateFabric documentation](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.GateFabric.html).

* Added a new template `kUpCCGSD`, which implements a unitary coupled cluster ansatz with generalized singles and pair doubles excitation operators, proposed by Joonho Lee *et al.* in [arXiv:1810.02327](https://arxiv.org/abs/1810.02327). [(#1743)](https://github.com/PennyLaneAI/pennylane/pull/1743)

An example of a circuit using `kUpCCGSD` template is:

python
coordinates = np.array([0.0, 0.0, -0.6614, 0.0, 0.0, 0.6614])
H, qubits = qml.qchem.molecular_hamiltonian(["H", "H"], coordinates)
ref_state = qml.qchem.hf_state(electrons=2, orbitals=qubits)

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

qml.qnode(dev)
def ansatz(weights):
qml.templates.kUpCCGSD(weights, wires=[0,1,2,3], k=0, delta_sz=0,
init_state=ref_state)
return qml.expval(H)


<h4>Improved utilities for quantum compilation and characterization</h4>

* The new `qml.fourier.qnode_spectrum` function extends the former `qml.fourier.spectrum` function and takes classical processing of QNode arguments into account. The frequencies are computed per (requested) QNode argument instead of per gate `id`. The gate `id`s are ignored. [(1681)](https://github.com/PennyLaneAI/pennylane/pull/1681) [(#1720)](https://github.com/PennyLaneAI/pennylane/pull/1720)

Consider the following example, which uses non-trainable inputs `x`, `y` and `z` as well as trainable parameters `w` as arguments to the QNode.

python
import pennylane as qml
import numpy as np

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

qml.qnode(dev)
def circuit(x, y, z, w):
for i in range(n_qubits):
qml.RX(0.5*x[i], wires=i)
qml.Rot(w[0,i,0], w[0,i,1], w[0,i,2], wires=i)
qml.RY(2.3*y[i], wires=i)
qml.Rot(w[1,i,0], w[1,i,1], w[1,i,2], wires=i)
qml.RX(z, wires=i)
return qml.expval(qml.PauliZ(wires=0))

x = np.array([1., 2., 3.])
y = np.array([0.1, 0.3, 0.5])
z = -1.8
w = np.random.random((2, n_qubits, 3))


This circuit looks as follows:

pycon
>>> print(qml.draw(circuit)(x, y, z, w))
0: ──RX(0.5)──Rot(0.598, 0.949, 0.346)───RY(0.23)──Rot(0.693, 0.0738, 0.246)──RX(-1.8)──┤ ⟨Z⟩
1: ──RX(1)────Rot(0.0711, 0.701, 0.445)──RY(0.69)──Rot(0.32, 0.0482, 0.437)───RX(-1.8)──┤
2: ──RX(1.5)──Rot(0.401, 0.0795, 0.731)──RY(1.15)──Rot(0.756, 0.38, 0.38)─────RX(-1.8)──┤


Applying the `qml.fourier.qnode_spectrum` function to the circuit for the non-trainable parameters, we obtain:

pycon
>>> spec = qml.fourier.qnode_spectrum(circuit, encoding_args={"x", "y", "z"})(x, y, z, w)
>>> for inp, freqs in spec.items():
... print(f"{inp}: {freqs}")
"x": {(0,): [-0.5, 0.0, 0.5], (1,): [-0.5, 0.0, 0.5], (2,): [-0.5, 0.0, 0.5]}
"y": {(0,): [-2.3, 0.0, 2.3], (1,): [-2.3, 0.0, 2.3], (2,): [-2.3, 0.0, 2.3]}
"z": {(): [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]}


We can see that all three parameters in the QNode arguments ``x`` and ``y`` contribute the spectrum of a Pauli rotation ``[-1.0, 0.0, 1.0]``, rescaled with the prefactor of the respective parameter in the circuit. The three ``RX`` rotations using the parameter ``z`` accumulate, yielding a more complex frequency spectrum.

For details on how to control for which parameters the spectrum is computed, a comparison to `qml.fourier.circuit_spectrum`, and other usage details, please see the [fourier.qnode_spectrum docstring](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.fourier.qnode_spectrum.html).

* Two new methods were added to the Device API, allowing PennyLane devices increased control over circuit decompositions. [(1651)](https://github.com/PennyLaneAI/pennylane/pull/1651)

- `Device.expand_fn(tape) -> tape`: expands a tape such that it is supported by the device. By default, performs the standard device-specific gate set decomposition done in the default QNode. Devices may overwrite this method in order to define their own decomposition logic.

Note that the numerical result after applying this method should remain unchanged; PennyLane will assume that the expanded tape returns exactly the same value as the original tape when executed.

- `Device.batch_transform(tape) -> (tapes, processing_fn)`: preprocesses the tape in the case where the device needs to generate multiple circuits to execute from the input circuit. The requirement of a post-processing function makes this distinct to the `expand_fn` method above.

By default, this method applies the transform

.. math:: \left\langle \sum_i c_i h_i\right\rangle → \sum_i c_i \left\langle h_i \right\rangle

if `expval(H)` is present on devices that do not natively support Hamiltonians with non-commuting terms.

* A new class has been added to store operator attributes, such as `self_inverses`, and `composable_rotation`, as a list of operation names. [(1763)](https://github.com/PennyLaneAI/pennylane/pull/1763)

A number of such attributes, for the purpose of compilation transforms, can be found in `ops/qubit/attributes.py`, but the class can also be used to create your own. For example, we can create a new Attribute, `pauli_ops`, like so:

pycon
>>> from pennylane.ops.qubit.attributes import Attribute
>>> pauli_ops = Attribute(["PauliX", "PauliY", "PauliZ"])


We can check either a string or an Operation for inclusion in this set:

pycon
>>> qml.PauliX(0) in pauli_ops
True
>>> "Hadamard" in pauli_ops
False


We can also dynamically add operators to the sets at runtime. This is useful for adding custom operations to the attributes such as `composable_rotations` and ``self_inverses`` that are used in compilation transforms. For example, suppose you have created a new Operation, `MyGate`, which you know to be its own inverse. Adding it to the set, like so

pycon
>>> from pennylane.ops.qubit.attributes import self_inverses
>>> self_inverses.add("MyGate")


will enable the gate to be considered by the `cancel_inverses` compilation transform if two such gates are adjacent in a circuit.

<h3>Improvements</h3>

* The `qml.metric_tensor` transform has been improved with regards to both function and performance. [(1638)](https://github.com/PennyLaneAI/pennylane/pull/1638) [(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)

- If the underlying device supports batch execution of circuits, the quantum circuits required to compute the metric tensor elements will be automatically submitted as a batched job. This can lead to significant performance improvements for devices with a non-trivial job submission overhead.

- Previously, the transform would only return the metric tensor with respect to gate arguments, and ignore any classical processing inside the QNode, even very trivial classical processing such as parameter permutation. The metric tensor now takes into account classical processing, and returns the metric tensor with respect to QNode arguments, not simply gate arguments:

pycon
>>> qml.qnode(dev)
... def circuit(x):
... qml.Hadamard(wires=1)
... qml.RX(x[0], wires=0)
... qml.CNOT(wires=[0, 1])
... qml.RY(x[1] ** 2, wires=1)
... qml.RY(x[1], wires=0)
... return qml.expval(qml.PauliZ(0))
>>> x = np.array([0.1, 0.2], requires_grad=True)
>>> qml.metric_tensor(circuit)(x)
array([[0.25 , 0. ],
[0. , 0.28750832]])


To revert to the previous behaviour of returning the metric tensor with respect to gate arguments, `qml.metric_tensor(qnode, hybrid=False)` can be passed.

pycon
>>> qml.metric_tensor(circuit, hybrid=False)(x)
array([[0.25 , 0. , 0. ],
[0. , 0.25 , 0. ],
[0. , 0. , 0.24750832]])


- The metric tensor transform now works with a larger set of operations. In particular, all operations that have a single variational parameter and define a generator are now
supported. In addition to a reduction in decomposition overhead, the change also results in fewer circuit evaluations.

* The expansion rule in the `qml.metric_tensor` transform has been changed. [(1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)

If `hybrid=False`, the changed expansion rule might lead to a changed output.

* The `ApproxTimeEvolution` template can now be used with Hamiltonians that have trainable coefficients. [(1789)](https://github.com/PennyLaneAI/pennylane/pull/1789)

The resulting QNodes can be differentiated with respect to both the time parameter *and* the Hamiltonian coefficients.

python
dev = qml.device('default.qubit', wires=2)
obs = [qml.PauliX(0) qml.PauliY(1), qml.PauliY(0) qml.PauliX(1)]

qml.qnode(dev)
def circuit(coeffs, t):
H = qml.Hamiltonian(coeffs, obs)
qml.templates.ApproxTimeEvolution(H, t, 2)
return qml.expval(qml.PauliZ(0))


pycon
>>> t = np.array(0.54, requires_grad=True)
>>> coeffs = np.array([-0.6, 2.0], requires_grad=True)
>>> qml.grad(circuit)(coeffs, t)
(array([-1.07813375, -1.07813375]), array(-2.79516158))


All differentiation methods, including backpropagation and the parameter-shift rule, are supported.

* Quantum function transforms and batch transforms can now be applied to devices. Once applied to a device, any quantum function executed on the modified device will be transformed prior to execution. [(1809)](https://github.com/PennyLaneAI/pennylane/pull/1809) [(#1810)](https://github.com/PennyLaneAI/pennylane/pull/1810)

python
dev = qml.device("default.mixed", wires=1)
dev = qml.transforms.merge_rotations()(dev)

qml.beta.qnode(dev)
def f(w, x, y, z):
qml.RX(w, wires=0)
qml.RX(x, wires=0)
qml.RX(y, wires=0)
qml.RX(z, wires=0)
return qml.expval(qml.PauliZ(0))


pycon
>>> print(f(0.9, 0.4, 0.5, 0.6))
-0.7373937155412453
>>> print(qml.draw(f, expansion_strategy="device")(0.9, 0.4, 0.5, 0.6))
0: ──RX(2.4)──┤ ⟨Z⟩


* It is now possible to draw QNodes that have been transformed by a 'batch transform'; that is, a transform that maps a single QNode into multiple circuits under the hood. Examples of batch transforms include `qml.metric_tensor` and `qml.gradients`. [(1762)](https://github.com/PennyLaneAI/pennylane/pull/1762)

For example, consider the parameter-shift rule, which generates two circuits per parameter; one circuit that has the parameter shifted forward, and another that has the parameter shifted backwards:

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

qml.gradients.param_shift
qml.beta.qnode(dev)
def circuit(x):
qml.RX(x, wires=0)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliZ(wires=0))


pycon
>>> print(qml.draw(circuit)(0.6))
0: ──RX(2.17)──╭C──┤ ⟨Z⟩
1: ────────────╰X──┤

0: ──RX(-0.971)──╭C──┤ ⟨Z⟩
1: ──────────────╰X──┤


* Support for differentiable execution of batches of circuits has been extended to the JAX interface for scalar functions, via the beta `pennylane.interfaces.batch` module. [(1634)](https://github.com/PennyLaneAI/pennylane/pull/1634) [(#1685)](https://github.com/PennyLaneAI/pennylane/pull/1685)

For example using the `execute` function from the `pennylane.interfaces.batch` module:

python
from pennylane.interfaces.batch import execute

def cost_fn(x):
with qml.tape.JacobianTape() as tape1:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) qml.PauliX(1))

with qml.tape.JacobianTape() as tape2:
qml.RX(x[0], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
qml.probs(wires=1)

result = execute(
[tape1, tape2], dev,
gradient_fn=qml.gradients.param_shift,
interface="autograd"
)
return (result[0] + result[1][0, 0])[0]

res = jax.grad(cost_fn)(params)


* All qubit operations have been re-written to use the `qml.math` framework for internal classical processing and the generation of their matrix representations. As a result these representations are now fully differentiable, and the framework-specific device classes no longer need to maintain framework-specific versions of these matrices. [(1749)](https://github.com/PennyLaneAI/pennylane/pull/1749) [(#1802)](https://github.com/PennyLaneAI/pennylane/pull/1802)

* The use of `expval(H)`, where `H` is a cost Hamiltonian generated by the `qaoa` module, has been sped up. This was achieved by making PennyLane decompose a circuit with an `expval(H)` measurement into subcircuits if the `Hamiltonian.grouping_indices` attribute is set, and setting this attribute in the relevant `qaoa` module functions. [(1718)](https://github.com/PennyLaneAI/pennylane/pull/1718)

* Operations can now have gradient recipes that depend on the state of the operation. [(1674)](https://github.com/PennyLaneAI/pennylane/pull/1674)

For example, this allows for gradient recipes that are parameter dependent:

python
class RX(qml.RX):

property
def grad_recipe(self):
The gradient is given by [f(2x) - f(0)] / (2 sin(x)), by subsituting
shift = x into the two term parameter-shift rule.
x = self.data[0]
c = 0.5 / np.sin(x)
return ([[c, 0.0, 2 * x], [-c, 0.0, 0.0]],)


* Shots can now be passed as a runtime argument to transforms that execute circuits in batches, similarly to QNodes. [(1707)](https://github.com/PennyLaneAI/pennylane/pull/1707)

An example of such a transform are the gradient transforms in the `qml.gradients` module. As a result, we can now call gradient transforms (such as `qml.gradients.param_shift`) and set the number of shots at runtime.

pycon
>>> dev = qml.device("default.qubit", wires=1, shots=1000)
>>> qml.beta.qnode(dev)
... def circuit(x):
... qml.RX(x, wires=0)
... return qml.expval(qml.PauliZ(0))
>>> grad_fn = qml.gradients.param_shift(circuit)
>>> param = np.array(0.564, requires_grad=True)
>>> grad_fn(param, shots=[(1, 10)]).T
array([[-1., -1., -1., -1., -1., 0., -1., 0., -1., 0.]])
>>> param2 = np.array(0.1233, requires_grad=True)
>>> grad_fn(param2, shots=None)
array([[-0.12298782]])


* Templates are now top level imported and can be used directly e.g. `qml.QFT(wires=0)`. [(1779)](https://github.com/PennyLaneAI/pennylane/pull/1779)

* `qml.probs` now accepts an attribute `op` that allows to rotate the computational basis and get the probabilities in the rotated basis. [(1692)](https://github.com/PennyLaneAI/pennylane/pull/1692)

* Refactored the `expand_fn` functionality in the Device class to avoid any edge cases leading to failures with plugins. [(1838)](https://github.com/PennyLaneAI/pennylane/pull/1838)

* Updated the `qml.QNGOptimizer.step_and_cost` method to avoid the use of deprecated functionality. [(1834)](https://github.com/PennyLaneAI/pennylane/pull/1834)

* Added a custom `torch.to_numpy` implementation to `pennylane/math/single_dispatch.py` to ensure compabilitity with PyTorch 1.10. [(1824)](https://github.com/PennyLaneAI/pennylane/pull/1824) [(#1825)](https://github.com/PennyLaneAI/pennylane/pull/1825)

* The default for an `Operation`'s `control_wires` attribute is now an empty `Wires` object instead of the attribute raising a `NonImplementedError`. [(1821)](https://github.com/PennyLaneAI/pennylane/pull/1821)

* `qml.circuit_drawer.MPLDrawer` will now automatically rotate and resize text to fit inside the rectangle created by the `box_gate` method. [(1764)](https://github.com/PennyLaneAI/pennylane/pull/1764)

* Operators now have a `label` method to determine how they are drawn. This will eventually override the `RepresentationResolver` class. [(1678)](https://github.com/PennyLaneAI/pennylane/pull/1678)

* The operation `label` method now supports string variables. [(1815)](https://github.com/PennyLaneAI/pennylane/pull/1815)

* A new utility class `qml.BooleanFn` is introduced. It wraps a function that takes a single argument and returns a Boolean. [(1734)](https://github.com/PennyLaneAI/pennylane/pull/1734)

After wrapping, `qml.BooleanFn` can be called like the wrapped function, and multiple instances can be manipulated and combined with the bitwise operators `&`, `|` and `~`.

* There is a new utility function `qml.math.is_independent` that checks whether a callable is independent of its arguments. [(1700)](https://github.com/PennyLaneAI/pennylane/pull/1700)

This function is experimental and might behave differently than expected.

Note that the test relies on both numerical and analytical checks, except when using the PyTorch interface which only performs a numerical check. It is known that there are edge cases on which this test will yield wrong results, in particular non-smooth functions may be problematic. For details, please refer to the [is_indpendent docstring](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.math.is_independent.html).

* The `qml.beta.QNode` now supports the `qml.qnn` module. [(1748)](https://github.com/PennyLaneAI/pennylane/pull/1748)

* `qml.beta.QNode` now supports the `qml.specs` transform. [(1739)](https://github.com/PennyLaneAI/pennylane/pull/1739)

* `qml.circuit_drawer.drawable_layers` and `qml.circuit_drawer.drawable_grid` process a list of operations to layer positions for drawing. [(1639)](https://github.com/PennyLaneAI/pennylane/pull/1639)

* `qml.transforms.batch_transform` now accepts `expand_fn`s that take additional arguments and keyword arguments. In fact, `expand_fn` and `transform_fn` now **must** have the same signature. [(1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)

* The `qml.batch_transform` decorator is now ignored during Sphinx builds, allowing the correct signature to display in the built documentation. [(1733)](https://github.com/PennyLaneAI/pennylane/pull/1733)

* The tests for qubit operations are split into multiple files. [(1661)](https://github.com/PennyLaneAI/pennylane/pull/1661)

* The transform for the Jacobian of the classical preprocessing within a QNode, `qml.transforms.classical_jacobian`, now takes a keyword argument `argnum` to specify the QNode argument indices with respect to which the Jacobian is computed. [(1645)](https://github.com/PennyLaneAI/pennylane/pull/1645)

An example for the usage of ``argnum`` is

python
qml.qnode(dev)
def circuit(x, y, z):
qml.RX(qml.math.sin(x), wires=0)
qml.CNOT(wires=[0, 1])
qml.RY(y ** 2, wires=1)
qml.RZ(1 / z, wires=1)
return qml.expval(qml.PauliZ(0))

jac_fn = qml.transforms.classical_jacobian(circuit, argnum=[1, 2])


The Jacobian can then be computed at specified parameters.

pycon
>>> x, y, z = np.array([0.1, -2.5, 0.71])
>>> jac_fn(x, y, z)
(array([-0., -5., -0.]), array([-0. , -0. , -1.98373339]))


The returned arrays are the derivatives of the three parametrized gates in the circuit with respect to `y` and `z` respectively.

There also are explicit tests for `classical_jacobian` now, which previously was tested implicitly via its use in the `metric_tensor` transform.

For more usage details, please see the [classical Jacobian docstring](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.transforms.classical_jacobian.html).

* A new utility function `qml.math.is_abstract(tensor)` has been added. This function returns `True` if the tensor is *abstract*; that is, it has no value or shape. This can occur if within a function that has been just-in-time compiled. [(1845)](https://github.com/PennyLaneAI/pennylane/pull/1845)

* ``qml.circuit_drawer.CircuitDrawer`` can accept a string for the ``charset`` keyword, instead of a ``CharSet`` object. [(1640)](https://github.com/PennyLaneAI/pennylane/pull/1640)

* ``qml.math.sort`` will now return only the sorted torch tensor and not the corresponding indices, making sort consistent across interfaces. [(1691)](https://github.com/PennyLaneAI/pennylane/pull/1691)

* Specific QNode execution options are now re-used by batch transforms to execute transformed QNodes. [(1708)](https://github.com/PennyLaneAI/pennylane/pull/1708)

* To standardize across all optimizers, `qml.optimize.AdamOptimizer` now also uses `accumulation` (in form of `collections.namedtuple`) to keep track of running quantities. Before it used three variables `fm`, `sm` and `t`. [(1757)](https://github.com/PennyLaneAI/pennylane/pull/1757)

<h3>Breaking changes</h3>

* The operator attributes `has_unitary_generator`, `is_composable_rotation`, `is_self_inverse`, `is_symmetric_over_all_wires`, and `is_symmetric_over_control_wires` have been removed as attributes from the base class. They have been replaced by the sets that store the names of operations with similar properties in `ops/qubit/attributes.py`. [(1763)](https://github.com/PennyLaneAI/pennylane/pull/1763)

* The `qml.inv` function has been removed, `qml.adjoint` should be used instead. [(1778)](https://github.com/PennyLaneAI/pennylane/pull/1778)

* The input signature of an `expand_fn` used in a `batch_transform` now **must** have the same signature as the provided `transform_fn`, and vice versa. [(1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)

* The `default.qubit.torch` device automatically determines if computations should be run on a CPU or a GPU and doesn't take a `torch_device` argument anymore. [(1705)](https://github.com/PennyLaneAI/pennylane/pull/1705)

* The utility function `qml.math.requires_grad` now returns `True` when using Autograd if and only if the `requires_grad=True` attribute is set on the NumPy array. Previously, this function would return `True` for *all* NumPy arrays and Python floats, unless `requires_grad=False` was explicitly set. [(1638)](https://github.com/PennyLaneAI/pennylane/pull/1638)

* The operation `qml.Interferometer` has been renamed `qml.InterferometerUnitary` in order to distinguish it from the template `qml.templates.Interferometer`. [(1714)](https://github.com/PennyLaneAI/pennylane/pull/1714)

* The `qml.transforms.invisible` decorator has been replaced with `qml.tape.stop_recording`, which may act as a context manager as well as a decorator to ensure that contained logic is non-recordable or non-queueable within a QNode or quantum tape context. [(1754)](https://github.com/PennyLaneAI/pennylane/pull/1754)

* Templates `SingleExcitationUnitary` and `DoubleExcitationUnitary` have been renamed to `FermionicSingleExcitation` and `FermionicDoubleExcitation`, respectively. [(1822)](https://github.com/PennyLaneAI/pennylane/pull/1822)

<h3>Deprecations</h3>

* Allowing cost functions to be differentiated using `qml.grad` or `qml.jacobian` without explicitly marking parameters as trainable is being deprecated, and will be removed in an upcoming release. Please specify the `requires_grad` attribute for every argument, or specify `argnum` when using `qml.grad` or `qml.jacobian`. [(1773)](https://github.com/PennyLaneAI/pennylane/pull/1773)

The following raises a warning in v0.19.0 and will raise an error in an upcoming release:

python
import pennylane as qml

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

qml.qnode(dev) def test(x): qml.RY(x, wires=[0])
return qml.expval(qml.PauliZ(0))

par = 0.3
qml.grad(test)(par)


Preferred approaches include specifying the `requires_grad` attribute:

python
import pennylane as qml
from pennylane import numpy as np

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

qml.qnode(dev)
def test(x):
qml.RY(x, wires=[0])
return qml.expval(qml.PauliZ(0))

par = np.array(0.3, requires_grad=True)
qml.grad(test)(par)


Or specifying the `argnum` argument when using `qml.grad` or `qml.jacobian`:

python
import pennylane as qml

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

qml.qnode(dev)
def test(x):
qml.RY(x, wires=[0])
return qml.expval(qml.PauliZ(0))

par = 0.3
qml.grad(test, argnum=0)(par)


<img src="https://pennylane.readthedocs.io/en/latest/_static/requires_grad.png" style="width: 100%;"/>

* The `default.tensor` device from the beta folder is no longer maintained and has been deprecated. It will be removed in future releases. [(1851)](https://github.com/PennyLaneAI/pennylane/pull/1851)

* The `qml.metric_tensor` and `qml.QNGOptimizer` keyword argument `diag_approx` is deprecated. Approximations can be controlled with the more fine-grained `approx` keyword argument, with `approx="block-diag"` (the default) reproducing the old behaviour. [(1721)](https://github.com/PennyLaneAI/pennylane/pull/1721) [(#1834)](https://github.com/PennyLaneAI/pennylane/pull/1834)

* The `template` decorator is now deprecated with a warning message and will be removed in release `v0.20.0`. It has been removed from different PennyLane functions. [(1794)](https://github.com/PennyLaneAI/pennylane/pull/1794) [(#1808)](https://github.com/PennyLaneAI/pennylane/pull/1808)

* The `qml.fourier.spectrum` function has been renamed to `qml.fourier.circuit_spectrum`, in order to clearly separate the new `qnode_spectrum` function from this one. `qml.fourier.spectrum` is now an alias for `circuit_spectrum` but is flagged for deprecation and will be removed soon. [(1681)](https://github.com/PennyLaneAI/pennylane/pull/1681)

* The `init` module, which contains functions to generate random parameter tensors for templates, is flagged for deprecation and will be removed in the next release cycle. Instead, the templates' `shape` method can be used to get the desired shape of the tensor, which can then be generated manually. [(1689)](https://github.com/PennyLaneAI/pennylane/pull/1689)

To generate the parameter tensors, the `np.random.normal` and `np.random.uniform` functions can be used (just like in the `init` module). Considering the default arguments of these functions as of NumPy v1.21, some non-default options were used by the `init` module:

* All functions generating normally distributed parameters used `np.random.normal` by passing `scale=0.1`;

* Most functions generating uniformly distributed parameters (except for certain CVQNN initializers) used `np.random.uniform` by passing `high=2*math.pi`;

* The `cvqnn_layers_r_uniform`, `cvqnn_layers_a_uniform`, `cvqnn_layers_kappa_uniform` functions used `np.random.uniform` by passing `high=0.1`.

* The `QNode.draw` method has been deprecated, and will be removed in an upcoming release. Please use the `qml.draw` transform instead. [(1746)](https://github.com/PennyLaneAI/pennylane/pull/1746)

* The `QNode.metric_tensor` method has been deprecated, and will be removed in an upcoming release. Please use the `qml.metric_tensor` transform instead. [(1638)](https://github.com/PennyLaneAI/pennylane/pull/1638)

* The `pad` parameter of the `qml.AmplitudeEmbedding` template has been removed. It has instead been renamed to the `pad_with` parameter. [(1805)](https://github.com/PennyLaneAI/pennylane/pull/1805)

<h3>Bug fixes</h3>

* Fixes a bug where `qml.math.dot` failed to work with `tf.function` autograph mode. [(1842)](https://github.com/PennyLaneAI/pennylane/pull/1842)

* Fixes a bug where in rare instances the parameters of a tape are returned unsorted by `Tape.get_parameters`. [(1836)](https://github.com/PennyLaneAI/pennylane/pull/1836)

* Fixes a bug with the arrow width in the `measure` of `qml.circuit_drawer.MPLDrawer`. [(1823)](https://github.com/PennyLaneAI/pennylane/pull/1823)

* The helper functions `qml.math.block_diag` and `qml.math.scatter_element_add` now are entirely differentiable when using Autograd. Previously only indexed entries of the block diagonal could be differentiated, while the derivative w.r.t to the second argument of `qml.math.scatter_element_add` dispatched to NumPy instead of Autograd. [(1816)](https://github.com/PennyLaneAI/pennylane/pull/1816) [(#1818)](https://github.com/PennyLaneAI/pennylane/pull/1818)

* Fixes a bug such that the original shot vector information of a device is preserved, so that outside the context manager the device remains unchanged. [(1792)](https://github.com/PennyLaneAI/pennylane/pull/1792)

* Modifies `qml.math.take` to be compatible with a breaking change released in JAX 0.2.24 and ensure that PennyLane supports this JAX version. [(1769)](https://github.com/PennyLaneAI/pennylane/pull/1769)

* Fixes a bug where the GPU cannot be used with `qml.qnn.TorchLayer`. [(1705)](https://github.com/PennyLaneAI/pennylane/pull/1705)

* Fix a bug where the devices cache the same result for different observables return types. [(1719)](https://github.com/PennyLaneAI/pennylane/pull/1719)

* Fixed a bug of the default circuit drawer where having more measurements compared to the number of measurements on any wire raised a `KeyError`. [(1702)](https://github.com/PennyLaneAI/pennylane/pull/1702)

* Fix a bug where it was not possible to use `jax.jit` on a `QNode` when using `QubitStateVector`. [(1683)](https://github.com/PennyLaneAI/pennylane/pull/1683)

* The device suite tests can now execute successfully if no shots configuration variable is given. [(1641)](https://github.com/PennyLaneAI/pennylane/pull/1641)

* Fixes a bug where the `qml.gradients.param_shift` transform would raise an error while attempting to compute the variance of a QNode with ragged output. [(1646)](https://github.com/PennyLaneAI/pennylane/pull/1646)

* Fixes a bug in `default.mixed`, to ensure that returned probabilities are always non-negative. [(1680)](https://github.com/PennyLaneAI/pennylane/pull/1680)

* Fixes a bug where gradient transforms would fail to apply to QNodes containing classical processing. [(1699)](https://github.com/PennyLaneAI/pennylane/pull/1699)

* Fixes a bug where the parameter-shift method was not correctly using the fallback gradient function when *all* circuit parameters required the fallback. [(1782)](https://github.com/PennyLaneAI/pennylane/pull/1782)

<h3>Documentation</h3>

* Adds a link to https://pennylane.ai/qml/demonstrations.html in the navbar. [(#1624)](https://github.com/PennyLaneAI/pennylane/pull/1624)

* Corrects the docstring of `ExpvalCost` by adding `wires` to the signature of the `ansatz` argument. [(1715)](https://github.com/PennyLaneAI/pennylane/pull/1715)

* Updated docstring examples using the `qchem.molecular_hamiltonian` function. [(1724)](https://github.com/PennyLaneAI/pennylane/pull/1724)

* Updates the 'Gradients and training' quickstart guide to provide information on gradient transforms. [(1751)](https://github.com/PennyLaneAI/pennylane/pull/1751)

* All instances of `qnode.draw()` have been updated to instead use the transform `qml.draw(qnode)`. [(1750)](https://github.com/PennyLaneAI/pennylane/pull/1750)

* Add the `jax` interface in QNode Documentation. [(1755)](https://github.com/PennyLaneAI/pennylane/pull/1755)

* Reorganized all the templates related to quantum chemistry under a common header `Quantum Chemistry templates`. [(1822)](https://github.com/PennyLaneAI/pennylane/pull/1822)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Catalina Albornoz, Juan Miguel Arrazola, Utkarsh Azad, Akash Narayanan B, Sam Banning, Thomas Bromley, Jack Ceroni, Alain Delgado, Olivia Di Matteo, Andrew Gardhouse, Anthony Hayes, Theodor Isacsson, David Ittah, Josh Izaac, Soran Jahangiri, Nathan Killoran, Christina Lee, Guillermo Alonso-Linaje, Romain Moyard, Lee James O'Riordan, Carrie-Anne Rubidge, Maria Schuld, Rishabh Singh, Jay Soni, Ingrid Strandberg, Antal Száva, Teresa Tamayo-Mendoza, Rodrigo Vargas, Cody Wang, David Wierichs, Moritz Willmann.

0.18.0post2

A minor post-release to update the PennyLane documentation navigation bar.

0.18.0post1

A minor post-release to update the PennyLane documentation navigation bar to include a link to the demonstrations.

0.18.0

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

<h4>PennyLane now comes packaged with <code>lightning.qubit</code></h4>

* The C++-based [lightning.qubit](https://pennylane-lightning.readthedocs.io/en/stable/) device is now included with installations of PennyLane. [(#1663)](https://github.com/PennyLaneAI/pennylane/pull/1663)

The `lightning.qubit` device is a fast state-vector simulator equipped with the efficient [adjoint method](https://arxiv.org/abs/2009.02823) for differentiating quantum circuits, check out the plugin [release notes](https://github.com/PennyLaneAI/pennylane-lightning/blob/v0.18.0/.github/CHANGELOG.md#new-features-since-last-release) for more details! The device can be accessed in the following way:

python
import pennylane as qml

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

qml.qnode(dev, diff_method="adjoint")
def circuit(weights):
qml.templates.StronglyEntanglingLayers(weights, wires=range(wires))
return qml.expval(qml.PauliZ(0))

weights = qml.init.strong_ent_layers_normal(layers, wires, seed=1967)


Evaluating circuits and their gradients on the device can be achieved using the standard approach:

pycon
>>> print(f"Circuit evaluated: {circuit(weights)}")
Circuit evaluated: 0.9801286266677633
>>> print(f"Circuit gradient:\n{qml.grad(circuit)(weights)}")
Circuit gradient:
[[[-9.35301749e-17 -1.63051504e-01 -4.14810501e-04]
[-7.88816484e-17 -1.50136528e-04 -1.77922957e-04]
[-5.20670796e-17 -3.92874550e-02 8.14523075e-05]]

[[-1.14472273e-04 3.85963953e-02 -9.39190132e-18]
[-5.76791765e-05 -9.78478343e-02 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00]]]


The adjoint method operates after a forward pass by iteratively applying inverse gates to scan backwards through the circuit. The method is already available in PennyLane's `default.qubit` device, but the version provided by `lightning.qubit` integrates with the C++ backend and is more performant, as shown in the plot below:

<img src="https://raw.githubusercontent.com/PennyLaneAI/pennylane-lightning/master/doc/_static/lightning_adjoint.png" width=70%/>

<h4>Support for native backpropagation using PyTorch</h4>

* The built-in PennyLane simulator `default.qubit` now supports backpropogation with PyTorch. [(1360)](https://github.com/PennyLaneAI/pennylane/pull/1360) [(#1598)](https://github.com/PennyLaneAI/pennylane/pull/1598)

As a result, `default.qubit` can now use end-to-end classical backpropagation as a means to compute gradients. End-to-end backpropagation can be faster than the parameter-shift rule for computing quantum gradients when the number of parameters to be optimized is large. This is now the default differentiation method when using `default.qubit` with PyTorch.

Using this method, the created QNode is a 'white-box' that is tightly integrated with your PyTorch computation, including TorchScript and GPU support.

python
x = torch.tensor(0.43316321, dtype=torch.float64, requires_grad=True)
y = torch.tensor(0.2162158, dtype=torch.float64, requires_grad=True)
z = torch.tensor(0.75110998, dtype=torch.float64, requires_grad=True)

p = torch.tensor([x, y, z], requires_grad=True)
dev = qml.device("default.qubit", wires=1)

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

res = circuit(p)
res.backward()

pycon
>>> res = circuit(p)
>>> res.backward()
>>> print(p.grad)
tensor([-9.1798e-17, -2.1454e-01, -1.0511e-16], dtype=torch.float64)


<h4>Improved quantum optimization methods</h4>

* The `RotosolveOptimizer` now can tackle general parametrized circuits, and is no longer restricted to single-qubit Pauli rotations. [(1489)](https://github.com/PennyLaneAI/pennylane/pull/1489)

This includes:

- layers of gates controlled by the same parameter,
- controlled variants of parametrized gates, and
- Hamiltonian time evolution.

Note that the eigenvalue spectrum of the gate generator needs to be known to use `RotosolveOptimizer` for a general gate, and it is required to produce equidistant frequencies. For details see [Vidal and Theis, 2018](https://arxiv.org/abs/1812.06323) and [Wierichs, Izaac, Wang, Lin 2021](https://arxiv.org/abs/2107.12390).

Consider a circuit with a mixture of Pauli rotation gates, controlled Pauli rotations, and single-parameter layers of Pauli rotations:

python
dev = qml.device('default.qubit', wires=3, shots=None)

qml.qnode(dev)
def cost_function(rot_param, layer_par, crot_param):
for i, par in enumerate(rot_param):
qml.RX(par, wires=i)
for w in dev.wires:
qml.RX(layer_par, wires=w)
for i, par in enumerate(crot_param):
qml.CRY(par, wires=[i, (i+1) % 3])

return qml.expval(qml.PauliZ(0) qml.PauliZ(1) qml.PauliZ(2))


This cost function has one frequency for each of the first `RX` rotation angles, three frequencies for the layer of `RX` gates that depend on `layer_par`, and two frequencies for each of the `CRY` gate parameters. Rotosolve can then be used to minimize the `cost_function`:

python
Initial parameters
init_param = [
np.array([0.3, 0.2, 0.67], requires_grad=True),
np.array(1.1, requires_grad=True),
np.array([-0.2, 0.1, -2.5], requires_grad=True),
]
Numbers of frequencies per parameter
num_freqs = [[1, 1, 1], 3, [2, 2, 2]]

opt = qml.RotosolveOptimizer()
param = init_param.copy()


In addition, the optimization technique for the Rotosolve substeps can be chosen via the `optimizer` and `optimizer_kwargs` keyword arguments and the minimized cost of the intermediate univariate reconstructions can be read out via `full_output`, including the cost _after_ the full Rotosolve step:

python
for step in range(3):
param, cost, sub_cost = opt.step_and_cost(
cost_function,
*param,
num_freqs=num_freqs,
full_output=True,
optimizer="brute",
)
print(f"Cost before step: {cost}")
print(f"Minimization substeps: {np.round(sub_cost, 6)}")

pycon
Cost before step: 0.042008210392535605
Minimization substeps: [-0.230905 -0.863336 -0.980072 -0.980072 -1. -1. -1. ]
Cost before step: -0.999999999068121
Minimization substeps: [-1. -1. -1. -1. -1. -1. -1.]
Cost before step: -1.0
Minimization substeps: [-1. -1. -1. -1. -1. -1. -1.]


For usage details please consider the [docstring of the optimizer](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.RotosolveOptimizer.html?highlight=rotosolveoptimizer#pennylane.RotosolveOptimizer).

<h4>Faster, trainable, Hamiltonian simulations</h4>

* Hamiltonians are now trainable with respect to their coefficients. [(1483)](https://github.com/PennyLaneAI/pennylane/pull/1483)

python
from pennylane import numpy as np

dev = qml.device("default.qubit", wires=2)
qml.qnode(dev)
def circuit(coeffs, param):
qml.RX(param, wires=0)
qml.RY(param, wires=0)
return qml.expval(
qml.Hamiltonian(coeffs, [qml.PauliX(0), qml.PauliZ(0)], simplify=True)
)

coeffs = np.array([-0.05, 0.17])
param = np.array(1.7)
grad_fn = qml.grad(circuit)

pycon
>>> grad_fn(coeffs, param)
(array([-0.12777055, 0.0166009 ]), array(0.0917819))


Furthermore, a gradient recipe for Hamiltonian coefficients has been added. This makes it possible to compute parameter-shift gradients of these coefficients on devices that natively support Hamiltonians. [(1551)](https://github.com/PennyLaneAI/pennylane/pull/1551)

* Hamiltonians are now natively supported on the `default.qubit` device if `shots=None`. This makes VQE workflows a lot faster in some cases. [(1551)](https://github.com/PennyLaneAI/pennylane/pull/1551) [(#1596)](https://github.com/PennyLaneAI/pennylane/pull/1596)

<img src="https://pennylane.readthedocs.io/en/latest/_static/hamiltonian_expval_h_plot.png" width=45%/>

* The Hamiltonian can now store grouping information, which can be accessed by a device to speed up computations of the expectation value of a Hamiltonian. [(1515)](https://github.com/PennyLaneAI/pennylane/pull/1515)

python
obs = [qml.PauliX(0), qml.PauliX(1), qml.PauliZ(0)]
coeffs = np.array([1., 2., 3.])
H = qml.Hamiltonian(coeffs, obs, grouping_type='qwc')


Initialization with a ``grouping_type`` other than ``None`` stores the indices
required to make groups of commuting observables and their coefficients.

pycon
>>> H.grouping_indices
[[0, 1], [2]]


<h4>Create multi-circuit quantum transforms and custom gradient rules</h4>

* Custom gradient transforms can now be created using the new `qml.gradients.gradient_transform` decorator on a batch-tape transform. [(1589)](https://github.com/PennyLaneAI/pennylane/pull/1589)

Quantum gradient transforms are a specific case of `qml.batch_transform`.

Supported gradient transforms must be of the following form:

python
qml.gradients.gradient_transform
def my_custom_gradient(tape, argnum=None, **kwargs):
...
return gradient_tapes, processing_fn


Various built-in quantum gradient transforms are provided within the `qml.gradients` module, including `qml.gradients.param_shift`. Once defined, quantum gradient transforms can be applied directly to QNodes:

pycon
>>> qml.qnode(dev)
... def circuit(x):
... qml.RX(x, wires=0)
... qml.CNOT(wires=[0, 1])
... return qml.expval(qml.PauliZ(0))
>>> circuit(0.3)
tensor(0.95533649, requires_grad=True)
>>> qml.gradients.param_shift(circuit)(0.5)
array([[-0.47942554]])


Quantum gradient transforms are fully differentiable, allowing higher order derivatives to be accessed:

pycon
>>> qml.grad(qml.gradients.param_shift(circuit))(0.5)
tensor(-0.87758256, requires_grad=True)


Refer to the page of [quantum gradient transforms](https://pennylane.readthedocs.io/en/stable/code/qml_gradients.html) for more details.

* The ability to define *batch* transforms has been added via the new `qml.batch_transform` decorator. [(1493)](https://github.com/PennyLaneAI/pennylane/pull/1493)

A batch transform is a transform that takes a single tape or QNode as input, and executes multiple tapes or QNodes independently. The results may then be post-processed before being returned.

For example, consider the following batch transform:

python
qml.batch_transform
def my_transform(tape, a, b):
"""Generates two tapes, one with all RX replaced with RY,
and the other with all RX replaced with RZ."""
tape1 = qml.tape.JacobianTape()
tape2 = qml.tape.JacobianTape()

loop through all operations on the input tape
for op in tape.operations + tape.measurements:
if op.name == "RX":
with tape1:
qml.RY(a * qml.math.abs(op.parameters[0]), wires=op.wires)
with tape2:
qml.RZ(b * qml.math.abs(op.parameters[0]), wires=op.wires)
else:
for t in [tape1, tape2]:
with t:
qml.apply(op)

def processing_fn(results):
return qml.math.sum(qml.math.stack(results))

return [tape1, tape2], processing_fn


We can transform a QNode directly using decorator syntax:

pycon
>>> my_transform(0.65, 2.5)
... qml.qnode(dev)
... def circuit(x):
... qml.Hadamard(wires=0)
... qml.RX(x, wires=0)
... return qml.expval(qml.PauliX(0))
>>> print(circuit(-0.5))
1.2629730888100839


Batch tape transforms are fully differentiable:

pycon
>>> gradient = qml.grad(circuit)(-0.5)
>>> print(gradient)
2.5800122591960153


Batch transforms can also be applied to existing QNodes,

pycon
>>> new_qnode = my_transform(existing_qnode, *transform_weights)
>>> new_qnode(weights)


or to tapes (in which case, the processed tapes and classical post-processing functions are returned):

pycon
>>> tapes, fn = my_transform(tape, 0.65, 2.5)
>>> from pennylane.interfaces.batch import execute
>>> dev = qml.device("default.qubit", wires=1)
>>> res = execute(tapes, dev, interface="autograd", gradient_fn=qml.gradients.param_shift)
1.2629730888100839


* Vector-Jacobian product transforms have been added to the `qml.gradients` package. [(1494)](https://github.com/PennyLaneAI/pennylane/pull/1494)

The new transforms include:

- `qml.gradients.vjp`
- `qml.gradients.batch_vjp`

* Support for differentiable execution of batches of circuits has been added, via the beta `pennylane.interfaces.batch` module. [(1501)](https://github.com/PennyLaneAI/pennylane/pull/1501) [(#1508)](https://github.com/PennyLaneAI/pennylane/pull/1508) [(#1542)](https://github.com/PennyLaneAI/pennylane/pull/1542) [(#1549)](https://github.com/PennyLaneAI/pennylane/pull/1549) [(#1608)](https://github.com/PennyLaneAI/pennylane/pull/1608) [(#1618)](https://github.com/PennyLaneAI/pennylane/pull/1618) [(#1637)](https://github.com/PennyLaneAI/pennylane/pull/1637)

For now, this is a low-level feature, and will be integrated into the QNode in a future release. For example:

python
from pennylane.interfaces.batch import execute

def cost_fn(x):
with qml.tape.JacobianTape() as tape1:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
qml.var(qml.PauliZ(0) qml.PauliX(1))

with qml.tape.JacobianTape() as tape2:
qml.RX(x[0], wires=0)
qml.RY(x[0], wires=1)
qml.CNOT(wires=[0, 1])
qml.probs(wires=1)

result = execute(
[tape1, tape2], dev,
gradient_fn=qml.gradients.param_shift,
interface="autograd"
)
return result[0] + result[1][0, 0]

res = qml.grad(cost_fn)(params)


<h3>Improvements</h3>

* A new operation `qml.SISWAP` has been added, the square-root of the `qml.ISWAP` operation. [(1563)](https://github.com/PennyLaneAI/pennylane/pull/1563)

* The `frobenius_inner_product` function has been moved to the `qml.math` module, and is now differentiable using all autodiff frameworks. [(1388)](https://github.com/PennyLaneAI/pennylane/pull/1388)

* A warning is raised to inform the user that specifying a list of shots is only supported for `QubitDevice` based devices. [(1659)](https://github.com/PennyLaneAI/pennylane/pull/1659)

* The `qml.circuit_drawer.MPLDrawer` class provides manual circuit drawing functionality using Matplotlib. While not yet integrated with automatic circuit drawing, this class provides customization and control. [(1484)](https://github.com/PennyLaneAI/pennylane/pull/1484)

python
from pennylane.circuit_drawer import MPLDrawer

drawer = MPLDrawer(n_wires=3, n_layers=3)

drawer.label([r"$|\Psi\rangle$", r"$|\theta\rangle$", "aux"])

drawer.box_gate(layer=0, wires=[0, 1, 2], text="Entangling Layers", text_options={'rotation': 'vertical'})
drawer.box_gate(layer=1, wires=[0, 1], text="U(θ)")

drawer.CNOT(layer=2, wires=[1, 2])
drawer.measure(layer=3, wires=2)

drawer.fig.suptitle('My Circuit', fontsize='xx-large')


<img src="https://pennylane.readthedocs.io/en/latest/_static/drawer/example_release_notes.png" width=70%/>

* The slowest tests, more than 1.5 seconds, now have the pytest mark `slow`, and can be selected or deselected during local execution of tests. [(1633)](https://github.com/PennyLaneAI/pennylane/pull/1633)

* The device test suite has been expanded to cover more qubit operations and observables. [(1510)](https://github.com/PennyLaneAI/pennylane/pull/1510)

* The `MultiControlledX` class now inherits from `Operation` instead of `ControlledQubitUnitary` which makes the `MultiControlledX` gate a non-parameterized gate. [(1557)](https://github.com/PennyLaneAI/pennylane/pull/1557)

* The `utils.sparse_hamiltonian` function can now deal with non-integer wire labels, and it throws an error for the edge case of observables that are created from multi-qubit operations. [(1550)](https://github.com/PennyLaneAI/pennylane/pull/1550)

* Added the matrix attribute to `qml.templates.subroutines.GroverOperator` [(1553)](https://github.com/PennyLaneAI/pennylane/pull/1553)

* The `tape.to_openqasm()` method now has a `measure_all` argument that specifies whether the serialized OpenQASM script includes computational basis measurements on all of the qubits or just those specified by the tape. [(1559)](https://github.com/PennyLaneAI/pennylane/pull/1559)

* An error is now raised when no arguments are passed to an observable, to inform that wires have not been supplied. [(1547)](https://github.com/PennyLaneAI/pennylane/pull/1547)

* The `group_observables` transform is now differentiable. [(1483)](https://github.com/PennyLaneAI/pennylane/pull/1483)

For example:

python
import jax
from jax import numpy as jnp

coeffs = jnp.array([1., 2., 3.])
obs = [PauliX(wires=0), PauliX(wires=1), PauliZ(wires=1)]

def group(coeffs, select=None):
_, grouped_coeffs = qml.grouping.group_observables(obs, coeffs)
in this example, grouped_coeffs is a list of two jax tensors
[DeviceArray([1., 2.], dtype=float32), DeviceArray([3.], dtype=float32)]
return grouped_coeffs[select]

jac_fn = jax.jacobian(group)

pycon
>>> jac_fn(coeffs, select=0)
[[1. 0. 0.]
[0. 1. 0.]]

>>> jac_fn(coeffs, select=1)
[[0., 0., 1.]]


* The tape does not verify any more that all Observables have owners in the annotated queue. [(1505)](https://github.com/PennyLaneAI/pennylane/pull/1505)

This allows manipulation of Observables inside a tape context. An example is `expval(Tensor(qml.PauliX(0), qml.Identity(1)).prune())` which makes the expval an owner of the pruned tensor and its constituent observables, but leaves the original tensor in the queue without an owner.

* The `qml.ResetError` is now supported for `default.mixed` device. [(1541)](https://github.com/PennyLaneAI/pennylane/pull/1541)

* `QNode.diff_method` will now reflect which method was selected from `diff_method="best"`. [(1568)](https://github.com/PennyLaneAI/pennylane/pull/1568)

* QNodes now support `diff_method=None`. This works the same as `interface=None`. Such QNodes accept floats, ints, lists and NumPy arrays and return NumPy output but can not be differentiated. [(1585)](https://github.com/PennyLaneAI/pennylane/pull/1585)

* QNodes now include validation to warn users if a supplied keyword argument is not one of the recognized arguments. [(1591)](https://github.com/PennyLaneAI/pennylane/pull/1591)

<h3>Breaking changes</h3>

* The `QFT` operation has been moved, and is now accessible via `pennylane.templates.QFT`. [(1548)](https://github.com/PennyLaneAI/pennylane/pull/1548)

* Specifying `shots=None` with `qml.sample` was previously deprecated. From this release onwards, setting `shots=None` when sampling will raise an error also for `default.qubit.jax`. [(1629)](https://github.com/PennyLaneAI/pennylane/pull/1629)

* An error is raised during QNode creation when a user requests backpropagation on a device with finite-shots. [(1588)](https://github.com/PennyLaneAI/pennylane/pull/1588)

* The class `qml.Interferometer` is deprecated and will be renamed `qml.InterferometerUnitary` after one release cycle. [(1546)](https://github.com/PennyLaneAI/pennylane/pull/1546)

* All optimizers except for Rotosolve and Rotoselect now have a public attribute `stepsize`. Temporary backward compatibility has been added to support the use of `_stepsize` for one release cycle. `update_stepsize` method is deprecated. [(1625)](https://github.com/PennyLaneAI/pennylane/pull/1625)


<h3>Bug fixes</h3>

* Fixed a bug with shot vectors and `Device` base class. [(1666)](https://github.com/PennyLaneAI/pennylane/pull/1666)

* Fixed a bug where `jax.jit` would fail on a QNode that used `qml.QubitStateVector`. [(1649)](https://github.com/PennyLaneAI/pennylane/pull/1649)

* Fixed a bug related to an edge case of single-qubit `zyz_decomposition` when only off-diagonal elements are present. [(1643)](https://github.com/PennyLaneAI/pennylane/pull/1643)

* `MottonenStatepreparation` can now be run with a single wire label not in a list. [(1620)](https://github.com/PennyLaneAI/pennylane/pull/1620)

* Fixed the circuit representation of CY gates to align with CNOT and CZ gates when calling the circuit drawer. [(1504)](https://github.com/PennyLaneAI/pennylane/issues/1504)

* Dask and CVXPY dependent tests are skipped if those packages are not installed. [(1617)](https://github.com/PennyLaneAI/pennylane/pull/1617)

* The `qml.layer` template now works with tensorflow variables. [(1615)](https://github.com/PennyLaneAI/pennylane/pull/1615)

* Remove `QFT` from possible operations in `default.qubit` and `default.mixed`. [(1600)](https://github.com/PennyLaneAI/pennylane/pull/1600)

* Fixed a bug when computing expectations of Hamiltonians using TensorFlow. [(1586)](https://github.com/PennyLaneAI/pennylane/pull/1586)

* Fixed a bug when computing the specs of a circuit with a Hamiltonian
observable. [(1533)](https://github.com/PennyLaneAI/pennylane/pull/1533)

<h3>Documentation</h3>

* The `qml.Identity` operation is placed under the sections Qubit observables and CV observables. [(1576)](https://github.com/PennyLaneAI/pennylane/pull/1576)

* Updated the documentation of `qml.grouping`, `qml.kernels` and `qml.qaoa` modules to present the list of functions first followed by the technical details of the module. [(1581)](https://github.com/PennyLaneAI/pennylane/pull/1581)

* Recategorized Qubit operations into new and existing categories so that code for each operation is easier to locate. [(1566)](https://github.com/PennyLaneAI/pennylane/pull/1583)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Vishnu Ajith, Akash Narayanan B, Thomas Bromley, Olivia Di Matteo, Sahaj Dhamija, Tanya Garg, Anthony Hayes, Theodor Isacsson, Josh Izaac, Prateek Jain, Ankit Khandelwal, Nathan Killoran, Christina Lee, Ian McLean, Johannes Jakob Meyer, Romain Moyard, Lee James O'Riordan, Esteban Payares, Pratul Saini, Maria Schuld, Arshpreet Singh, Jay Soni, Ingrid Strandberg, Antal Száva, Slimane Thabet, David Wierichs, Vincent Wong.

0.17.0

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

<h4>Circuit optimization</h4>

* PennyLane can now perform quantum circuit optimization using the top-level transform `qml.compile`. The `compile` transform allows you to chain together sequences of tape and quantum function transforms into custom circuit optimization pipelines. [(1475)](https://github.com/PennyLaneAI/pennylane/pull/1475)

For example, take the following decorated quantum function:

python
dev = qml.device('default.qubit', wires=[0, 1, 2])

qml.qnode(dev)
qml.compile()
def qfunc(x, y, z):
qml.Hadamard(wires=0)
qml.Hadamard(wires=1)
qml.Hadamard(wires=2)
qml.RZ(z, wires=2)
qml.CNOT(wires=[2, 1])
qml.RX(z, wires=0)
qml.CNOT(wires=[1, 0])
qml.RX(x, wires=0)
qml.CNOT(wires=[1, 0])
qml.RZ(-z, wires=2)
qml.RX(y, wires=2)
qml.PauliY(wires=2)
qml.CZ(wires=[1, 2])
return qml.expval(qml.PauliZ(wires=0))


The default behaviour of `qml.compile` is to apply a sequence of three transforms: `commute_controlled`, `cancel_inverses`, and then `merge_rotations`.

pycon
>>> print(qml.draw(qfunc)(0.2, 0.3, 0.4))
0: ──H───RX(0.6)──────────────────┤ ⟨Z⟩
1: ──H──╭X────────────────────╭C──┤
2: ──H──╰C────────RX(0.3)──Y──╰Z──┤


The `qml.compile` transform is flexible and accepts a custom pipeline of tape and quantum function transforms (you can even write your own!). For example, if we wanted to only push single-qubit gates through controlled gates and cancel adjacent inverses, we could do:

python
from pennylane.transforms import commute_controlled, cancel_inverses
pipeline = [commute_controlled, cancel_inverses]

qml.qnode(dev)
qml.compile(pipeline=pipeline)
def qfunc(x, y, z):
qml.Hadamard(wires=0)
qml.Hadamard(wires=1)
qml.Hadamard(wires=2)
qml.RZ(z, wires=2)
qml.CNOT(wires=[2, 1])
qml.RX(z, wires=0)
qml.CNOT(wires=[1, 0])
qml.RX(x, wires=0)
qml.CNOT(wires=[1, 0])
qml.RZ(-z, wires=2)
qml.RX(y, wires=2)
qml.PauliY(wires=2)
qml.CZ(wires=[1, 2])
return qml.expval(qml.PauliZ(wires=0))


pycon
>>> print(qml.draw(qfunc)(0.2, 0.3, 0.4))
0: ──H───RX(0.4)──RX(0.2)────────────────────────────┤ ⟨Z⟩
1: ──H──╭X───────────────────────────────────────╭C──┤
2: ──H──╰C────────RZ(0.4)──RZ(-0.4)──RX(0.3)──Y──╰Z──┤


The following compilation transforms have been added and are also available to use, either independently, or within a `qml.compile` pipeline:

* `commute_controlled`: push commuting single-qubit gates through controlled operations. [(1464)](https://github.com/PennyLaneAI/pennylane/pull/1464)

* `cancel_inverses`: removes adjacent pairs of operations that cancel out. [(1455)](https://github.com/PennyLaneAI/pennylane/pull/1455)

* `merge_rotations`: combines adjacent rotation gates of the same type into a single gate, including controlled rotations. [(1455)](https://github.com/PennyLaneAI/pennylane/pull/1455)

* `single_qubit_fusion`: acts on all sequences of single-qubit operations in a quantum function, and converts each sequence to a single `Rot` gate. [(1458)](https://github.com/PennyLaneAI/pennylane/pull/1458)

For more details on `qml.compile` and the available compilation transforms, see [the compilation documentation](https://pennylane.readthedocs.io/en/stable/code/qml_transforms.html#transforms-for-circuit-compilation).
<h4>QNodes are even more powerful</h4>

* Computational basis samples directly from the underlying device can now be returned directly from QNodes via `qml.sample()`. [(1441)](https://github.com/PennyLaneAI/pennylane/pull/1441)

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

qml.qnode(dev)
def circuit_1():
qml.Hadamard(wires=0)
qml.Hadamard(wires=1)
return qml.sample()

qml.qnode(dev)
def circuit_2():
qml.Hadamard(wires=0)
qml.Hadamard(wires=1)
return qml.sample(wires=[0,2]) no observable provided and wires specified


pycon
>>> print(circuit_1())
[[1, 0, 0],
[1, 1, 0],
[1, 0, 0],
[0, 0, 0],
[0, 1, 0]]

>>> print(circuit_2())
[[1, 0],
[1, 0],
[1, 0],
[0, 0],
[0, 0]]

>>> print(qml.draw(circuit_2)())
0: ──H──╭┤ Sample[basis]
1: ──H──│┤
2: ─────╰┤ Sample[basis]


* The new `qml.apply` function can be used to add operations that might have already been instantiated elsewhere to the QNode and other queuing contexts: [(1433)](https://github.com/PennyLaneAI/pennylane/pull/1433)

python
op = qml.RX(0.4, wires=0)
dev = qml.device("default.qubit", wires=2)

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


pycon
>>> print(qml.draw(circuit)(0.6))
0: ──RY(0.6)──RX(0.4)──┤ ⟨Z⟩


Previously instantiated measurements can also be applied to QNodes.

<h4>Device Resource Tracker</h4>

* The new Device Tracker capabilities allows for flexible and versatile tracking of executions, even inside parameter-shift gradients. This functionality will improve the ease of monitoring large batches and remote jobs. [(1355)](https://github.com/PennyLaneAI/pennylane/pull/1355)

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

qml.qnode(dev, diff_method="parameter-shift")
def circuit(x):
qml.RX(x, wires=0)
return qml.expval(qml.PauliZ(0))

x = np.array(0.1)

with qml.Tracker(circuit.device) as tracker:
qml.grad(circuit)(x)


pycon
>>> tracker.totals
{'executions': 3, 'shots': 300, 'batches': 1, 'batch_len': 2}
>>> tracker.history
{'executions': [1, 1, 1],
'shots': [100, 100, 100],
'batches': [1],
'batch_len': [2]}
>>> tracker.latest
{'batches': 1, 'batch_len': 2}


Users can also provide a custom function to the `callback` keyword that gets called each time the information is updated. This functionality allows users to monitor remote jobs or large parameter-shift batches.

pycon
>>> def shots_info(totals, history, latest):
... print("Total shots: ", totals['shots'])
>>> with qml.Tracker(circuit.device, callback=shots_info) as tracker:
... qml.grad(circuit)(0.1)
Total shots: 100
Total shots: 200
Total shots: 300
Total shots: 300


<h4>Containerization support</h4>

* Docker support for building PennyLane with support for all interfaces (TensorFlow, Torch, and Jax), as well as device plugins and QChem, for GPUs and CPUs, has been added. [(1391)](https://github.com/PennyLaneAI/pennylane/pull/1391)

The build process using Docker and `make` requires that the repository source code is cloned or downloaded from GitHub. Visit the the detailed description for an [extended list of options](https://pennylane.readthedocs.io/en/stable/development/guide/installation.html#installation).

<h4>Improved Hamiltonian simulations</h4>

* Added a sparse Hamiltonian observable and the functionality to support computing its expectation value with `default.qubit`. [(1398)](https://github.com/PennyLaneAI/pennylane/pull/1398)

For example, the following QNode returns the expectation value of a sparse Hamiltonian:

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

qml.qnode(dev, diff_method="parameter-shift")
def circuit(param, H):
qml.PauliX(0)
qml.SingleExcitation(param, wires=[0, 1])
return qml.expval(qml.SparseHamiltonian(H, [0, 1]))


We can execute this QNode, passing in a sparse identity matrix:

pycon
>>> print(circuit([0.5], scipy.sparse.eye(4).tocoo()))
0.9999999999999999


The expectation value of the sparse Hamiltonian is computed directly, which leads to executions that are faster by orders of magnitude. Note that "parameter-shift" is the only differentiation method that is currently supported when the observable is a sparse Hamiltonian.

* VQE problems can now be intuitively set up by passing the Hamiltonian as an observable. [(1474)](https://github.com/PennyLaneAI/pennylane/pull/1474)

python
dev = qml.device("default.qubit", wires=2)
H = qml.Hamiltonian([1., 2., 3.], [qml.PauliZ(0), qml.PauliY(0), qml.PauliZ(1)])
w = qml.init.strong_ent_layers_uniform(1, 2, seed=1967)

qml.qnode(dev)
def circuit(w):
qml.templates.StronglyEntanglingLayers(w, wires=range(2))
return qml.expval(H)


pycon
>>> print(circuit(w))
-1.5133943637878295
>>> print(qml.grad(circuit)(w))
[[[-8.32667268e-17 1.39122955e+00 -9.12462052e-02]
[ 1.02348685e-16 -7.77143238e-01 -1.74708049e-01]]]


Note that other measurement types like `var(H)` or `sample(H)`, as well as multiple expectations like `expval(H1), expval(H2)` are not supported.

* Added functionality to compute the sparse matrix representation of a `qml.Hamiltonian` object. [(1394)](https://github.com/PennyLaneAI/pennylane/pull/1394)

<h4>New gradients module</h4>

* A new gradients module `qml.gradients` has been added, which provides differentiable quantum gradient transforms. [(1476)](https://github.com/PennyLaneAI/pennylane/pull/1476) [(#1479)](https://github.com/PennyLaneAI/pennylane/pull/1479) [(#1486)](https://github.com/PennyLaneAI/pennylane/pull/1486)

Available quantum gradient transforms include:

- `qml.gradients.finite_diff`
- `qml.gradients.param_shift`
- `qml.gradients.param_shift_cv`

For example,

pycon
>>> params = np.array([0.3,0.4,0.5], requires_grad=True)
>>> with qml.tape.JacobianTape() as tape:
... qml.RX(params[0], wires=0)
... qml.RY(params[1], wires=0)
... qml.RX(params[2], wires=0)
... qml.expval(qml.PauliZ(0))
... qml.var(qml.PauliZ(0))
>>> tape.trainable_params = {0, 1, 2}
>>> gradient_tapes, fn = qml.gradients.finite_diff(tape)
>>> res = dev.batch_execute(gradient_tapes)
>>> fn(res)
array([[-0.69688381, -0.32648317, -0.68120105],
[ 0.8788057 , 0.41171179, 0.85902895]])


<h4>Even more new operations and templates</h4>

* Grover Diffusion Operator template added. [(1442)](https://github.com/PennyLaneAI/pennylane/pull/1442)

For example, if we have an oracle that marks the "all ones" state with a negative sign:

python
n_wires = 3
wires = list(range(n_wires))

def oracle():
qml.Hadamard(wires[-1])
qml.Toffoli(wires=wires)
qml.Hadamard(wires[-1])


We can perform [Grover's Search Algorithm](https://en.wikipedia.org/wiki/Grover%27s_algorithm):

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

qml.qnode(dev)
def GroverSearch(num_iterations=1):
for wire in wires:
qml.Hadamard(wire)

for _ in range(num_iterations):
oracle()
qml.templates.GroverOperator(wires=wires)

return qml.probs(wires)


We can see this circuit yields the marked state with high probability:

pycon
>>> GroverSearch(num_iterations=1)
tensor([0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125,
0.78125], requires_grad=True)
>>> GroverSearch(num_iterations=2)
tensor([0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125,
0.0078125, 0.9453125], requires_grad=True)


* A decomposition has been added to `QubitUnitary` that makes the single-qubit case fully differentiable in all interfaces. Furthermore, a quantum function transform, `unitary_to_rot()`, has been added to decompose all single-qubit instances of `QubitUnitary` in a quantum circuit. [(1427)](https://github.com/PennyLaneAI/pennylane/pull/1427)

Instances of `QubitUnitary` may now be decomposed directly to `Rot` operations, or `RZ` operations if the input matrix is diagonal. For example, let

python
>>> U = np.array([
[-0.28829348-0.78829734j, 0.30364367+0.45085995j],
[ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
])


Then, we can compute the decomposition as:

pycon
>>> qml.QubitUnitary.decomposition(U, wires=0)
[Rot(-0.24209530281458358, 1.1493817777199102, 1.733058145303424, wires=[0])]


We can also apply the transform directly to a quantum function, and compute the gradients of parameters used to construct the unitary matrices.

python
def qfunc_with_qubit_unitary(angles):
z, x = angles[0], angles[1]

Z_mat = np.array([[np.exp(-1j * z / 2), 0.0], [0.0, np.exp(1j * z / 2)]])

c = np.cos(x / 2)
s = np.sin(x / 2) * 1j
X_mat = np.array([[c, -s], [-s, c]])

qml.Hadamard(wires="a")
qml.QubitUnitary(Z_mat, wires="a")
qml.QubitUnitary(X_mat, wires="b")
qml.CNOT(wires=["b", "a"])
return qml.expval(qml.PauliX(wires="a"))


pycon
>>> dev = qml.device("default.qubit", wires=["a", "b"])
>>> transformed_qfunc = qml.transforms.unitary_to_rot(qfunc_with_qubit_unitary)
>>> transformed_qnode = qml.QNode(transformed_qfunc, dev)
>>> input = np.array([0.3, 0.4], requires_grad=True)
>>> transformed_qnode(input)
tensor(0.95533649, requires_grad=True)
>>> qml.grad(transformed_qnode)(input)
array([-0.29552021, 0. ])


* Ising YY gate functionality added. [(1358)](https://github.com/PennyLaneAI/pennylane/pull/1358)


<h3>Improvements</h3>

* The `step` and `step_and_cost` methods of `QNGOptimizer` now accept a custom `grad_fn` keyword argument to use for gradient computations. [(1487)](https://github.com/PennyLaneAI/pennylane/pull/1487)

* The precision used by `default.qubit.jax` now matches the float precision indicated by

python
from jax.config import config
config.read('jax_enable_x64')

where `True` means `float64`/`complex128` and `False` means `float32`/`complex64`. [(1485)](https://github.com/PennyLaneAI/pennylane/pull/1485)

* The `./pennylane/ops/qubit.py` file is broken up into a folder of six separate files. [(1467)](https://github.com/PennyLaneAI/pennylane/pull/1467)

* Changed to using commas as the separator of wires in the string representation of `qml.Hamiltonian` objects for multi-qubit terms. [(1465)](https://github.com/PennyLaneAI/pennylane/pull/1465)

* Changed to using `np.object_` instead of `np.object` as per the NumPy deprecations starting version 1.20. [(1466)](https://github.com/PennyLaneAI/pennylane/pull/1466)

* Change the order of the covariance matrix and the vector of means internally in `default.gaussian`. [(1331)](https://github.com/PennyLaneAI/pennylane/pull/1331)

* Added the `id` attribute to templates. [(1438)](https://github.com/PennyLaneAI/pennylane/pull/1438)

* The `qml.math` module, for framework-agnostic tensor manipulation, has two new functions available: [(1490)](https://github.com/PennyLaneAI/pennylane/pull/1490)

- `qml.math.get_trainable_indices(sequence_of_tensors)`: returns the indices corresponding to trainable tensors in the input sequence.

- `qml.math.unwrap(sequence_of_tensors)`: unwraps a sequence of tensor-like objects to NumPy arrays.

In addition, the behaviour of `qml.math.requires_grad` has been improved in order to correctly determine trainability during Autograd and JAX backwards passes.

* A new tape method, `tape.unwrap()` is added. This method is a context manager; inside the context, the tape's parameters are unwrapped to NumPy arrays and floats, and the trainable parameter indices are set. [(1491)](https://github.com/PennyLaneAI/pennylane/pull/1491)

These changes are temporary, and reverted on exiting the context.

pycon
>>> with tf.GradientTape():
... with qml.tape.QuantumTape() as tape:
... qml.RX(tf.Variable(0.1), wires=0)
... qml.RY(tf.constant(0.2), wires=0)
... qml.RZ(tf.Variable(0.3), wires=0)
... with tape.unwrap():
... print("Trainable params:", tape.trainable_params)
... print("Unwrapped params:", tape.get_parameters())
Trainable params: {0, 2}
Unwrapped params: [0.1, 0.3]
>>> print("Original parameters:", tape.get_parameters())
Original parameters: [<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.1>,
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.3>]


In addition, `qml.tape.Unwrap` is a context manager that unwraps multiple tapes:

pycon
>>> with qml.tape.Unwrap(tape1, tape2):


<h3>Breaking changes</h3>

* Removed the deprecated tape methods `get_resources` and `get_depth` as they are superseded by the `specs` tape attribute. [(1522)](https://github.com/PennyLaneAI/pennylane/pull/1522)

* Specifying `shots=None` with `qml.sample` was previously deprecated. From this release onwards, setting `shots=None` when sampling will raise an error. [(1522)](https://github.com/PennyLaneAI/pennylane/pull/1522)

* The existing `pennylane.collections.apply` function is no longer accessible via `qml.apply`, and needs to be imported directly from the `collections` package. [(1358)](https://github.com/PennyLaneAI/pennylane/pull/1358)

<h3>Bug fixes</h3>

* Fixes a bug in `qml.adjoint` and `qml.ctrl` where the adjoint of operations outside of a `QNode` or a `QuantumTape` could not be obtained. [(1532)](https://github.com/PennyLaneAI/pennylane/pull/1532)

* Fixes a bug in `GradientDescentOptimizer` and `NesterovMomentumOptimizer` where a cost function with one trainable parameter and non-trainable parameters raised an error. [(1495)](https://github.com/PennyLaneAI/pennylane/pull/1495)

* Fixed an example in the documentation's [introduction to numpy gradients](https://pennylane.readthedocs.io/en/stable/introduction/interfaces/numpy.html), where the wires were a non-differentiable argument to the QNode. [(#1499)](https://github.com/PennyLaneAI/pennylane/pull/1499)

* Fixed a bug where the adjoint of `qml.QFT` when using the `qml.adjoint` function was not correctly computed. [(1451)](https://github.com/PennyLaneAI/pennylane/pull/1451)

* Fixed the differentiability of the operation`IsingYY` for Autograd, Jax and Tensorflow. [(1425)](https://github.com/PennyLaneAI/pennylane/pull/1425)

* Fixed a bug in the `torch` interface that prevented gradients from being computed on a GPU. [(1426)](https://github.com/PennyLaneAI/pennylane/pull/1426)

* Quantum function transforms now preserve the format of the measurement results, so that a single measurement returns a single value rather than an array with a single element. [(1434)](https://github.com/PennyLaneAI/pennylane/pull/1434)

* Fixed a bug in the parameter-shift Hessian implementation, which resulted in the incorrect Hessian being returned for a cost function that performed post-processing on a vector-valued QNode. [(1436)](https://github.com/PennyLaneAI/pennylane/pull/1436)

* Fixed a bug in the initialization of `QubitUnitary` where the size of the matrix was not checked against the number of wires. [(1439)](https://github.com/PennyLaneAI/pennylane/pull/1439)

<h3>Documentation</h3>

* Improved Contribution Guide and Pull Requests Guide. [(1461)](https://github.com/PennyLaneAI/pennylane/pull/1461)

* Examples have been added to clarify use of the continuous-variable `FockStateVector` operation in the multi-mode case. [(1472)](https://github.com/PennyLaneAI/pennylane/pull/1472)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Juan Miguel Arrazola, Olivia Di Matteo, Anthony Hayes, Theodor Isacsson, Josh Izaac, Soran Jahangiri, Nathan Killoran, Arshpreet Singh Khangura, Leonhard Kunczik, Christina Lee, Romain Moyard, Lee James O'Riordan, Ashish Panigrahi, Nahum Sá, Maria Schuld, Jay Soni, Antal Száva, David Wierichs.

Page 6 of 11

© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.