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.
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
>>> 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=, log_base=2) >>> circuit_entropy(np.pi/2) 1.0
and mutual information via
>>> dev = qml.device("default.qubit", wires=2) >>> @qml.qnode(dev) ... def circuit(x): ... qml.IsingXX(x, wires=[0,1]) ... return qml.mutual_info(wires0=, wires1=, 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
dev = qml.device("default.qubit", wires=3) @qml.qnode(dev) def circ(params): qml.RY(params, wires=1) qml.CNOT(wires=(1,0)) qml.RY(params, wires=1) qml.RZ(params, 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]
- the fidelity between two arbitrary states via
- the Von Neumann entropy via
- the mutual information via
- reduced density matrices via
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
qml.qinfo.quantum_fisher, which are both hardware compatible).
More exciting features can be found in the new
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
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:
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=) qml.RY(y, wires=) qml.CNOT(wires=[0, 1]) return qml.probs(wires=)
>>> 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) >>> jax.grad(cost, argnums=)(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:
qml.IsingXYgate is now available.
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_partialis available which behaves similarly to
functools.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_commutingis 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>
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.
argnumkeyword argument for
qml.gradients.param_shift_hessianis now allowed to be a two-dimensional Boolean
array_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, wires=0) qml.RY(x, wires=0) qml.RX(x, 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
qml.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
lightning.gpu. For more details, please see the release notes for lightning.qubit and lightning.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.
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
- The module
qml.gradients.param_shift_hessianhas been renamed to
qml.gradients.parameter_shift_hessianin order to distinguish it from the identically named function. Note that the
param_shift_hessianfunction is unaffected by this change and can be invoked in the same manner as before via the
- The properties
Operatorclass were replaced with the methods
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:
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.