The latest release of PennyLane is now out and available for everyone to use. It comes with powerful new additions, such as support for circuit compilation routines, new gradient transforms, and a quantum device resource tracker. It also contains improvements and quality-of-life updates, including provided Docker images, new templates, and improved optimizers.

## Quantum circuit optimization using compilation transforms

PennyLane can now perform quantum circuit optimization using the top-level
transform `qml.compile`. The compile transform allows you to chain together
sequences of specific transforms into custom circuit optimization pipelines.

The following optimization transforms are now available to use, either independently or within a compilation pipeline:

`commute_controlled`: push commuting single-qubit gates through controlled operations`cancel_inverses`: remove adjacent pairs of operations that cancel out`merge_rotations`: combine adjacent rotation gates of the same type into a single gate`single_qubit_fusion`: convert each sequence of single-qubit operations into a single Rot gate

The default `qml.compile` behaviour shown below applies a single sequence of
`commute_controlled`, `cancel_inverses`, and `merge_rotations`, though
users can supply an ordered list of transforms to the `pipeline` keyword.

```
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))
```

Using `qml.compile()`, the above QNode is compiled into the following circuit:

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

You can read more about these transformations in the compilation documentation.

## Faster and more intuitive VQE simulations

You can now leverage sparse Hamiltonians, and gain significant speed-ups in your simulations! Furthermore, the expectation values of Hamiltonians can be directly returned in your quantum circuits:

```
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)
```

```
>>> 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]]]
```

## QNodes are even more powerful

Return samples in the computational basis directly via

`qml.sample()`in a QNode:dev = qml.device("default.qubit", wires=2, shots=5) @qml.qnode(dev) def circuit(): qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) return qml.sample()

>>> circuit() array([[0, 0], [1, 1], [0, 0], [0, 0], [1, 1]])

Operations that have been instantiated elsewhere can now be easily added to QNodes and other queuing contexts using

`qml.apply`: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))

## Device Resource Tracker

Use the new device tracker to track executions of a QNode, even when computing parameter-shift gradients. This functionality will improve the ease of monitoring large batches and remote jobs by providing easy access to the number of executions, batches, shots, and more.

```
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)
```

```
>>> 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}
```

Custom callback functions can also be provided; these callback functions are called whenever the tracker is updated. This makes it possible to monitor remote jobs or large parameter-shift batches.

```
>>> 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
```

## Docker containerization support

Getting started using or contributing to PennyLane is now even easier with provided Docker PennyLane images. There’s support for all interfaces (TensorFlow, Torch, and Jax), as well as plugins and QChem.

In addition, both CPU and GPU (Nvidia CUDA 11.1+) images are provided. See a more detailed description here.

## Module for differentiable quantum gradient transforms

A new gradients module `qml.gradients` has been added, providing the new quantum gradient transforms:

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

These quantum gradient transforms act directly on low-level quantum tape datastructures, returning tapes to be executed on hardware, and a corresponding post-processing function.

```
>>> 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.param_shift(tape)
```

These gradient tapes can be executed on quantum devices and post-processed to compute the gradient:

```
>>> res = dev.batch_execute(gradient_tapes)
>>> fn(res)
array([[-0.69688381, -0.32648317, -0.68120105],
[ 0.8788057 , 0.41171179, 0.85902895]])
```

All gradient transforms are differentiable, unlocking higher-order derivatives on hardware.

## New operations and templates

Play around with the new Grover Diffusion Operator template

`qml.templates.GroversOperator`. For example, use it to perform Grover’s Search Algorithm with an oracle function that marks the “all ones” state with a negative sign.n_wires = 3 wires = list(range(n_wires)) def oracle(): qml.Hadamard(wires[-1]) qml.Toffoli(wires=wires) qml.Hadamard(wires[-1]) 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)

Running the above QNOde will yield the marked state with high probability:

>>> 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)

Instances of

`QubitUnitary`may now be decomposed directly to`Rot`operations, or`RZ`operations if the input matrix is diagonal. This can be achieved using the new decomposition method added to`QubitUnitary`or the quantum function transform`unitary_to_rot()`.See the documentation for more details on this new quantum transform.

The

`qml.IsingYY`has been added.

## Improvements

In addition to the new features listed above, the release contains a wide array of improvements and optimizations:

- The
`QNGOptimizer`now accepts a custom`grad_fn`keyword argument to use for gradient computations, allowing it to be used with additional interfaces such as JAX. - The precision used by JAX simulations now matches the float precision indicated by the JAX configuration.
- Quantum tape parameters can now be easily converted to NumPy arrays
via the new
`qml.tape.Unwrap()`context manager.

## Breaking changes and deprecations

As new things are added, old things can be pruned away. Here’s what will be disappearing in the future:

- The deprecated tape methods
`get_resources`and`get_depth`have been removed, as they are superseded by the`qml.specs`function. - Specifying
`shots=None`with`qml.sample`was previously deprecated. From this release onwards, setting`shots=None`when sampling will raise an error. - The existing
`pennylane.collections.apply`function is no longer accessible via`qml.apply`, and needs to be imported directly from the collections package.

These highlights are just scratching the surface — check out the full release notes for more details.

## Contributors

As always, this release would not have been possible without the hard work of our development team and contributors:

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.