Pennylane

Latest version: v0.39.0

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

Scan your dependencies

Page 5 of 11

0.23.0

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

<h4> More powerful circuit cutting βœ‚οΈ</h4>

* Quantum circuit cutting (running `N`-wire circuits on devices with fewer than `N` wires) is now supported for QNodes of finite-shots using the new `qml.cut_circuit_mc` transform. [(2313)](https://github.com/PennyLaneAI/pennylane/pull/2313) [(#2321)](https://github.com/PennyLaneAI/pennylane/pull/2321) [(#2332)](https://github.com/PennyLaneAI/pennylane/pull/2332) [(#2358)](https://github.com/PennyLaneAI/pennylane/pull/2358) [(#2382)](https://github.com/PennyLaneAI/pennylane/pull/2382) [(#2399)](https://github.com/PennyLaneAI/pennylane/pull/2399) [(#2407)](https://github.com/PennyLaneAI/pennylane/pull/2407) [(#2444)](https://github.com/PennyLaneAI/pennylane/pull/2444)

With these new additions, samples from the original circuit can be simulated using a Monte Carlo method, using fewer qubits at the expense of more device executions. Additionally, this transform can take an optional classical processing function as an argument and return an expectation value.

The following `3`-qubit circuit contains a `WireCut` operation and a `sample` measurement. When decorated with `qml.cut_circuit_mc`, we can cut the circuit into two `2`-qubit fragments:

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

qml.cut_circuit_mc
qml.qnode(dev)
def circuit(x):
qml.RX(0.89, wires=0)
qml.RY(0.5, wires=1)
qml.RX(1.3, wires=2)

qml.CNOT(wires=[0, 1])
qml.WireCut(wires=1)
qml.CNOT(wires=[1, 2])

qml.RX(x, wires=0)
qml.RY(0.7, wires=1)
qml.RX(2.3, wires=2)
return qml.sample(wires=[0, 2])


we can then execute the circuit as usual by calling the QNode:

pycon
>>> x = 0.3
>>> circuit(x)
tensor([[1, 1],
[0, 1],
[0, 1],
...,
[0, 1],
[0, 1],
[0, 1]], requires_grad=True)


Furthermore, the number of shots can be temporarily altered when calling the QNode:

pycon
>>> results = circuit(x, shots=123)
>>> results.shape
(123, 2)


The `cut_circuit_mc` transform also supports returning sample-based expectation values of observables using the `classical_processing_fn` argument. Refer to the `UsageDetails` section of the [transform documentation](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.cut_circuit_mc.html) for an example.

* The `cut_circuit` transform now supports automatic graph partitioning by specifying `auto_cutter=True` to cut arbitrary tape-converted graphs using the general purpose graph partitioning framework [KaHyPar](https://pypi.org/project/kahypar/). [(#2330)](https://github.com/PennyLaneAI/pennylane/pull/2330) [(#2428)](https://github.com/PennyLaneAI/pennylane/pull/2428)

Note that `KaHyPar` needs to be installed separately with the `auto_cutter=True` option.

For integration with the existing low-level manual cut pipeline, refer to the [documentation of the
function](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.transforms.qcut.find_and_place_cuts.html).
pycon
qml.cut_circuit(auto_cutter=True)
qml.qnode(dev)
def circuit(x):
qml.RX(x, wires=0)
qml.RY(0.9, wires=1)
qml.RX(0.3, wires=2)
qml.CZ(wires=[0, 1])
qml.RY(-0.4, wires=0)
qml.CZ(wires=[1, 2])
return qml.expval(qml.grouping.string_to_pauli_word("ZZZ"))

pycon
>>> x = np.array(0.531, requires_grad=True)
>>> circuit(x)
0.47165198882111165
>>> qml.grad(circuit)(x)
-0.276982865449393


<h4>Grand QChem unification βš›οΈ 🏰</h4>

* Quantum chemistry functionality --- previously split between an external `pennylane-qchem` package and internal `qml.hf` differentiable Hartree-Fock solver --- is now unified into a single, included, `qml.qchem` module. [(2164)](https://github.com/PennyLaneAI/pennylane/pull/2164) [(#2385)](https://github.com/PennyLaneAI/pennylane/pull/2385) [(#2352)](https://github.com/PennyLaneAI/pennylane/pull/2352) [(#2420)](https://github.com/PennyLaneAI/pennylane/pull/2420) [(#2454)](https://github.com/PennyLaneAI/pennylane/pull/2454) [(#2199)](https://github.com/PennyLaneAI/pennylane/pull/2199) [(#2371)](https://github.com/PennyLaneAI/pennylane/pull/2371) [(#2272)](https://github.com/PennyLaneAI/pennylane/pull/2272) [(#2230)](https://github.com/PennyLaneAI/pennylane/pull/2230) [(#2415)](https://github.com/PennyLaneAI/pennylane/pull/2415) [(#2426)](https://github.com/PennyLaneAI/pennylane/pull/2426) [(#2465)](https://github.com/PennyLaneAI/pennylane/pull/2465)

The `qml.qchem` module provides a differentiable Hartree-Fock solver and the functionality to construct a fully-differentiable molecular Hamiltonian.

For example, one can continue to generate molecular Hamiltonians using `qml.qchem.molecular_hamiltonian`:

python
symbols = ["H", "H"]
geometry = np.array([[0., 0., -0.66140414], [0., 0., 0.66140414]])
hamiltonian, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, method="dhf")


By default, this will use the differentiable Hartree-Fock solver; however, simply set `method="pyscf"` to continue to use PySCF for Hartree-Fock calculations.

* Functions are added for building a differentiable dipole moment observable. Functions for computing multipole moment molecular integrals, needed for building the dipole moment observable, are also added. [(2173)](https://github.com/PennyLaneAI/pennylane/pull/2173) [(#2166)](https://github.com/PennyLaneAI/pennylane/pull/2166)

The dipole moment observable can be constructed using `qml.qchem.dipole_moment`:

python
symbols = ['H', 'H']
geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]])
mol = qml.qchem.Molecule(symbols, geometry)
args = [geometry]
D = qml.qchem.dipole_moment(mol)(*args)


* The efficiency of computing molecular integrals and Hamiltonian is improved. This has been done by adding optimized functions for building fermionic and qubit observables and optimizing the functions used for computing the electron repulsion integrals. [(2316)](https://github.com/PennyLaneAI/pennylane/pull/2316)


* The `6-31G` basis set is added to the qchem basis set repo. This addition allows performing differentiable Hartree-Fock calculations with basis sets beyond the minimal `sto-3g` basis set for atoms with atomic number 1-10. [(2372)](https://github.com/PennyLaneAI/pennylane/pull/2372)

The `6-31G` basis set can be used to construct a Hamiltonian as

python
symbols = ["H", "H"]
geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]])
H, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, basis="6-31g")


* External dependencies are replaced with local functions for spin and particle number observables. [(2197)](https://github.com/PennyLaneAI/pennylane/pull/2197) [(#2362)](https://github.com/PennyLaneAI/pennylane/pull/2362)

<h4>Pattern matching optimization πŸ”Ž πŸ’Ž </h4>

* Added an optimization transform that matches pieces of user-provided identity templates in a circuit and replaces them with an equivalent component. [(2032)](https://github.com/PennyLaneAI/pennylane/pull/2032)

For example, consider the following circuit where we want to replace sequence of two `pennylane.S` gates with a `pennylane.PauliZ` gate.

python
def circuit():
qml.S(wires=0)
qml.PauliZ(wires=0)
qml.S(wires=1)
qml.CZ(wires=[0, 1])
qml.S(wires=1)
qml.S(wires=2)
qml.CZ(wires=[1, 2])
qml.S(wires=2)
return qml.expval(qml.PauliX(wires=0))


We specify use the following pattern that implements the identity:

python
with qml.tape.QuantumTape() as pattern:
qml.S(wires=0)
qml.S(wires=0)
qml.PauliZ(wires=0)


To optimize the circuit with this identity pattern, we apply the `qml.transforms.pattern_matching` transform.

pycon
>>> dev = qml.device('default.qubit', wires=5)
>>> qnode = qml.QNode(circuit, dev)
>>> optimized_qfunc = qml.transforms.pattern_matching_optimization(pattern_tapes=[pattern])(circuit)
>>> optimized_qnode = qml.QNode(optimized_qfunc, dev)
>>> print(qml.draw(qnode)())
0: ──S──Z─╭C─────────── <X>
1: ──S────╰Z──S─╭C─────
2: ──S──────────╰Z──S──
>>> print(qml.draw(optimized_qnode)())
0: ──S⁻¹─╭C───── <X>
1: ──Z───╰Z─╭C──
2: ──Z──────╰Z──


For more details on using pattern matching optimization you can check the [corresponding documentation](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.pattern_matching_optimization.html) and also the following [paper](https://dl.acm.org/doi/full/10.1145/3498325).

<h4>Measure the distance between two unitariesπŸ“</h4>

* Added the `HilbertSchmidt` and the `LocalHilbertSchmidt` templates to be used for computing distance measures between unitaries. [(2364)](https://github.com/PennyLaneAI/pennylane/pull/2364)

Given a unitary `U`, `qml.HilberSchmidt` can be used to measure the distance between unitaries and to define a cost function (`cost_hst`) used for learning a unitary `V` that is equivalent to `U` up to a global phase:
python
Represents unitary U
with qml.tape.QuantumTape(do_queue=False) as u_tape:
qml.Hadamard(wires=0)

Represents unitary V
def v_function(params):
qml.RZ(params[0], wires=1)

qml.qnode(dev)
def hilbert_test(v_params, v_function, v_wires, u_tape):
qml.HilbertSchmidt(v_params, v_function=v_function, v_wires=v_wires, u_tape=u_tape)
return qml.probs(u_tape.wires + v_wires)

def cost_hst(parameters, v_function, v_wires, u_tape):
return (1 - hilbert_test(v_params=parameters, v_function=v_function, v_wires=v_wires, u_tape=u_tape)[0])

pycon
>>> cost_hst(parameters=[0.1], v_function=v_function, v_wires=[1], u_tape=u_tape)
tensor(0.999, requires_grad=True)

For more information refer to the [documentation of qml.HilbertSchmidt](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.HilbertSchmidt.html).

<h4>More tensor network support πŸ•ΈοΈ</h4>

* Adds the `qml.MERA` template for implementing quantum circuits with the shape of a multi-scale entanglement renormalization ansatz (MERA). [(2418)](https://github.com/PennyLaneAI/pennylane/pull/2418)

MERA follows the style of previous tensor network templates and is similar to [quantum convolutional neural networks](https://arxiv.org/abs/1810.03787).

python
def block(weights, wires):
qml.CNOT(wires=[wires[0],wires[1]])
qml.RY(weights[0], wires=wires[0])
qml.RY(weights[1], wires=wires[1])

n_wires = 4
n_block_wires = 2
n_params_block = 2
n_blocks = qml.MERA.get_n_blocks(range(n_wires),n_block_wires)
template_weights = [[0.1,-0.3]]*n_blocks

dev= qml.device('default.qubit',wires=range(n_wires))
qml.qnode(dev)
def circuit(template_weights):
qml.MERA(range(n_wires),n_block_wires,block, n_params_block, template_weights)
return qml.expval(qml.PauliZ(wires=1))

It may be necessary to reorder the wires to see the MERA architecture clearly:
pycon
>>> print(qml.draw(circuit,expansion_strategy='device',wire_order=[2,0,1,3])(template_weights))
2: ───────────────╭C──RY(0.10)──╭X──RY(-0.30)────────────────
0: ─╭X──RY(-0.30)─│─────────────╰C──RY(0.10)──╭C──RY(0.10)───
1: ─╰C──RY(0.10)──│─────────────╭X──RY(-0.30)─╰X──RY(-0.30)── <Z>
3: ───────────────╰X──RY(-0.30)─╰C──RY(0.10)─────────────────


<h4>New transform for transpilation βš™οΈ </h4>

* Added a swap based transpiler transform. [(2118)](https://github.com/PennyLaneAI/pennylane/pull/2118)

The transpile function takes a quantum function and a coupling map as inputs and compiles the circuit to ensure that it can be executed on corresponding hardware. The transform can be used as a decorator in the following way:

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

qml.qnode(dev)
qml.transforms.transpile(coupling_map=[(0, 1), (1, 2), (2, 3)])
def circuit(param):
qml.CNOT(wires=[0, 1])
qml.CNOT(wires=[0, 2])
qml.CNOT(wires=[0, 3])
qml.PhaseShift(param, wires=0)
return qml.probs(wires=[0, 1, 2, 3])

pycon
>>> print(qml.draw(circuit)(0.3))
0: ─╭C───────╭C──────────╭C──RΟ•(0.30)── β•­Probs
1: ─╰X─╭SWAP─╰X────╭SWAP─╰X──────────── β”œProbs
2: ────╰SWAP─╭SWAP─╰SWAP─────────────── β”œProbs
3: ──────────╰SWAP───────────────────── β•°Probs


<h3>Improvements</h3>

* `QuantumTape` objects are now iterable, allowing iteration over the contained operations and measurements. [(2342)](https://github.com/PennyLaneAI/pennylane/pull/2342)

python
with qml.tape.QuantumTape() as tape:
qml.RX(0.432, wires=0)
qml.RY(0.543, wires=0)
qml.CNOT(wires=[0, 'a'])
qml.RX(0.133, wires='a')
qml.expval(qml.PauliZ(wires=[0]))


Given a `QuantumTape` object the underlying quantum circuit can be iterated over using a `for` loop:

pycon
>>> for op in tape:
... print(op)
RX(0.432, wires=[0])
RY(0.543, wires=[0])
CNOT(wires=[0, 'a'])
RX(0.133, wires=['a'])
expval(PauliZ(wires=[0]))


Indexing into the circuit is also allowed via `tape[i]`:

pycon
>>> tape[0]
RX(0.432, wires=[0])


A tape object can also be converted to a sequence (e.g., to a `list`) of operations and measurements:

pycon
>>> list(tape)
[RX(0.432, wires=[0]),
RY(0.543, wires=[0]),
CNOT(wires=[0, 'a']),
RX(0.133, wires=['a']),
expval(PauliZ(wires=[0]))]


* Added the `QuantumTape.shape` method and `QuantumTape.numeric_type` attribute to allow extracting information about the shape and numeric type of the output returned by a quantum tape after execution. [(2044)](https://github.com/PennyLaneAI/pennylane/pull/2044)

python
dev = qml.device("default.qubit", wires=2)
a = np.array([0.1, 0.2, 0.3])

def func(a):
qml.RY(a[0], wires=0)
qml.RX(a[1], wires=0)
qml.RY(a[2], wires=0)

with qml.tape.QuantumTape() as tape:
func(a)
qml.state()

pycon
>>> tape.shape(dev)
(1, 4)
>>> tape.numeric_type
complex


* Defined a `MeasurementProcess.shape` method and a `MeasurementProcess.numeric_type` attribute to allow extracting information about the shape and numeric type of results obtained when evaluating QNodes using the specific measurement process. [(2044)](https://github.com/PennyLaneAI/pennylane/pull/2044)

* The parameter-shift Hessian can now be computed for arbitrary operations that support the general parameter-shift rule for gradients, using `qml.gradients.param_shift_hessian` [(2319)](https://github.com/XanaduAI/pennylane/pull/2319)

Multiple ways to obtain the gradient recipe are supported, in the following order of preference:

- A custom `grad_recipe`. It is iterated to obtain the shift rule for the second-order derivatives in the diagonal entries of the Hessian.

- Custom `parameter_frequencies`. The second-order shift rule can directly be computed using them.

- An operation's `generator`. Its eigenvalues will be used to obtain `parameter_frequencies`, if they are not given explicitly for an operation.

* The strategy for expanding a circuit can now be specified with the `qml.specs` transform, for example to calculate the specifications of the
circuit that will actually be executed by the device (`expansion_strategy="device"`). [(2395)](https://github.com/PennyLaneAI/pennylane/pull/2395)

* The `default.qubit` and `default.mixed` devices now skip over identity operators instead of performing matrix multiplication with the identity. [(2356)](https://github.com/PennyLaneAI/pennylane/pull/2356) [(#2365)](https://github.com/PennyLaneAI/pennylane/pull/2365)

* The function `qml.eigvals` is modified to use the efficient `scipy.sparse.linalg.eigsh` method for obtaining the eigenvalues of a `SparseHamiltonian`. This `scipy` method is called to compute :math:`k` eigenvalues of a sparse :math:`N \times N` matrix if `k` is smaller than :math:`N-1`. If a larger :math:`k` is requested, the dense matrix representation of the Hamiltonian is constructed and the regular `qml.math.linalg.eigvalsh` is applied. [(2333)](https://github.com/PennyLaneAI/pennylane/pull/2333)

* The function `qml.ctrl` was given the optional argument `control_values=None`. If overridden, `control_values` takes an integer or a list of integers corresponding to the binary value that each control value should take. The same change is reflected in `ControlledOperation`. Control values of `0` are implemented by `qml.PauliX` applied before and after the controlled operation [(2288)](https://github.com/PennyLaneAI/pennylane/pull/2288)

* Operators now have a `has_matrix` property denoting whether or not the operator defines a matrix. [(2331)](https://github.com/PennyLaneAI/pennylane/pull/2331) [(#2476)](https://github.com/PennyLaneAI/pennylane/pull/2476)

* Circuit cutting now performs expansion to search for wire cuts in contained operations or tapes. [(2340)](https://github.com/PennyLaneAI/pennylane/pull/2340)

* The `qml.draw` and `qml.draw_mpl` transforms are now located in the `drawer` module. They can still be accessed via the top-level `qml` namespace. [(2396)](https://github.com/PennyLaneAI/pennylane/pull/2396)

* Raise a warning where caching produces identical shot noise on execution results with finite shots. [(2478)](https://github.com/PennyLaneAI/pennylane/pull/2478)

<h3>Deprecations</h3>

* The `ObservableReturnTypes` `Sample`, `Variance`, `Expectation`, `Probability`, `State`, and `MidMeasure` have been moved to `measurements` from `operation`. [(2329)](https://github.com/PennyLaneAI/pennylane/pull/2329) [(#2481)](https://github.com/PennyLaneAI/pennylane/pull/2481)

<h3>Breaking changes</h3>

* The caching ability of devices has been removed. Using the caching on the QNode level is the recommended alternative going forward. [(2443)](https://github.com/PennyLaneAI/pennylane/pull/2443)

One way for replicating the removed `QubitDevice` caching behaviour is by creating a `cache` object (e.g., a dictionary) and passing it to the `QNode`:
python
n_wires = 4
wires = range(n_wires)

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

cache = {}

qml.qnode(dev, diff_method='parameter-shift', cache=cache)
def expval_circuit(params):
qml.templates.BasicEntanglerLayers(params, wires=wires, rotation=qml.RX)
return qml.expval(qml.PauliZ(0) qml.PauliY(1) qml.PauliX(2) qml.PauliZ(3))

shape = qml.templates.BasicEntanglerLayers.shape(5, n_wires)
params = np.random.random(shape)

pycon
>>> expval_circuit(params)
tensor(0.20598436, requires_grad=True)
>>> dev.num_executions
1
>>> expval_circuit(params)
tensor(0.20598436, requires_grad=True)
>>> dev.num_executions
1


* The `qml.finite_diff` function has been removed. Please use `qml.gradients.finite_diff` to compute the gradient of tapes of QNodes. Otherwise, manual implementation is required. [(2464)](https://github.com/PennyLaneAI/pennylane/pull/2464)

* The `get_unitary_matrix` transform has been removed, please use `qml.matrix` instead. [(2457)](https://github.com/PennyLaneAI/pennylane/pull/2457)

* The `update_stepsize` method has been removed from `GradientDescentOptimizer` and its child optimizers. The `stepsize` property can be interacted with directly instead. [(2370)](https://github.com/PennyLaneAI/pennylane/pull/2370)

* Most optimizers no longer flatten and unflatten arguments during computation. Due to this change, user provided gradient functions *must* return the same shape as `qml.grad`. [(2381)](https://github.com/PennyLaneAI/pennylane/pull/2381)

* The old circuit text drawing infrastructure has been removed. [(2310)](https://github.com/PennyLaneAI/pennylane/pull/2310)

- `RepresentationResolver` was replaced by the `Operator.label` method.
- `qml.drawer.CircuitDrawer` was replaced by `qml.drawer.tape_text`.
- `qml.drawer.CHARSETS` was removed because unicode is assumed to be accessible.
- `Grid` and `qml.drawer.drawable_grid` were removed because the custom data class was replaced by list of sets of operators or measurements.
- `qml.transforms.draw_old` was replaced by `qml.draw`.
- `qml.CircuitGraph.greedy_layers` was deleted, as it was no longer needed by the circuit drawer and did not seem to have uses outside of that situation.
- `qml.CircuitGraph.draw` was deleted, as we draw tapes instead.
- The tape method `qml.tape.QuantumTape.draw` now simply calls `qml.drawer.tape_text`.
- In the new pathway, the `charset` keyword was deleted, the `max_length` keyword defaults to `100`, and the `decimals` and `show_matrices` keywords were added.

* The deprecated QNode, available via `qml.qnode_old.QNode`, has been removed. Please transition to using the standard `qml.QNode`. [(2336)](https://github.com/PennyLaneAI/pennylane/pull/2336) [(#2337)](https://github.com/PennyLaneAI/pennylane/pull/2337) [(#2338)](https://github.com/PennyLaneAI/pennylane/pull/2338)

In addition, several other components which powered the deprecated QNode have been removed:

- The deprecated, non-batch compatible interfaces, have been removed.

- The deprecated tape subclasses `QubitParamShiftTape`, `JacobianTape`, `CVParamShiftTape`, and `ReversibleTape` have been removed.

* The deprecated tape execution method `tape.execute(device)` has been removed. Please use `qml.execute([tape], device)` instead. [(2339)](https://github.com/PennyLaneAI/pennylane/pull/2339)

<h3>Bug fixes</h3>

* Fixed a bug in the `qml.PauliRot` operation, where computing the generator was not taking into account the operation wires. [(2466)](https://github.com/PennyLaneAI/pennylane/pull/2466)

* Fixed a bug where non-trainable arguments were shifted in the `NesterovMomentumOptimizer` if a trainable argument was after it in the argument list. [(2466)](https://github.com/PennyLaneAI/pennylane/pull/2466)

* Fixed a bug with `jax.jit` for grad when `diff_method="adjoint"` and `mode="backward"`. [(2460)](https://github.com/PennyLaneAI/pennylane/pull/2460)

* Fixed a bug where `qml.DiagonalQubitUnitary` did not support `jax.jit` and `tf.function`. [(2445)](https://github.com/PennyLaneAI/pennylane/pull/2445)

* Fixed a bug in the `qml.PauliRot` operation, where computing the generator was not taking into account the operation wires. [(2442)](https://github.com/PennyLaneAI/pennylane/pull/2442)

* Fixed a bug with the padding capability of `AmplitudeEmbedding` where the inputs are on the GPU. [(2431)](https://github.com/PennyLaneAI/pennylane/pull/2431)

* Fixed a bug by adding a comprehensible error message for calling `qml.probs` without passing wires or an observable. [(2438)](https://github.com/PennyLaneAI/pennylane/pull/2438)

* The behaviour of `qml.about()` was modified to avoid warnings being emitted due to legacy behaviour of `pip`. [(2422)](https://github.com/PennyLaneAI/pennylane/pull/2422)

* Fixed a bug where observables were not considered when determining the use of the `jax-jit` interface. [(2427)](https://github.com/PennyLaneAI/pennylane/pull/2427) [(#2474)](https://github.com/PennyLaneAI/pennylane/pull/2474)

* Fixed a bug where computing statistics for a relatively few number of shots (e.g., `shots=10`), an error arose due to indexing into an array using a `DeviceArray`. [(2427)](https://github.com/PennyLaneAI/pennylane/pull/2427)

* PennyLane Lightning version in Docker container is pulled from latest wheel-builds. [(2416)](https://github.com/PennyLaneAI/pennylane/pull/2416)

* Optimizers only consider a variable trainable if they have `requires_grad = True`. [(2381)](https://github.com/PennyLaneAI/pennylane/pull/2381)

* Fixed a bug with `qml.expval`, `qml.var`, `qml.state` and `qml.probs` (when `qml.probs` is the only measurement) where the `dtype` specified on the device did not match the `dtype` of the QNode output. [(2367)](https://github.com/PennyLaneAI/pennylane/pull/2367)

* Fixed a bug where the output shapes from batch transforms are inconsistent with the QNode output shape. [(2215)](https://github.com/PennyLaneAI/pennylane/pull/2215)

* Fixed a bug caused by the squeezing in `qml.gradients.param_shift_hessian`. [(2215)](https://github.com/PennyLaneAI/pennylane/pull/2215)

* Fixed a bug in which the `expval`/`var` of a `Tensor(Observable)` would depend on the order in which the observable is defined: [(2276)](https://github.com/PennyLaneAI/pennylane/pull/2276)
pycon
>>> qml.qnode(dev)
... def circ(op):
... qml.RX(0.12, wires=0)
... qml.RX(1.34, wires=1)
... qml.RX(3.67, wires=2)
... return qml.expval(op)
>>> op1 = qml.Identity(wires=0) qml.Identity(wires=1) qml.PauliZ(wires=2)
>>> op2 = qml.PauliZ(wires=2) qml.Identity(wires=0) qml.Identity(wires=1)
>>> print(circ(op1), circ(op2))
-0.8636111153905662 -0.8636111153905662


* Fixed a bug where `qml.hf.transform_hf()` would fail due to missing wires in the qubit operator that is prepared for tapering the HF state. [(2441)](https://github.com/PennyLaneAI/pennylane/pull/2441)

* Fixed a bug with custom device defined jacobians not being returned properly. [(2485)](https://github.com/PennyLaneAI/pennylane/pull/2485)

<h3>Documentation</h3>

* The sections on adding operator and observable support in the "How to add a plugin" section of the plugins page have been updated. [(2389)](https://github.com/PennyLaneAI/pennylane/pull/2389)

* The missing arXiv reference in the `LieAlgebra` optimizer has been fixed. [(2325)](https://github.com/PennyLaneAI/pennylane/pull/2325)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Karim Alaa El-Din, Guillermo Alonso-Linaje, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Sam Banning, Thomas Bromley, Alain Delgado, Isaac De Vlugt, Olivia Di Matteo, Amintor Dusko, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Nathan Killoran, Christina Lee, Angus Lowe, Romain Moyard, Zeyue Niu, Matthew Silverman, Lee James O'Riordan, Maria Schuld, Jay Soni, Antal SzΓ‘va, Maurice Weber, David Wierichs.

0.22.2

<h3>Bug fixes</h3>

* Most compilation transforms, and relevant subroutines, have been updated to support just-in-time compilation with `jax.jit`. This fix was intended to be included in `v0.22.0`, but due to a bug was incomplete. [(2397)](https://github.com/PennyLaneAI/pennylane/pull/2397)

<h3>Documentation</h3>

* The documentation run has been updated to require `jinja2==3.0.3` due to an issue that arises with `jinja2` `v3.1.0` and `sphinx` `v3.5.3`. [(2378)](https://github.com/PennyLaneAI/pennylane/pull/2378)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Olivia Di Matteo, Christina Lee, Romain Moyard, Antal SzΓ‘va.

0.22.1

<h3>Bug fixes</h3>

* Fixes cases with `qml.measure` where unexpected operations were added to the circuit. [(2328)](https://github.com/PennyLaneAI/pennylane/pull/2328)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Guillermo Alonso-Linaje, Antal SzΓ‘va.

0.22.0

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

<h4>Quantum circuit cutting βœ‚οΈ</h4>

* You can now run `N`-wire circuits on devices with fewer than `N` wires, by strategically placing `WireCut` operations that allow their circuit to be partitioned into smaller fragments, at a cost of needing to perform a greater number of device executions. Circuit cutting is enabled by decorating a QNode with the `qml.cut_circuit` transform. [(2107)](https://github.com/PennyLaneAI/pennylane/pull/2107) [(#2124)](https://github.com/PennyLaneAI/pennylane/pull/2124) [(#2153)](https://github.com/PennyLaneAI/pennylane/pull/2153) [(#2165)](https://github.com/PennyLaneAI/pennylane/pull/2165) [(#2158)](https://github.com/PennyLaneAI/pennylane/pull/2158) [(#2169)](https://github.com/PennyLaneAI/pennylane/pull/2169) [(#2192)](https://github.com/PennyLaneAI/pennylane/pull/2192) [(#2216)](https://github.com/PennyLaneAI/pennylane/pull/2216) [(#2168)](https://github.com/PennyLaneAI/pennylane/pull/2168) [(#2223)](https://github.com/PennyLaneAI/pennylane/pull/2223) [(#2231)](https://github.com/PennyLaneAI/pennylane/pull/2231) [(#2234)](https://github.com/PennyLaneAI/pennylane/pull/2234) [(#2244)](https://github.com/PennyLaneAI/pennylane/pull/2244) [(#2251)](https://github.com/PennyLaneAI/pennylane/pull/2251) [(#2265)](https://github.com/PennyLaneAI/pennylane/pull/2265) [(#2254)](https://github.com/PennyLaneAI/pennylane/pull/2254) [(#2260)](https://github.com/PennyLaneAI/pennylane/pull/2260) [(#2257)](https://github.com/PennyLaneAI/pennylane/pull/2257) [(#2279)](https://github.com/PennyLaneAI/pennylane/pull/2279)

The example below shows how a three-wire circuit can be run on a two-wire device:

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

qml.cut_circuit
qml.qnode(dev)
def circuit(x):
qml.RX(x, wires=0)
qml.RY(0.9, wires=1)
qml.RX(0.3, wires=2)

qml.CZ(wires=[0, 1])
qml.RY(-0.4, wires=0)

qml.WireCut(wires=1)

qml.CZ(wires=[1, 2])

return qml.expval(qml.grouping.string_to_pauli_word("ZZZ"))


Instead of executing the circuit directly, it will be partitioned into smaller fragments according to the `WireCut` locations, and each fragment executed multiple times. Combining the results of the fragment executions will recover the expected output of the original uncut circuit.

pycon
>>> x = np.array(0.531, requires_grad=True)
>>> circuit(0.531)
0.47165198882111165


Circuit cutting support is also differentiable:

pycon
>>> qml.grad(circuit)(x)
-0.276982865449393


For more details on circuit cutting, check out the [qml.cut_circuit](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.cut_circuit.html) documentation page or [Peng et. al](https://arxiv.org/abs/1904.00102).

<h4>Conditional operations: quantum teleportation unlocked πŸ”“πŸŒ€</h4>

* Support for mid-circuit measurements and conditional operations has been added, to enable use cases like quantum teleportation, quantum error correction and quantum error mitigation. [(2211)](https://github.com/PennyLaneAI/pennylane/pull/2211) [(#2236)](https://github.com/PennyLaneAI/pennylane/pull/2236) [(#2275)](https://github.com/PennyLaneAI/pennylane/pull/2275)

Two new functions have been added to support this capability:

- `qml.measure()` places mid-circuit measurements in the middle of a quantum function.

- `qml.cond()` allows operations and quantum functions to be conditioned on the result of a previous measurement.

For example, the code below shows how to teleport a qubit from wire 0 to wire 2:

python
dev = qml.device("default.qubit", wires=3)
input_state = np.array([1, -1], requires_grad=False) / np.sqrt(2)

qml.qnode(dev)
def teleport(state):
Prepare input state
qml.QubitStateVector(state, wires=0)

Prepare Bell state
qml.Hadamard(wires=1)
qml.CNOT(wires=[1, 2])

Apply gates
qml.CNOT(wires=[0, 1])
qml.Hadamard(wires=0)

Measure first two wires
m1 = qml.measure(0)
m2 = qml.measure(1)

Condition final wire on results
qml.cond(m2 == 1, qml.PauliX)(wires=2)
qml.cond(m1 == 1, qml.PauliZ)(wires=2)

Return state on final wire
return qml.density_matrix(wires=2)


We can double-check that the qubit has been teleported by computing the overlap between the input state and the resulting state on wire 2:

pycon
>>> output_state = teleport(input_state)
>>> output_state
tensor([[ 0.5+0.j, -0.5+0.j],
[-0.5+0.j, 0.5+0.j]], requires_grad=True)
>>> input_state.conj() output_state input_state
tensor(1.+0.j, requires_grad=True)


For a full description of new capabilities, refer to the [Mid-circuit measurements and conditional operations](https://pennylane.readthedocs.io/en/latest/introduction/measurements.html#mid-circuit-measurements-and-conditional-operations) section in the documentation.

* Train mid-circuit measurements by deferring them, via the new `qml.defer_measurements` transform. [(2211)](https://github.com/PennyLaneAI/pennylane/pull/2211) [(#2236)](https://github.com/PennyLaneAI/pennylane/pull/2236) [(#2275)](https://github.com/PennyLaneAI/pennylane/pull/2275)

If a device doesn't natively support mid-circuit measurements, the `qml.defer_measurements` transform can be applied to the QNode to transform the QNode into one with _terminal_ measurements and _controlled_ operations:

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

qml.qnode(dev)
qml.defer_measurements
def circuit(x):
qml.Hadamard(wires=0)

m = qml.measure(0)

def op_if_true():
return qml.RX(x**2, wires=1)

def op_if_false():
return qml.RY(x, wires=1)

qml.cond(m==1, op_if_true, op_if_false)()

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


pycon
>>> x = np.array(0.7, requires_grad=True)
>>> print(qml.draw(circuit, expansion_strategy="device")(x))
0: ──H─╭C─────────X─╭C─────────X──
1: ────╰RX(0.49)────╰RY(0.70)───── <Z>
>>> circuit(x)
tensor(0.82358752, requires_grad=True)


Deferring mid-circuit measurements also enables differentiation:

pycon
>>> qml.grad(circuit)(x)
-0.651546965338656


<h4>Debug with mid-circuit quantum snapshots πŸ“·</h4>

* A new operation `qml.Snapshot` has been added to assist in debugging quantum functions. [(2233)](https://github.com/PennyLaneAI/pennylane/pull/2233) [(#2289)](https://github.com/PennyLaneAI/pennylane/pull/2289) [(#2291)](https://github.com/PennyLaneAI/pennylane/pull/2291) [(#2315)](https://github.com/PennyLaneAI/pennylane/pull/2315)

`qml.Snapshot` saves the internal state of devices at arbitrary points of execution.

Currently supported devices include:

- `default.qubit`: each snapshot saves the quantum state vector
- `default.mixed`: each snapshot saves the density matrix
- `default.gaussian`: each snapshot saves the covariance matrix and vector of means

During normal execution, the snapshots are ignored:

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

qml.qnode(dev, interface=None)
def circuit():
qml.Snapshot()
qml.Hadamard(wires=0)
qml.Snapshot("very_important_state")
qml.CNOT(wires=[0, 1])
qml.Snapshot()
return qml.expval(qml.PauliX(0))


However, when using the `qml.snapshots` transform, intermediate device states will be stored and returned alongside the results.

pycon
>>> qml.snapshots(circuit)()
{0: array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]),
'very_important_state': array([0.70710678+0.j, 0. +0.j, 0.70710678+0.j, 0. +0.j]),
2: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]),
'execution_results': array(0.)}


<h4>Batch embedding and state preparation data πŸ“¦</h4>

* Added the `qml.batch_input` transform to enable batching non-trainable gate parameters. In addition, the `qml.qnn.KerasLayer` class has been updated to natively support batched training data. [(2069)](https://github.com/PennyLaneAI/pennylane/pull/2069)

As with other transforms, `qml.batch_input` can be used to decorate QNodes:
python
dev = qml.device("default.qubit", wires=2, shots=None)

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


Batched input parameters can then be passed during QNode evaluation:

pycon
>>> x = tf.random.uniform((10, 2), 0, 1)
>>> w = tf.random.uniform((2,), 0, 1)
>>> circuit(x, w)
<tf.Tensor: shape=(10,), dtype=float64, numpy=
array([0.46230079, 0.73971315, 0.95666004, 0.5355225 , 0.66180948,
0.44519553, 0.93874261, 0.9483197 , 0.78737918, 0.90866411])>


<h4>Even more mighty quantum transforms πŸ›βž‘πŸ¦‹</h4>

* New functions and transforms of operators have been added:

- `qml.matrix()` for computing the matrix representation of one or more unitary operators. [(2241)](https://github.com/PennyLaneAI/pennylane/pull/2241)

- `qml.eigvals()` for computing the eigenvalues of one or more operators. [(2248)](https://github.com/PennyLaneAI/pennylane/pull/2248)

- `qml.generator()` for computing the generator of a single-parameter unitary operation. [(2256)](https://github.com/PennyLaneAI/pennylane/pull/2256)

All operator transforms can be used on instantiated operators,

pycon
>>> op = qml.RX(0.54, wires=0)
>>> qml.matrix(op)
[[0.9637709+0.j 0. -0.26673144j]
[0. -0.26673144j 0.9637709+0.j ]]


Operator transforms can also be used in a functional form:

pycon
>>> x = torch.tensor(0.6, requires_grad=True)
>>> matrix_fn = qml.matrix(qml.RX)
>>> matrix_fn(x, wires=[0])
tensor([[0.9553+0.0000j, 0.0000-0.2955j],
[0.0000-0.2955j, 0.9553+0.0000j]], grad_fn=<AddBackward0>)


In its functional form, it is fully differentiable with respect to gate arguments:

pycon
>>> loss = torch.real(torch.trace(matrix_fn(x, wires=0)))
>>> loss.backward()
>>> x.grad
tensor(-0.2955)


Some operator transform can also act on multiple operations, by passing quantum functions or tapes:

pycon
>>> def circuit(theta):
... qml.RX(theta, wires=1)
... qml.PauliZ(wires=0)
>>> qml.matrix(circuit)(np.pi / 4)
array([[ 0.92387953+0.j, 0.+0.j , 0.-0.38268343j, 0.+0.j],
[ 0.+0.j, -0.92387953+0.j, 0.+0.j, 0. +0.38268343j],
[ 0. -0.38268343j, 0.+0.j, 0.92387953+0.j, 0.+0.j],
[ 0.+0.j, 0.+0.38268343j, 0.+0.j, -0.92387953+0.j]])


* A new transform has been added to construct the pairwise-commutation directed acyclic graph (DAG) representation of a quantum circuit. [(1712)](https://github.com/PennyLaneAI/pennylane/pull/1712)

In the DAG, each node represents a quantum operation, and edges represent non-commutation between two operations.

This transform takes into account that not all operations can be moved next to each other by pairwise commutation:

pycon
>>> def circuit(x, y, z):
... qml.RX(x, wires=0)
... qml.RX(y, wires=0)
... qml.CNOT(wires=[1, 2])
... qml.RY(y, wires=1)
... qml.Hadamard(wires=2)
... qml.CRZ(z, wires=[2, 0])
... qml.RY(-y, wires=1)
... return qml.expval(qml.PauliZ(0))
>>> dag_fn = qml.commutation_dag(circuit)
>>> dag = dag_fn(np.pi / 4, np.pi / 3, np.pi / 2)


Nodes in the commutation DAG can be accessed via the `get_nodes()` method, returning a list of the form `(ID, CommutationDAGNode)`:

pycon
>>> nodes = dag.get_nodes()
>>> nodes
NodeDataView({0: <pennylane.transforms.commutation_dag.CommutationDAGNode object at 0x7f461c4bb580>, ...}, data='node')


Specific nodes in the commutation DAG can be accessed via the `get_node()` method:


>>> second_node = dag.get_node(2)
>>> second_node
<pennylane.transforms.commutation_dag.CommutationDAGNode object at 0x136f8c4c0>
>>> second_node.op
CNOT(wires=[1, 2])
>>> second_node.successors
[3, 4, 5, 6]
>>> second_node.predecessors
[]


<h3>Improvements</h3>

* The text-based drawer accessed via `qml.draw()` has been optimized and improved. [(2128)](https://github.com/PennyLaneAI/pennylane/pull/2128) [(#2198)](https://github.com/PennyLaneAI/pennylane/pull/2198)

The new drawer has:

* a `decimals` keyword for controlling parameter rounding
* a `show_matrices` keyword for controlling display of matrices
* a different algorithm for determining positions
* deprecation of the `charset` keyword
* additional minor cosmetic changes

python
qml.qnode(qml.device('lightning.qubit', wires=2))
def circuit(a, w):
qml.Hadamard(0)
qml.CRX(a, wires=[0, 1])
qml.Rot(*w, wires=[1])
qml.CRX(-a, wires=[0, 1])
return qml.expval(qml.PauliZ(0) qml.PauliZ(1))


pycon
>>> print(qml.draw(circuit, decimals=2)(a=2.3, w=[1.2, 3.2, 0.7]))
0: ──H─╭C─────────────────────────────╭C────────── β•­<ZZ>
1: ────╰RX(2.30)──Rot(1.20,3.20,0.70)─╰RX(-2.30)── β•°<ZZ>


* The frequencies of gate parameters are now accessible as an operation property and can be used for circuit analysis, optimization via the `RotosolveOptimizer` and differentiation with the parameter-shift rule (including the general shift rule). [(2180)](https://github.com/PennyLaneAI/pennylane/pull/2180) [(#2182)](https://github.com/PennyLaneAI/pennylane/pull/2182) [(#2227)](https://github.com/PennyLaneAI/pennylane/pull/2227)

pycon
>>> op = qml.CRot(0.4, 0.1, 0.3, wires=[0, 1])
>>> op.parameter_frequencies
[(0.5, 1.0), (0.5, 1.0), (0.5, 1.0)]


When using `qml.gradients.param_shift`, either a custom `grad_recipe` or the parameter frequencies are used to obtain the shift rule for the operation, in that order of preference.

See [Vidal and Theis (2018)](https://arxiv.org/abs/1812.06323) and [Wierichs et al. (2021)](https://arxiv.org/abs/2107.12390) for theoretical background information on the general parameter-shift rule.

* No two-term parameter-shift rule is assumed anymore by default. [(2227)](https://github.com/PennyLaneAI/pennylane/pull/2227)

Previously, operations marked for analytic differentiation that did not provide a `generator`, `parameter_frequencies` or a custom `grad_recipe` were assumed to satisfy the two-term shift rule. This now has to be made explicit for custom operations by adding any of the above attributes.

* Most compilation transforms, and relevant subroutines, have been updated to support just-in-time compilation with `jax.jit`. [(1894)](https://github.com/PennyLaneAI/pennylane/pull/1894/)

* The `qml.draw_mpl` transform supports a `expansion_strategy` keyword argument. [(2271)](https://github.com/PennyLaneAI/pennylane/pull/2271/)

* The `qml.gradients` module has been streamlined and special-purpose functions moved closer to their use cases, while preserving existing behaviour. [(2200)](https://github.com/PennyLaneAI/pennylane/pull/2200)

* Added a new `partition_pauli_group` function to the `grouping` module for efficiently measuring the `N`-qubit Pauli group with `3 ** N` qubit-wise commuting terms. [(2185)](https://github.com/PennyLaneAI/pennylane/pull/2185)

* The Operator class has undergone a major refactor with the following changes:

* **Matrices**: the static method `Operator.compute_matrices()` defines the matrix representation of the operator, and the function `qml.matrix(op)` computes this for a given instance. [(1996)](https://github.com/PennyLaneAI/pennylane/pull/1996)

* **Eigvals**: the static method `Operator.compute_eigvals()` defines the matrix representation of the operator, and the function `qml.eigvals(op)` computes this for a given instance. [(2048)](https://github.com/PennyLaneAI/pennylane/pull/2048)

* **Decompositions**: the static method `Operator.compute_decomposition()` defines the matrix representation of the operator, and the method `op.decomposition()` computes this for a given instance. [(2024)](https://github.com/PennyLaneAI/pennylane/pull/2024) [(#2053)](https://github.com/PennyLaneAI/pennylane/pull/2053)

* **Sparse matrices**: the static method `Operator.compute_sparse_matrix()` defines the sparse matrix representation of the operator, and the method `op.sparse_matrix()` computes this for a given instance. [(2050)](https://github.com/PennyLaneAI/pennylane/pull/2050)

* **Linear combinations of operators**: The static method `compute_terms()`, used for representing the linear combination of coefficients and operators representing the operator, has been added. The method `op.terms()` computes this for a given instance. Currently, only the `Hamiltonian` class overwrites `compute_terms()` to store coefficients and operators. The `Hamiltonian.terms` property hence becomes a proper method called by `Hamiltonian.terms()`. [(2036)](https://github.com/PennyLaneAI/pennylane/pull/2036)

* **Diagonalization**: The `diagonalizing_gates()` representation has been moved to the highest-level `Operator` class and is therefore available to all subclasses. A condition `qml.operation.defines_diagonalizing_gates` has been added, which can be used in tape contexts without queueing. In addition, a static `compute_diagonalizing_gates` method has been added, which is called by default in `diagonalizing_gates()`. [(1985)](https://github.com/PennyLaneAI/pennylane/pull/1985) [(#1993)](https://github.com/PennyLaneAI/pennylane/pull/1993)

* Error handling has been improved for Operator representations. Custom errors subclassing `OperatorPropertyUndefined` are raised if a representation has not been defined. This replaces the `NotImplementedError` and allows finer control for developers. [(2064)](https://github.com/PennyLaneAI/pennylane/pull/2064) [(#2287)](https://github.com/PennyLaneAI/pennylane/pull/2287/)

* A `Operator.hyperparameters` attribute, used for storing operation parameters that are *never* trainable, has been added to the operator class. [(2017)](https://github.com/PennyLaneAI/pennylane/pull/2017)

* The `string_for_inverse` attribute is removed. [(2021)](https://github.com/PennyLaneAI/pennylane/pull/2021)

* The `expand()` method was moved from the `Operation` class to the main `Operator` class. [(2053)](https://github.com/PennyLaneAI/pennylane/pull/2053) [(#2239)](https://github.com/PennyLaneAI/pennylane/pull/2239)

<h3>Deprecations</h3>

* There are several important changes when creating custom operations: [(2214)](https://github.com/PennyLaneAI/pennylane/pull/2214) [(#2227)](https://github.com/PennyLaneAI/pennylane/pull/2227) [(#2030)](https://github.com/PennyLaneAI/pennylane/pull/2030) [(#2061)](https://github.com/PennyLaneAI/pennylane/pull/2061)

- The `Operator.matrix` method has been deprecated and `Operator.compute_matrix` should be defined instead. Operator matrices should be accessed using `qml.matrix(op)`. If you were previously defining the class method `Operator._matrix()`, this is a a **breaking change** --- please update your operation to instead overwrite `Operator.compute_matrix`.

- The `Operator.decomposition` method has been deprecated and `Operator.compute_decomposition` should be defined instead. Operator decompositions should be accessed using `Operator.decomposition()`.

- The `Operator.eigvals` method has been deprecated and `Operator.compute_eigvals` should be defined instead. Operator eigenvalues should be accessed using `qml.eigvals(op)`.

- The `Operator.generator` property is now a method, and should return an *operator instance* representing the generator. Note that unlike the other representations above, this is a **breaking change**. Operator generators should be accessed using `qml.generator(op)`.

- The `Operation.get_parameter_shift` method has been deprecated and will be removed in a future release.

Instead, the functionalities for general parameter-shift rules in the `qml.gradients` module should be used, together with the operation attributes `parameter_frequencies` or `grad_recipe`.

* Executing tapes using `tape.execute(dev)` is deprecated. Please use the `qml.execute([tape], dev)` function instead. [(2306)](https://github.com/PennyLaneAI/pennylane/pull/2306)

* The subclasses of the quantum tape, including `JacobianTape`, `QubitParamShiftTape`, `CVParamShiftTape`, and `ReversibleTape` are deprecated. Instead of calling `JacobianTape.jacobian()` and `JacobianTape.hessian()`, please use a standard `QuantumTape`, and apply gradient transforms using the `qml.gradients` module. [(2306)](https://github.com/PennyLaneAI/pennylane/pull/2306)

* `qml.transforms.get_unitary_matrix()` has been deprecated and will be removed in a future release. For extracting matrices of operations and quantum functions, please use `qml.matrix()`. [(2248)](https://github.com/PennyLaneAI/pennylane/pull/2248)

* The `qml.finite_diff()` function has been deprecated and will be removed in an upcoming release. Instead, `qml.gradients.finite_diff()` can be used to compute purely quantum gradients (that is, gradients of tapes or QNode). [(2212)](https://github.com/PennyLaneAI/pennylane/pull/2212)

* The `MultiControlledX` operation now accepts a single `wires` keyword argument for both `control_wires` and `wires`. The single `wires` keyword should be all the control wires followed by a single target wire. [(2121)](https://github.com/PennyLaneAI/pennylane/pull/2121) [(#2278)](https://github.com/PennyLaneAI/pennylane/pull/2278)

<h3>Breaking changes</h3>

* The representation of an operator as a matrix has been overhauled. [(1996)](https://github.com/PennyLaneAI/pennylane/pull/1996)

The "canonical matrix", which is independent of wires, is now defined in the static method `compute_matrix()` instead of `_matrix`. By default, this method is assumed to take all parameters and non-trainable hyperparameters that define the operation.

pycon
>>> qml.RX.compute_matrix(0.5)
[[0.96891242+0.j 0. -0.24740396j]
[0. -0.24740396j 0.96891242+0.j ]]


If no canonical matrix is specified for a gate, `compute_matrix()` raises a `MatrixUndefinedError`.

* The generator property has been updated to an instance method, `Operator.generator()`. It now returns an instantiated operation, representing the generator of the instantiated operator. [(2030)](https://github.com/PennyLaneAI/pennylane/pull/2030) [(#2061)](https://github.com/PennyLaneAI/pennylane/pull/2061)

Various operators have been updated to specify the generator as either an `Observable`, `Tensor`, `Hamiltonian`, `SparseHamiltonian`, or `Hermitian` operator.

In addition, `qml.generator(operation)` has been added to aid in retrieving generator representations of operators.

* The argument `wires` in `heisenberg_obs`, `heisenberg_expand` and `heisenberg_tr` was renamed to `wire_order` to be consistent with other matrix representations. [(2051)](https://github.com/PennyLaneAI/pennylane/pull/2051)

* The property `kraus_matrices` has been changed to a method, and `_kraus_matrices` renamed to `compute_kraus_matrices`, which is now a static method. [(2055)](https://github.com/PennyLaneAI/pennylane/pull/2055)

* The `pennylane.measure` module has been renamed to `pennylane.measurements`. [(2236)](https://github.com/PennyLaneAI/pennylane/pull/2236)

<h3>Bug fixes</h3>

* The `basis` property of `qml.SWAP` was set to `"X"`, which is incorrect; it is now set to `None`. [(2287)](https://github.com/PennyLaneAI/pennylane/pull/2287/)

* The `qml.RandomLayers` template now decomposes when the weights are a list of lists. [(2266)](https://github.com/PennyLaneAI/pennylane/pull/2266/)

* The `qml.QubitUnitary` operation now supports just-in-time compilation using JAX. [(2249)](https://github.com/PennyLaneAI/pennylane/pull/2249)

* Fixes a bug in the JAX interface where `DeviceArray` objects were not being converted to NumPy arrays before executing an external device. [(2255)](https://github.com/PennyLaneAI/pennylane/pull/2255)

* The `qml.ctrl` transform now works correctly with gradient transforms such as the parameter-shift rule. [(2238)](https://github.com/PennyLaneAI/pennylane/pull/2238)

* Fixes a bug in which passing required arguments into operations as keyword arguments would throw an error because the documented call signature didn't match the function definition. [(1976)](https://github.com/PennyLaneAI/pennylane/pull/1976)

* The operation `OrbitalRotation` previously was wrongfully registered to satisfy the four-term parameter shift rule. The correct eight-term rule will now be used when using the parameter-shift rule. [(2180)](https://github.com/PennyLaneAI/pennylane/pull/2180)

* Fixes a bug where `qml.gradients.param_shift_hessian` would produce an error whenever all elements of the Hessian are known in advance to be 0. [(2299)](https://github.com/PennyLaneAI/pennylane/pull/2299)

<h3>Documentation</h3>

* The developer guide on adding templates and the architecture overview were rewritten to reflect the past and planned changes of the operator refactor. [(2066)](https://github.com/PennyLaneAI/pennylane/pull/2066)

* Links to the Strawberry Fields documentation for information on the CV model. [(2259)](https://github.com/PennyLaneAI/pennylane/pull/2259)

* Fixes the documentation example for `qml.QFT`. [(2232)](https://github.com/PennyLaneAI/pennylane/pull/2232)

* Fixes the documentation example for using `qml.sample` with `jax.jit`. [(2196)](https://github.com/PennyLaneAI/pennylane/pull/2196)

* The `qml.numpy` subpackage is now included in the PennyLane API documentation. [(2179)](https://github.com/PennyLaneAI/pennylane/pull/2179)

* Improves the documentation of `RotosolveOptimizer` regarding the usage of the passed `substep_optimizer` and its keyword arguments. [(2160)](https://github.com/PennyLaneAI/pennylane/pull/2160)

* Ensures that signatures of `qml.qfunc_transform` decorated functions display correctly in the docs. [(2286)](https://github.com/PennyLaneAI/pennylane/pull/2286)

* Docstring examples now display using the updated text-based circuit drawer. [(2252)](https://github.com/PennyLaneAI/pennylane/pull/2252)

* Add docstring to `OrbitalRotation.grad_recipe`. [(2193)](https://github.com/PennyLaneAI/pennylane/pull/2193)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Catalina Albornoz, Jack Y. Araz, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Sam Banning, Thomas Bromley, Olivia Di Matteo, Christian Gogolin, Diego Guala, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Nathan Killoran, Christina Lee, Angus Lowe, Maria Fernanda Morris, Romain Moyard, Zeyue Niu, Lee James O'Riordan, Chae-Yeun Park, Maria Schuld, Jay Soni, Antal SzΓ‘va, David Wierichs.

0.21.0

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

<h4>Reduce qubit requirements of simulating Hamiltonians βš›οΈ</h4>

* Functions for tapering qubits based on molecular symmetries have been added, following results from [Setia et al](https://arxiv.org/abs/1910.14644). [(#1966)](https://github.com/PennyLaneAI/pennylane/pull/1966) [(#1974)](https://github.com/PennyLaneAI/pennylane/pull/1974) [(#2041)](https://github.com/PennyLaneAI/pennylane/pull/2041) [(#2042)](https://github.com/PennyLaneAI/pennylane/pull/2042)

With this functionality, a molecular Hamiltonian and the corresponding Hartree-Fock (HF) state can be transformed to a new Hamiltonian and HF state that acts on a reduced number of qubits, respectively.
python
molecular geometry
symbols = ["He", "H"]
geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.4588684632]])
mol = qml.hf.Molecule(symbols, geometry, charge=1)

generate the qubit Hamiltonian
H = qml.hf.generate_hamiltonian(mol)(geometry)

determine Hamiltonian symmetries
generators, paulix_ops = qml.hf.generate_symmetries(H, len(H.wires))
opt_sector = qml.hf.optimal_sector(H, generators, mol.n_electrons)

taper the Hamiltonian
H_tapered = qml.hf.transform_hamiltonian(H, generators, paulix_ops, opt_sector)


We can compare the number of qubits required by the original Hamiltonian and the tapered Hamiltonian:

pycon
>>> len(H.wires)
4
>>> len(H_tapered.wires)
2


For quantum chemistry algorithms, the Hartree-Fock state can also be tapered:

python
n_elec = mol.n_electrons
n_qubits = mol.n_orbitals * 2

hf_tapered = qml.hf.transform_hf(
generators, paulix_ops, opt_sector, n_elec, n_qubits
)

pycon
>>> hf_tapered
tensor([1, 1], requires_grad=True)


<h4>New tensor network templates πŸͺ’</h4>

* Quantum circuits with the shape of a matrix product state tensor network can now be easily implemented using the new `qml.MPS` template, based on the work [arXiv:1803.11537](https://arxiv.org/abs/1803.11537). [(#1871)](https://github.com/PennyLaneAI/pennylane/pull/1871)

python
def block(weights, wires):
qml.CNOT(wires=[wires[0], wires[1]])
qml.RY(weights[0], wires=wires[0])
qml.RY(weights[1], wires=wires[1])

n_wires = 4
n_block_wires = 2
n_params_block = 2
template_weights = np.array([[0.1, -0.3], [0.4, 0.2], [-0.15, 0.5]], requires_grad=True)

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

qml.qnode(dev)
def circuit(weights):
qml.MPS(range(n_wires), n_block_wires, block, n_params_block, weights)
return qml.expval(qml.PauliZ(wires=n_wires - 1))


The resulting circuit is:
pycon
>>> print(qml.draw(circuit, expansion_strategy="device")(template_weights))
0: ──╭C──RY(0.1)────────────────────────────────
1: ──╰X──RY(-0.3)──╭C──RY(0.4)──────────────────
2: ────────────────╰X──RY(0.2)──╭C──RY(-0.15)───
3: ─────────────────────────────╰X──RY(0.5)───── ⟨Z⟩


* Added a template for tree tensor networks, `qml.TTN`. [(2043)](https://github.com/PennyLaneAI/pennylane/pull/2043)
python
def block(weights, wires):
qml.CNOT(wires=[wires[0], wires[1]])
qml.RY(weights[0], wires=wires[0])
qml.RY(weights[1], wires=wires[1])

n_wires = 4
n_block_wires = 2
n_params_block = 2
n_blocks = qml.MPS.get_n_blocks(range(n_wires), n_block_wires)
template_weights = [[0.1, -0.3]] * n_blocks

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

qml.qnode(dev)
def circuit(template_weights):
qml.TTN(range(n_wires), n_block_wires, block, n_params_block, template_weights)
return qml.expval(qml.PauliZ(wires=n_wires - 1))

The resulting circuit is:
pycon
>>> print(qml.draw(circuit, expansion_strategy="device")(template_weights))
0: ──╭C──RY(0.1)──────────────────
1: ──╰X──RY(-0.3)──╭C──RY(0.1)────
2: ──╭C──RY(0.1)───│──────────────
3: ──╰X──RY(-0.3)──╰X──RY(-0.3)─── ⟨Z⟩


<h4>Generalized RotosolveOptmizer πŸ“‰</h4>

* The `RotosolveOptimizer` has been generalized to arbitrary frequency spectra in the cost function. Also note the changes in behaviour listed under *Breaking changes*. [(2081)](https://github.com/PennyLaneAI/pennylane/pull/2081)

Previously, the RotosolveOptimizer only supported variational circuits using special gates such as single-qubit Pauli rotations. Now, circuits with arbitrary gates are supported natively without decomposition, as long as the frequencies of the gate parameters are known. This new generalization extends the Rotosolve optimization method to a larger class of circuits, and can reduce the cost of the optimization compared to decomposing all gates to single-qubit rotations.

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

qml.qnode(dev)
def qnode(x, Y):
qml.RX(2.5 * x, wires=0)
qml.CNOT(wires=[0, 1])
qml.RZ(0.3 * Y[0], wires=0)
qml.CRY(1.1 * Y[1], wires=[1, 0])
return qml.expval(qml.PauliX(0) qml.PauliZ(1))

x = np.array(0.8, requires_grad=True)
Y = np.array([-0.2, 1.5], requires_grad=True)


Its frequency spectra can be easily obtained via `qml.fourier.qnode_spectrum`:
pycon
>>> spectra = qml.fourier.qnode_spectrum(qnode)(x, Y)
>>> spectra
{'x': {(): [-2.5, 0.0, 2.5]},
'Y': {(0,): [-0.3, 0.0, 0.3], (1,): [-1.1, -0.55, 0.0, 0.55, 1.1]}}


We may then initialize the `RotosolveOptimizer` and minimize the QNode cost function by providing this information about the frequency spectra. We also compare the cost at each step to the initial cost.
pycon
>>> cost_init = qnode(x, Y)
>>> opt = qml.RotosolveOptimizer()
>>> for _ in range(2):
... x, Y = opt.step(qnode, x, Y, spectra=spectra)
... print(f"New cost: {np.round(qnode(x, Y), 3)}; Initial cost: {np.round(cost_init, 3)}")
New cost: 0.0; Initial cost: 0.706
New cost: -1.0; Initial cost: 0.706


The optimization with `RotosolveOptimizer` is performed in substeps. The minimal cost of these substeps can be retrieved by setting `full_output=True`.
pycon
>>> x = np.array(0.8, requires_grad=True)
>>> Y = np.array([-0.2, 1.5], requires_grad=True)
>>> opt = qml.RotosolveOptimizer()
>>> for _ in range(2):
... (x, Y), history = opt.step(qnode, x, Y, spectra=spectra, full_output=True)
... print(f"New cost: {np.round(qnode(x, Y), 3)} reached via substeps {np.round(history, 3)}")
New cost: 0.0 reached via substeps [-0. 0. 0.]
New cost: -1.0 reached via substeps [-1. -1. -1.]

However, note that these intermediate minimal values are evaluations of the *reconstructions* that Rotosolve creates and uses internally for the optimization, and not of the original objective function. For noisy cost functions, these intermediate evaluations may differ significantly from evaluations of the original cost function.

<h4>Improved JAX support πŸ’»</h4>

* The JAX interface now supports evaluating vector-valued QNodes. [(2110)](https://github.com/PennyLaneAI/pennylane/pull/2110)

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

Consider a QNode that returns basis-state probabilities:
python
dev = qml.device('default.qubit', wires=2)
x = jnp.array(0.543)
y = jnp.array(-0.654)

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

The QNode can be evaluated and its jacobian can be computed:
pycon
>>> circuit(x, y)
DeviceArray([0.8397495 , 0.16025047], dtype=float32)
>>> jax.jacobian(circuit, argnums=[0, 1])(x, y)
(DeviceArray([-0.2050439, 0.2050439], dtype=float32, weak_type=True),
DeviceArray([ 0.26043, -0.26043], dtype=float32, weak_type=True))

Note that `jax.jit` is not yet supported for vector-valued QNodes.

<h4>Speedier quantum natural gradient ⚑</h4>

* A new function for computing the metric tensor on simulators, `qml.adjoint_metric_tensor`, has been added, that uses classically efficient methods to massively improve performance. [(1992)](https://github.com/PennyLaneAI/pennylane/pull/1992)

This method, detailed in [Jones (2020)](https://arxiv.org/abs/2011.02991), computes the metric tensor using four copies of the state vector and a number of operations that scales quadratically in the number of trainable parameters.

Note that as it makes use of state cloning, it is inherently classical and can only be used with statevector simulators and `shots=None`.

It is particularly useful for larger circuits for which backpropagation requires inconvenient or even unfeasible amounts of storage, but is slower. Furthermore, the adjoint method is only available for analytic computation, not for measurements simulation with `shots!=None`.

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

qml.qnode(dev)
def circuit(x, y):
qml.Rot(*x[0], wires=0)
qml.Rot(*x[1], wires=1)
qml.Rot(*x[2], wires=2)
qml.CNOT(wires=[0, 1])
qml.CNOT(wires=[1, 2])
qml.CNOT(wires=[2, 0])
qml.RY(y[0], wires=0)
qml.RY(y[1], wires=1)
qml.RY(y[0], wires=2)
return qml.expval(qml.PauliZ(0) qml.PauliZ(1)), qml.expval(qml.PauliY(1))

x = np.array([[0.2, 0.4, -0.1], [-2.1, 0.5, -0.2], [0.1, 0.7, -0.6]], requires_grad=False)
y = np.array([1.3, 0.2], requires_grad=True)


pycon
>>> qml.adjoint_metric_tensor(circuit)(x, y)
tensor([[ 0.25495723, -0.07086695],
[-0.07086695, 0.24945606]], requires_grad=True)


Computational cost

The adjoint method uses :math:`2P^2+4P+1` gates and state cloning operations if the circuit is composed only of trainable gates, where :math:`P` is the number of trainable operations. If non-trainable gates are included, each of them is applied about :math:`n^2-n` times, where :math:`n` is the number of trainable operations that follow after the respective non-trainable operation in the circuit. This means that non-trainable gates later in the circuit are executed less often, making the adjoint method a bit cheaper if such gates appear later. The adjoint method requires memory for 4 independent state vectors, which corresponds roughly
to storing a state vector of a system with 2 additional qubits.

<h4>Compute the Hessian on hardware ⬆️</h4>

* A new gradient transform `qml.gradients.param_shift_hessian` has been added to directly compute the Hessian (2nd order partial derivative matrix) of
QNodes on hardware.
[(1884)](https://github.com/PennyLaneAI/pennylane/pull/1884)

The function generates parameter-shifted tapes which allow the Hessian to be computed analytically on hardware and software devices. Compared to using an auto-differentiation framework to compute the Hessian via parameter shifts, this function will use fewer device invocations and can be used to inspect the parameter-shifted "Hessian tapes" directly. The function remains fully differentiable on all supported PennyLane interfaces.

Additionally, the parameter-shift Hessian comes with a new batch transform decorator `qml.gradients.hessian_transform`, which can be used to create custom Hessian functions.

The following code demonstrates how to use the parameter-shift Hessian:

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

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

x = np.array([0.1, 0.2], requires_grad=True)

hessian = qml.gradients.param_shift_hessian(circuit)(x)

pycon
>>> hessian
tensor([[-0.97517033, 0.01983384],
[ 0.01983384, -0.97517033]], requires_grad=True)


<h3>Improvements</h3>

* The `qml.transforms.insert` transform now supports adding operation after or before certain specific gates. [(1980)](https://github.com/PennyLaneAI/pennylane/pull/1980)

* Added a modified version of the `simplify` function to the `hf` module. [(2103)](https://github.com/PennyLaneAI/pennylane/pull/2103)

This function combines redundant terms in a Hamiltonian and eliminates terms with a coefficient smaller than a cutoff value. The new function makes construction of molecular Hamiltonians more efficient. For LiH, as an example, the time to construct the Hamiltonian is reduced roughly by a factor of 20.

* The QAOA module now accepts both NetworkX and RetworkX graphs as function inputs. [(1791)](https://github.com/PennyLaneAI/pennylane/pull/1791)

* The `CircuitGraph`, used to represent circuits via directed acyclic graphs, now uses RetworkX for its internal representation. This results in significant speedup for algorithms that rely on a directed acyclic graph representation. [(1791)](https://github.com/PennyLaneAI/pennylane/pull/1791)

* For subclasses of `Operator` where the number of parameters is known before instantiation, the `num_params` is reverted back to being a static property. This allows to programmatically know the number of parameters before an operator is instantiated without changing the user interface. A test was added to ensure that different ways of defining `num_params` work as expected. [(2101)](https://github.com/PennyLaneAI/pennylane/pull/2101) [(#2135)](https://github.com/PennyLaneAI/pennylane/pull/2135)

* A `WireCut` operator has been added for manual wire cut placement when constructing a QNode. [(2093)](https://github.com/PennyLaneAI/pennylane/pull/2093)

* The new function `qml.drawer.tape_text` produces a string drawing of a tape. This function differs in implementation and minor stylistic details from the old string circuit drawing infrastructure. [(1885)](https://github.com/PennyLaneAI/pennylane/pull/1885)

* The `RotosolveOptimizer` now raises an error if no trainable arguments are detected, instead of silently skipping update steps for all arguments. [(2109)](https://github.com/PennyLaneAI/pennylane/pull/2109)

* The function `qml.math.safe_squeeze` is introduced and `gradient_transform` allows for QNode argument axes of size `1`. [(2080)](https://github.com/PennyLaneAI/pennylane/pull/2080)

`qml.math.safe_squeeze` wraps `qml.math.squeeze`, with slight modifications:

- When provided the `axis` keyword argument, axes that do not have size `1` will be ignored, instead of raising an error.

- The keyword argument `exclude_axis` allows to explicitly exclude axes from the squeezing.

* The `adjoint` transform now raises and error whenever the object it is applied to is not callable. [(2060)](https://github.com/PennyLaneAI/pennylane/pull/2060)

An example is a list of operations to which one might apply `qml.adjoint`:

python
dev = qml.device("default.qubit", wires=2)
qml.qnode(dev)
def circuit_wrong(params):
Note the difference: v v
qml.adjoint(qml.templates.AngleEmbedding(params, wires=dev.wires))
return qml.state()

qml.qnode(dev)
def circuit_correct(params):
Note the difference: v v
qml.adjoint(qml.templates.AngleEmbedding)(params, wires=dev.wires)
return qml.state()

params = list(range(1, 3))


Evaluating `circuit_wrong(params)` now raises a `ValueError` and if we apply `qml.adjoint` correctly, we get

pycon
>>> circuit_correct(params)
[ 0.47415988+0.j 0. 0.73846026j 0. 0.25903472j
-0.40342268+0.j ]


* A precision argument has been added to the tape's ``to_openqasm`` function to control the precision of parameters. [(2071)](https://github.com/PennyLaneAI/pennylane/pull/2071)

* Interferometer now has a `shape` method. [(1946)](https://github.com/PennyLaneAI/pennylane/pull/1946)

* The Barrier and Identity operations now support the `adjoint` method. [(2062)](https://github.com/PennyLaneAI/pennylane/pull/2062) [(#2063)](https://github.com/PennyLaneAI/pennylane/pull/2063)

* `qml.BasisStatePreparation` now supports the `batch_params` decorator. [(2091)](https://github.com/PennyLaneAI/pennylane/pull/2091)

* Added a new `multi_dispatch` decorator that helps ease the definition of new functions inside PennyLane. The decorator is used throughout the math module, demonstrating use cases. [(2082)](https://github.com/PennyLaneAI/pennylane/pull/2084) [(#2096)](https://github.com/PennyLaneAI/pennylane/pull/2096)

We can decorate a function, indicating the arguments that are tensors handled by the interface:

pycon
>>> qml.math.multi_dispatch(argnum=[0, 1])
... def some_function(tensor1, tensor2, option, like):
... the interface string is stored in ``like``.
... ...


Previously, this was done using the private utility function `_multi_dispatch`.

pycon
>>> def some_function(tensor1, tensor2, option):
... interface = qml.math._multi_dispatch([tensor1, tensor2])
... ...


* The `IsingZZ` gate was added to the `diagonal_in_z_basis` attribute. For this an explicit `_eigvals` method was added. [(2113)](https://github.com/PennyLaneAI/pennylane/pull/2113)

* The `IsingXX`, `IsingYY` and `IsingZZ` gates were added to the `composable_rotations` attribute. [(2113)](https://github.com/PennyLaneAI/pennylane/pull/2113)

<h3>Breaking changes</h3>

* QNode arguments will no longer be considered trainable by default when using the Autograd interface. In order to obtain derivatives with respect to a parameter, it should be instantiated via PennyLane's NumPy wrapper using the `requires_grad=True` attribute. The previous behaviour was deprecated in version v0.19.0 of PennyLane. [(2116)](https://github.com/PennyLaneAI/pennylane/pull/2116) [(#2125)](https://github.com/PennyLaneAI/pennylane/pull/2125) [(#2139)](https://github.com/PennyLaneAI/pennylane/pull/2139) [(#2148)](https://github.com/PennyLaneAI/pennylane/pull/2148) [(#2156)](https://github.com/PennyLaneAI/pennylane/pull/2156)

python
from pennylane import numpy as np

qml.qnode(qml.device("default.qubit", wires=2))
def circuit(x):
...

x = np.array([0.1, 0.2], requires_grad=True)
qml.grad(circuit)(x)


For the `qml.grad` and `qml.jacobian` functions, trainability can alternatively be indicated via the `argnum` keyword:

python
import numpy as np

qml.qnode(qml.device("default.qubit", wires=2))
def circuit(hyperparam, param):
...

x = np.array([0.1, 0.2])
qml.grad(circuit, argnum=1)(0.5, x)


* `qml.jacobian` now follows a different convention regarding its output shape. [(2059)](https://github.com/PennyLaneAI/pennylane/pull/2059)

Previously, `qml.jacobian` would attempt to stack the Jacobian for multiple QNode arguments, which succeeded whenever the arguments have the same shape. In this case, the stacked Jacobian would also be transposed, leading to the output shape `(*reverse_QNode_args_shape, *reverse_output_shape, num_QNode_args)`

If no stacking and transposing occurs, the output shape instead is a `tuple` where each entry corresponds to one QNode argument and has the shape `(*output_shape, *QNode_arg_shape)`.

This breaking change alters the behaviour in the first case and removes the attempt to stack and transpose, so that the output always has the shape of the second type.

Note that the behaviour is unchanged --- that is, the Jacobian tuple is unpacked into a single Jacobian --- if `argnum=None` and there is only one QNode argument with respect to which the differentiation takes place, or if an integer is provided as `argnum`.

A workaround that allowed `qml.jacobian` to differentiate multiple QNode arguments will no longer support higher-order derivatives. In such cases, combining multiple arguments into a single array is recommended.

* `qml.metric_tensor`, `qml.adjoint_metric_tensor` and `qml.transforms.classical_jacobian` now follow a different convention regarding their output shape when being used with the Autograd interface [(2059)](https://github.com/PennyLaneAI/pennylane/pull/2059)

See the previous entry for details. This breaking change immediately follows from the change in `qml.jacobian` whenever `hybrid=True` is used in the above methods.

* The behaviour of `RotosolveOptimizer` has been changed regarding its keyword arguments. [(2081)](https://github.com/PennyLaneAI/pennylane/pull/2081)

The keyword arguments `optimizer` and `optimizer_kwargs` for the `RotosolveOptimizer` have been renamed to `substep_optimizer` and `substep_kwargs`, respectively. Furthermore they have been moved from `step` and `step_and_cost` to the initialization `__init__`.

The keyword argument `num_freqs` has been renamed to `nums_frequency` and is expected to take a different shape now: Previously, it was expected to be an `int` or a list of entries, with each entry in turn being either an `int` or a `list` of `int` entries. Now the expected structure is a nested dictionary, matching the formatting expected by [qml.fourier.reconstruct](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.fourier.reconstruct.html) This also matches the expected formatting of the new keyword arguments `spectra` and `shifts`.

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

<h3>Deprecations</h3>

* Deprecates the caching ability provided by `QubitDevice`. [(2154)](https://github.com/PennyLaneAI/pennylane/pull/2154)

Going forward, the preferred way is to use the caching abilities of the QNode:
python
dev = qml.device("default.qubit", wires=2)

cache = {}

qml.qnode(dev, diff_method='parameter-shift', cache=cache)
def circuit():
qml.RY(0.345, wires=0)
return qml.expval(qml.PauliZ(0))

pycon
>>> for _ in range(10):
... circuit()
>>> dev.num_executions
1


<h3>Bug fixes</h3>

* Fixes a bug where an incorrect number of executions are recorded by a QNode using a custom cache with `diff_method="backprop"`. [(2171)](https://github.com/PennyLaneAI/pennylane/pull/2171)

* Fixes a bug where the `default.qubit.jax` device can't be used with `diff_method=None` and jitting. [(2136)](https://github.com/PennyLaneAI/pennylane/pull/2136)

* Fixes a bug where the Torch interface was not properly unwrapping Torch tensors to NumPy arrays before executing gradient tapes on devices. [(2117)](https://github.com/PennyLaneAI/pennylane/pull/2117)

* Fixes a bug for the TensorFlow interface where the dtype of input tensors was not cast. [(2120)](https://github.com/PennyLaneAI/pennylane/pull/2120)

* Fixes a bug where batch transformed QNodes would fail to apply batch transforms provided by the underlying device. [(2111)](https://github.com/PennyLaneAI/pennylane/pull/2111)

* An error is now raised during QNode creation if backpropagation is requested on a device with finite shots specified. [(2114)](https://github.com/PennyLaneAI/pennylane/pull/2114)

* Pytest now ignores any `DeprecationWarning` raised within autograd's `numpy_wrapper` module. Other assorted minor test warnings are fixed. [(2007)](https://github.com/PennyLaneAI/pennylane/pull/2007)

* Fixes a bug where the QNode was not correctly diagonalizing qubit-wise commuting observables. [(2097)](https://github.com/PennyLaneAI/pennylane/pull/2097)

* Fixes a bug in `gradient_transform` where the hybrid differentiation of circuits with a single parametrized gate failed and QNode argument axes of size `1` where removed from the output gradient. [(2080)](https://github.com/PennyLaneAI/pennylane/pull/2080)

* The available `diff_method` options for QNodes has been corrected in both the error messages and the documentation. [(2078)](https://github.com/PennyLaneAI/pennylane/pull/2078)

* Fixes a bug in `DefaultQubit` where the second derivative of QNodes at positions corresponding to vanishing state vector amplitudes is wrong. [(2057)](https://github.com/PennyLaneAI/pennylane/pull/2057)

* Fixes a bug where PennyLane didn't require v0.20.0 of PennyLane-Lightning, but raised an error with versions of Lightning earlier than v0.20.0 due to the new batch execution pipeline. [(2033)](https://github.com/PennyLaneAI/pennylane/pull/2033)

* Fixes a bug in `classical_jacobian` when used with Torch, where the Jacobian of the preprocessing was also computed for non-trainable parameters. [(2020)](https://github.com/PennyLaneAI/pennylane/pull/2020)

* Fixes a bug in queueing of the `two_qubit_decomposition` method that originally led to circuits with >3 two-qubit unitaries failing when passed through the `unitary_to_rot` optimization transform. [(2015)](https://github.com/PennyLaneAI/pennylane/pull/2015)

* Fixes a bug which allows using `jax.jit` to be compatible with circuits which return `qml.probs` when the `default.qubit.jax` is provided with a custom shot vector. [(2028)](https://github.com/PennyLaneAI/pennylane/pull/2028)

* Updated the `adjoint()` method for non-parametric qubit operations to solve a bug where repeated `adjoint()` calls don't return the correct operator. [(2133)](https://github.com/PennyLaneAI/pennylane/pull/2133)

* Fixed a bug in `insert()` which prevented operations that inherited from multiple classes to be inserted. [(2172)](https://github.com/PennyLaneAI/pennylane/pull/2172)

<h3>Documentation</h3>

* Fixes an error in the signs of equations in the `DoubleExcitation` page. [(2072)](https://github.com/PennyLaneAI/pennylane/pull/2072)

* Extends the interfaces description page to explicitly mention device compatibility. [(2031)](https://github.com/PennyLaneAI/pennylane/pull/2031)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Sam Banning, Thomas Bromley, Esther Cruz, Christian Gogolin, Nathan Killoran, Christina Lee, Olivia Di Matteo, Diego Guala, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Edward Jiang, Ankit Khandelwal, Korbinian Kottmann, Romain Moyard, Lee James O'Riordan, Maria Schuld, Jay Soni, Antal SzΓ‘va, David Wierichs, Shaoming Zhang.

0.20.0

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

<h4>Shiny new circuit drawer!πŸŽ¨πŸ–ŒοΈ </h4>

* PennyLane now supports drawing a QNode with matplotlib! [(1803)](https://github.com/PennyLaneAI/pennylane/pull/1803) [(#1811)](https://github.com/PennyLaneAI/pennylane/pull/1811) [(#1931)](https://github.com/PennyLaneAI/pennylane/pull/1931) [(#1954)](https://github.com/PennyLaneAI/pennylane/pull/1954)

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

qml.qnode(dev)
def circuit(x, z):
qml.QFT(wires=(0,1,2,3))
qml.Toffoli(wires=(0,1,2))
qml.CSWAP(wires=(0,2,3))
qml.RX(x, wires=0)
qml.CRZ(z, wires=(3,0))
return qml.expval(qml.PauliZ(0))
fig, ax = qml.draw_mpl(circuit)(1.2345, 1.2345)
fig.show()


<img src="https://pennylane.readthedocs.io/en/latest/_images/main_example.png" width=70%/>

<h4>New and improved quantum-aware optimizers</h4>


* Added `qml.LieAlgebraOptimizer`, a new quantum-aware Lie Algebra optimizer that allows one to perform gradient descent on the special unitary group. [(1911)](https://github.com/PennyLaneAI/pennylane/pull/1911)

python
dev = qml.device("default.qubit", wires=2)
H = -1.0 * qml.PauliX(0) - qml.PauliZ(1) - qml.PauliY(0) qml.PauliX(1)

qml.qnode(dev)
def circuit():
qml.RX(0.1, wires=[0])
qml.RY(0.5, wires=[1])
qml.CNOT(wires=[0,1])
qml.RY(0.6, wires=[0])
return qml.expval(H)
opt = qml.LieAlgebraOptimizer(circuit=circuit, stepsize=0.1)


Note that, unlike other optimizers, the `LieAlgebraOptimizer` accepts a QNode with *no* parameters, and instead grows the circuit by pending operations during the optimization:

pycon
>>> circuit()
tensor(-1.3351865, requires_grad=True)
>>> circuit1, cost = opt.step_and_cost()
>>> circuit1()
tensor(-1.99378872, requires_grad=True)


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

* The `qml.metric_tensor` transform can now be used to compute the full tensor, beyond the block diagonal approximation. [(1725)](https://github.com/PennyLaneAI/pennylane/pull/1725)

This is performed using Hadamard tests, and requires an additional wire on the device to execute the circuits produced by the transform, as compared to the number of wires required by the original circuit. The transform defaults to computing the full tensor, which can be controlled by the `approx` keyword argument.

As an example, consider the QNode

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

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

weights = np.array([0.2, 1.2, -0.9], requires_grad=True)


Then we can compute the (block) diagonal metric tensor as before, now using the `approx="block-diag"` keyword:

pycon
>>> qml.metric_tensor(circuit, approx="block-diag")(weights)
[[0.25 0. 0. ]
[0. 0.24013262 0. ]
[0. 0. 0.21846983]]


Instead, we now can also compute the full metric tensor, using Hadamard tests on the additional wire of the device:

pycon
>>> qml.metric_tensor(circuit)(weights)
[[ 0.25 0. -0.23300977]
[ 0. 0.24013262 0.01763859]
[-0.23300977 0.01763859 0.21846983]]


See the [metric tensor documentation](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.transforms.metric_tensor.html) for more information and usage details.

<h4>Faster performance with optimized quantum workflows</h4>

* The QNode has been re-written to support batch execution across the board, custom gradients, better decomposition strategies, and higher-order derivatives. [(1807)](https://github.com/PennyLaneAI/pennylane/pull/1807) [(#1969)](https://github.com/PennyLaneAI/pennylane/pull/1969)

- Internally, if multiple circuits are generated for simultaneous execution, 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 or simulator devices with parallelization capabilities.

- 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.qnode(dev, diff_method=my_gradient_transform)
def circuit():


For breaking changes related to the use of the new QNode, refer to the Breaking Changes section.

Note that the old QNode remains accessible at `qml.qnode_old.qnode`, however this will be removed in the next release.

* Custom decompositions can now be applied to operations at the device level. [(1900)](https://github.com/PennyLaneAI/pennylane/pull/1900)

For example, suppose we would like to implement the following QNode:

python
def circuit(weights):
qml.BasicEntanglerLayers(weights, wires=[0, 1, 2])
return qml.expval(qml.PauliZ(0))

original_dev = qml.device("default.qubit", wires=3)
original_qnode = qml.QNode(circuit, original_dev)


pycon
>>> weights = np.array([[0.4, 0.5, 0.6]])
>>> print(qml.draw(original_qnode, expansion_strategy="device")(weights))
0: ──RX(0.4)──╭C──────╭X─── ⟨Z⟩
1: ──RX(0.5)──╰X──╭C──│────
2: ──RX(0.6)──────╰X──╰C───


Now, let's swap out the decomposition of the `CNOT` gate into `CZ` and `Hadamard`, and furthermore the decomposition of `Hadamard` into `RZ` and `RY` rather than the decomposition already available in PennyLane. We define the two decompositions like so, and pass them to a device:

python
def custom_cnot(wires):
return [
qml.Hadamard(wires=wires[1]),
qml.CZ(wires=[wires[0], wires[1]]),
qml.Hadamard(wires=wires[1])
]

def custom_hadamard(wires):
return [
qml.RZ(np.pi, wires=wires),
qml.RY(np.pi / 2, wires=wires)
]

Can pass the operation itself, or a string
custom_decomps = {qml.CNOT : custom_cnot, "Hadamard" : custom_hadamard}

decomp_dev = qml.device("default.qubit", wires=3, custom_decomps=custom_decomps)
decomp_qnode = qml.QNode(circuit, decomp_dev)


Now when we draw or run a QNode on this device, the gates will be expanded according to our specifications:

pycon
>>> print(qml.draw(decomp_qnode, expansion_strategy="device")(weights))
0: ──RX(0.4)──────────────────────╭C──RZ(3.14)──RY(1.57)──────────────────────────╭Z──RZ(3.14)──RY(1.57)─── ⟨Z⟩
1: ──RX(0.5)──RZ(3.14)──RY(1.57)──╰Z──RZ(3.14)──RY(1.57)──╭C──────────────────────│────────────────────────
2: ──RX(0.6)──RZ(3.14)──RY(1.57)──────────────────────────╰Z──RZ(3.14)──RY(1.57)──╰C───────────────────────


A separate context manager, `set_decomposition`, has also been implemented to enable application of custom decompositions on devices that have already been created.

pycon
>>> with qml.transforms.set_decomposition(custom_decomps, original_dev):
... print(qml.draw(original_qnode, expansion_strategy="device")(weights))
0: ──RX(0.4)──────────────────────╭C──RZ(3.14)──RY(1.57)──────────────────────────╭Z──RZ(3.14)──RY(1.57)─── ⟨Z⟩
1: ──RX(0.5)──RZ(3.14)──RY(1.57)──╰Z──RZ(3.14)──RY(1.57)──╭C──────────────────────│────────────────────────
2: ──RX(0.6)──RZ(3.14)──RY(1.57)──────────────────────────╰Z──RZ(3.14)──RY(1.57)──╰C───────────────────────


* Given an operator of the form :math:`U=e^{iHt}`, where :math:`H` has commuting terms and known eigenvalues, `qml.gradients.generate_shift_rule` computes the generalized parameter shift rules for determining the gradient of the expectation value :math:`f(t) = \langle 0|U(t)^\dagger \hat{O} U(t)|0\rangle` on hardware. [(1788)](https://github.com/PennyLaneAI/pennylane/pull/1788) [(#1932)](https://github.com/PennyLaneAI/pennylane/pull/1932)

Given $H = \sum_i a_i h_i$, where the eigenvalues of :math:`H` are known and all :math:`h_i` commute, we can compute the *frequencies* (the unique positive differences of any two eigenvalues) using `qml.gradients.eigvals_to_frequencies`.

`qml.gradients.generate_shift_rule` can then be used to compute the parameter shift rules to compute :math:`f'(t)` using `2R` shifted cost function evaluations. This becomes cheaper than the standard application of the chain rule and two-term shift rule when `R` is less than the number of Pauli words in the generator.

For example, consider the case where :math:`H` has eigenspectrum `(-1, 0, 1)`:

pycon
>>> frequencies = qml.gradients.eigvals_to_frequencies((-1, 0, 1))
>>> frequencies
(1, 2)
>>> coeffs, shifts = qml.gradients.generate_shift_rule(frequencies)
>>> coeffs
array([ 0.85355339, -0.85355339, -0.14644661, 0.14644661])
>>> shifts
array([ 0.78539816, -0.78539816, 2.35619449, -2.35619449])


As we can see, `generate_shift_rule` returns four coefficients :math:`c_i` and shifts :math:`s_i` corresponding to a four term parameter shift rule. The gradient can then be reconstructed via:

.. math:: \frac{\partial}{\partial\phi}f = \sum_{i} c_i f(\phi + s_i),

where :math:`f(\phi) = \langle 0|U(\phi)^\dagger \hat{O} U(\phi)|0\rangle` for some observable :math:`\hat{O}` and the unitary :math:`U(\phi)=e^{iH\phi}`.

<h4>Support for TensorFlow AutoGraph mode with quantum hardware</h4>

* It is now possible to use TensorFlow's [AutoGraph
mode](https://www.tensorflow.org/guide/function) with QNodes on all devices and with arbitrary
differentiation methods. Previously, AutoGraph mode only support `diff_method="backprop"`. This
will result in significantly more performant model execution, at the cost of a more expensive initial compilation. [(1866)](https://github.com/PennyLaneAI/pennylane/pull/1886)

Use AutoGraph to convert your QNodes or cost functions into TensorFlow graphs by decorating them with `tf.function`:

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

qml.qnode(dev, diff_method="adjoint", interface="tf", max_diff=1)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
return qml.expval(qml.PauliZ(0) qml.PauliZ(1)), qml.expval(qml.PauliZ(0))

tf.function
def cost(x):
return tf.reduce_sum(circuit(x))

x = tf.Variable([0.5, 0.7], dtype=tf.float64)

with tf.GradientTape() as tape:
loss = cost(x)

grad = tape.gradient(loss, x)


The initial execution may take slightly longer than when executing the circuit in
eager mode; this is because TensorFlow is tracing the function to create the graph. Subsequent executions will be much more performant.

Note that using AutoGraph with backprop-enabled devices, such as `default.qubit`, will yield the best performance.

For more details, please see the [TensorFlow AutoGraph documentation](https://www.tensorflow.org/guide/function).

<h4>Characterize your quantum models with classical QNode reconstruction</h4>

* The `qml.fourier.reconstruct` function is added. It can be used to reconstruct QNodes outputting expectation values along a specified parameter dimension, with a minimal number of calls to the original QNode. The returned reconstruction is exact and purely classical, and can be evaluated without any quantum executions. [(1864)](https://github.com/PennyLaneAI/pennylane/pull/1864)

The reconstruction technique differs for functions with equidistant frequencies that are reconstructed using the function value at equidistant sampling points, and for functions with arbitrary frequencies reconstructed using arbitrary sampling points.

As an example, consider the following QNode:

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

qml.qnode(dev)
def circuit(x, Y, f=1.0):
qml.RX(f * x, wires=0)
qml.RY(Y[0], wires=0)
qml.RY(Y[1], wires=1)
qml.CNOT(wires=[0, 1])
qml.RY(3 * Y[1], wires=1)
return qml.expval(qml.PauliZ(0) qml.PauliZ(1))


It has three variational parameters overall: A scalar input `x` and an array-valued input `Y` with two entries. Additionally, we can tune the dependence on `x` with the frequency `f`. We then can reconstruct the QNode output function with respect to `x` via

pycon
>>> x = 0.3
>>> Y = np.array([0.1, -0.9])
>>> rec = qml.fourier.reconstruct(circuit, ids="x", nums_frequency={"x": {0: 1}})(x, Y)
>>> rec
{'x': {0: <function pennylane.fourier.reconstruct._reconstruct_equ.<locals>._reconstruction(x)>}}


As we can see, we get a nested dictionary in the format of the input `nums_frequency` with functions as values. These functions are simple float-to-float callables:

pycon
>>> univariate = rec["x"][0]
>>> univariate(x)
-0.880208251507


For more details on usage, reconstruction cost and differentiability support, please see the [fourier.reconstruct docstring](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.fourier.reconstruct.html).

<h4>State-of-the-art operations and templates</h4>

* A circuit template for time evolution under a commuting Hamiltonian utilizing generalized parameter shift rules for cost function gradients is available as `qml.CommutingEvolution`. [(1788)](https://github.com/PennyLaneAI/pennylane/pull/1788)

If the template is handed a frequency spectrum during its instantiation, then `generate_shift_rule` is internally called to obtain the general parameter shift rules with respect to `CommutingEvolution`'s :math:`t` parameter, otherwise the shift rule for a decomposition of `CommutingEvolution` will be used.

The template can be initialized within a `qnode` as:

python
import pennylane as qml

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

coeffs = [1, -1]
obs = [qml.PauliX(0) qml.PauliY(1), qml.PauliY(0) qml.PauliX(1)]
hamiltonian = qml.Hamiltonian(coeffs, obs)
frequencies = (2,4)

qml.qnode(dev)
def circuit(time):
qml.PauliX(0)
qml.CommutingEvolution(hamiltonian, time, frequencies)
return qml.expval(qml.PauliZ(0))


Note that there is no internal validation that 1) the input `qml.Hamiltonian` is fully commuting and 2) the eigenvalue frequency spectrum is correct, since these checks become prohibitively expensive for large Hamiltonians.

* The `qml.Barrier()` operator has been added. With it we can separate blocks in compilation or use it as a visual tool. [(1844)](https://github.com/PennyLaneAI/pennylane/pull/1844)

* Added the identity observable to be an operator. Now we can explicitly call the identity operation on our quantum circuits for both qubit and CV devices. [(1829)](https://github.com/PennyLaneAI/pennylane/pull/1829)

* Added the `qml.QubitDensityMatrix` initialization gate for mixed state simulation. [(1850)](https://github.com/PennyLaneAI/pennylane/pull/1850)

* A thermal relaxation channel is added to the Noisy channels. The channel description can be found on the supplementary information of [Quantum classifier with tailored quantum kernels](https://arxiv.org/abs/1909.02611). [(#1766)](https://github.com/PennyLaneAI/pennylane/pull/1766)

<h4>Manipulate QNodes to your ❀️s content with new transforms</h4>

* The `merge_amplitude_embedding` transformation has been created to automatically merge all gates of this type into one. [(1933)](https://github.com/PennyLaneAI/pennylane/pull/1933)
python
from pennylane.transforms import merge_amplitude_embedding

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

qml.qnode(dev)
merge_amplitude_embedding
def qfunc():
qml.AmplitudeEmbedding([0,1,0,0], wires = [0,1])
qml.AmplitudeEmbedding([0,1], wires = 2)
return qml.expval(qml.PauliZ(wires = 0))

pycon
>>> print(qml.draw(qnode)())
0: ──╭AmplitudeEmbedding(M0)─── ⟨Z⟩
1: β”€β”€β”œAmplitudeEmbedding(M0)───
2: ──╰AmplitudeEmbedding(M0)───
M0 =
[0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]


* The `undo_swaps` transformation has been created to automatically remove all swaps of a circuit. [(1960)](https://github.com/PennyLaneAI/pennylane/pull/1960)
python
dev = qml.device('default.qubit', wires=3)

qml.qnode(dev)
qml.transforms.undo_swaps
def qfunc():
qml.Hadamard(wires=0)
qml.PauliX(wires=1)
qml.SWAP(wires=[0,1])
qml.SWAP(wires=[0,2])
qml.PauliY(wires=0)
return qml.expval(qml.PauliZ(0))


pycon
>>> print(qml.draw(qfunc)())
0: ──Y─── ⟨Z⟩
1: ──H───
2: ──X───


<h3>Improvements</h3>

* Added functions for computing the values of atomic and molecular orbitals at a given position.
[(1867)](https://github.com/PennyLaneAI/pennylane/pull/1867)

The functions `atomic_orbital` and `molecular_orbital` can be used, as shown in the following codeblock, to evaluate the orbitals. By generating values of the orbitals at different positions, one can plot the spatial shape of a desired orbital.

python
symbols = ['H', 'H']
geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]], requires_grad = False)
mol = hf.Molecule(symbols, geometry)
hf.generate_scf(mol)()

ao = mol.atomic_orbital(0)
mo = mol.molecular_orbital(1)


pycon
>>> print(ao(0.0, 0.0, 0.0))
>>> print(mo(0.0, 0.0, 0.0))
0.6282468778183719
0.018251285973461928


* Added support for Python 3.10. [(1964)](https://github.com/PennyLaneAI/pennylane/pull/1964)

* The execution of QNodes that have

- multiple return types;
- a return type other than Variance and Expectation

now raises a descriptive error message when using the JAX interface. [(2011)](https://github.com/PennyLaneAI/pennylane/pull/2011)

* The PennyLane `qchem` package is now lazily imported; it will only be imported the first time it is accessed. [(1962)](https://github.com/PennyLaneAI/pennylane/pull/1962)

* `qml.math.scatter_element_add` now supports adding multiple values at multiple indices with a single function call, in all interfaces [(1864)](https://github.com/PennyLaneAI/pennylane/pull/1864)

For example, we may set five values of a three-dimensional tensor in the following way:

pycon
>>> X = tf.zeros((3, 2, 9), dtype=tf.float64)
>>> indices = [(0, 0, 1, 2, 2), (0, 0, 0, 0, 1), (1, 3, 8, 6, 7)]
>>> values = [1 * i for i in range(1,6)]
>>> qml.math.scatter_element_add(X, indices, values)
<tf.Tensor: shape=(3, 2, 9), dtype=float64, numpy=
array([[[0., 1., 0., 2., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.]],

[[0., 0., 0., 0., 0., 0., 0., 0., 3.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.]],

[[0., 0., 0., 0., 0., 0., 4., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 5., 0.]]])>


* All instances of `str.format` have been replace with f-strings. [(1970)](https://github.com/PennyLaneAI/pennylane/pull/1970)

* Tests do not loop over automatically imported and instantiated operations any more, which was opaque and created unnecessarily many tests. [(1895)](https://github.com/PennyLaneAI/pennylane/pull/1895)

* A `decompose()` method has been added to the `Operator` class such that we can obtain (and queue) decompositions directly from instances of operations. [(1873)](https://github.com/PennyLaneAI/pennylane/pull/1873)

pycon
>>> op = qml.PhaseShift(0.3, wires=0)
>>> op.decompose()
[RZ(0.3, wires=[0])]


* `qml.circuit_drawer.tape_mpl` produces a matplotlib figure and axes given a tape. [(1787)](https://github.com/PennyLaneAI/pennylane/pull/1787)

* The `AngleEmbedding`, `BasicEntanglerLayers` and `MottonenStatePreparation` templates now support the `batch_params` decorator. [(1812)](https://github.com/PennyLaneAI/pennylane/pull/1812) [(#1883)](https://github.com/PennyLaneAI/pennylane/pull/1883) [(#1893)](https://github.com/PennyLaneAI/pennylane/pull/1893)

* Added a new `qml.PauliError` channel that allows the application of an arbitrary number of Pauli operators on an arbitrary number of wires. [(1781)](https://github.com/PennyLaneAI/pennylane/pull/1781)

* CircuitDrawer now supports a `max_length` argument to help prevent text overflows when printing circuits to the CLI. [(1892)](https://github.com/PennyLaneAI/pennylane/pull/1892)

* `Identity` operation is now part of both the `ops.qubit` and `ops.cv` modules. [(1956)](https://github.com/PennyLaneAI/pennylane/pull/1956)

<h3>Breaking changes</h3>

* The QNode has been re-written to support batch execution across the board, custom gradients, better decomposition strategies, and higher-order derivatives. [(1807)](https://github.com/PennyLaneAI/pennylane/pull/1807) [(#1969)](https://github.com/PennyLaneAI/pennylane/pull/1969)

- 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). Increasing this value allows for higher order derivatives to be extracted, at the cost of additional (classical) computational overhead during the backwards pass.

- When decomposing the circuit, the default decomposition strategy `expansion_strategy="gradient"` will prioritize decompositions that result in the smallest number of parametrized operations required to satisfy the differentiation method. While this may lead to a slight increase in classical processing, it significantly reduces the number of circuit evaluations needed to compute gradients of complicated unitaries.

To return to the old behaviour, `expansion_strategy="device"` can be specified.

Note that the old QNode remains accessible at `qml.qnode_old.qnode`, however this will be removed in the next release.

* Certain features deprecated in `v0.19.0` have been removed: [(1963)](https://github.com/PennyLaneAI/pennylane/pull/1963) [(#1981)](https://github.com/PennyLaneAI/pennylane/pull/1981)

- The `qml.template` decorator (use a [`QuantumTape`](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.tape.QuantumTape.html) as a context manager to record operations and its `operations` attribute to return them, see the linked page for examples);
- The `default.tensor` and `default.tensor.tf` experimental devices;
- The `qml.fourier.spectrum` function (use the `qml.fourier.circuit_spectrum`
or `qml.fourier.qnode_spectrum` functions instead);
- The `diag_approx` keyword argument of `qml.metric_tensor` and
`qml.QNGOptimizer` (pass `approx='diag'` instead).

* The default behaviour of the `qml.metric_tensor` transform has been modified. By default, the full metric tensor is computed, leading to higher cost than the previous default of computing the block diagonal only. At the same time, the Hadamard tests for the full metric tensor require an additional wire on the device, so that

pycon
>>> qml.metric_tensor(some_qnode)(weights)


will revert back to the block diagonal restriction and raise a warning if the used device does not have an additional wire. [(1725)](https://github.com/PennyLaneAI/pennylane/pull/1725)

* The `circuit_drawer` module has been renamed `drawer`. [(1949)](https://github.com/PennyLaneAI/pennylane/pull/1949)

* The `par_domain` attribute in the operator class has been removed. [(1907)](https://github.com/PennyLaneAI/pennylane/pull/1907)

* The `mutable` keyword argument has been removed from the QNode, due to underlying bugs that result in incorrect results being returned from immutable QNodes. This functionality will return in an upcoming release. [(1807)](https://github.com/PennyLaneAI/pennylane/pull/1807)

* The reversible QNode differentiation method has been removed; the adjoint differentiation method is preferred instead (`diff_method='adjoint'`). [(1807)](https://github.com/PennyLaneAI/pennylane/pull/1807)

* `QuantumTape.trainable_params` now is a list instead of a set. This means that `tape.trainable_params` will return a list unlike before, but setting the `trainable_params` with a set works exactly as before. [(1904)](https://github.com/PennyLaneAI/pennylane/pull/1904)

* The `num_params` attribute in the operator class is now dynamic. This makes it easier to define operator subclasses with a flexible number of parameters. [(1898)](https://github.com/PennyLaneAI/pennylane/pull/1898) [(#1909)](https://github.com/PennyLaneAI/pennylane/pull/1909)

* The static method `decomposition()`, formerly in the `Operation` class, has been moved to the base `Operator` class. [(1873)](https://github.com/PennyLaneAI/pennylane/pull/1873)

* `DiagonalOperation` is not a separate subclass any more. [(1889)](https://github.com/PennyLaneAI/pennylane/pull/1889)

Instead, devices can check for the diagonal property using attributes:

python
from pennylane.ops.qubit.attributes import diagonal_in_z_basis

if op in diagonal_in_z_basis:
do something

Custom operations can be added to this attribute at runtime via `diagonal_in_z_basis.add("MyCustomOp")`.

<h3>Bug fixes</h3>

* Fixes a bug with `qml.probs` when using `default.qubit.jax`. [(1998)](https://github.com/PennyLaneAI/pennylane/pull/1998)

* Fixes a bug where output tensors of a QNode would always be put on the default GPU with `default.qubit.torch`. [(1982)](https://github.com/PennyLaneAI/pennylane/pull/1982)

* Device test suite doesn't use empty circuits so that it can also test the IonQ plugin, and it checks if operations are supported in more places. [(1979)](https://github.com/PennyLaneAI/pennylane/pull/1979)

* Fixes a bug where the metric tensor was computed incorrectly when using gates with `gate.inverse=True`. [(1987)](https://github.com/PennyLaneAI/pennylane/pull/1987)

* Corrects the documentation of `qml.transforms.classical_jacobian` for the Autograd interface (and improves test coverage). [(1978)](https://github.com/PennyLaneAI/pennylane/pull/1978)

* Fixes a bug where differentiating a QNode with `qml.state` using the JAX interface raised an error. [(1906)](https://github.com/PennyLaneAI/pennylane/pull/1906)

* Fixes a bug with the adjoint of `qml.QFT`. [(1955)](https://github.com/PennyLaneAI/pennylane/pull/1955)

* Fixes a bug where the `ApproxTimeEvolution` template was not correctly computing the operation wires from the input Hamiltonian. This did not affect computation with the `ApproxTimeEvolution` template, but did cause circuit drawing to fail. [(1952)](https://github.com/PennyLaneAI/pennylane/pull/1952)

* Fixes a bug where the classical preprocessing Jacobian computed by `qml.transforms.classical_jacobian` with JAX returned a reduced submatrix of the Jacobian. [(1948)](https://github.com/PennyLaneAI/pennylane/pull/1948)

* Fixes a bug where the operations are not accessed in the correct order in `qml.fourier.qnode_spectrum`, leading to wrong outputs. [(1935)](https://github.com/PennyLaneAI/pennylane/pull/1935)

* Fixes several Pylint errors. [(1951)](https://github.com/PennyLaneAI/pennylane/pull/1951)

* Fixes a bug where the device test suite wasn't testing certain operations. [(1943)](https://github.com/PennyLaneAI/pennylane/pull/1943)

* Fixes a bug where batch transforms would mutate a QNodes execution options. [(1934)](https://github.com/PennyLaneAI/pennylane/pull/1934)

* `qml.draw` now supports arbitrary templates with matrix parameters. [(1917)](https://github.com/PennyLaneAI/pennylane/pull/1917)

* `QuantumTape.trainable_params` now is a list instead of a set, making it more stable in very rare edge cases. [(1904)](https://github.com/PennyLaneAI/pennylane/pull/1904)

* `ExpvalCost` now returns corrects results shape when `optimize=True` with shots batch. [(1897)](https://github.com/PennyLaneAI/pennylane/pull/1897)

* `qml.circuit_drawer.MPLDrawer` was slightly modified to work with matplotlib version 3.5. [(1899)](https://github.com/PennyLaneAI/pennylane/pull/1899)

* `qml.CSWAP` and `qml.CRot` now define `control_wires`, and `qml.SWAP` returns the default empty wires object. [(1830)](https://github.com/PennyLaneAI/pennylane/pull/1830)

* The `requires_grad` attribute of `qml.numpy.tensor` objects is now preserved when pickling/unpickling the object. [(1856)](https://github.com/PennyLaneAI/pennylane/pull/1856)

* Device tests no longer throw warnings about the `requires_grad` attribute of variational parameters. [(1913)](https://github.com/PennyLaneAI/pennylane/pull/1913)

* `AdamOptimizer` and `AdagradOptimizer` had small fixes to their optimization step updates. [(1929)](https://github.com/PennyLaneAI/pennylane/pull/1929)

* Fixes a bug where differentiating a QNode with multiple array arguments via `qml.gradients.param_shift` throws an error. [(1989)](https://github.com/PennyLaneAI/pennylane/pull/1989)

* `AmplitudeEmbedding` template no longer produces a `ComplexWarning` when the `features` parameter is batched and provided as a 2D array. [(1990)](https://github.com/PennyLaneAI/pennylane/pull/1990)

* `qml.circuit_drawer.CircuitDrawer` no longer produces an error when attempting to draw tapes inside of circuits (e.g. from decomposition of an operation or manual placement). [(1994)](https://github.com/PennyLaneAI/pennylane/pull/1994)

* Fixes a bug where using SciPy sparse matrices with the new QNode could lead to a warning being raised about prioritizing the TensorFlow and PyTorch interfaces. [(2001)](https://github.com/PennyLaneAI/pennylane/pull/2001)

* Fixed a bug where the `QueueContext` was not empty when first importing PennyLane. [(1957)](https://github.com/PennyLaneAI/pennylane/pull/1957)

* Fixed circuit drawing problem with `Interferometer` and `CVNeuralNet`. [(1953)](https://github.com/PennyLaneAI/pennylane/pull/1953)

<h3>Documentation</h3>

* Added examples in documentation for some operations. [(1902)](https://github.com/PennyLaneAI/pennylane/pull/1902)

* Improves the Developer's Guide Testing document. [(1896)](https://github.com/PennyLaneAI/pennylane/pull/1896)

* Added documentation examples for `AngleEmbedding`, `BasisEmbedding`, `StronglyEntanglingLayers`, `SqueezingEmbedding`, `DisplacementEmbedding`, `MottonenStatePreparation` and `Interferometer`. [(1910)](https://github.com/PennyLaneAI/pennylane/pull/1910) [(#1908)](https://github.com/PennyLaneAI/pennylane/pull/1908) [(#1912)](https://github.com/PennyLaneAI/pennylane/pull/1912) [(#1920)](https://github.com/PennyLaneAI/pennylane/pull/1920) [(#1936)](https://github.com/PennyLaneAI/pennylane/pull/1936) [(#1937)](https://github.com/PennyLaneAI/pennylane/pull/1937)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Catalina Albornoz, Guillermo Alonso-Linaje, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Samuel Banning, Benjamin Cordier, Alain Delgado, Olivia Di Matteo, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Jalani Kanem, Ankit Khandelwal, Nathan Killoran, Shumpei Kobayashi, Robert Lang, Christina Lee, Cedric Lin, Alejandro Montanez, Romain Moyard, Lee James O'Riordan, Chae-Yeun Park, Isidor Schoch, Maria Schuld, Jay Soni, Antal SzΓ‘va, Rodrigo Vargas, David Wierichs, Roeland Wiersema, Moritz Willmann

Page 5 of 11

Β© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.