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

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**

Easily construct a hybrid machine learning model using the new `KerasLayer`

class!

## Code example

```
from pennylane.qnn import KerasLayer
@qml.qnode(dev)
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 `default.tensor.tf`

device backend).

## Code example

```
dev = qml.device("default.tensor.tf", 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**

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**

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**

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 Release notes — PennyLane 0.27.0 documentation.

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