The latest release of PennyLane is now out and available for everyone to use. It comes with many new additions, including automatic circuit cutting, a unification of our quantum chemistry packages, new templates and compilation transforms, 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/
More powerful circuit-cutting ✂
In the previous release of PennyLane, we announced support for circuit cutting — the ability to execute a quantum algorithm that requires N wires on fewer than N wires.
Simply ‘cut’ wires within your QNode using qml.WireCut
, apply the @qml.cut_circuit
transform, and PennyLane will partition your algorithm into smaller fragments for execution, before combining and post-processing the results.
We are excited to now extend circuit cutting to support automatic placement of cut locations, saving you the worry of working out how to split up your circuit! In addition, we have also built in support for cutting circuits that return sample-based measurements.
Automatic circuit cuts
In addition to manually placing where you would like your circuit to be cut, the @qml.cut_circuit
transform now supports automatic cutting via the auto_cutter
argument:
>>> dev = qml.device("default.qubit", wires=2) >>> @qml.cut_circuit(auto_cutter=True) ... @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) ... qml.CZ(wires=[1, 2]) ... return qml.expval(qml.grouping.string_to_pauli_word("ZZZ")) >>> x = np.array(0.531, requires_grad=True) >>> circuit(x) 0.47165198882111165 >>> qml.grad(circuit)(x) -0.276982865449393
Note that the default automatic cutter algorithm utilizes the KaHyPar package to determine the best cut locations, and will require KaHyPar be separately installed. Alternatively, custom cutting functions can be passed to auto_cutter
; these functions should accept a NetworkX MultiDiGraph
representing the circuit, and return the list of edges to cut. Additional arguments to such custom cutting functions can also be directly supplied as keyword arguments to @qml.cut_circuit
.
For more details on the built-in KaHyPar automatic cut placement algorithm, please see qml.transforms.qcut.find_and_place_cuts()
.
Cut your sampling circuits into fragments
Using Monte-Carlo methods, the @qml.cut_circuit_mc
transform allows you to cut sample-based circuits into smaller fragments for execution.
In addition, processing functions can be provided which allow the ‘cut’ samples to be processed into expectation values:
dev = qml.device("default.qubit", wires=2, shots=10000) def ZZ_expectation(bitstring): return (-1) ** np.sum(bitstring) @qml.cut_circuit_mc(classical_processing_fn=ZZ_expectation) @qml.qnode(dev) def circuit(x): qml.RX(0.89, wires=0) qml.RY(0.5, wires=1) qml.RX(1.3, wires=2) qml.CNOT(wires=[0, 1]) # place a cut in the circuit qml.WireCut(wires=1) qml.CNOT(wires=[1, 2]) qml.RX(x, wires=0) qml.RY(0.7, wires=1) qml.RX(2.3, wires=2) return qml.sample(wires=[0, 2]) >>> circuit(x) tensor(-0.776, requires_grad=True)
In addition, @qml.cut_circuit_mc
also supports automatic cut placements with KaHyPar by passing the auto_cutter=True
keyword argument.
Grand quantum chemistry unification ⚛️🏰
In previous releases, PennyLane provided two modules for quantum chemistry workflows:
qml.qchem
, provided by the external packagepennylane-qchem
, for integrating external packages such as PySCF and OpenFermion with PennyLane, as well asqml.hf
, a fully differentiable Hartree-Fock solver.
With PennyLane version 0.23, both packages have been unified into a single module, qml.qchem
, directly built in to PennyLane — no external package installations needed!
The new qml.qchem
module provides all the same features and capabilities you love from the previous version of PennyLane — including a few new ones! — but unified under a single set of functions.
For example, you can continue to generate molecular Hamiltonians using qml.qchem.molecular_hamiltonian
:
symbols = ["H", "H"] geometry = np.array([[0., 0., -0.66140414], [0., 0., 0.66140414]]) hamiltonian, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, method="dhf")
By default, this will use the differentiable Hartree-Fock solver; however, simply set method="pyscf"
to continue to use PySCF for Hartree-Fock calculations.
In addition to this unification, new features now available in the qml.qchem
module include:
- Functionality for building differentiable dipole moment observables via
qml.qchem.dipole_moment(mol)
- Support for the 6-31G basis set, allowing differentiable Hartree-Fock calculations with basis sets beyond the minimal sto-3g basis set for atoms with atomic number 1-10.
For more details, please see the quantum chemistry quickstart.
Even more quantum compilation transforms 🐛➡🦋
SWAP-based transpilation
When working with quantum hardware devices with restricted connectivity, it can be important to transpile our circuits to ensure we are only applying entangling gates on wires that are connected.
While this has always been supported automatically when using PennyLane with hardware, the new qml.transforms.transpile()
compilation transform provides direct access to SWAP-based transpilation:
dev = qml.device('default.qubit', wires=4) @qml.qnode(dev) @qml.transforms.transpile(coupling_map=[(0, 1), (1, 2), (2, 3)]) def circuit(param): qml.CNOT(wires=[0, 1]) qml.CNOT(wires=[0, 2]) qml.CNOT(wires=[0, 3]) qml.PhaseShift(param, wires=0) return qml.probs(wires=[0, 1, 2, 3]) >>> print(qml.draw(circuit)(0.6)) 0: ─╭C───────╭C──────────╭C──Rϕ(0.60)─┤ ╭Probs 1: ─╰X─╭SWAP─╰X────╭SWAP─╰X───────────┤ ├Probs 2: ────╰SWAP─╭SWAP─╰SWAP──────────────┤ ├Probs 3: ──────────╰SWAP────────────────────┤ ╰Probs
Pattern matching optimization
In addition, qml.transforms.pattern_matching_optimization
optimizes a QNode given an identity relation:
with qml.tape.QuantumTape() as identity: qml.S(wires=0) qml.S(wires=0) qml.PauliZ(wires=0) @qml.qnode(dev) @qml.transforms.pattern_matching_optimization(pattern_tapes=[identity]) def circuit(): qml.S(wires=0) qml.PauliZ(wires=0) qml.S(wires=1) qml.CZ(wires=[0, 1]) qml.S(wires=1) qml.S(wires=2) qml.CZ(wires=[1, 2]) qml.S(wires=2) return qml.expval(qml.PauliX(wires=0)) >>> print(qml.draw(circuit)()) 0: ──S⁻¹─╭C────┤ <X> 1: ──Z───╰Z─╭C─┤ 2: ──Z──────╰Z─┤
Note that the pattern matching optimizer is able to match every sub-pattern of our identity template, resulting in simplifications such as SZ=(S−1S)SZ=S−1(SSZ)=S−1.
A growing collection of templates 🧩
Alongside the great new features above, we also have new (differentiable!) templates to share for tensor networks and distance measures.
- Tensor network templates:
qml.MERA
, the Multi-scale Entanglement Renormalization Ansatz (MERA), joins existing circuit templates that represent the shape and connectivity of tensor networks, includingqml.MPS
andqml.TTN
. - Distance measures:
qml.HilbertSchmidt
andqml.LocalHilbertSchmidt
, can be used to compute the distance between two unitaries |Tr(V†U)|2. This provides a convenient cost function for training variational quantum compilation algorithms.
Improvements
In addition to the new features listed above, the release contains a wide array of improvements and optimizations:
- The parameter-shift Hessian can now be computed for arbitrary operations that support the general parameter-shift rule for gradients, using
qml.gradients.param_shift_hessian
. Operations have multiple ways of ensuring they are compatible with the parameter-shift Hessian, including by providing a customgrad_recipe
, specifying theparameter_frequencies
, or defining thegenerator()
. lightning.qubit
now computes samples natively in C++, leading to performance improvements when using finite shots.- The
qml.ctrl
transform now accepts the optional argumentcontrol_values
, which can be passed an integer or a list of integers corresponding to the binary value that activates each control. default.qubit
,default.mixed
, andlightning.qubit
now skip over identity operators instead of applying the identity operation to the circuit, resulting in a performance boost.qml.eigvals()
now uses the efficientscipy.sparse.linalg.eigsh
method for obtaining the eigenvalues ofqml.SparseHamiltonian
.QuantumTape
objects now support iteration over the contained operations and measurements:
>>> for op in tape: ... # iterate over the tape ... print(op) >>> list(tape) # will create a list of all operations >>> tape[2] # operations can be accessed via indexing
In addition, the QuantumTape.numeric_type()
and QuantumTape.shape()
return information about the type and shape of numeric results expected after tape execution.
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
- The
ObservableReturnTypes
enumerations,Sample
,Variance
,Expectation
,Probability
,State
, andMidMeasure
, have been moved frompennylane.operation
topennylane.measurements
.
Breaking changes
-
The ability to cache execution results on the Device has been removed. The recommended alternative going forward is to use QNode caching via the
cache
argument.Any object that implements the special methods
__getitem__()
,__setitem__()
, and__delitem__()
, such as a dictionary, may be used as a QNode cache:
import pennylane as qml cache = {} @qml.qnode(dev, cache=cache) def circuit1(weights): ... @qml.qnode(dev, cache=cache) def circuit2(weights): ...
-
The deprecated QNode infrastructure, used in PennyLane versions < 0.20 and previously available via
qml.qnode_old.QNode
, has been removed. Please transition to using the standardqml.QNode
.In addition, several other components which powered the deprecated QNode have been removed:
-
The deprecated, non-batch compatible interfaces, have been removed.
-
The deprecated tape subclasses
QubitParamShiftTape
,JacobianTape
,CVParamShiftTape
, andReversibleTape
have been removed.
-
-
The deprecated tape execution method
tape.execute(device)
has been removed. Please useqml.execute([tape], device)
instead. -
The
qml.finite_diff
function has been removed. Please useqml.gradients.finite_diff
to compute the gradient of tapes of QNodes. -
The
get_unitary_matrix
transform has been removed, please useqml.matrix
instead. -
The
update_stepsize
method has been removed fromqml.GradientDescentOptimizer
and its child optimizers. Thestepsize
property can be interacted with directly instead. -
Most optimizers no longer flatten and unflatten arguments during computation. Due to this change, custom gradient functions must return the same shape as
qml.grad
. -
The old text-based circuit drawer has been overhauled. Please use
qml.drawer.tape_text
instead ofqml.drawer.CircuitDrawer
. For specifying the text-based representation of custom operations, theOperator.label
method should be defined. As part of the improvements, thedecimals
andshow_matrices
keyword arguments have been added for additional flexibility, and themax_length
argument now defaults to 100 characters.
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:
Karim Alaa El-Din, Guillermo Alonso-Linaje, Juan Miguel Arrazola, Ali Asadi, Utkarsh Azad, Sam Banning, Thomas Bromley, Alain Delgado, Isaac De Vlugt, Olivia Di Matteo, Amintor Dusko, Anthony Hayes, David Ittah, Josh Izaac, Soran Jahangiri, Nathan Killoran, Christina Lee, Angus Lowe, Romain Moyard, Zeyue Niu, Matthew Silverman, Lee James O’Riordan, Chae-Yeun Park, Maria Schuld, Jay Soni, Antal Száva, Trevor Vincent, Maurice Weber, and David Wierichs.
About the author
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.