Version 0.26 of PennyLane is upon us! Brace yourselves for amazing new features, including classical shadows, and qutrits, the QNSPSA optimizer, and more.
Check out the table of contents below, or keep reading to find out more.
Classical shadows 👤
The popular classical shadows measurement protocol is now directly built-in to PennyLane. Return classical shadows from your quantum functions, use them to estimate expectation values, and even perform state tomography on hardware.
The classical-shadow measurement protocol is described in detail in the paper Predicting Many Properties of a Quantum System from Very Few Measurements. As part of the support for classical shadows in this release, two new finite-shot (and of course, fully-differentiable) measurements are available.
Measuring classical shadows
qml.classical_shadow() measurement will return both
bits (0 or 1 if the 1 or -1 eigenvalue is sampled, respectively) and
(the randomized Pauli measurements that are performed for each qubit, labelled by integer):
dev = qml.device("default.qubit", wires=2, shots=3) @qml.qnode(dev) def circuit(): qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) return qml.classical_shadow(wires=[0, 1])
>>> bits, recipes = circuit() >>> bits tensor([[0, 0], [1, 0], [0, 1]], dtype=uint8, requires_grad=True) >>> recipes tensor([[2, 2], [0, 2], [0, 2]], dtype=uint8, requires_grad=True)
Estimating expectation values with shadows
The new measurement
returns an estimate of the expectation value estimation using classical shadows:
dev = qml.device("default.qubit", wires=range(2), shots=10000) @qml.qnode(dev) def circuit(x, H): qml.Hadamard(0) qml.CNOT((0,1)) qml.RX(x, wires=0) return qml.shadow_expval(H) x = np.array(0.5, requires_grad=True) H = qml.Hamiltonian( [1., 1.], [qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliX(0) @ qml.PauliX(1)] )
>>> circuit(x, H), tensor(1.8486, requires_grad=True) >>> qml.grad(circuit)(x, H)) -0.4797000000000001
For convenient post-processing, we’ve also added the ability to calculate general Renyi entropies
by way of the
entropy method, which requires the wires of the subsystem of interest
and the Renyi entropy order:
>>> shadow = qml.ClassicalShadow(bits, recipes) >>> vN_entropy = shadow.entropy(wires=[0, 3], alpha=1)
Qutrits: quantum circuits for tertiary degrees of freedom ☘
Good things come in threes!
An entirely new framework for quantum computing is now simulatable with the addition of qutrit functionalities.
Qutrits are like qubits, but they aren’t binary degrees of freedom, they are tertiary. The advent of qutrits allows for all sorts of interesting theoretical, practical, and algorithmic capabilities that have yet to be discovered.
To facilitate qutrit circuits requires a new device:
default.qutrit device is a Python-based simulator, akin to
default.qubit, and is defined as per usual:
dev = qml.device("default.qutrit", wires=2) @qml.qnode(dev) def circuit(U, obs): qml.TShift(0) qml.TClock(0) qml.QutritUnitary(U, wires=0) return qml.expval(qml.THermitian(obs, wires=0))
>>> U = np.ones([3, 3]) / np.sqrt(3) >>> obs = np.array([[1, 1, 0], [1, -1, 0], [0, 0, np.sqrt(2)]]) / np.sqrt(2) >>> circuit(U, obs) tensor(0.80473785, requires_grad=True)
The following operations are supported on
- The qutrit shift operator,
qml.TShift, and the ternary clock operator,
qml.TClock, as defined in this paper by Yeh et al. (2022), which are the qutrit analogs of the Pauli X and Pauli Z operations, respectively.
qml.TSWAPoperations, which are the qutrit analogs of the CNOT and SWAP operations, respectively.
- Custom unitary operations via
- Measuring user-specified Hermitian matrix observables via
We will continue to add more and more support for qutrits in future releases.
Simplifying just got… simpler 😌
To put it simply, we’ve simplified how
qml.simplify simplifies your circuits. Simple as that.
qml.simplify can now perform the following:
- simplify parametrized operations
- simplify the adjoint and power of specific operators
- group like terms in a sum
- resolve products of Pauli operators
- combine rotation angles of identical rotation gates
Here is an example of
qml.simplify in action with parameterized rotation gates.
In this case, the angles of rotation are simplified to be modulo \(4\pi\).
>>> op1 = qml.RX(30.0, wires=0) >>> qml.simplify(op1) RX(4.867258771281655, wires=) >>> op2 = qml.RX(4 * np.pi, wires=0) >>> qml.simplify(op2) Identity(wires=)
All of these simplification features can be applied directly to quantum functions, QNodes, and tapes via decorating with
@qml.simplify, as well:
dev = qml.device("default.qubit", wires=2) @qml.simplify @qml.qnode(dev) def circuit(): qml.adjoint(qml.prod(qml.RX(1, 0) ** 1, qml.RY(1, 0), qml.RZ(1, 0))) return qml.probs(wires=0)
>>> print(qml.draw(circuit)()) 0: ──RZ(11.57)@RY(11.57)@RX(11.57)─┤ Probs
Enjoy the simple things!
QNSPSA optimizer 💪
Good golly! Another optimizer!
A new optimizer called
qml.QNSPSAOptimizer that implements the quantum natural simultaneous
perturbation stochastic approximation (QNSPSA) method based on
Simultaneous Perturbation Stochastic Approximation of the Quantum Fisher Information is available.
qml.QNSPSAOptimizer is a second-order SPSA algorithm,
which combines the convergence power of the quantum-aware Quantum Natural Gradient
(QNG) optimization method with the reduced quantum evaluations of SPSA methods.
While the QNSPSA optimizer requires additional circuit executions (10 executions per step) compared to standard SPSA optimization (3 executions per step), these additional evaluations are used to provide a stochastic estimation of a second-order metric tensor, which often helps the optimizer to achieve faster convergence.
qml.QNSPSAOptimizer like you would any other optimizer:
max_iterations = 50 opt = qml.QNSPSAOptimizer() for _ in range(max_iterations): params, cost = opt.step_and_cost(cost, params)
Check out our demo on the QNSPSA optimizer for more information.
In addition to the new features listed above, the release contains a wide array of improvements and optimizations:
- By default,
qml.countsonly returns the outcomes observed in sampling. Optionally, specifying
qml.counts(all_outcomes=True)will return a dictionary containing all possible outcomes.
qml.matrixcan now compute the matrix of tapes and QNodes that contain multiple broadcasted operations or non-broadcasted operations after broadcasted ones. A common scenario in which this becomes relevant is the decomposition of broadcasted operations.
- Products and sums of operators created using
qml.op_sumnow support the
sparse_matrix()method, and their matrix computation, sparse or not, is now more efficient.
lightning.gpunow natively support
qml.Hamiltonianexpectation values in the forward pass. In addition,
qml.Hamiltonianexpectation values with the adjoint Jacobian gradient method.
qml.math.expand_matrix()method now allows the sparse matrix representation of an operator to be extended to a larger Hilbert space.
Deprecations and breaking changes 💔
As new things are added, outdated features are removed. Here’s what will be changing in this release:
- In-place inversion is now deprecated. This includes
op.inverse=value. Please use
qml.pow(op, -1), or
op ** -1instead. Note that for unitary operations,
qml.adjoint(op)is the most efficient approach.
- Measuring an operator that might not be hermitian now raises a warning instead of an
error. To definitively determine whether or not an operator is hermitian, use
qml.grouping.utils.is_commutinghas been removed, and its Pauli word logic is now part of
qml.is_commutinghas been moved from
These highlights are just scratching the surface — check out the full release notes for more details.
As always, this release would not have been possible without the hard work of our development team and contributors:
Ali Asadi, Juan Miguel Arrazola, Utkarsh Azad, Tom Bromley, Isaac De Vlugt, Olivia Di Matteo, Amintor Dusko, Yiheng Duan, Lillian Marie Austin Frederiksen, Josh Izaac, Soran Jahangiri, Edward Jiang, Ankit Khandelwal, Korbinian Kottmann, Meenu Kumari, Christina Lee, Albert Mitjans Coma, Romain Moyard, Rashid N. H. M., Zeyue Niu, Lee James O’Riordan, Mudit Pandey, Chae-Yeun Park, Matthew Silverman, Jay Soni, Antal Száva, Trevor Vincent, Cody Wang, David Wierichs