Pennylane

Latest version: v0.39.0

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

Scan your dependencies

Page 7 of 11

0.16.0

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

<h4>First class support for quantum kernels</h4>

* The new `qml.kernels` module provides basic functionalities for [working with quantum kernels](https://pennylane.readthedocs.io/en/stable/code/qml_kernels.html) as well as post-processing methods to mitigate sampling errors and device noise: [(#1102)](https://github.com/PennyLaneAI/pennylane/pull/1102)

python

num_wires = 6
wires = range(num_wires)
dev = qml.device('default.qubit', wires=wires)

qml.qnode(dev)
def kernel_circuit(x1, x2):
qml.templates.AngleEmbedding(x1, wires=wires)
qml.adjoint(qml.templates.AngleEmbedding)(x2, wires=wires)
return qml.probs(wires)

kernel = lambda x1, x2: kernel_circuit(x1, x2)[0]
X_train = np.random.random((10, 6))
X_test = np.random.random((5, 6))

Create symmetric square kernel matrix (for training)
K = qml.kernels.square_kernel_matrix(X_train, kernel)

Compute kernel between test and training data.
K_test = qml.kernels.kernel_matrix(X_train, X_test, kernel)
K1 = qml.kernels.mitigate_depolarizing_noise(K, num_wires, method='single')


<h4>Extract the fourier representation of quantum circuits</h4>

* PennyLane now has a `fourier` module, which hosts a [growing library of methods](https://pennylane.readthedocs.io/en/stable/code/qml_fourier.html) that help with investigating the Fourier representation of functions implemented by quantum circuits. The Fourier representation can be used to examine and characterize the expressivity of the quantum circuit. [(#1160)](https://github.com/PennyLaneAI/pennylane/pull/1160) [(#1378)](https://github.com/PennyLaneAI/pennylane/pull/1378)

For example, one can plot distributions over Fourier series coefficients like this one:

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

<h4>Seamless support for working with the Pauli group</h4>

* Added functionality for constructing and manipulating the Pauli group [(1181)](https://github.com/PennyLaneAI/pennylane/pull/1181).

The function `qml.grouping.pauli_group` provides a generator to easily loop over the group, or construct and store it in its entirety. For example, we can construct the single-qubit Pauli group like so:

pycon
>>> from pennylane.grouping import pauli_group
>>> pauli_group_1_qubit = list(pauli_group(1))
>>> pauli_group_1_qubit
[Identity(wires=[0]), PauliZ(wires=[0]), PauliX(wires=[0]), PauliY(wires=[0])]


We can multiply together its members at the level of Pauli words using the `pauli_mult` and `pauli_multi_with_phase` functions. This can be done on arbitrarily-labeled wires as well, by defining a wire map.

pycon
>>> from pennylane.grouping import pauli_group, pauli_mult
>>> wire_map = {'a' : 0, 'b' : 1, 'c' : 2}
>>> pg = list(pauli_group(3, wire_map=wire_map))
>>> pg[3]
PauliZ(wires=['b']) PauliZ(wires=['c'])
>>> pg[55]
PauliY(wires=['a']) PauliY(wires=['b']) PauliZ(wires=['c'])
>>> pauli_mult(pg[3], pg[55], wire_map=wire_map)
PauliY(wires=['a']) PauliX(wires=['b'])


Functions for conversion of Pauli observables to strings (and back) are included.

pycon
>>> from pennylane.grouping import pauli_word_to_string, string_to_pauli_word
>>> pauli_word_to_string(pg[55], wire_map=wire_map)
'YYZ'
>>> string_to_pauli_word('ZXY', wire_map=wire_map)
PauliZ(wires=['a']) PauliX(wires=['b']) PauliY(wires=['c'])


Calculation of the matrix representation for arbitrary Paulis and wire maps is now also supported.

pycon
>>> from pennylane.grouping import pauli_word_to_matrix
>>> wire_map = {'a' : 0, 'b' : 1}
>>> pauli_word = qml.PauliZ('b') corresponds to Pauli 'IZ'
>>> pauli_word_to_matrix(pauli_word, wire_map=wire_map)
array([[ 1., 0., 0., 0.],
[ 0., -1., 0., -0.],
[ 0., 0., 1., 0.],
[ 0., -0., 0., -1.]])


<h4>New transforms</h4>

* The `qml.specs` QNode transform creates a function that returns specifications or details about the QNode, including depth, number of gates, and number of gradient executions required. [(1245)](https://github.com/PennyLaneAI/pennylane/pull/1245)

For example:

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

qml.qnode(dev, diff_method='parameter-shift')
def circuit(x, y):
qml.RX(x[0], wires=0)
qml.Toffoli(wires=(0, 1, 2))
qml.CRY(x[1], wires=(0, 1))
qml.Rot(x[2], x[3], y, wires=0)
return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliX(1))


We can now use the `qml.specs` transform to generate a function that returns details and resource information:

pycon
>>> x = np.array([0.05, 0.1, 0.2, 0.3], requires_grad=True)
>>> y = np.array(0.4, requires_grad=False)
>>> specs_func = qml.specs(circuit)
>>> specs_func(x, y)
{'gate_sizes': defaultdict(int, {1: 2, 3: 1, 2: 1}),
'gate_types': defaultdict(int, {'RX': 1, 'Toffoli': 1, 'CRY': 1, 'Rot': 1}),
'num_operations': 4,
'num_observables': 2,
'num_diagonalizing_gates': 1,
'num_used_wires': 3,
'depth': 4,
'num_trainable_params': 4,
'num_parameter_shift_executions': 11,
'num_device_wires': 4,
'device_name': 'default.qubit',
'diff_method': 'parameter-shift'}


The tape methods `get_resources` and `get_depth` are superseded by `specs` and will be deprecated after one release cycle.

* Adds a decorator `qml.qfunc_transform` to easily create a transformation that modifies the behaviour of a quantum function.
[(1315)](https://github.com/PennyLaneAI/pennylane/pull/1315)

For example, consider the following transform, which scales the parameter of all `RX` gates by :math:`x \rightarrow \sin(a) \sqrt{x}`, and the parameters of all `RY` gates by :math:`y \rightarrow \cos(a * b) y`:

python
qml.qfunc_transform
def my_transform(tape, a, b):
for op in tape.operations + tape.measurements:
if op.name == "RX":
x = op.parameters[0]
qml.RX(qml.math.sin(a) * qml.math.sqrt(x), wires=op.wires)
elif op.name == "RY":
y = op.parameters[0]
qml.RX(qml.math.cos(a * b) * y, wires=op.wires)
else:
op.queue()


We can now apply this transform to any quantum function:

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

def ansatz(x):
qml.Hadamard(wires=0)
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
qml.CNOT(wires=[0, 1])

qml.qnode(dev)
def circuit(params, transform_weights):
qml.RX(0.1, wires=0)

apply the transform to the ansatz
my_transform(*transform_weights)(ansatz)(params)

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


We can print this QNode to show that the qfunc transform is taking place:

pycon
>>> x = np.array([0.5, 0.3], requires_grad=True)
>>> transform_weights = np.array([0.1, 0.6], requires_grad=True)
>>> print(qml.draw(circuit)(x, transform_weights))
0: ──RX(0.1)────H──RX(0.0706)──╭C──┤
1: ──RX(0.299)─────────────────╰X──┤ ⟨Z⟩


Evaluating the QNode, as well as the derivative, with respect to the gate parameter *and* the transform weights:

pycon
>>> circuit(x, transform_weights)
tensor(0.00672829, requires_grad=True)
>>> qml.grad(circuit)(x, transform_weights)
(array([ 0.00671711, -0.00207359]), array([6.69695008e-02, 3.73694364e-06]))


* Adds a `hamiltonian_expand` tape transform. This takes a tape ending in `qml.expval(H)`, where `H` is a Hamiltonian, and maps it to a collection of tapes which can be executed and passed into a post-processing function yielding the expectation value. [(1142)](https://github.com/PennyLaneAI/pennylane/pull/1142)

Example use:

python
H = qml.PauliZ(0) + 3 * qml.PauliZ(0) qml.PauliX(1)

with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=1)
qml.expval(H)

tapes, fn = qml.transforms.hamiltonian_expand(tape)


We can now evaluate the transformed tapes, and apply the post-processing function:

pycon
>>> dev = qml.device("default.qubit", wires=3)
>>> res = dev.batch_execute(tapes)
>>> fn(res)
3.999999999999999


* The `quantum_monte_carlo` transform has been added, allowing an input circuit to be transformed into the full quantum Monte Carlo algorithm. [(1316)](https://github.com/PennyLaneAI/pennylane/pull/1316)

Suppose we want to measure the expectation value of the sine squared function according to a standard normal distribution. We can calculate the expectation value analytically as `0.432332`, but we can also estimate using the quantum Monte Carlo algorithm. The first step is to discretize the problem:

python
from scipy.stats import norm

m = 5
M = 2 ** m

xmax = np.pi bound to region [-pi, pi]
xs = np.linspace(-xmax, xmax, M)

probs = np.array([norm().pdf(x) for x in xs])
probs /= np.sum(probs)

func = lambda i: np.sin(xs[i]) ** 2
r_rotations = np.array([2 * np.arcsin(np.sqrt(func(i))) for i in range(M)])


The `quantum_monte_carlo` transform can then be used:

python
from pennylane.templates.state_preparations.mottonen import (
_uniform_rotation_dagger as r_unitary,
)

n = 6
N = 2 ** n

a_wires = range(m)
wires = range(m + 1)
target_wire = m
estimation_wires = range(m + 1, n + m + 1)

dev = qml.device("default.qubit", wires=(n + m + 1))

def fn():
qml.templates.MottonenStatePreparation(np.sqrt(probs), wires=a_wires)
r_unitary(qml.RY, r_rotations, control_wires=a_wires[::-1], target_wire=target_wire)

qml.qnode(dev)
def qmc():
qml.quantum_monte_carlo(fn, wires, target_wire, estimation_wires)()
return qml.probs(estimation_wires)

phase_estimated = np.argmax(qmc()[:int(N / 2)]) / N


The estimated value can be retrieved using:

pycon
>>> (1 - np.cos(np.pi * phase_estimated)) / 2
0.42663476277231915


The resources required to perform the quantum Monte Carlo algorithm can also be inspected using the `specs` transform.

<h4>Extended QAOA module</h4>

* Functionality to support solving the maximum-weighted cycle problem has been added to the `qaoa`
module. [(1207)](https://github.com/PennyLaneAI/pennylane/pull/1207) [(#1209)](https://github.com/PennyLaneAI/pennylane/pull/1209) [(#1251)](https://github.com/PennyLaneAI/pennylane/pull/1251) [(#1213)](https://github.com/PennyLaneAI/pennylane/pull/1213) [(#1220)](https://github.com/PennyLaneAI/pennylane/pull/1220) [(#1214)](https://github.com/PennyLaneAI/pennylane/pull/1214) [(#1283)](https://github.com/PennyLaneAI/pennylane/pull/1283) [(#1297)](https://github.com/PennyLaneAI/pennylane/pull/1297) [(#1396)](https://github.com/PennyLaneAI/pennylane/pull/1396) [(#1403)](https://github.com/PennyLaneAI/pennylane/pull/1403)

The `max_weight_cycle` function returns the appropriate cost and mixer Hamiltonians:

pycon
>>> a = np.random.random((3, 3))
>>> np.fill_diagonal(a, 0)
>>> g = nx.DiGraph(a) create a random directed graph
>>> cost, mixer, mapping = qml.qaoa.max_weight_cycle(g)
>>> print(cost)
(-0.9775906842165344) [Z2]
+ (-0.9027248603361988) [Z3]
+ (-0.8722207409852838) [Z0]
+ (-0.6426184210832898) [Z5]
+ (-0.2832594164291379) [Z1]
+ (-0.0778133996933755) [Z4]
>>> print(mapping)
{0: (0, 1), 1: (0, 2), 2: (1, 0), 3: (1, 2), 4: (2, 0), 5: (2, 1)}

Additional functionality can be found in the [qml.qaoa.cycle](https://pennylane.readthedocs.io/en/latest/code/api/pennylane.qaoa.cycle.html) module.


<h4>Extended operations and templates</h4>

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

python
coeffs = [1, -0.45]
obs = [qml.PauliZ(0) qml.PauliZ(1), qml.PauliY(0) qml.PauliZ(1)]
H = qml.Hamiltonian(coeffs, obs)
H_sparse = qml.utils.sparse_hamiltonian(H)


The resulting matrix is a sparse matrix in scipy coordinate list (COO) format:

python
>>> H_sparse
<4x4 sparse matrix of type '<class 'numpy.complex128'>'
with 8 stored elements in COOrdinate format>


The sparse matrix can be converted to an array as:

python
>>> H_sparse.toarray()
array([[ 1.+0.j , 0.+0.j , 0.+0.45j, 0.+0.j ],
[ 0.+0.j , -1.+0.j , 0.+0.j , 0.-0.45j],
[ 0.-0.45j, 0.+0.j , -1.+0.j , 0.+0.j ],
[ 0.+0.j , 0.+0.45j, 0.+0.j , 1.+0.j ]])


* Adds the new template `AllSinglesDoubles` to prepare quantum states of molecules using the `SingleExcitation` and `DoubleExcitation` operations. The new template reduces significantly the number of operations and the depth of the quantum circuit with respect to the traditional UCCSD unitary. [(1383)](https://github.com/PennyLaneAI/pennylane/pull/1383)

For example, consider the case of two particles and four qubits. First, we define the Hartree-Fock initial state and generate all possible single and double excitations.

python
import pennylane as qml
from pennylane import numpy as np

electrons = 2
qubits = 4

hf_state = qml.qchem.hf_state(electrons, qubits)
singles, doubles = qml.qchem.excitations(electrons, qubits)


Now we can use the template ``AllSinglesDoubles`` to define the quantum circuit,

python
from pennylane.templates import AllSinglesDoubles

wires = range(qubits)

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

qml.qnode(dev)
def circuit(weights, hf_state, singles, doubles):
AllSinglesDoubles(weights, wires, hf_state, singles, doubles)
return qml.expval(qml.PauliZ(0))

params = np.random.normal(0, np.pi, len(singles) + len(doubles))

and execute it:
pycon
>>> circuit(params, hf_state, singles=singles, doubles=doubles)
tensor(-0.73772194, requires_grad=True)


* Adds `QubitCarry` and `QubitSum` operations for basic arithmetic. [(1169)](https://github.com/PennyLaneAI/pennylane/pull/1169)

The following example adds two 1-bit numbers, returning a 2-bit answer:

python
dev = qml.device('default.qubit', wires = 4)
a = 0
b = 1

qml.qnode(dev)
def circuit():
qml.BasisState(np.array([a, b]), wires=[1, 2])
qml.QubitCarry(wires=[0, 1, 2, 3])
qml.CNOT(wires=[1, 2])
qml.QubitSum(wires=[0, 1, 2])
return qml.probs(wires=[3, 2])

probs = circuit()
bitstrings = tuple(itertools.product([0, 1], repeat = 2))
indx = np.argwhere(probs == 1).flatten()[0]
output = bitstrings[indx]


pycon
>>> print(output)
(0, 1)


* Added the `qml.Projector` observable, which is available on all devices inheriting from the `QubitDevice` class. [(1356)](https://github.com/PennyLaneAI/pennylane/pull/1356) [(#1368)](https://github.com/PennyLaneAI/pennylane/pull/1368)

Using `qml.Projector`, we can define the basis state projectors to use when computing expectation values. Let us take for example a circuit that prepares Bell states:

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

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


We can then specify the `|00>` basis state to construct the `|00><00|` projector and compute the expectation value:

pycon
>>> basis_state = [0, 0]
>>> circuit(basis_state)
tensor(0.5, requires_grad=True)


As expected, we get similar results when specifying the `|11>` basis state:

pycon
>>> basis_state = [1, 1]
>>> circuit(basis_state)
tensor(0.5, requires_grad=True)


* The following new operations have been added:

- The IsingXX gate `qml.IsingXX` [(1194)](https://github.com/PennyLaneAI/pennylane/pull/1194)
- The IsingZZ gate `qml.IsingZZ` [(1199)](https://github.com/PennyLaneAI/pennylane/pull/1199)
- The ISWAP gate `qml.ISWAP` [(1298)](https://github.com/PennyLaneAI/pennylane/pull/1298)
- The reset error noise channel `qml.ResetError` [(1321)](https://github.com/PennyLaneAI/pennylane/pull/1321)


<h3>Improvements</h3>

* The ``argnum`` keyword argument can now be specified for a QNode to define a subset of trainable parameters used to estimate the Jacobian. [(1371)](https://github.com/PennyLaneAI/pennylane/pull/1371)

For example, consider two trainable parameters and a quantum function:

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

x = np.array(0.543, requires_grad=True)
y = np.array(-0.654, requires_grad=True)

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


When computing the gradient of the QNode, we can specify the trainable parameters to consider by passing the ``argnum`` keyword argument:

pycon
>>> qnode1 = qml.QNode(circuit, dev, diff_method="parameter-shift", argnum=[0,1])
>>> print(qml.grad(qnode1)(x,y))
(array(0.31434679), array(0.67949903))


Specifying a proper subset of the trainable parameters will estimate the Jacobian:

pycon
>>> qnode2 = qml.QNode(circuit, dev, diff_method="parameter-shift", argnum=[0])
>>> print(qml.grad(qnode2)(x,y))
(array(0.31434679), array(0.))


* Allows creating differentiable observables that return custom objects such that the observable is supported by devices. [(1291)](https://github.com/PennyLaneAI/pennylane/pull/1291)

As an example, first we define `NewObservable` class:

python
from pennylane.devices import DefaultQubit

class NewObservable(qml.operation.Observable):
"""NewObservable"""

num_wires = qml.operation.AnyWires
num_params = 0
par_domain = None

def diagonalizing_gates(self):
"""Diagonalizing gates"""
return []


Once we have this new observable class, we define a `SpecialObject` class that can be used to encode data in an observable and a new device that supports our new observable and returns a `SpecialObject` as the expectation value (the code is shortened for brevity, the extended example can be found as a test in the previously referenced pull request):

python
class SpecialObject:

def __init__(self, val):
self.val = val

def __mul__(self, other):
new = SpecialObject(self.val)
new *= other
return new

...

class DeviceSupportingNewObservable(DefaultQubit):
name = "Device supporting NewObservable"
short_name = "default.qubit.newobservable"
observables = DefaultQubit.observables.union({"NewObservable"})

def expval(self, observable, **kwargs):
if self.shots is None and isinstance(observable, NewObservable):
val = super().expval(qml.PauliZ(wires=0), **kwargs)
return SpecialObject(val)

return super().expval(observable, **kwargs)


At this point, we can create a device that will support the differentiation of a `NewObservable` object:

python
dev = DeviceSupportingNewObservable(wires=1, shots=None)

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


We can then compute the jacobian of this object:

pycon
>>> result = qml.jacobian(qnode)(0.2)
>>> print(result)
<__main__.SpecialObject object at 0x7fd2c54721f0>
>>> print(result.item().val)
-0.19866933079506116


* PennyLane NumPy now includes the [random module's](https://numpy.org/doc/stable/reference/random/index.html#module-numpy.random) `Generator` objects, the recommended way of random number generation. This allows for random number generation using a local, rather than global seed. [(1267)](https://github.com/PennyLaneAI/pennylane/pull/1267)

python
from pennylane import numpy as np

rng = np.random.default_rng()
random_mat1 = rng.random((3,2))
random_mat2 = rng.standard_normal(3, requires_grad=False)


* The performance of adjoint jacobian differentiation was significantly improved as the method now reuses the state computed on the forward pass. This can be turned off to save memory with the Torch and TensorFlow interfaces by passing `adjoint_cache=False` during QNode creation. [(1341)](https://github.com/PennyLaneAI/pennylane/pull/1341)

* The `Operator` (and by inheritance, the `Operation` and `Observable` class and their children) now have an `id` attribute, which can mark an operator in a circuit, for example to identify it on the tape by a tape transform. [(1377)](https://github.com/PennyLaneAI/pennylane/pull/1377)

* The `benchmark` module was deleted, since it was outdated and is superseded by the new separate [benchmark repository](https://github.com/PennyLaneAI/benchmark). [(#1343)](https://github.com/PennyLaneAI/pennylane/pull/1343)

* Decompositions in terms of elementary gates has been added for:

- `qml.CSWAP` [(1306)](https://github.com/PennyLaneAI/pennylane/issues/1306)
- `qml.SWAP` [(1329)](https://github.com/PennyLaneAI/pennylane/pull/1329)
- `qml.SingleExcitation` [(1303)](https://github.com/PennyLaneAI/pennylane/pull/1303)
- `qml.SingleExcitationPlus` and `qml.SingleExcitationMinus` [(1278)](https://github.com/PennyLaneAI/pennylane/pull/1278)
- `qml.DoubleExcitation` [(1303)](https://github.com/PennyLaneAI/pennylane/pull/1303)
- `qml.Toffoli` [(1320)](https://github.com/PennyLaneAI/pennylane/pull/1320)
- `qml.MultiControlledX`. [(1287)](https://github.com/PennyLaneAI/pennylane/pull/1287)

When controlling on three or more wires, an ancilla register of worker wires is required to support the decomposition.

python
ctrl_wires = [f"c{i}" for i in range(5)]
work_wires = [f"w{i}" for i in range(3)]
target_wires = ["t0"]
all_wires = ctrl_wires + work_wires + target_wires

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

with qml.tape.QuantumTape() as tape:
qml.MultiControlledX(control_wires=ctrl_wires, wires=target_wires, work_wires=work_wires)


pycon
>>> tape = tape.expand(depth=1)
>>> print(tape.draw(wire_order=qml.wires.Wires(all_wires)))

c0: ──────────────╭C──────────────────────╭C──────────┤
c1: ──────────────├C──────────────────────├C──────────┤
c2: ──────────╭C──│───╭C──────────────╭C──│───╭C──────┤
c3: ──────╭C──│───│───│───╭C──────╭C──│───│───│───╭C──┤
c4: ──╭C──│───│───│───│───│───╭C──│───│───│───│───│───┤
w0: ──│───│───├C──╰X──├C──│───│───│───├C──╰X──├C──│───┤
w1: ──│───├C──╰X──────╰X──├C──│───├C──╰X──────╰X──├C──┤
w2: ──├C──╰X──────────────╰X──├C──╰X──────────────╰X──┤
t0: ──╰X──────────────────────╰X──────────────────────┤


* Added `qml.CPhase` as an alias for the existing `qml.ControlledPhaseShift` operation. [(1319)](https://github.com/PennyLaneAI/pennylane/pull/1319).

* The `Device` class now uses caching when mapping wires. [(1270)](https://github.com/PennyLaneAI/pennylane/pull/1270)

* The `Wires` class now uses caching for computing its `hash`. [(1270)](https://github.com/PennyLaneAI/pennylane/pull/1270)

* Added custom gate application for Toffoli in `default.qubit`. [(1249)](https://github.com/PennyLaneAI/pennylane/pull/1249)

* Added validation for noise channel parameters. Invalid noise parameters now
raise a `ValueError`. [(1357)](https://github.com/PennyLaneAI/pennylane/pull/1357)

* The device test suite now provides test cases for checking gates by comparing
expectation values. [(1212)](https://github.com/PennyLaneAI/pennylane/pull/1212)

* PennyLane's test suite is now code-formatted using `black -l 100`. [(1222)](https://github.com/PennyLaneAI/pennylane/pull/1222)

* PennyLane's `qchem` package and tests are now code-formatted using `black -l 100`. [(1311)](https://github.com/PennyLaneAI/pennylane/pull/1311)

<h3>Breaking changes</h3>

* The `qml.inv()` function is now deprecated with a warning to use the more general `qml.adjoint()`. [(1325)](https://github.com/PennyLaneAI/pennylane/pull/1325)

* Removes support for Python 3.6 and adds support for Python 3.9. [(1228)](https://github.com/XanaduAI/pennylane/pull/1228)

* The tape methods `get_resources` and `get_depth` are superseded by `specs` and will be
deprecated after one release cycle. [(1245)](https://github.com/PennyLaneAI/pennylane/pull/1245)

* Using the `qml.sample()` measurement on devices with `shots=None` continue to
raise a warning with this functionality being fully deprecated and raising an
error after one release cycle. [(1079)](https://github.com/PennyLaneAI/pennylane/pull/1079) [(#1196)](https://github.com/PennyLaneAI/pennylane/pull/1196)

<h3>Bug fixes</h3>

* QNodes now display readable information when in interactive environments or when printed. [(1359)](https://github.com/PennyLaneAI/pennylane/pull/1359).

* Fixes a bug with `qml.math.cast` where the `MottonenStatePreparation` operation expected
a float type instead of double. [(1400)](https://github.com/XanaduAI/pennylane/pull/1400)

* Fixes a bug where a copy of `qml.ControlledQubitUnitary` was non-functional as it did not have all the necessary information. [(1411)](https://github.com/PennyLaneAI/pennylane/pull/1411)

* Warns when adjoint or reversible differentiation specified or called on a device with finite shots. [(1406)](https://github.com/PennyLaneAI/pennylane/pull/1406)

* Fixes the differentiability of the operations `IsingXX` and `IsingZZ` for Autograd, Jax and Tensorflow. [(1390)](https://github.com/PennyLaneAI/pennylane/pull/1390)

* Fixes a bug where multiple identical Hamiltonian terms will produce a different result with ``optimize=True`` using ``ExpvalCost``. [(1405)](https://github.com/XanaduAI/pennylane/pull/1405)

* Fixes bug where `shots=None` was not reset when changing shots temporarily in a QNode call like `circuit(0.1, shots=3)`. [(1392)](https://github.com/XanaduAI/pennylane/pull/1392)

* Fixes floating point errors with `diff_method="finite-diff"` and `order=1` when parameters are `float32`. [(1381)](https://github.com/PennyLaneAI/pennylane/pull/1381)

* Fixes a bug where `qml.ctrl` would fail to transform gates that had no control defined and no decomposition defined. [(1376)](https://github.com/PennyLaneAI/pennylane/pull/1376)

* Copying the `JacobianTape` now correctly also copies the `jacobian_options` attribute. This fixes a bug allowing the JAX interface to support adjoint differentiation. [(1349)](https://github.com/PennyLaneAI/pennylane/pull/1349)

* Fixes drawing QNodes that contain multiple measurements on a single wire. [(1353)](https://github.com/PennyLaneAI/pennylane/pull/1353)

* Fixes drawing QNodes with no operations. [(1354)](https://github.com/PennyLaneAI/pennylane/pull/1354)

* Fixes incorrect wires in the decomposition of the `ControlledPhaseShift` operation. [(1338)](https://github.com/PennyLaneAI/pennylane/pull/1338)

* Fixed tests for the `Permute` operation that used a QNode and hence expanded tapes twice instead of once due to QNode tape expansion and an explicit tape expansion call. [(1318)](https://github.com/PennyLaneAI/pennylane/pull/1318).

* Prevent Hamiltonians that share wires from being multiplied together. [(1273)](https://github.com/PennyLaneAI/pennylane/pull/1273)

* Fixed a bug where the custom range sequences could not be passed to the `StronglyEntanglingLayers` template. [(1332)](https://github.com/PennyLaneAI/pennylane/pull/1332)

* Fixed a bug where `qml.sum()` and `qml.dot()` do not support the JAX interface. [(1380)](https://github.com/PennyLaneAI/pennylane/pull/1380)

<h3>Documentation</h3>

* Math present in the `QubitParamShiftTape` class docstring now renders correctly. [(1402)](https://github.com/PennyLaneAI/pennylane/pull/1402)

* Fix typo in the documentation of `qml.StronglyEntanglingLayers`. [(1367)](https://github.com/PennyLaneAI/pennylane/pull/1367)

* Fixed typo in TensorFlow interface documentation [(1312)](https://github.com/PennyLaneAI/pennylane/pull/1312)

* Fixed typos in the mathematical expressions in documentation of `qml.DoubleExcitation`. [(1278)](https://github.com/PennyLaneAI/pennylane/pull/1278)

* Remove unsupported `None` option from the `qml.QNode` docstrings. [(1271)](https://github.com/PennyLaneAI/pennylane/pull/1271)

* Updated the docstring of `qml.PolyXP` to reference the new location of internal usage. [(1262)](https://github.com/PennyLaneAI/pennylane/pull/1262)

* Removes occurrences of the deprecated device argument ``analytic`` from the documentation. [(1261)](https://github.com/PennyLaneAI/pennylane/pull/1261)

* Updated PyTorch and TensorFlow interface introductions. [(1333)](https://github.com/PennyLaneAI/pennylane/pull/1333)

* Updates the quantum chemistry quickstart to reflect recent changes to the `qchem` module. [(1227)](https://github.com/PennyLaneAI/pennylane/pull/1227)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Marius Aglitoiu, Vishnu Ajith, Juan Miguel Arrazola, Thomas Bromley, Jack Ceroni, Alaric Cheng, Miruna Daian, Olivia Di Matteo, Tanya Garg, Christian Gogolin, Alain Delgado Gran, Diego Guala, Anthony Hayes, Ryan Hill, Theodor Isacsson, Josh Izaac, Soran Jahangiri, Pavan Jayasinha, Nathan Killoran, Christina Lee, Ryan Levy, Alberto Maldonado, Johannes Jakob Meyer, Romain Moyard, Ashish Panigrahi, Nahum Sá, Maria Schuld, Brian Shi, Antal Száva, David Wierichs, Vincent Wong.

0.15.1

<h3>Bug fixes</h3>

* Fixes two bugs in the parameter-shift Hessian. [(1260)](https://github.com/PennyLaneAI/pennylane/pull/1260)

- Fixes a bug where having an unused parameter in the Autograd interface would result in an indexing error during backpropagation.

- The parameter-shift Hessian only supports the two-term parameter-shift rule currently, so raises an error if asked to differentiate any unsupported gates (such as the controlled rotation gates).

* A bug which resulted in `qml.adjoint()` and `qml.inv()` failing to work with templates has been fixed. [(1243)](https://github.com/PennyLaneAI/pennylane/pull/1243)

* Deprecation warning instances in PennyLane have been changed to `UserWarning`, to account for recent changes to how Python warnings are filtered in [PEP565](https://www.python.org/dev/peps/pep-0565/). [(#1211)](https://github.com/PennyLaneAI/pennylane/pull/1211)

* The version requirement for PySCF has been modified to allow for `pyscf>=1.7.2`. [(1254)](https://github.com/PennyLaneAI/pennylane/pull/1254)

<h3>Documentation</h3>

* Updated the order of the parameters to the `GaussianState` operation to match the way that the PennyLane-SF plugin uses them. [(1255)](https://github.com/PennyLaneAI/pennylane/pull/1255)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Josh Izaac, Maria Schuld, Antal Száva.

0.15.0

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

<h4>Better and more flexible shot control</h4>

* Adds a new optimizer `qml.ShotAdaptiveOptimizer`, a gradient-descent optimizer where the shot rate is adaptively calculated using the variances of the parameter-shift gradient. [(1139)](https://github.com/PennyLaneAI/pennylane/pull/1139)

By keeping a running average of the parameter-shift gradient and the *variance* of the parameter-shift gradient, this optimizer frugally distributes a shot budget across the partial derivatives of each parameter.

In addition, if computing the expectation value of a Hamiltonian, weighted random sampling can be used to further distribute the shot budget across the local terms from which the Hamiltonian is constructed.

This optimizer is based on both the [iCANS1](https://quantum-journal.org/papers/q-2020-05-11-263) and [Rosalin](https://arxiv.org/abs/2004.06252) shot-adaptive optimizers.

Once constructed, the cost function can be passed directly to the optimizer's `step` method. The attribute `opt.total_shots_used` can be used to track the number of shots per iteration.

pycon
>>> coeffs = [2, 4, -1, 5, 2]
>>> obs = [
... qml.PauliX(1),
... qml.PauliZ(1),
... qml.PauliX(0) qml.PauliX(1),
... qml.PauliY(0) qml.PauliY(1),
... qml.PauliZ(0) qml.PauliZ(1)
... ]
>>> H = qml.Hamiltonian(coeffs, obs)
>>> dev = qml.device("default.qubit", wires=2, shots=100)
>>> cost = qml.ExpvalCost(qml.templates.StronglyEntanglingLayers, H, dev)
>>> params = qml.init.strong_ent_layers_uniform(n_layers=2, n_wires=2)
>>> opt = qml.ShotAdaptiveOptimizer(min_shots=10)
>>> for i in range(5):
... params = opt.step(cost, params)
... print(f"Step {i}: cost = {cost(params):.2f}, shots_used = {opt.total_shots_used}")
Step 0: cost = -5.68, shots_used = 240
Step 1: cost = -2.98, shots_used = 336
Step 2: cost = -4.97, shots_used = 624
Step 3: cost = -5.53, shots_used = 1054
Step 4: cost = -6.50, shots_used = 1798


* Batches of shots can now be specified as a list, allowing measurement statistics to be course-grained with a single QNode evaluation. [(1103)](https://github.com/PennyLaneAI/pennylane/pull/1103)

pycon
>>> shots_list = [5, 10, 1000]
>>> dev = qml.device("default.qubit", wires=2, shots=shots_list)


When QNodes are executed on this device, a single execution of 1015 shots will be submitted. However, three sets of measurement statistics will be returned; using the first 5 shots, second set of 10 shots, and final 1000 shots, separately.

For example, executing a circuit with two outputs will lead to a result of shape `(3, 2)`:

pycon
>>> qml.qnode(dev)
... def circuit(x):
... qml.RX(x, wires=0)
... qml.CNOT(wires=[0, 1])
... return qml.expval(qml.PauliZ(0) qml.PauliX(1)), qml.expval(qml.PauliZ(0))
>>> circuit(0.5)
[[0.33333333 1. ]
[0.2 1. ]
[0.012 0.868 ]]


This output remains fully differentiable.

- The number of shots can now be specified on a per-call basis when evaluating a QNode. [(1075)](https://github.com/PennyLaneAI/pennylane/pull/1075).

For this, the qnode should be called with an additional `shots` keyword argument:

pycon
>>> dev = qml.device('default.qubit', wires=1, shots=10) default is 10
>>> qml.qnode(dev)
... def circuit(a):
... qml.RX(a, wires=0)
... return qml.sample(qml.PauliZ(wires=0))
>>> circuit(0.8)
[ 1 1 1 -1 -1 1 1 1 1 1]
>>> circuit(0.8, shots=3)
[ 1 1 1]
>>> circuit(0.8)
[ 1 1 1 -1 -1 1 1 1 1 1]


<h4>New differentiable quantum transforms</h4>

A new module is available, [qml.transforms](https://pennylane.rtfd.io/en/stable/code/qml_transforms.html), which contains *differentiable quantum transforms*. These are functions that act on QNodes, quantum functions, devices, and tapes, transforming them while remaining fully differentiable.

* A new adjoint transform has been added. [(1111)](https://github.com/PennyLaneAI/pennylane/pull/1111) [(#1135)](https://github.com/PennyLaneAI/pennylane/pull/1135)

This new method allows users to apply the adjoint of an arbitrary sequence of operations.

python
def subroutine(wire):
qml.RX(0.123, wires=wire)
qml.RY(0.456, wires=wire)

dev = qml.device('default.qubit', wires=1)
qml.qnode(dev)
def circuit():
subroutine(0)
qml.adjoint(subroutine)(0)
return qml.expval(qml.PauliZ(0))


This creates the following circuit:

pycon
>>> print(qml.draw(circuit)())
0: --RX(0.123)--RY(0.456)--RY(-0.456)--RX(-0.123)--| <Z>


Directly applying to a gate also works as expected.

python
qml.adjoint(qml.RX)(0.123, wires=0) applies RX(-0.123)


* A new transform `qml.ctrl` is now available that adds control wires to subroutines. [(1157)](https://github.com/PennyLaneAI/pennylane/pull/1157)

python
def my_ansatz(params):
qml.RX(params[0], wires=0)
qml.RZ(params[1], wires=1)

Create a new operation that applies `my_ansatz`
controlled by the "2" wire.
my_ansatz2 = qml.ctrl(my_ansatz, control=2)

qml.qnode(dev)
def circuit(params):
my_ansatz2(params)
return qml.state()


This is equivalent to:

python
qml.qnode(...)
def circuit(params):
qml.CRX(params[0], wires=[2, 0])
qml.CRZ(params[1], wires=[2, 1])
return qml.state()


* The `qml.transforms.classical_jacobian` transform has been added. [(1186)](https://github.com/PennyLaneAI/pennylane/pull/1186)

This transform returns a function to extract the Jacobian matrix of the classical part of a QNode, allowing the classical dependence between the QNode arguments and the quantum gate arguments to be extracted.

For example, given the following QNode:

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


We can use this transform to extract the relationship :math:`f: \mathbb{R}^n \rightarrow\mathbb{R}^m` between the input QNode arguments :math:`w` and the gate arguments :math:`g`, for a given value of the QNode arguments:

pycon
>>> cjac_fn = qml.transforms.classical_jacobian(circuit)
>>> weights = np.array([1., 1., 1.], requires_grad=True)
>>> cjac = cjac_fn(weights)
>>> print(cjac)
[[1. 0. 0.]
[1. 0. 0.]
[0. 0. 2.]]


The returned Jacobian has rows corresponding to gate arguments, and columns corresponding to QNode arguments; that is, :math:`J_{ij} = \frac{\partial}{\partial g_i} f(w_j)`.

<h4>More operations and templates</h4>

* Added the `SingleExcitation` two-qubit operation, which is useful for quantum chemistry applications. [(1121)](https://github.com/PennyLaneAI/pennylane/pull/1121)

It can be used to perform an SO(2) rotation in the subspace spanned by the states :math:`|01\rangle` and :math:`|10\rangle`. For example, the following circuit performs the transformation :math:`|10\rangle \rightarrow \cos(\phi/2)|10\rangle - \sin(\phi/2)|01\rangle`:

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

qml.qnode(dev)
def circuit(phi):
qml.PauliX(wires=0)
qml.SingleExcitation(phi, wires=[0, 1])


The `SingleExcitation` operation supports analytic gradients on hardware using only four expectation value calculations, following results from [Kottmann et al.](https://arxiv.org/abs/2011.05938)

* Added the `DoubleExcitation` four-qubit operation, which is useful for quantum chemistry applications. [(1123)](https://github.com/PennyLaneAI/pennylane/pull/1123)

It can be used to perform an SO(2) rotation in the subspace spanned by the states :math:`|1100\rangle` and :math:`|0011\rangle`. For example, the following circuit performs the transformation :math:`|1100\rangle\rightarrow \cos(\phi/2)|1100\rangle - \sin(\phi/2)|0011\rangle`:

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

qml.qnode(dev)
def circuit(phi):
qml.PauliX(wires=0)
qml.PauliX(wires=1)
qml.DoubleExcitation(phi, wires=[0, 1, 2, 3])


The `DoubleExcitation` operation supports analytic gradients on hardware using only four expectation value calculations, following results from [Kottmann et al.](https://arxiv.org/abs/2011.05938).

* Added the `QuantumMonteCarlo` template for performing quantum Monte Carlo estimation of an expectation value on simulator. [(1130)](https://github.com/PennyLaneAI/pennylane/pull/1130)

The following example shows how the expectation value of sine squared over a standard normal distribution can be approximated:

python
from scipy.stats import norm

m = 5
M = 2 ** m
n = 10
N = 2 ** n
target_wires = range(m + 1)
estimation_wires = range(m + 1, n + m + 1)

xmax = np.pi bound to region [-pi, pi]
xs = np.linspace(-xmax, xmax, M)

probs = np.array([norm().pdf(x) for x in xs])
probs /= np.sum(probs)

func = lambda i: np.sin(xs[i]) ** 2

dev = qml.device("default.qubit", wires=(n + m + 1))

qml.qnode(dev)
def circuit():
qml.templates.QuantumMonteCarlo(
probs,
func,
target_wires=target_wires,
estimation_wires=estimation_wires,
)
return qml.probs(estimation_wires)

phase_estimated = np.argmax(circuit()[:int(N / 2)]) / N
expectation_estimated = (1 - np.cos(np.pi * phase_estimated)) / 2


* Added the `QuantumPhaseEstimation` template for performing quantum phase estimation for an input unitary matrix. [(1095)](https://github.com/PennyLaneAI/pennylane/pull/1095)

Consider the matrix corresponding to a rotation from an `RX` gate:

pycon
>>> phase = 5
>>> target_wires = [0]
>>> unitary = qml.RX(phase, wires=0).matrix


The ``phase`` parameter can be estimated using ``QuantumPhaseEstimation``. For example, using five phase-estimation qubits:

python
n_estimation_wires = 5
estimation_wires = range(1, n_estimation_wires + 1)

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

qml.qnode(dev)
def circuit():
Start in the |+> eigenstate of the unitary
qml.Hadamard(wires=target_wires)

QuantumPhaseEstimation(
unitary,
target_wires=target_wires,
estimation_wires=estimation_wires,
)

return qml.probs(estimation_wires)

phase_estimated = np.argmax(circuit()) / 2 ** n_estimation_wires

Need to rescale phase due to convention of RX gate
phase_estimated = 4 * np.pi * (1 - phase)


- Added the `ControlledPhaseShift` gate as well as the `QFT` operation for applying quantum Fourier transforms. [(1064)](https://github.com/PennyLaneAI/pennylane/pull/1064)

python
qml.qnode(dev)
def circuit_qft(basis_state):
qml.BasisState(basis_state, wires=range(3))
qml.QFT(wires=range(3))
return qml.state()


- Added the `ControlledQubitUnitary` operation. This enables implementation of multi-qubit gates with a variable number of control qubits. It is also possible to specify a different state for the control qubits using the `control_values` argument (also known as a mixed-polarity multi-controlled operation). [(1069)](https://github.com/PennyLaneAI/pennylane/pull/1069) [(#1104)](https://github.com/PennyLaneAI/pennylane/pull/1104)

For example, we can create a multi-controlled T gate using:

python
T = qml.T._matrix()
qml.ControlledQubitUnitary(T, control_wires=[0, 1, 3], wires=2, control_values="110")


Here, the T gate will be applied to wire `2` if control wires `0` and `1` are in state `1`, and control wire `3` is in state `0`. If no value is passed to `control_values`, the gate will be applied if all control wires are in the `1` state.

- Added `MultiControlledX` for multi-controlled `NOT` gates.
This is a special case of `ControlledQubitUnitary` that applies a
Pauli X gate conditioned on the state of an arbitrary number of
control qubits.
[(1104)](https://github.com/PennyLaneAI/pennylane/pull/1104)

<h4>Support for higher-order derivatives on hardware</h4>

* Computing second derivatives and Hessians of QNodes is now supported with the parameter-shift differentiation method, on all machine learning interfaces. [(1130)](https://github.com/PennyLaneAI/pennylane/pull/1130) [(#1129)](https://github.com/PennyLaneAI/pennylane/pull/1129) [(#1110)](https://github.com/PennyLaneAI/pennylane/pull/1110)

Hessians are computed using the parameter-shift rule, and can be evaluated on both hardware and simulator devices.

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

qml.qnode(dev, diff_method="parameter-shift")
def circuit(p):
qml.RY(p[0], wires=0)
qml.RX(p[1], wires=0)
return qml.expval(qml.PauliZ(0))

x = np.array([1.0, 2.0], requires_grad=True)


python
>>> hessian_fn = qml.jacobian(qml.grad(circuit))
>>> hessian_fn(x)
[[0.2248451 0.7651474]
[0.7651474 0.2248451]]


* Added the function `finite_diff()` to compute finite-difference approximations to the gradient and the second-order derivatives of arbitrary callable functions. [(1090)](https://github.com/PennyLaneAI/pennylane/pull/1090)

This is useful to compute the derivative of parametrized `pennylane.Hamiltonian` observables with respect to their parameters.

For example, in quantum chemistry simulations it can be used to evaluate the derivatives of the electronic Hamiltonian with respect to the nuclear coordinates:

pycon
>>> def H(x):
... return qml.qchem.molecular_hamiltonian(['H', 'H'], x)[0]
>>> x = np.array([0., 0., -0.66140414, 0., 0., 0.66140414])
>>> grad_fn = qml.finite_diff(H, N=1)
>>> grad = grad_fn(x)
>>> deriv2_fn = qml.finite_diff(H, N=2, idx=[0, 1])
>>> deriv2_fn(x)


* The JAX interface now supports all devices, including hardware devices, via the parameter-shift differentiation method. [(1076)](https://github.com/PennyLaneAI/pennylane/pull/1076)

For example, using the JAX interface with Cirq:

python
dev = qml.device('cirq.simulator', wires=1)
qml.qnode(dev, interface="jax", diff_method="parameter-shift")
def circuit(x):
qml.RX(x[1], wires=0)
qml.Rot(x[0], x[1], x[2], wires=0)
return qml.expval(qml.PauliZ(0))
weights = jnp.array([0.2, 0.5, 0.1])
print(circuit(weights))


Currently, when used with the parameter-shift differentiation method, only a single returned expectation value or variance is supported. Multiple expectations/variances, as well as probability and state returns, are not currently allowed.

<h3>Improvements</h3>

* The ``MottonenStatePreparation`` template has improved performance on states with only real amplitudes by reducing the number of redundant CNOT gates at the end of a circuit.

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

inputstate = [np.sqrt(0.2), np.sqrt(0.3), np.sqrt(0.4), np.sqrt(0.1)]

qml.qnode(dev)
def circuit():
mottonen.MottonenStatePreparation(inputstate,wires=[0, 1])
return qml.expval(qml.PauliZ(0))


Previously returned:

pycon
>>> print(qml.draw(circuit)())
0: ──RY(1.57)──╭C─────────────╭C──╭C──╭C──┤ ⟨Z⟩
1: ──RY(1.35)──╰X──RY(0.422)──╰X──╰X──╰X──┤


In this release, it now returns:

pycon
>>> print(qml.draw(circuit)())
0: ──RY(1.57)──╭C─────────────╭C──┤ ⟨Z⟩
1: ──RY(1.35)──╰X──RY(0.422)──╰X──┤


- The templates are now classes inheriting from `Operation`, and define the ansatz in their `expand()` method. This change does not affect the user interface. [(1138)](https://github.com/PennyLaneAI/pennylane/pull/1138) [(#1156)](https://github.com/PennyLaneAI/pennylane/pull/1156) [(#1163)](https://github.com/PennyLaneAI/pennylane/pull/1163) [(#1192)](https://github.com/PennyLaneAI/pennylane/pull/1192)

For convenience, some templates have a new method that returns the expected shape of the trainable parameter tensor, which can be used to create random tensors.

python
shape = qml.templates.BasicEntanglerLayers.shape(n_layers=2, n_wires=4)
weights = np.random.random(shape)
qml.templates.BasicEntanglerLayers(weights, wires=range(4))


- `QubitUnitary` now validates to ensure the input matrix is two dimensional. [(1128)](https://github.com/PennyLaneAI/pennylane/pull/1128)

* Most layers in Pytorch or Keras accept arbitrary dimension inputs, where each dimension barring the last (in the case where the actual weight function of the layer operates on one-dimensional vectors) is broadcast over. This is now also supported by KerasLayer and TorchLayer. [(1062)](https://github.com/PennyLaneAI/pennylane/pull/1062).

Example use:

python
dev = qml.device("default.qubit", wires=4)
x = tf.ones((5, 4, 4))

qml.qnode(dev)
def layer(weights, inputs):
qml.templates.AngleEmbedding(inputs, wires=range(4))
qml.templates.StronglyEntanglingLayers(weights, wires=range(4))
return [qml.expval(qml.PauliZ(i)) for i in range(4)]

qlayer = qml.qnn.KerasLayer(layer, {"weights": (4, 4, 3)}, output_dim=4)
out = qlayer(x)


The output tensor has the following shape:
pycon
>>> out.shape
(5, 4, 4)


* If only one argument to the function `qml.grad` has the `requires_grad` attribute set to True, then the returned gradient will be a NumPy array, rather than a tuple of length 1. [(1067)](https://github.com/PennyLaneAI/pennylane/pull/1067) [(#1081)](https://github.com/PennyLaneAI/pennylane/pull/1081)

* An improvement has been made to how `QubitDevice` generates and post-processess samples, allowing QNode measurement statistics to work on devices with more than 32 qubits. [(1088)](https://github.com/PennyLaneAI/pennylane/pull/1088)

* Due to the addition of `density_matrix()` as a return type from a QNode, tuples are now supported by the `output_dim` parameter in `qnn.KerasLayer`. [(1070)](https://github.com/PennyLaneAI/pennylane/pull/1070)

* Two new utility methods are provided for working with quantum tapes. [(1175)](https://github.com/PennyLaneAI/pennylane/pull/1175)

- `qml.tape.get_active_tape()` gets the currently recording tape.

- `tape.stop_recording()` is a context manager that temporarily stops the currently recording tape from recording additional tapes or quantum operations.

For example:

pycon
>>> with qml.tape.QuantumTape():
... qml.RX(0, wires=0)
... current_tape = qml.tape.get_active_tape()
... with current_tape.stop_recording():
... qml.RY(1.0, wires=1)
... qml.RZ(2, wires=1)
>>> current_tape.operations
[RX(0, wires=[0]), RZ(2, wires=[1])]


* When printing `qml.Hamiltonian` objects, the terms are sorted by number of wires followed by coefficients. [(981)](https://github.com/PennyLaneAI/pennylane/pull/981)

* Adds `qml.math.conj` to the PennyLane math module. [(1143)](https://github.com/PennyLaneAI/pennylane/pull/1143)

This new method will do elementwise conjugation to the given tensor-like object, correctly dispatching to the required tensor-manipulation framework to preserve differentiability.

python
>>> a = np.array([1.0 + 2.0j])
>>> qml.math.conj(a)
array([1.0 - 2.0j])


* The four-term parameter-shift rule, as used by the controlled rotation operations, has been updated to use coefficients that minimize the variance as per https://arxiv.org/abs/2104.05695. [(#1206)](https://github.com/PennyLaneAI/pennylane/pull/1206)

* A new transform `qml.transforms.invisible` has been added, to make it easier to transform QNodes. [(1175)](https://github.com/PennyLaneAI/pennylane/pull/1175)

<h3>Breaking changes</h3>

* Devices do not have an `analytic` argument or attribute anymore. Instead, `shots` is the source of truth for whether a simulator estimates return values from a finite number of shots, or whether it returns analytic results (`shots=None`). [(1079)](https://github.com/PennyLaneAI/pennylane/pull/1079) [(#1196)](https://github.com/PennyLaneAI/pennylane/pull/1196)

python
dev_analytic = qml.device('default.qubit', wires=1, shots=None)
dev_finite_shots = qml.device('default.qubit', wires=1, shots=1000)

def circuit():
qml.Hadamard(wires=0)
return qml.expval(qml.PauliZ(wires=0))

circuit_analytic = qml.QNode(circuit, dev_analytic)
circuit_finite_shots = qml.QNode(circuit, dev_finite_shots)


Devices with `shots=None` return deterministic, exact results:

pycon
>>> circuit_analytic()
0.0
>>> circuit_analytic()
0.0

Devices with `shots > 0` return stochastic results estimated from
samples in each run:

pycon
>>> circuit_finite_shots()
-0.062
>>> circuit_finite_shots()
0.034


The `qml.sample()` measurement can only be used on devices on which the number of shots is set explicitly.

* If creating a QNode from a quantum function with an argument named `shots`, a `DeprecationWarning` is raised, warning the user that this is a reserved argument to change the number of shots on a per-call basis. [(1075)](https://github.com/PennyLaneAI/pennylane/pull/1075)

* For devices inheriting from `QubitDevice`, the methods `expval`, `var`, `sample` accept two new keyword arguments --- `shot_range` and `bin_size`. [(1103)](https://github.com/PennyLaneAI/pennylane/pull/1103)

These new arguments allow for the statistics to be performed on only a subset of device samples. This finer level of control is accessible from the main UI by instantiating a device with a batch of shots.

For example, consider the following device:

pycon
>>> dev = qml.device("my_device", shots=[5, (10, 3), 100])


This device will execute QNodes using 135 shots, however measurement statistics will be **course grained** across these 135 shots:

* All measurement statistics will first be computed using the first 5 shots --- that is, `shots_range=[0, 5]`, `bin_size=5`.

* Next, the tuple `(10, 3)` indicates 10 shots, repeated 3 times. This will use `shot_range=[5, 35]`, performing the expectation value in bins of size 10 (`bin_size=10`).

* Finally, we repeat the measurement statistics for the final 100 shots, `shot_range=[35, 135]`, `bin_size=100`.


* The old PennyLane core has been removed, including the following modules: [(1100)](https://github.com/PennyLaneAI/pennylane/pull/1100)

- `pennylane.variables`
- `pennylane.qnodes`

As part of this change, the location of the new core within the Python
module has been moved:

- Moves `pennylane.tape.interfaces` → `pennylane.interfaces`
- Merges `pennylane.CircuitGraph` and `pennylane.TapeCircuitGraph` → `pennylane.CircuitGraph`
- Merges `pennylane.OperationRecorder` and `pennylane.TapeOperationRecorder` →
- `pennylane.tape.operation_recorder`
- Merges `pennylane.measure` and `pennylane.tape.measure` → `pennylane.measure`
- Merges `pennylane.operation` and `pennylane.tape.operation` → `pennylane.operation`
- Merges `pennylane._queuing` and `pennylane.tape.queuing` → `pennylane.queuing`

This has no affect on import location.

In addition,

- All tape-mode functions have been removed (`qml.enable_tape()`, `qml.tape_mode_active()`),
- All tape fixtures have been deleted,
- Tests specifically for non-tape mode have been deleted.

* The device test suite no longer accepts the `analytic` keyword. [(1216)](https://github.com/PennyLaneAI/pennylane/pull/1216)

<h3>Bug fixes</h3>

* Fixes a bug where using the circuit drawer with a `ControlledQubitUnitary` operation raised an error. [(1174)](https://github.com/PennyLaneAI/pennylane/pull/1174)

* Fixes a bug and a test where the ``QuantumTape.is_sampled`` attribute was not being updated. [(1126)](https://github.com/PennyLaneAI/pennylane/pull/1126)

* Fixes a bug where `BasisEmbedding` would not accept inputs whose bits are all ones or all zeros. [(1114)](https://github.com/PennyLaneAI/pennylane/pull/1114)

* The `ExpvalCost` class raises an error if instantiated with non-expectation measurement statistics. [(1106)](https://github.com/PennyLaneAI/pennylane/pull/1106)

* Fixes a bug where decompositions would reset the differentiation method of a QNode. [(1117)](https://github.com/PennyLaneAI/pennylane/pull/1117)

* Fixes a bug where the second-order CV parameter-shift rule would error if attempting to compute the gradient of a QNode with more than one second-order observable. [(1197)](https://github.com/PennyLaneAI/pennylane/pull/1197)

* Fixes a bug where repeated Torch interface applications after expansion caused an error. [(1223)](https://github.com/PennyLaneAI/pennylane/pull/1223)

* Sampling works correctly with batches of shots specified as a list. [(1232)](https://github.com/PennyLaneAI/pennylane/pull/1232)

<h3>Documentation</h3>

- Updated the diagram used in the Architectural overview page of the Development guide such that it doesn't mention Variables. [(1235)](https://github.com/PennyLaneAI/pennylane/pull/1235)

- Typos addressed in templates documentation. [(1094)](https://github.com/PennyLaneAI/pennylane/pull/1094)

- Upgraded the documentation to use Sphinx 3.5.3 and the new m2r2 package. [(1186)](https://github.com/PennyLaneAI/pennylane/pull/1186)

- Added `flaky` as dependency for running tests in the documentation. [(1113)](https://github.com/PennyLaneAI/pennylane/pull/1113)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Shahnawaz Ahmed, Juan Miguel Arrazola, Thomas Bromley, Olivia Di Matteo, Alain Delgado Gran, Kyle Godbey, Diego Guala, Theodor Isacsson, Josh Izaac, Soran Jahangiri, Nathan Killoran, Christina Lee, Daniel Polatajko, Chase Roberts, Sankalp Sanand, Pritish Sehzpaul, Maria Schuld, Antal Száva, David Wierichs.

0.14.1

<h3>Bug fixes</h3>

* Fixes a bug where inverse operations could not be differentiated using backpropagation on `default.qubit`. [(1072)](https://github.com/PennyLaneAI/pennylane/pull/1072)

* The QNode has a new keyword argument, `max_expansion`, that determines the maximum number of times the internal circuit should be expanded when executed on a device. In addition, the default number of max expansions has been increased from 2 to 10, allowing devices that require more than two operator decompositions to be supported. [(1074)](https://github.com/PennyLaneAI/pennylane/pull/1074)

* Fixes a bug where `Hamiltonian` objects created with non-list arguments raised an error for arithmetic operations. [(1082)](https://github.com/PennyLaneAI/pennylane/pull/1082)

* Fixes a bug where `Hamiltonian` objects with no coefficients or operations would return a faulty result when used with `ExpvalCost`. [(1082)](https://github.com/PennyLaneAI/pennylane/pull/1082)

* Fixes a testing bug where tests that required JAX would fail if JAX was not installed. The tests will now instead be skipped if JAX cannot be imported. [(1066)](https://github.com/PennyLaneAI/pennylane/pull/1066)

<h3>Documentation</h3>

* Updates mentions of `generate_hamiltonian` to `molecular_hamiltonian` in the docstrings of the `ExpvalCost` and `Hamiltonian` classes. [(1077)](https://github.com/PennyLaneAI/pennylane/pull/1077)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Thomas Bromley, Josh Izaac, Antal Száva.

0.14.0

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

<h4>Perform quantum machine learning with JAX</h4>

* QNodes created with `default.qubit` now support a JAX interface, allowing JAX to be used to create, differentiate, and optimize hybrid quantum-classical models. [(947)](https://github.com/PennyLaneAI/pennylane/pull/947)

This is supported internally via a new `default.qubit.jax` device. This device runs end to end in JAX, meaning that it supports all of the awesome JAX transformations (`jax.vmap`, `jax.jit`, `jax.hessian`, etc).

Here is an example of how to use the new JAX interface:

python
dev = qml.device("default.qubit", wires=1)
qml.qnode(dev, interface="jax", diff_method="backprop")
def circuit(x):
qml.RX(x[1], wires=0)
qml.Rot(x[0], x[1], x[2], wires=0)
return qml.expval(qml.PauliZ(0))

weights = jnp.array([0.2, 0.5, 0.1])
grad_fn = jax.grad(circuit)
print(grad_fn(weights))


Currently, only `diff_method="backprop"` is supported, with plans to support more in the future.

<h4>New, faster, quantum gradient methods</h4>

* A new differentiation method has been added for use with simulators. The `"adjoint"` method operates after a forward pass by iteratively applying inverse gates to scan backwards through the circuit. [(1032)](https://github.com/PennyLaneAI/pennylane/pull/1032)

This method is similar to the reversible method, but has a lower time overhead and a similar memory overhead. It follows the approach provided by [Jones and Gacon](https://arxiv.org/abs/2009.02823). This method is only compatible with certain statevector-based devices such as `default.qubit`.

Example use:

python
import pennylane as qml

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

qml.qnode(device, diff_method="adjoint")
def f(params):
qml.RX(0.1, wires=0)
qml.Rot(*params, wires=0)
qml.RX(-0.3, wires=0)
return qml.expval(qml.PauliZ(0))

params = [0.1, 0.2, 0.3]
qml.grad(f)(params)


* The default logic for choosing the 'best' differentiation method has been altered to improve performance. [(1008)](https://github.com/PennyLaneAI/pennylane/pull/1008)

- If the quantum device provides its own gradient, this is now the preferred differentiation method.

- If the quantum device natively supports classical backpropagation, this is now preferred over the parameter-shift rule.

This will lead to marked speed improvement during optimization when using `default.qubit`, with a sight penalty on the forward-pass evaluation.

More details are available below in the 'Improvements' section for plugin developers.

* PennyLane now supports analytical quantum gradients for noisy channels, in addition to its existing support for unitary operations. The noisy channels `BitFlip`, `PhaseFlip`, and `DepolarizingChannel` all support analytic gradients out of the box. [(968)](https://github.com/PennyLaneAI/pennylane/pull/968)

* A method has been added for calculating the Hessian of quantum circuits using the second-order parameter shift formula. [(961)](https://github.com/PennyLaneAI/pennylane/pull/961)

The following example shows the calculation of the Hessian:

python
n_wires = 5
weights = [2.73943676, 0.16289932, 3.4536312, 2.73521126, 2.6412488]

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

with qml.tape.QubitParamShiftTape() as tape:
for i in range(n_wires):
qml.RX(weights[i], wires=i)

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

qml.expval(qml.PauliZ(1))

print(tape.hessian(dev))


The Hessian is not yet supported via classical machine learning interfaces, but will be added in a future release.

<h4>More operations and templates</h4>

* Two new error channels, `BitFlip` and `PhaseFlip` have been added. [(954)](https://github.com/PennyLaneAI/pennylane/pull/954)

They can be used in the same manner as existing error channels:

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

qml.qnode(dev)
def circuit():
qml.RX(0.3, wires=0)
qml.RY(0.5, wires=1)
qml.BitFlip(0.01, wires=0)
qml.PhaseFlip(0.01, wires=1)
return qml.expval(qml.PauliZ(0))


* Apply permutations to wires using the `Permute` subroutine. [(952)](https://github.com/PennyLaneAI/pennylane/pull/952)

python
import pennylane as qml
dev = qml.device('default.qubit', wires=5)

qml.qnode(dev)
def apply_perm():
Send contents of wire 4 to wire 0, of wire 2 to wire 1, etc.
qml.templates.Permute([4, 2, 0, 1, 3], wires=dev.wires)
return qml.expval(qml.PauliZ(0))


<h4>QNode transformations</h4>

* The `qml.metric_tensor` function transforms a QNode to produce the Fubini-Study metric tensor with full autodifferentiation support---even on hardware. [(1014)](https://github.com/PennyLaneAI/pennylane/pull/1014)

Consider the following QNode:

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

qml.qnode(dev, interface="autograd")
def circuit(weights):
layer 1
qml.RX(weights[0, 0], wires=0)
qml.RX(weights[0, 1], wires=1)

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

layer 2
qml.RZ(weights[1, 0], wires=0)
qml.RZ(weights[1, 1], wires=2)

qml.CNOT(wires=[0, 1])
qml.CNOT(wires=[1, 2])
return qml.expval(qml.PauliZ(0) qml.PauliZ(1)), qml.expval(qml.PauliY(2))


We can use the `metric_tensor` function to generate a new function, that returns the metric tensor of this QNode:

pycon
>>> met_fn = qml.metric_tensor(circuit)
>>> weights = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]], requires_grad=True)
>>> met_fn(weights)
tensor([[0.25 , 0. , 0. , 0. ],
[0. , 0.25 , 0. , 0. ],
[0. , 0. , 0.0025, 0.0024],
[0. , 0. , 0.0024, 0.0123]], requires_grad=True)


The returned metric tensor is also fully differentiable, in all interfaces. For example, differentiating the `(3, 2)` element:

pycon
>>> grad_fn = qml.grad(lambda x: met_fn(x)[3, 2])
>>> grad_fn(weights)
array([[ 0.04867729, -0.00049502, 0. ],
[ 0. , 0. , 0. ]])


Differentiation is also supported using Torch, Jax, and TensorFlow.

* Adds the new function `qml.math.cov_matrix()`. This function accepts a list of commuting observables, and the probability distribution in the shared observable eigenbasis after the application of an ansatz. It uses these to construct the covariance matrix in a *framework independent* manner, such that the output covariance matrix is autodifferentiable. [(1012)](https://github.com/PennyLaneAI/pennylane/pull/1012)

For example, consider the following ansatz and observable list:

python3
obs_list = [qml.PauliX(0) qml.PauliZ(1), qml.PauliY(2)]
ansatz = qml.templates.StronglyEntanglingLayers


We can construct a QNode to output the probability distribution in the shared eigenbasis of the observables:

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

qml.qnode(dev, interface="autograd")
def circuit(weights):
ansatz(weights, wires=[0, 1, 2])
rotate into the basis of the observables
for o in obs_list:
o.diagonalizing_gates()
return qml.probs(wires=[0, 1, 2])


We can now compute the covariance matrix:

pycon
>>> weights = qml.init.strong_ent_layers_normal(n_layers=2, n_wires=3)
>>> cov = qml.math.cov_matrix(circuit(weights), obs_list)
>>> cov
array([[0.98707611, 0.03665537],
[0.03665537, 0.99998377]])


Autodifferentiation is fully supported using all interfaces:

pycon
>>> cost_fn = lambda weights: qml.math.cov_matrix(circuit(weights), obs_list)[0, 1]
>>> qml.grad(cost_fn)(weights)[0]
array([[[ 4.94240914e-17, -2.33786398e-01, -1.54193959e-01],
[-3.05414996e-17, 8.40072236e-04, 5.57884080e-04],
[ 3.01859411e-17, 8.60411436e-03, 6.15745204e-04]],

[[ 6.80309533e-04, -1.23162742e-03, 1.08729813e-03],
[-1.53863193e-01, -1.38700657e-02, -1.36243323e-01],
[-1.54665054e-01, -1.89018172e-02, -1.56415558e-01]]])


* A new `qml.draw` function is available, allowing QNodes to be easily drawn without execution by providing example input. [(962)](https://github.com/PennyLaneAI/pennylane/pull/962)

python
qml.qnode(dev)
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))


The QNode circuit structure may depend on the input arguments; this is taken into account by passing example QNode arguments to the `qml.draw()` drawing function:

pycon
>>> drawer = qml.draw(circuit)
>>> result = drawer(a=2.3, w=[1.2, 3.2, 0.7])
>>> print(result)
0: ──H──╭C────────────────────────────╭C─────────╭┤ ⟨Z ⊗ Z⟩
1: ─────╰RX(2.3)──Rot(1.2, 3.2, 0.7)──╰RX(-2.3)──╰┤ ⟨Z ⊗ Z⟩


<h4>A faster, leaner, and more flexible core</h4>

* The new core of PennyLane, rewritten from the ground up and developed over the last few release cycles, has achieved feature parity and has been made the new default in PennyLane v0.14. The old core has been marked as deprecated, and will be removed in an upcoming release. [(1046)](https://github.com/PennyLaneAI/pennylane/pull/1046) [(#1040)](https://github.com/PennyLaneAI/pennylane/pull/1040) [(#1034)](https://github.com/PennyLaneAI/pennylane/pull/1034) [(#1035)](https://github.com/PennyLaneAI/pennylane/pull/1035) [(#1027)](https://github.com/PennyLaneAI/pennylane/pull/1027) [(#1026)](https://github.com/PennyLaneAI/pennylane/pull/1026) [(#1021)](https://github.com/PennyLaneAI/pennylane/pull/1021) [(#1054)](https://github.com/PennyLaneAI/pennylane/pull/1054) [(#1049)](https://github.com/PennyLaneAI/pennylane/pull/1049)

While high-level PennyLane code and tutorials remain unchanged, the new core provides several advantages and improvements:

- **Faster and more optimized**: The new core provides various performance optimizations, reducing pre- and post-processing overhead, and reduces the number of quantum evaluations in certain cases.

- **Support for in-QNode classical processing**: this allows for differentiable classical processing within the QNode.

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

qml.qnode(dev, interface="tf")
def circuit(p):
qml.RX(tf.sin(p[0])**2 + p[1], wires=0)
return qml.expval(qml.PauliZ(0))


The classical processing functions used within the QNode must match the QNode interface. Here, we use TensorFlow:

pycon
>>> params = tf.Variable([0.5, 0.1], dtype=tf.float64)
>>> with tf.GradientTape() as tape:
... res = circuit(params)
>>> grad = tape.gradient(res, params)
>>> print(res)
tf.Tensor(0.9460913127754935, shape=(), dtype=float64)
>>> print(grad)
tf.Tensor([-0.27255248 -0.32390003], shape=(2,), dtype=float64)


As a result of this change, quantum decompositions that require classical processing are fully supported and end-to-end differentiable in tape mode.

- **No more Variable wrapping**: QNode arguments no longer become `Variable` objects within the QNode.

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

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


Internal QNode parameters can be easily inspected, printed, and manipulated:

pycon
>>> circuit(0.5)
Parameter value: 0.5
tensor(0.87758256, requires_grad=True)


- **Less restrictive QNode signatures**: There is no longer any restriction on the QNode signature; the QNode can be defined and called following the same rules as standard Python functions.

For example, the following QNode uses positional, named, and variable keyword arguments:

python
x = torch.tensor(0.1, requires_grad=True)
y = torch.tensor([0.2, 0.3], requires_grad=True)
z = torch.tensor(0.4, requires_grad=True)

qml.qnode(dev, interface="torch")
def circuit(p1, p2=y, **kwargs):
qml.RX(p1, wires=0)
qml.RY(p2[0] * p2[1], wires=0)
qml.RX(kwargs["p3"], wires=0)
return qml.var(qml.PauliZ(0))


When we call the QNode, we may pass the arguments by name even if defined positionally; any argument not provided will use the default value.

pycon
>>> res = circuit(p1=x, p3=z)
>>> print(res)
tensor(0.2327, dtype=torch.float64, grad_fn=<SelectBackward>)
>>> res.backward()
>>> print(x.grad, y.grad, z.grad)
tensor(0.8396) tensor([0.0289, 0.0193]) tensor(0.8387)


This extends to the `qnn` module, where `KerasLayer` and `TorchLayer` modules can be created from QNodes with unrestricted signatures.

- **Smarter measurements:** QNodes can now measure wires more than once, as long as all observables are commuting:

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


Further, the `qml.ExpvalCost()` function allows for optimizing measurements to reduce the number of quantum evaluations required.

With the new PennyLane core, there are a few small breaking changes, detailed below in the 'Breaking Changes' section.

<h3>Improvements</h3>

* The built-in PennyLane optimizers allow more flexible cost functions. The cost function passed to most optimizers may accept any combination of trainable arguments, non-trainable arguments, and keyword arguments. [(959)](https://github.com/PennyLaneAI/pennylane/pull/959) [(#1053)](https://github.com/PennyLaneAI/pennylane/pull/1053)

The full changes apply to:

* `AdagradOptimizer`
* `AdamOptimizer`
* `GradientDescentOptimizer`
* `MomentumOptimizer`
* `NesterovMomentumOptimizer`
* `RMSPropOptimizer`
* `RotosolveOptimizer`

The `requires_grad=False` property must mark any non-trainable constant argument. The `RotoselectOptimizer` allows passing only keyword arguments.

Example use:

python
def cost(x, y, data, scale=1.0):
return scale * (x[0]-data)**2 + scale * (y-data)**2

x = np.array([1.], requires_grad=True)
y = np.array([1.0])
data = np.array([2.], requires_grad=False)

opt = qml.GradientDescentOptimizer()

the optimizer step and step_and_cost methods can
now update multiple parameters at once
x_new, y_new, data = opt.step(cost, x, y, data, scale=0.5)
(x_new, y_new, data), value = opt.step_and_cost(cost, x, y, data, scale=0.5)

list and tuple unpacking is also supported
params = (x, y, data)
params = opt.step(cost, *params)


* The circuit drawer has been updated to support the inclusion of unused or inactive wires, by passing the `show_all_wires` argument. [(1033)](https://github.com/PennyLaneAI/pennylane/pull/1033)

python
dev = qml.device('default.qubit', wires=[-1, "a", "q2", 0])

qml.qnode(dev)
def circuit():
qml.Hadamard(wires=-1)
qml.CNOT(wires=[-1, "q2"])
return qml.expval(qml.PauliX(wires="q2"))


pycon
>>> print(qml.draw(circuit, show_all_wires=True)())
>>>
-1: ──H──╭C──┤
a: ─────│───┤
q2: ─────╰X──┤ ⟨X⟩
0: ─────────┤


* The logic for choosing the 'best' differentiation method has been altered to improve performance. [(1008)](https://github.com/PennyLaneAI/pennylane/pull/1008)

- If the device provides its own gradient, this is now the preferred differentiation method.

- If a device provides additional interface-specific versions that natively support classical backpropagation, this is now preferred over the parameter-shift rule.

Devices define additional interface-specific devices via their `capabilities()` dictionary. For example, `default.qubit` supports supplementary devices for TensorFlow, Autograd, and JAX:

python
{
"passthru_devices": {
"tf": "default.qubit.tf",
"autograd": "default.qubit.autograd",
"jax": "default.qubit.jax",
},
}


As a result of this change, if the QNode `diff_method` is not explicitly provided, it is possible that the QNode will run on a *supplementary device* of the device that was specifically provided:

python
dev = qml.device("default.qubit", wires=2)
qml.QNode(dev) will default to backprop on default.qubit.autograd
qml.QNode(dev, interface="tf") will default to backprop on default.qubit.tf
qml.QNode(dev, interface="jax") will default to backprop on default.qubit.jax


* The `default.qubit` device has been updated so that internally it applies operations in a more functional style, i.e., by accepting an input state and returning an evolved state. [(1025)](https://github.com/PennyLaneAI/pennylane/pull/1025)

* A new test series, `pennylane/devices/tests/test_compare_default_qubit.py`, has been added, allowing to test if a chosen device gives the same result as `default.qubit`. [(897)](https://github.com/PennyLaneAI/pennylane/pull/897)

Three tests are added:

- `test_hermitian_expectation`,
- `test_pauliz_expectation_analytic`, and
- `test_random_circuit`.

* Adds the following agnostic tensor manipulation functions to the `qml.math` module: `abs`, `angle`, `arcsin`, `concatenate`, `dot`, `squeeze`, `sqrt`, `sum`, `take`, `where`. These functions are required to fully support end-to-end differentiable Mottonen and Amplitude embedding. [(922)](https://github.com/PennyLaneAI/pennylane/pull/922) [(#1011)](https://github.com/PennyLaneAI/pennylane/pull/1011)

* The `qml.math` module now supports JAX. [(985)](https://github.com/XanaduAI/software-docs/pull/274)

* Several improvements have been made to the `Wires` class to reduce overhead and simplify the logic of how wire labels are interpreted: [(1019)](https://github.com/PennyLaneAI/pennylane/pull/1019) [(#1010)](https://github.com/PennyLaneAI/pennylane/pull/1010) [(#1005)](https://github.com/PennyLaneAI/pennylane/pull/1005) [(#983)](https://github.com/PennyLaneAI/pennylane/pull/983) [(#967)](https://github.com/PennyLaneAI/pennylane/pull/967)

- If the input `wires` to a wires class instantiation `Wires(wires)` can be iterated over, its elements are interpreted as wire labels. Otherwise, `wires` is interpreted as a single wire label. The only exception to this are strings, which are always interpreted as a single wire label, so users can address wires with labels such as `"ancilla"`.

- Any type can now be a wire label as long as it is hashable. The hash is used to establish the uniqueness of two labels.

- Indexing wires objects now returns a label, instead of a new `Wires` object. For example:

pycon
>>> w = Wires([0, 1, 2])
>>> w[1]
>>> 1


- The check for uniqueness of wires moved from `Wires` instantiation to the `qml.wires._process` function in order to reduce overhead from repeated creation of `Wires` instances.

- Calls to the `Wires` class are substantially reduced, for example by avoiding to call Wires on Wires instances on `Operation` instantiation, and by using labels instead of `Wires` objects inside the default qubit device.

* Adds the `PauliRot` generator to the `qml.operation` module. This generator is required to construct the metric tensor. [(963)](https://github.com/PennyLaneAI/pennylane/pull/963)

* The templates are modified to make use of the new `qml.math` module, for framework-agnostic tensor manipulation. This allows the template library to be differentiable in backpropagation mode (`diff_method="backprop"`). [(873)](https://github.com/PennyLaneAI/pennylane/pull/873)

* The circuit drawer now allows for the wire order to be (optionally) modified: [(992)](https://github.com/PennyLaneAI/pennylane/pull/992)

pycon
>>> dev = qml.device('default.qubit', wires=["a", -1, "q2"])
>>> qml.qnode(dev)
... def circuit():
... qml.Hadamard(wires=-1)
... qml.CNOT(wires=["a", "q2"])
... qml.RX(0.2, wires="a")
... return qml.expval(qml.PauliX(wires="q2"))


Printing with default wire order of the device:

pycon
>>> print(circuit.draw())
a: ─────╭C──RX(0.2)──┤
-1: ──H──│────────────┤
q2: ─────╰X───────────┤ ⟨X⟩


Changing the wire order:

pycon
>>> print(circuit.draw(wire_order=["q2", "a", -1]))
q2: ──╭X───────────┤ ⟨X⟩
a: ──╰C──RX(0.2)──┤
-1: ───H───────────┤


<h3>Breaking changes</h3>

* QNodes using the new PennyLane core will no longer accept ragged arrays as inputs.

* When using the new PennyLane core and the Autograd interface, non-differentiable data passed as a QNode argument or a gate must have the `requires_grad` property set to `False`:

python
qml.qnode(dev)
def circuit(weights, data):
basis_state = np.array([1, 0, 1, 1], requires_grad=False)
qml.BasisState(basis_state, wires=[0, 1, 2, 3])
qml.templates.AmplitudeEmbedding(data, wires=[0, 1, 2, 3])
qml.templates.BasicEntanglerLayers(weights, wires=[0, 1, 2, 3])
return qml.probs(wires=0)

data = np.array(data, requires_grad=False)
weights = np.array(weights, requires_grad=True)
circuit(weights, data)


<h3>Bug fixes</h3>

* Fixes an issue where if the constituent observables of a tensor product do not exist in the queue, an error is raised. With this fix, they are first queued before annotation occurs. [(1038)](https://github.com/PennyLaneAI/pennylane/pull/1038)

* Fixes an issue with tape expansions where information about sampling (specifically the `is_sampled` tape attribute) was not preserved. [(1027)](https://github.com/PennyLaneAI/pennylane/pull/1027)

* Tape expansion was not properly taking into devices that supported inverse operations, causing inverse operations to be unnecessarily decomposed. The QNode tape expansion logic, as well as the `Operation.expand()` method, has been modified to fix this. [(956)](https://github.com/PennyLaneAI/pennylane/pull/956)

* Fixes an issue where the Autograd interface was not unwrapping non-differentiable PennyLane tensors, which can cause issues on some devices. [(941)](https://github.com/PennyLaneAI/pennylane/pull/941)

* `qml.vqe.Hamiltonian` prints any observable with any number of strings. [(987)](https://github.com/PennyLaneAI/pennylane/pull/987)

* Fixes a bug where parameter-shift differentiation would fail if the QNode contained a single probability output. [(1007)](https://github.com/PennyLaneAI/pennylane/pull/1007)

* Fixes an issue when using trainable parameters that are lists/arrays with `tape.vjp`. [(1042)](https://github.com/PennyLaneAI/pennylane/pull/1042)

* The `TensorN` observable is updated to support being copied without any parameters or wires passed. [(1047)](https://github.com/PennyLaneAI/pennylane/pull/1047)

* Fixed deprecation warning when importing `Sequence` from `collections` instead of `collections.abc` in `vqe/vqe.py`. [(1051)](https://github.com/PennyLaneAI/pennylane/pull/1051)

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Juan Miguel Arrazola, Thomas Bromley, Olivia Di Matteo, Theodor Isacsson, Josh Izaac, Christina Lee, Alejandro Montanez, Steven Oud, Chase Roberts, Sankalp Sanand, Maria Schuld, Antal Száva, David Wierichs, Jiahao Yao.

0.13.0post2

A minor post-release to update the main page of the PennyLane documentation.

Page 7 of 11

© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.