The latest-and-greatest release of PennyLane is now out and available for everyone to use. It comes with many new additions, including a brand new quantum information module, parameter broadcasting, improvements to the JAX JIT interface, 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/
All new quantum information quantities 📏
Quantum information functionalities have entered the fold! QNodes can now measure and be transformed to output quantum information quantities while remaining differentiable on statevector devices.
There is a multitude of high-impact research and development showing how quantum information quantities enhance our understanding, efficiency, and speed of optimizing quantum circuits.
Bolster your PennyLane experience with these new features 👇
Two new QNode measurements
Two new quantities can now be measured in QNodes, including the Von Neumann entropy via qml.vn_entropy
:
>>> dev = qml.device("default.qubit", wires=2) >>> @qml.qnode(dev) ... def circuit_entropy(x): ... qml.IsingXX(x, wires=[0,1]) ... return qml.vn_entropy(wires=[0], log_base=2) >>> circuit_entropy(np.pi/2) 1.0
and mutual information via qml.mutual_info
:
>>> dev = qml.device("default.qubit", wires=2) >>> @qml.qnode(dev) ... def circuit(x): ... qml.IsingXX(x, wires=[0,1]) ... return qml.mutual_info(wires0=[0], wires1=[1], log_base=2) >>> circuit(np.pi/2) 2.0
Quantum information transforms for QNodes
Transform your existing QNode to suit your quantum information needs, including classical and quantum Fisher information via qml.qinfo.classical_fisher
and qml.qinfo.quantum_fisher
, respectively:
dev = qml.device("default.qubit", wires=3) @qml.qnode(dev) def circ(params): qml.RY(params[0], wires=1) qml.CNOT(wires=(1,0)) qml.RY(params[1], wires=1) qml.RZ(params[2], wires=1) return qml.expval(qml.PauliX(0) @ qml.PauliX(1) - 0.5 * qml.PauliZ(1)) params = np.array([0.5, 1., 0.2], requires_grad=True) cfim = qml.qinfo.classical_fisher(circ)(params) qfim = qml.qinfo.quantum_fisher(circ)(params)
These quantities are typically employed in variational optimization schemes to tilt the gradient in a more favourable direction — producing what is known as the natural gradient. For example:
>>> grad = qml.grad(circ)(params) >>> cfim @ grad # natural gradient [ 5.94225615e-01 -2.61509542e-02 -1.18674655e-18] >>> qfim @ grad # quantum natural gradient [ 0.59422561 -0.02615095 -0.03989212]
Additional transforms,
- the fidelity between two arbitrary states via
qml.qinfo.fidelity
, - the Von Neumann entropy via
qml.qinfo.vn_entropy
, - the mutual information via
qml.qinfo.mutual_info
, and - reduced density matrices via
qml.qinfo.reduced_dm
also exist with similar functionality.
Currently, all quantum information measurements and transforms are differentiable, but only support statevector devices, with hardware support to come in a future release (with the exception of qml.qinfo.classical_fisher
and qml.qinfo.quantum_fisher
, which are both hardware compatible).
More exciting features can be found in the new qinfo
module.
Support for quantum parameter broadcasting 📡
Are you tired of having to call your circuit repeatedly with different values? Well, now you can simply pass your circuit multidimensional arrays of parameters that you want to evaluate it with!
>>> dev = qml.device('default.qubit', wires=1) >>> @qml.qnode(dev) ... def circuit_rx(x, z): ... qml.RX(x, wires=0) ... qml.RZ(z, wires=0) ... qml.RY(0.3, wires=0) ... return qml.probs(wires=0) >>> circuit_rx([0.1, 0.2], [0.3, 0.4]) tensor([[0.97092256, 0.02907744], [0.95671515, 0.04328485]], requires_grad=True)
Parameter broadcasting refers to passing parameters with additional leading dimensions to quantum operators; additional dimensions will flow through the computation, and produce additional dimensions at the output.
Parameter broadcasting is supported on all devices, hardware and simulator. Note that if not natively supported by the underlying device, parameter broadcasting may result in additional quantum device evaluations. We are working on native broadcast support for all devices, which will make parameter broadcasting faster than serially evaluating your circuit with, say, a for
loop.
Improved JAX JIT support 🏎
While Python is a great environment for rapid prototyping, as PennyLane scales up we often need the speed that comes from compiled languages such as C++. However, with just-in-time (JIT) compilation with JAX, you get the best of both worlds: the flexibility of Python, with the speed of compilation.
With this release, PennyLane’s support of JAX JIT compilation gets even better, with support for vector-valued QNodes. This enables new types of workflows and significant performance boosts.
Vector-valued QNodes include those with:
qml.probs
;qml.state
;qml.sample
or- multiple
qml.expval
/qml.var
measurements. Consider a QNode that returns basis-state probabilities:
dev = qml.device('default.qubit', wires=2) x = jnp.array(0.543) y = jnp.array(-0.654) @jax.jit @qml.qnode(dev, diff_method="parameter-shift", interface="jax") def circuit(x, y): qml.RX(x, wires=[0]) qml.RY(y, wires=[1]) qml.CNOT(wires=[0, 1]) return qml.probs(wires=[1]) >>> circuit(x, y) DeviceArray([0.8397495 , 0.16025047], dtype=float32)
Note that computing the jacobian of vector-valued QNodes is not supported with JAX JIT. The output of vector-valued QNodes can, however, be used in the definition of scalar-valued cost functions whose gradients can be computed.
For example, one can define a cost function that outputs the first element of the probability vector:
>>> def cost(x, y): ... return circuit(x, y)[0] >>> jax.grad(cost, argnums=[0])(x, y) (DeviceArray(-0.2050439, dtype=float32),)
New operations & transforms 🤖
There are always new operations and transforms to add as we get feedback from our amazing users 🙋 and as research progresses 🧑🔬. Here is what’s new in this release:
- The
qml.IsingXY
gate is now available. - The
qml.ECR
(echoed cross-resonance) operation is now available. This gate is a maximally-entangling gate and is equivalent to a CNOT gate up to single-qubit pre-rotations. - A new transform
qml.batch_partial
is available which behaves similarly tofunctools.partial
, but supports batching in the unevaluated parameters. This is useful for executing a circuit with a batch dimension in some of its parameters. - A new transform
qml.split_non_commuting
is available, which splits a quantum function or tape into multiple functions/tapes determined by groups of commuting observables:
dev = qml.device("default.qubit", wires=1) @qml.transforms.split_non_commuting @qml.qnode(dev) def circuit(x): qml.RX(x,wires=0) return [qml.expval(qml.PauliX(0)), qml.expval(qml.PauliZ(0))] >>> print(qml.draw(circuit)(0.5)) 0: ──RX(0.50)─┤ <X> \ 0: ──RX(0.50)─┤ <Z>
Improvements 🛠
If it ain’t broke, don’t fix it try to improve it 💪. Here’s what we improved in this release:
- Expectation values of multiple non-commuting observables from within a single QNode are now supported.
- Selecting which parts of parameter-shift Hessians are computed is now possible. The
argnum
keyword argument forqml.gradients.param_shift_hessian
is now allowed to be a two-dimensional Booleanarray_like
. Only the indicated entries of the Hessian will then be computed. A particularly useful example is the computation of the diagonal of the Hessian:
dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def circuit(x): qml.RX(x[0], wires=0) qml.RY(x[1], wires=0) qml.RX(x[2], wires=0) return qml.expval(qml.PauliZ(0)) argnum = qml.math.eye(3, dtype=bool) x = np.array([0.2, -0.9, 1.1], requires_grad=True) >>> qml.gradients.param_shift_hessian(circuit, argnum=argnum) tensor([[-0.09928388, 0. , 0. ], [ 0. , -0.27633945, 0. ], [ 0. , 0. , -0.09928388]], requires_grad=True)
- Commuting Pauli operators are now measured faster. The logic that checks for qubit-wise commuting (QWC) observables has been improved, resulting in a performance boost that is noticable when many commuting Pauli operators of the same type are measured.
- When using
expval()
withqml.SparseHamiltonian
, computations are now faster and use less memory thanks to changing representations to Compressed Sparse Row (CSR) format. - Operations and simulations are now remarkably faster with new highly-performant kernels added to
lighting.qubit
andlightning.gpu
. For more details, please see the release notes forlightning.qubit
andlightning.gpu
. The new kernels drastically improve the runtime performance, especially for quantum chemistry problems:
For the full list of improvements, please refer to the release notes.
Deprecations 📟
qml.ExpvalCost
has been deprecated, and usage will now raise a warning.
Instead, it is recommended to simply pass Hamiltonians to the qml.expval
function inside QNodes.
@qml.qnode(dev) def ansatz(params): some_qfunc(params) return qml.expval(Hamiltonian)
Breaking changes 💔
In with the new, out with the old! Here’s what will be removed in this release.
- PennyLane no longer supports TensorFlow
<=2.3
. - The module
qml.gradients.param_shift_hessian
has been renamed toqml.gradients.parameter_shift_hessian
in order to distinguish it from the identically named function. Note that theparam_shift_hessian
function is unaffected by this change and can be invoked in the same manner as before via theqml.gradients
module. - The properties
eigval
andmatrix
from theOperator
class were replaced with the methodseigval()
andmatrix(wire_order=None)
.
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:
Guillermo Alonso-Linaje, Mikhail Andrenkov, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Samuel Banning, Avani Bhardwaj, Thomas Bromley, Albert Mitjans Coma, Isaac De Vlugt, Amintor Dusko, Trent Fridey, Christian Gogolin, Qi Hu, Katharine Hyatt, David Ittah, Josh Izaac, Soran Jahangiri, Edward Jiang, Nathan Killoran, Korbinian Kottmann, Ankit Khandelwal, Christina Lee, Lee James O’Riordan, Chae-Yeun Park, Mason Moreland, Romain Moyard, Maria Schuld, Shuli Shu, Jay Soni, Antal Száva, tal66, David Wierichs, Roeland Wiersema, Trevor Vincent, David Wierichs, WingCode.
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 🤠