PennyLane v0.16 released

PennyLane team

PennyLane v0.16 is now live! This release is chock full of new goodies with over 50 PRs from 26 contributors and two new modules.

Extract the Fourier representation of quantum circuits

The new fourier module provides a host of methods for extracting the Fourier representation of functions implemented by quantum circuits. With these newly provided tools, you can examine (and visualize!) the expressivity of your quantum circuit 📈

For more information, check out the Fourier module documentation.

graphic representing fourier coefficients of a circuit

First-class support for quantum kernels

The new kernels module provides easy access to quantum kernels for all your machine learning needs.

Similar to classical kernel methods, which compute distance metrics between embedded data in feature space, quantum kernels take advantage of the high-dimensional Hilbert space of quantum computers, utilizing measurements to compute distances between encoded quantum datasets.

Use functions from the kernels module to evaluate quantum kernel functions on provided datasets:

n_points = 5
dev = qml.device('default.qubit', wires=n_wires)

def kernel_circuit(x1, x2):
    qml.templates.AngleEmbedding(x1, wires=range(n_wires))
    qml.adjoint(qml.templates.AngleEmbedding)(x2, wires=range(n_wires))
    return qml.probs(range(n_wires))

kernel = lambda x1, x2: kernel_circuit(x1, x2)[0]

rng = np.random.default_rng(seed=1234)
X = rng.random((n_points, n_wires))

K = qml.kernels.square_kernel_matrix(X, kernel)
Distance between pairwise points

In addition, the kernels module provides functionality for mitigating error associated with the evaluated kernel matrix.

For more information on quantum kernels, you can visit the module documentation or read Xanadu Researcher Maria Schuld’s paper on the topic.

New transforms

Do more with your QNodes! Quantum transforms allow you to manipulate your QNodes, turn them into something new, or just look at them in a different light. Transforms preserve differentiability, allowing even the transform to be optimized and trained.

  • Specifications: Need to know the size of your circuit before you run it? How many executions will the gradient take? Is the circuit too deep for a QPU? Look no further than the specs transform.

    dev = qml.device('default.qubit', wires=4)
    @qml.qnode(dev, diff_method='parameter-shift')
    def circuit(x, y):
        qml.RX(x[0], wires=0)
        qml.Toffoli(wires=(0, 1, 2))
        qml.CRY(x[1], wires=(0, 1))
        qml.Rot(x[2], x[3], y, wires=0)
        return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliX(1))
    x = np.array([0.05, 0.1, 0.2, 0.3], requires_grad=True)
    y = np.array(0.4, requires_grad=False)
    specs_func = qml.specs(circuit)
    info = specs_func(x, y)
    >>> info
    {'gate_sizes': defaultdict(int, {1: 2, 3: 1, 2: 1}),
    'gate_types': defaultdict(int, {'RX': 1, 'Toffoli': 1, 'CRY': 1, 'Rot': 1}),
    'num_operations': 4,
    'num_observables': 2,
    'num_diagonalizing_gates': 1,
    'num_used_wires': 3,
    'depth': 4,
    'num_trainable_params': 4,
    'num_parameter_shift_executions': 11,
    'num_device_wires': 4,
    'device_name': 'default.qubit',
    'diff_method': 'parameter-shift'}
  • Custom transform decorator: Write your own transforms! The new decorators make it easy to manipulate quantum circuits in any way you see fit.

    For example, you can scale every X rotation:

    def my_transform(tape, a):
        for op in tape.operations + tape.measurements:
            if == "RX":
                x = op.parameters[0]
                qml.RX(a * x, wires=op.wires)

    And then apply it in a circuit:

    dev = qml.device("default.qubit", wires=2)
    params = np.array([1.0, 1.0])
    def ansatz(x):
        qml.RX(x[0], wires=0)
        qml.RY(x[1], wires=1)
        qml.CNOT(wires=[0, 1])
    def ansatz_plain(params):
        return qml.expval(qml.PauliZ(1))
    def ansatz_transformed(params, scale):
        return qml.expval(qml.PauliZ(1))

    If we draw both circuits, we can see that the parameters of the transformed version have been modified:

    >>> print(qml.draw(ansatz_plain)(params))
     0: ──RX(1)──╭C──┤ ⟨Z⟩
     1: ──RY(1)──╰X──┤
    >>> print(qml.draw(ansatz_transformed)(params, 2.0))
     0: ──RX(2)──╭C──┤ ⟨Z⟩
     1: ──RY(1)──╰X──┤

    Transforms are fully differentiable, allowing you to take gradients with respect to transform parameters:

    >>> qml.grad(ansatz_transformed)(params, 2.0)
    (array([-1.81859485e+00, -2.77555756e-17]), array(-0.90929743))
  • Quantum Monte Carlo: The Monte Carlo estimation algorithm 🎲 calculates the expectation value over some input distribution. The quantum version can potentially calculate that value faster than classical sampling. PennyLane already has a qml.templates.QuantumMonteCarlo template, and the new transform allows for easier benchmarking and specifications tabulation.

New operations

PennyLane’s library of operations and templates continues to grow, with several new operations available with this release:

Check out the operations guide and templates gallery for a full list of available operations and templates.


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

  • Compute quantum gradients with respect to a subset of stochastically chosen parameters.
  • The adjoint differentiation method is up to 15% faster because of eliminated redundant evaluations.
  • Tag your operators with an id attribute, useful for all sorts of transformations and manipulations.
  • PennyLane now provides support for Python 3.9.

Breaking changes and deprecations

As new things are added, old things can be pruned away. Here’s what will be disappearing in the future:

  • With this release, support for Python 3.6 has been removed.
  • The transform qml.inv() is deprecated in favor of the more flexible qml.adjoint().
  • The tape methods tape.get_resources() and tape.get_depth() are deprecated in favor of the new attribute tape.specs.

These highlights are just scratching the surface — check out the full release notes for more details.


In this release cycle, we participated in unitaryHACK, a quantum open source hackathon sponsored by the Unitary Fund. We merged 12 PRs from a number of community contributors (with some still in the pipeline for the next release!):

Congrats to the Unitary Fund for a successful event, and thanks to our many new contributors! 🙏


As always, this release would not have been possible without the hard work of our development team and contributors:

Marius Aglitoiu, Vishnu Ajith, Juan Miguel Arrazola, Thomas Bromley, Jack Ceroni, Alaric Cheng, Miruna Daian, Olivia Di Matteo, Tanya Garg, Christian Gogolin, Alain Delgado Gran, Diego Guala, Anthony Hayes, Ryan Hill, Theodor Isacsson, Josh Izaac, Soran Jahangiri, Pavan Jayasinha, Nathan Killoran, Christina Lee, Ryan Levy, Alberto Maldonado, Johannes Jakob Meyer, Romain Moyard, Ashish Panigrahi, Nahum Sá, Maria Schuld, Brian Shi, Antal Száva, David Wierichs, Vincent Wong.