They say good things come in threes… well 3^3 = 27 🤯! But if that isn’t enough to persuade you about how good version 0.27 of PennyLane is, then check out all of the awesome new functionality below.
# This code block gets replaced with the table of contents. # Documentation: https://www.gatsbyjs.com/plugins/gatsby-remark-table-of-contents/
Get your fix of quantum data 💾
Classical machine learning ushered in many standardized datasets that are widely used for benchmarking new algorithms. PennyLane is taking steps in the analogous quantum direction with our all-new qml.data module. A quantum dataset can be comprised of anything that is an input to or an output from a quantum device. Easily browse, download, and use a wide variety of quantum data — including input to your quantum functions, and pre-generated outputs.
Downloadable datasets
With this release, there are a variety of quantum datasets available for dowload and we will continue to add more in the future. The list of currently available datasets can be accessed directly within PennyLane via qml.data.list_datasets()
. To load one of these datasets, use qml.data.load()
.
For example, let’s load the data corresponding to the Hydrogen molecule, with bond length 1.1:
>>> H2data = qml.data.load(data_name="qchem", molname="H2", basis="STO-3G", bondlength=1.1) >>> print(H2data) [<Dataset = description: qchem/H2/STO-3G/1.1, attributes: ['molecule', 'hamiltonian', ...]>]
Once a dataset is loaded, its properties can be accessed and used directly in PennyLane workflows:
>>> N = H2data[0].hamiltonian.wires >>> dev = qml.device('default.qubit', wires=N) >>> @qml.qnode(dev) ... def circuit(): ... return qml.expval(H2data[0].hamiltonian) >>> print(circuit()) 0.4810692051726486
Create custom datasets
Create your own custom quantum datasets with qml.data.Dataset
:
>>> H = 1.0 * qml.PauliZ(wires=0) + 0.5 * qml.PauliX(wires=1) >>> E = np.linalg.eigvalsh(qml.matrix(H)) >>> my_data = qml.data.Dataset(data_name='Example', hamiltonian=H, energies=E) >>> my_data.data_name 'Example' >>> my_data.hamiltonian (0.5) [X1] + (1.0) [Z0] >>> my_data.energies array([-1.5, -0.5, 0.5, 1.5])
Saving and reading from your custom datasets is as easy as using qml.data.Dataset.write
and qml.data.Dataset.read
, respectively.
Check out the full release notes for more information. If you have any feedback, or additional datasets you would like to see included, let us know either on GitHub or on our discussion forum.
Adaptive optimization 🏃🏋️🏊
PennyLane adapts to new discoveries in quantum computing, machine learning, and chemistry. Circuits themselves can adapt too… meta adaptation!
In addition to qml.LieAlgebraOptimizer
, v0.27 introduces a new adaptive optimizer: qml.AdaptiveOptimizer
. The qml.AdaptiveOptimizer
takes an initial circuit and a collection of operators as input, and adds a selected gate to the circuit at each optimization step. This process is then repeated until the cost function has converged to a local minimum (within a given threshold).
Use the adaptive optimizer to build, explore, and even generalize influential algorithms such as ADAPT-VQE!
qml.AdaptiveOptimizer
is defined like any other optimizer:
opt = qml.optimize.AdaptiveOptimizer()
Next, let’s define an ADAPT-VQE procedure by:
- creating a Hamiltonian
- defining an
operator_pool
which our adaptive optimization will use to grow the circuit - creating a circuit to optimize
symbols = ["H", "H", "H"] geometry = np.array([[0.01076341, 0.04449877, 0.0], [0.98729513, 1.63059094, 0.0], [1.87262415, -0.00815842, 0.0]], requires_grad=False) H, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, charge = 1) n_electrons = 2 singles, doubles = qml.qchem.excitations(n_electrons, qubits) singles_excitations = [qml.SingleExcitation(0.0, x) for x in singles] doubles_excitations = [qml.DoubleExcitation(0.0, x) for x in doubles] operator_pool = doubles_excitations + singles_excitations hf_state = qml.qchem.hf_state(n_electrons, qubits) dev = qml.device("default.qubit", wires=qubits) @qml.qnode(dev) def circuit(): qml.BasisState(hf_state, wires=range(qubits)) return qml.expval(H)
Now we optimize!
for i in range(len(operator_pool)): circuit, energy, gradient = opt.step_and_cost( circuit, operator_pool, drain_pool=True ) if gradient < 1e-3: break
For a detailed breakdown of its implementation, check out our demo.
QNodes are smarter than ever 🧩
Wouldn’t it be nice if PennyLane just knew what machine learning library it needed to interface with? It sure would. Fret no more! QNodes now accept an auto
argument which automatically detects the machine learning library to use.
import pennylane as qml import torch import jax from jax import numpy as jnp dev = qml.device("default.qubit", wires=2) @qml.qnode(dev, interface="auto") def circuit(weight): qml.RX(weight[0], wires=0) qml.RY(weight[1], wires=1) return qml.expval(qml.PauliZ(0)) >>> circuit(torch.tensor([0, 1])) tensor(1.0000, dtype=torch.float64) >>> jax.grad(circuit)(jnp.array([1.34, 1.5])) DeviceArray([-9.7348452e-01, -2.2351742e-08], dtype=float32)
Upgraded JAX-JIT gradient support 🏎
With this release, JAX-JIT support for computing the gradient of QNodes that return a single vector of probabilities or multiple expectation values is now available.
from jax.config import config config.update("jax_enable_x64", True) dev = qml.device("lightning.qubit", wires=2) @jax.jit @qml.qnode(dev, diff_method="parameter-shift", interface="jax") def circuit(x, y): qml.RY(x, wires=0) qml.RY(y, wires=1) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) x = jnp.array(1.0) y = jnp.array(2.0) >>> jax.jacobian(circuit, argnums=[0, 1])(x, y) (DeviceArray([-0.84147098, 0.35017549], dtype=float64, weak_type=True), DeviceArray([ 0. , -0.4912955], dtype=float64, weak_type=True))
Note that this change depends on jax.pure_callback
, which requires jax>=0.3.17
.
Improvements 🛠
In addition to the new features listed above, the release contains a wide array of improvements and optimizations:
qml.adjoint
now supports parameter batching or broadcasting if the base operation supports parameter broadcasting.qml.OrbitalRotation
is now decomposed into twoqml.SingleExcitation
operations for faster execution and more efficient parameter-shift gradient calculations on devices that natively supportqml.SingleExcitation
.- Added support for sums and products of operator classes with scalar tensors of any interface (NumPy, JAX, Tensorflow, and PyTorch).
- qml.Identity now accepts multiple wires.
- Improved the performance of the
qml.math.expand_matrix
function for dense and sparse matrices. - Explicit support for
qml.SparseHamiltonian
using the adjoint gradient method withlightning.gpu
. This can result in more efficient simulations when working with large Hamiltonians on a single GPU.
Deprecations and breaking changes 💔
As new things are added, outdated features are removed. To keep track of things in the deprecation pipeline, we’ve created a deprecation page.
Here’s what will be changing in this release:
- The grouping module
qml.grouping
has been deprecated. Useqml.pauli
orqml.pauli.grouping
instead. The module will still be available until v0.28. Operator.compute_terms
is removed. On a specific instance of an operator,op.terms()
can be used instead. There is no longer a static method for this.
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:
Kamal Mohamed Ali, Guillermo Alonso-Linaje, Juan Miguel Arrazola, Utkarsh Azad, Thomas Bromley, Albert Mitjans Coma, Isaac De Vlugt, Olivia Di Matteo, Amintor Dusko, Lillian M. A. Frederiksen, Diego Guala, Josh Izaac, Soran Jahangiri, Edward Jiang, Korbinian Kottmann, Christina Lee, Romain Moyard, Lee J. O’Riordan, Mudit Pandey, Chae-Yeun Park, Monit Sharma, Shuli Shu Matthew Silverman, Jay Soni, Antal Száva, Trevor Vincent, and David Wierichs.
About the author
Isaac De Vlugt
My job is to help manage the PennyLane and Catalyst feature roadmap... and spam lots of emojis in the chat 🤠