PennyLane v0.9.0 Released

Hi everyone! We’re very happy to announce the release of PennyLane version 0.9.0. :trophy: :tada:

This release contains a lot of new exciting features; including new machine learning integrations with Keras, faster simulation evaluations, new templates, and new quantum-aware optimizers to minimize your quantum function of choice.

New machine learning integrations :robot:

Easily construct a hybrid machine learning model using the new KerasLayer class!

Code example
from pennylane.qnn import KerasLayer

def circuit(inputs, weights_0, weight_1):
    # define the circuit
    # ...

weight_shapes = {"weights_0": 3, "weight_1": 1}
qlayer = qml.qnn.KerasLayer(circuit, weight_shapes, output_dim=2)
model = tf.keras.models.Sequential([qlayer, tf.keras.layers.Dense(2)])

Use the new “backprop” differentiation method with the qnode decorator to utilize end-to-end differentiability when creating a circuit with a compatible simulator (supported by PennyLane’s device backend).

Code example
dev = qml.device("", wires=1)

@qml.qnode(dev, interface="tf", diff_method="backprop")
def circuit(x):
    qml.RX(x[1], wires=0)
    qml.Rot(x[0], x[1], x[2], wires=0)
    return qml.expval(qml.PauliZ(0))

vars = tf.Variable([0.2, 0.5, 0.1])

with tf.GradientTape() as tape:
    res = circuit(vars)

tape.gradient(res, vars)

New gradient-free optimizers :loop:

Try out the new gradient-free qml.RotosolveOptimizer that optimizes circuits by updating each parameter one-at-a-time. Alternatively, using the qml.RotoselectOptimizer, it’s also possible to optimize both the applied rotation operations and their parameters.

Rotosolve code example

Initialize the optimizer, set the initial values of x to be used and set the number of steps to optimize over.

>>> opt = qml.optimize.RotosolveOptimizer()
>>> x = [0.3, 0.7]
>>> n_steps = 1000
>>> dev = qml.device("default.qubit", analytic=True, wires=2)
... @qml.qnode(dev)
... def circuit(params):
...     qml.RX(params[0], wires=0)
...     qml.RY(params[1], wires=1)
...     qml.CNOT(wires=[0, 1])
...     return qml.expval(qml.PauliY(0)), qml.expval(qml.PauliZ(1))

Define a cost function (that takes a list of values as input and return a single value) based on the above circuit.

>>> def cost(x):
...     Y_1, Z_2 = circuit(x)
...     return 0.2 * Y_1 + 0.5 * Z_2
>>> cost_rotosel = []
>>> for _ in range(n_steps):
...     cost_rotosel.append(cost(x))
...     x = opt.step(cost, x)

The optimized values for x should now be stored in x and steps-vs-cost can be seen by plotting cost_rotosel.

For more information, and an example using the Rotoselect optimizer, see the documentation pages for qml.Rotosolve and qml.Rotoselect.

Faster simulator evaluations with new operations :zap:

The new DiagonalQubitUnitary— encountered in, for example, IQP circuits— allows faster execution on simulator devices. Further operations such as the PauliRot and MultiRZ operations have been added.

New templates using broadcasting :brain:

The new qml.broadcast() function allows templates to be constructed easily. New templates such as ArbitraryUnitary, ArbitraryStatePreparation and IQPEmbedding have been added using broadcasting.

Other new features include improved speed and performance of the default.qubit device and an improved circuit drawer.

We have also carried out a redesign of our plugin documentation! Check out the fresh new look of the Qiskit, Cirq and Strawberry Fields plugin docs.

The full release notes are available at

As always, this release would not have been possible without all the help from our contributors:

Ville Bergholm, Lana Bozanic, @Tom_Bromley, @theodor, @josh, @nathan, Maggie Li, @johannesjmeyer, @Maria_Schuld, Sukin Sim @antalszava

1 Like