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.
# This code block gets replaced with the table of contents. # Documentation: https://www.gatsbyjs.com/plugins/gatsby-remark-table-of-contents/
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
The new qml.classical_shadow()
measurement will return both bits
(0 or 1 if the 1 or -1 eigenvalue is sampled, respectively) and recipes
(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 qml.shadow_expval()
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
Fully-differentiable QNode transforms for both new classical-shadows measurements are also available via qml.shadows.shadow_state
and qml.shadows.shadow_expval
, respectively.
For convenient post-processing, we’ve also added the ability to calculate general Renyi entropies by way of the ClassicalShadow
class’ 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
. The 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 default.qutrit
devices:
- 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. - The
qml.TAdd
andqml.TSWAP
operations, which are the qutrit analogs of the CNOT and SWAP operations, respectively. - Custom unitary operations via
qml.QutritUnitary
. qml.state
andqml.probs
measurements.- Measuring user-specified Hermitian matrix observables via
qml.THermitian
.
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π.
>>> op1 = qml.RX(30.0, wires=0) >>> qml.simplify(op1) RX(4.867258771281655, wires=[0]) >>> op2 = qml.RX(4 * np.pi, wires=0) >>> qml.simplify(op2) Identity(wires=[0])
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.
Use 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.
Improvements 🛠
In addition to the new features listed above, the release contains a wide array of improvements and optimizations:
- By default,
qml.counts
only returns the outcomes observed in sampling. Optionally, specifyingqml.counts(all_outcomes=True)
will return a dictionary containing all possible outcomes. qml.matrix
can 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.prod
andqml.op_sum
now support thesparse_matrix()
method, and their matrix computation, sparse or not, is now more efficient. - You can now iterate over products and sums of operators created from
qml.op_sum
andqml.prod
. - Both
lightning.qubit
andlightning.gpu
now natively supportqml.Hamiltonian
expectation values in the forward pass. In addition,lightning.gpu
also supportsqml.Hamiltonian
expectation values with the adjoint Jacobian gradient method. - The
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.inv()
andop.inverse=value
. Please useqml.adjoint(op)
,qml.pow(op, -1)
, orop ** -1
instead. 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.is_hermitian
. qml.grouping.utils.is_commuting
has been removed, and its Pauli word logic is now part ofqml.is_commuting
.qml.is_commuting
has been moved frompennylane.transforms.commutation_dag
topennylane.ops.functions
.
These highlights are just scratching the surface — check out the full release notes for more details.
Contributors ✍️
As always, this release would not have been possible without the hard work of our development team and contributors:
Ali Asadi, Juan Miguel Arrazola, Utkarsh Azad, Thomas 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
About the authors
Josh Izaac
Josh is a theoretical physicist, software tinkerer, and occasional baker. At Xanadu, he contributes to the development and growth of Xanadu’s open-source quantum software products.
Isaac De Vlugt
My job is to help manage the PennyLane and Catalyst feature roadmap... and spam lots of emojis in the chat 🤠