PennyLane
  • Why PennyLane
  • Getting Started
  • Documentation
  • Ecosystem
Install
Install
  1. Blog/
  2. Releases/
  3. PennyLane v0.22 released

March 15, 2022

PennyLane v0.22 released

Josh Izaac

Josh Izaac

The latest release of PennyLane is now out and available for everyone to use. It comes with many new additions, including executing large circuits with fewer qubits, differentiable mid-circuit measurements, a new high-performance GPU simulator, tools for quantum debugging, better batching support, 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/

Cut your circuits into fragments for execution with fewer qubits βœ‚

When building new quantum algorithms, we often want to push the limits of what is possible, and test or deploy our algorithms on more and more wires. Unfortunately, all too often we end up constrained in our quest for more qubits by the underlying hardware or simulator device we are using.

With PennyLane v0.22, you can now execute a quantum algorithm that requires N wires on fewer than N wires, by taking advantage of circuit cutting.

Simply β€˜cut’ wires within your QNode, and PennyLane will partition your algorithm into smaller fragments for execution, before combining and post-processing the results.

cut2

Circuit cutting is enabled by decorating a QNode with the @qml.cut_circuit transform. For example, to execute a three-wire circuit on a two-wire device:

dev = qml.device("default.qubit", wires=2) @qml.cut_circuit @qml.qnode(dev) def circuit(x): qml.RX(x, wires=0) qml.RY(0.9, wires=1) qml.RX(0.3, wires=2) qml.CZ(wires=[0, 1]) qml.RY(-0.4, wires=0) # cut the circuit into two fragments at wire 1 qml.WireCut(wires=1) qml.CZ(wires=[1, 2]) return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1) @ qml.PauliZ(2))

Instead of executing the circuit directly, it will be partitioned into smaller fragments according to the qml.WireCut locations, and each fragment executed multiple times. Combining the results of the fragment executions will recover the expected output of the original uncut circuit:

>>> x = np.array(0.531, requires_grad=True) >>> circuit(0.531) 0.47165198882111165

As always, circuit cutting support is also differentiable:

>>> qml.grad(circuit)(x) -0.276982865449393

Note that while circuit cutting allows for executing a circuit on a device with fewer qubits, cutting a circuit can be expensive in terms of the number of executions required as well as additional classical postprocessing. For more details on circuit cutting, check out the qml.cut_circuit documentation page.

Quantum teleport with mid-circuit measurements πŸŒ€

teleport2

One of the most highly requested features over the years has been the ability to perform quantum teleportation with PennyLane, and this is now unlocked πŸ”“ in version 0.22, with the introduction of mid-circuit measurements and conditional operations.

Conditional operations

  • qml.measure() allows circuit measurements to be placed in the middle of a quantum function.
  • The new qml.cond() transform allows operations to be conditioned on the result of a previous measurement.

Use mid-circuit measurements and conditional operations to build and run algorithms such as quantum teleportation, quantum error correction, and quantum error mitigation.

For example, the code below shows how to teleport a qubit from wire 0 to wire 2:

dev = qml.device("default.qubit", wires=3) input_state = np.array([1, -1], requires_grad=False) / np.sqrt(2) @qml.qnode(dev) def teleport(state): # Prepare input state qml.QubitStateVector(state, wires=0) # Prepare Bell state qml.Hadamard(wires=1) qml.CNOT(wires=[1, 2]) # Apply gates qml.CNOT(wires=[0, 1]) qml.Hadamard(wires=0) # Measure first two wires m1 = qml.measure(0) m2 = qml.measure(1) # Condition final wire on results qml.cond(m2 == 1, qml.PauliX)(wires=2) qml.cond(m1 == 1, qml.PauliZ)(wires=2) # Return state on final wire return qml.density_matrix(wires=2) >>> output_state = teleport(input_state) >>> output_state tensor([[ 0.5+0.j, -0.5+0.j], [-0.5+0.j, 0.5+0.j]], requires_grad=True)

We can double-check that the qubit has been teleported by computing the overlap between the input state and the resulting state on wire 2:

>>> input_state.conj() @ output_state @ input_state tensor(1.+0.j, requires_grad=True)

Train mid-circuit measurements by deferring them

If a device doesn’t natively support mid-circuit measurements, the @qml.defer_measurements transform can be applied to the QNode to transform the QNode into one with terminal measurements and controlled operations:

@qml.qnode(dev) @qml.defer_measurements def circuit(x): qml.Hadamard(wires=0) m = qml.measure(0) qml.cond( m == 1, # measurement condition lambda: qml.RX(x**2, wires=1), # qfunc to apply if condition is True (m==1) lambda: qml.RY(x, wires=1) # qfunc to apply if condition is False (m!=1) )() return qml.expval(qml.PauliZ(1)) >>> x = np.array(0.7, requires_grad=True) >>> print(qml.draw(circuit, expansion_strategy="device")(x)) 0: ──H─╭C─────────X─╭C─────────X── 1: ────╰RX(0.49)────╰RY(0.70)───── <Z> >>> circuit(x) tensor(0.82358752, requires_grad=True)

Deferring mid-circuit measurements also enables differentiation: don’t just evaluate your mid-circuit algorithms, but train πŸš‚ them as well!

>>> qml.grad(circuit)(x) -0.651546965338656

For a full description of new capabilities, refer to the Mid-circuit measurements and conditional operations section in the documentation.

Accelerate your simulations with cuQuantum GPU support ⚑

We are excited to announce the release of GPU support for our high-performance lightning.qubit simulator: lightning.gpu. This new device utilizes the NVIDIA cuQuantum library under-the-hood, and includes efficient computation of quantum gradients via adjoint differentiation.

Use lightning.gpu for a significant speed-up for large quantum circuit evaluations, even when using multiple CPU-threads:

lightning_gpu_bm

The lightning.gpu device can be installed via pip:

pip install pennylane-lightning[gpu]

Once installed, it can be loaded and used with any PennyLane QNodes:

dev = qml.device("lightning.gpu", wires=22) @qml.qnode(dev, diff_method="adjoint") def circuit(weights): qml.StronglyEntanglingLayers(weights, wires=list(range(n_wires))) return [qml.expval(qml.PauliZ(i)) for i in range(n_wires)] param_shape = qml.StronglyEntanglingLayers.shape(n_layers=2, n_wires=22) params = np.random.random(param_shape) jac = qml.jacobian(circuit)(params)

For more details on installing and using lightning.gpu, check out the device documentation.

Debug with mid-circuit quantum snapshots πŸ“·

Designing new quantum algorithms is tough, and debugging quantum algorithms is sometimes even harder!

snapshot

To help alleviate the frustration of (quantum) debugging, v0.22 of PennyLane introduces the qml.Snapshot operation, which saves the internal state of simulator devices at arbitrary points within the quantum execution.

dev = qml.device("default.qubit", wires=2) @qml.qnode(dev, interface=None) def circuit(): qml.Snapshot() qml.Hadamard(wires=0) qml.Snapshot("very_important_state") qml.CNOT(wires=[0, 1]) qml.Snapshot() return qml.expval(qml.PauliX(0))

During normal execution, the snapshots are ignored:

>>> circuit() array(0.)

However, when using the qml.snapshots transform, intermediate values of the statevector will be stored and returned alongside the results:

>>> qml.snapshots(circuit)() {0: array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]), 'very_important_state': array([0.70710678+0.j, 0. +0.j, 0.70710678+0.j, 0. +0.j]), 2: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]), 'execution_results': array(0.)}

Currently, snapshots are supported on default.qubit, default.mixed, and default.gaussian, returning representations of the quantum state. Over time, we plan to add more options for snapshots, across both simulator and hardware devices.

Better batching πŸ“¦

We previously added the @qml.batch_params transform to enable batching over trainable parameters in your quantum algorithms. In this release, we extend this support to all gate parameters, including embeddings and state preparation, via @qml.batch_input.

dev = qml.device("default.qubit", wires=2) @qml.batch_input(argnum=0) @qml.qnode(dev, diff_method="parameter-shift", interface="tf") def circuit(inputs, weights): # add a batch dimension to the embedding data qml.AngleEmbedding(inputs, wires=range(2), rotation="Y") qml.RY(weights[0], wires=0) qml.RY(weights[1], wires=1) return qml.expval(qml.PauliZ(1))

Batched input parameters can then be passed during QNode evaluation:

>>> import tensorflow as tf >>> x = tf.random.uniform((10, 2), 0, 1) >>> w = tf.random.uniform((2,), 0, 1) >>> circuit(x, w) <tf.Tensor: shape=(10,), dtype=float64, numpy= array([0.46230079, 0.73971315, 0.95666004, 0.5355225 , 0.66180948, 0.44519553, 0.93874261, 0.9483197 , 0.78737918, 0.90866411])>

Even more mighty quantum transforms πŸ›βž‘πŸ¦‹

From circuit cutting, to conditional operations, snapshots, deferred measurements, and input batching, you must be thinking β€˜Wow! This release couldn’t possibly fit even more transforms!’

And yet! We have barely scratched the surface. Here is a quick rundown of more quantum transforms that are debuting in version 0.22:

  • qml.matrix() for computing the matrix representation of one or more unitary operators.
  • qml.eigvals() for computing the eigenvalues of one or more operators.
  • qml.generator() for computing the generator of a single-parameter unitary operation.
  • qml.commutation_dag() to construct the pairwise-commutation directed acyclic graph (DAG) representation of a quantum circuit.

As always, all transforms support a functional user interface with differentiation support (where possible):

>>> def circuit(theta): ... qml.RX(theta, wires=1) ... qml.PauliZ(wires=0) >>> qml.matrix(circuit)(np.pi / 4) array([[ 0.92387953+0.j, 0.+0.j , 0.-0.38268343j, 0.+0.j], [ 0.+0.j, -0.92387953+0.j, 0.+0.j, 0. +0.38268343j], [ 0. -0.38268343j, 0.+0.j, 0.92387953+0.j, 0.+0.j], [ 0.+0.j, 0.+0.38268343j, 0.+0.j, -0.92387953+0.j]])

Improvements

In addition to the new features listed above, the release contains a wide array of improvements and optimizations:

  • Most compilation transforms, and relevant subroutines, have been updated to support just-in-time compilation with @jax.jit.
  • The frequencies of gate parameters are now accessible as an operation property, and are used for circuit analysis, optimization via the RotosolveOptimizer, and differentiation with the parameter-shift rule (including the general shift rule).
  • When computing gradients on quantum hardware, the parameter-shift rule now uses parameter frequencies to compute general shift rules when operation gradient recipes are not defined. This enables hardware gradient support on a wider set of operations.
  • The text-based drawer accessed via qml.draw() has been improved, with new options for displaying parameters and matrices, an improved algorithm for determining gate positions, and cosmetic improvements.

For the full list of improvements, please refer to the release notes.

Deprecations and breaking changes

As new things are added, outdated features are removed. Here’s what will be changing in this release.

Deprecations

  • qml.transforms.get_unitary_matrix() has been deprecated and will be removed in a future release. For extracting matrices of operations and quantum functions, please use qml.matrix().
  • The qml.finite_diff() function has been deprecated and will be removed in an upcoming release. Instead, qml.gradients.finite_diff() can be used to compute purely quantum gradients (that is, gradients of tapes or QNodes).
  • The MultiControlledX operation now accepts a single wires keyword argument for both control_wires and wires. The single wires keyword should contain all control wires followed by a single target wire.
  • Executing tapes using tape.execute(dev) is deprecated. Please use the qml.execute([tape], dev) function instead.
  • The subclasses of the quantum tape, including JacobianTape, are deprecated. Instead of calling JacobianTape.jacobian() please use a standard QuantumTape, and apply gradient transforms using the qml.gradients module.

In addition, there are several important changes when creating custom operations:

  • The Operator.matrix method has been deprecated and Operator.compute_matrix should be defined instead. Operator matrices can be accessed using qml.matrix(op).
  • The Operator.decomposition method has been deprecated and Operator.compute_decomposition should be defined instead. Operator decompositions can be accessed using Operator.decomposition().
  • The Operator.eigvals method has been deprecated and Operator.compute_eigvals should be defined instead. Operator eigenvalues can be accessed using qml.eigvals(op).
  • The Operator.generator property is now a method, and should return an operator instance representing the generator. Operator generators can be accessed using qml.generator(op).

For more details on adding custom operations in version 0.22, please see the Adding new operators page in the documentation.

Breaking changes

  • The pennylane.measure module has been renamed to pennylane.measurements.

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:

Catalina Albornoz, Jack Y. Araz, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Sam Banning, Thomas Bromley, Olivia Di Matteo, Christian Gogolin, Diego Guala, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Nathan Killoran, Christina Lee, Angus Lowe, Maria Fernanda Morris, Romain Moyard, Zeyue Niu, Lee James O’Riordan, Chae-Yeun Park, Maria Schuld, Jay Soni, Antal SzΓ‘va, Trevor Vincent, and David Wierichs.

About the author

Josh Izaac
Josh Izaac

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.

Last modified: August 06, 2024

Related Blog Posts

PennyLane

PennyLane is an open-source software framework for quantum machine learning, quantum chemistry, and quantum computing, with the ability to run on all hardware. Built with ❀️ by Xanadu.

Stay updated with our newsletter

For researchers

  • Research
  • Features
  • Demos
  • Compilation
  • Datasets
  • Performance
  • Learn
  • Videos
  • Documentation
  • Teach

For learners

  • Learn
  • Codebook
  • Teach
  • Videos
  • Challenges
  • Demos
  • Compilation
  • Glossary

For developers

  • Features
  • Documentation
  • API
  • GitHub
  • Datasets
  • Demos
  • Compilation
  • Performance
  • Devices
  • Catalyst

Β© Copyright 2025 | Xanadu | All rights reserved

TensorFlow, the TensorFlow logo and any related marks are trademarks of Google Inc.

Privacy Policy|Terms of Service|Cookie Policy|Code of Conduct