New features since last release
New and improved simulators
-
PennyLane now supports a new device,
default.mixed
, designed for simulating mixed-state quantum computations. This enables native support for implementing noisy channels in a circuit, which generally map pure states to mixed states. (#794) (#807) (#819)The device can be initialized as
>>> dev = qml.device("default.mixed", wires=1)
This allows the construction of QNodes that include non-unitary operations, such as noisy channels:
>>> @qml.qnode(dev) ... def circuit(params): ... qml.RX(params[0], wires=0) ... qml.RY(params[1], wires=0) ... qml.AmplitudeDamping(0.5, wires=0) ... return qml.expval(qml.PauliZ(0)) >>> print(circuit([0.54, 0.12])) 0.9257702929524184 >>> print(circuit([0, np.pi])) 0.0
New tools for optimizing measurements
-
The new
grouping
module provides functionality for grouping simultaneously measurable Pauli word observables. (#761) (#850) (#852)-
The
optimize_measurements
function will take as input a list of Pauli word observables and their corresponding coefficients (if any), and will return the partitioned Pauli terms diagonalized in the measurement basis and the corresponding diagonalizing circuits.from pennylane.grouping import optimize_measurements h, nr_qubits = qml.qchem.molecular_hamiltonian("h2", "h2.xyz") rotations, grouped_ops, grouped_coeffs = optimize_measurements(h.ops, h.coeffs, grouping="qwc")
The diagonalizing circuits of
rotations
correspond to the diagonalized Pauli word groupings ofgrouped_ops
. -
Pauli word partitioning utilities are performed by the
PauliGroupingStrategy
class. An input list of Pauli words can be partitioned into mutually commuting, qubit-wise-commuting, or anticommuting groupings.For example, partitioning Pauli words into anticommutative groupings by the Recursive Largest First (RLF) graph colouring heuristic:
from pennylane import PauliX, PauliY, PauliZ, Identity from pennylane.grouping import group_observables pauli_words = [ Identity('a') @ Identity('b'), Identity('a') @ PauliX('b'), Identity('a') @ PauliY('b'), PauliZ('a') @ PauliX('b'), PauliZ('a') @ PauliY('b'), PauliZ('a') @ PauliZ('b') ] groupings = group_observables(pauli_words, grouping_type='anticommuting', method='rlf')
-
Various utility functions are included for obtaining and manipulating Pauli words in the binary symplectic vector space representation.
For instance, two Pauli words may be converted to their binary vector representation:
>>> from pennylane.grouping import pauli_to_binary >>> from pennylane.wires import Wires >>> wire_map = {Wires('a'): 0, Wires('b'): 1} >>> pauli_vec_1 = pauli_to_binary(qml.PauliX('a') @ qml.PauliY('b')) >>> pauli_vec_2 = pauli_to_binary(qml.PauliZ('a') @ qml.PauliZ('b')) >>> pauli_vec_1 [1. 1. 0. 1.] >>> pauli_vec_2 [0. 0. 1. 1.]
Their product up to a phase may be computed by taking the sum of their binary vector representations, and returned in the operator representation.
>>> from pennylane.grouping import binary_to_pauli >>> binary_to_pauli((pauli_vec_1 + pauli_vec_2) % 2, wire_map) Tensor product ['PauliY', 'PauliX']: 0 params, wires ['a', 'b']
For more details on the grouping module, see the grouping module documentation
-
Returning the quantum state from simulators
-
The quantum state of a QNode can now be returned using the
qml.state()
return function. (#818)import pennylane as qml dev = qml.device("default.qubit", wires=3) qml.enable_tape() @qml.qnode(dev) def qfunc(x, y): qml.RZ(x, wires=0) qml.CNOT(wires=[0, 1]) qml.RY(y, wires=1) qml.CNOT(wires=[0, 2]) return qml.state() >>> qfunc(0.56, 0.1) array([0.95985437-0.27601028j, 0. +0.j , 0.04803275-0.01381203j, 0. +0.j , 0. +0.j , 0. +0.j , 0. +0.j , 0. +0.j ])
Differentiating the state is currently available when using the classical backpropagation differentiation method (
diff_method="backprop"
) with a compatible device, and when using the new tape mode.
New operations and channels
-
PennyLane now includes standard channels such as the Amplitude-damping, Phase-damping, and Depolarizing channels, as well as the ability to make custom qubit channels. (#760) (#766) (#778)
-
The controlled-Y operation is now available via
qml.CY
. For devices that do not natively support the controlled-Y operation, it will be decomposed intoqml.RY
,qml.CNOT
, andqml.S
operations. (#806)
Preview the next-generation PennyLane QNode
-
The new PennyLane
tape
module provides a re-formulated QNode class, rewritten from the ground-up, that uses a newQuantumTape
object to represent the QNode's quantum circuit. Tape mode provides several advantages over the standard PennyLane QNode. (#785) (#792) (#796) (#800) (#803) (#804) (#805) (#808) (#810) (#811) (#815) (#820) (#823) (#824) (#829)-
Support for in-QNode classical processing: Tape mode allows for differentiable classical processing within the QNode.
-
No more Variable wrapping: In tape mode, QNode arguments no longer become
Variable
objects within the QNode. -
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.
-
Unifying all QNodes: The tape-mode QNode merges all QNodes (including the
JacobianQNode
and thePassthruQNode
) into a single unified QNode, with identical behaviour regardless of the differentiation type. -
Optimizations: Tape mode provides various performance optimizations, reducing pre- and post-processing overhead, and reduces the number of quantum evaluations in certain cases.
Note that tape mode is experimental, and does not currently have feature-parity with the existing QNode. Feedback and bug reports are encouraged and will help improve the new tape mode.
Tape mode can be enabled globally via the
qml.enable_tape
function, without changing your PennyLane code:qml.enable_tape() dev = qml.device("default.qubit", wires=1) @qml.qnode(dev, interface="tf") def circuit(p): print("Parameter value:", p) qml.RX(tf.sin(p[0])**2 + p[1], wires=0) return qml.expval(qml.PauliZ(0))
For more details, please see the tape mode documentation.
-
Improvements
-
QNode caching has been introduced, allowing the QNode to keep track of the results of previous device executions and reuse those results in subsequent calls. Note that QNode caching is only supported in the new and experimental tape-mode. (#817)
Caching is available by passing a
caching
argument to the QNode:dev = qml.device("default.qubit", wires=2) qml.enable_tape() @qml.qnode(dev, caching=10) # cache up to 10 evaluations def qfunc(x): qml.RX(x, wires=0) qml.RX(0.3, wires=1) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(1)) qfunc(0.1) # first evaluation executes on the device qfunc(0.1) # second evaluation accesses the cached result
-
Sped up the application of certain gates in
default.qubit
by using array/tensor manipulation tricks. The following gates are affected:PauliX
,PauliY
,PauliZ
,Hadamard
,SWAP
,S
,T
,CNOT
,CZ
. (#772) -
The computation of marginal probabilities has been made more efficient for devices with a large number of wires, achieving in some cases a 5x speedup. (#799)
-
Adds arithmetic operations (addition, tensor product, subtraction, and scalar multiplication) between
Hamiltonian
,Tensor
, andObservable
objects, and inline arithmetic operations between Hamiltonians and other observables. (#765)Hamiltonians can now easily be defined as sums of observables:
>>> H = 3 * qml.PauliZ(0) - (qml.PauliX(0) @ qml.PauliX(1)) + qml.Hamiltonian([4], [qml.PauliZ(0)]) >>> print(H) (7.0) [Z0] + (-1.0) [X0 X1]
-
Adds
compare()
method toObservable
andHamiltonian
classes, which allows for comparison between observable quantities. (#765)>>> H = qml.Hamiltonian([1], [qml.PauliZ(0)]) >>> obs = qml.PauliZ(0) @ qml.Identity(1) >>> print(H.compare(obs)) True
>>> H = qml.Hamiltonian([2], [qml.PauliZ(0)]) >>> obs = qml.PauliZ(1) @ qml.Identity(0) >>> print(H.compare(obs)) False
-
Adds
simplify()
method to theHamiltonian
class. (#765)>>> H = qml.Hamiltonian([1, 2], [qml.PauliZ(0), qml.PauliZ(0) @ qml.Identity(1)]) >>> H.simplify() >>> print(H) (3.0) [Z0]
-
Added a new bit-flip mixer to the
qml.qaoa
module. (#774) -
Summation of two
Wires
objects is now supported and will return aWires
object containing the set of all wires defined by the terms in the summation. (#812)
Breaking changes
-
The PennyLane NumPy module now returns scalar (zero-dimensional) arrays where Python scalars were previously returned. (#820) (#833)
For example, this affects array element indexing, and summation:
>>> x = np.array([1, 2, 3], requires_grad=False) >>> x[0] tensor(1, requires_grad=False) >>> np.sum(x) tensor(6, requires_grad=True)
This may require small updates to user code. A convenience method,
np.tensor.unwrap()
, has been added to help ease the transition. This converts PennyLane NumPy tensors to standard NumPy arrays and Python scalars:>>> x = np.array(1.543, requires_grad=False) >>> x.unwrap() 1.543
Note, however, that information regarding array differentiability will be lost.
-
The device capabilities dictionary has been redesigned, for clarity and robustness. In particular, the capabilities dictionary is now inherited from the parent class, various keys have more expressive names, and all keys are now defined in the base device class. For more details, please refer to the developer documentation. (#781)
PennyLane-QChem
-
The functions
one_particle
andtwo_particle
have been implemented to extend PennyLane-QChem capabilities to construct observables of many-body quantum systems. These functions can be used in conjunction with theobservable
function to construct electronic structure hamiltonians involving one- and two-particle operators. (#809) -
The function
observable
in theobs
module has been generalized to build many-body observables combining one- and two-particle operators (e.g., Hamiltonians) (#791) -
Fix calculation of the contribution of core orbitals to two-particle operators in the function two_particle. (#825)
Bug fixes
-
Changed to use lists for storing variable values inside
BaseQNode
allowing complex matrices to be passed toQubitUnitary
. (#773) -
Fixed a bug within
default.qubit
, resulting in greater efficiency when applying a state vector to all wires on the device. (#849)
Documentation
- Equations have been added to the
qml.sample
andqml.probs
docstrings to clarify the mathematical foundation of the performed measurements. (#843)
Contributors
This release contains contributions from (in alphabetical order):
Aroosa Ijaz, Juan Miguel Arrazola, Thomas Bromley, Jack Ceroni, Alain Delgado Gran, Josh Izaac, Soran Jahangiri, Nathan Killoran, Robert Lang, Cedric Lin, Olivia Di Matteo, Nicolás Quesada, Maria Schuld, Antal Száva.