Rotosolve optimizer

Create diagram

from lambeq import BobcatParser

parser = BobcatParser()
train_diagram = parser.sentence2diagram('Alice loves Bob')

Create circuits

from lambeq import StronglyEntanglingAnsatz, AtomicType

ansatz = StronglyEntanglingAnsatz({AtomicType.NOUN: 1, AtomicType.SENTENCE: 1},
                                   n_layers=2, n_single_qubit_params=3)

train_circuit = ansatz(train_diagram)

train_circuit.draw(figsize=(14, 12))
../_images/caef00dbe3eef5c03a97ac482b68246ba78c0c5617be1a7d8f7d09b1a9d3950e.png

Parameterise

from lambeq import NumpyModel

model = NumpyModel.from_diagrams([train_circuit], use_jit=True)
model.initialise_weights()
model([train_circuit]).shape
(1, 2)

Define evaluation metric

from jax import jit

@jit
def loss(y_pred, _):
    """The goal is to minimise the expectation value of the Pauli Z operator.
    lambeq does not provide functionality to directly calculate expectation
    values. Therefore, we need to calculate the expectation value from the
    measurement probabilities of the Ket(0) and Ket(1) using their
    eigenvalues."""

    # 0 state probability
    p0 = y_pred[:, 0]
    # 1 state probability
    p1 = y_pred[:, 1]

    # expectation value
    exp = p0 - p1  # eigenvalues are 1 and -1

    return - exp.mean()  # minimise expectation of measuring a 1

loss(model([train_circuit]), None)
Array(-0.21474883, dtype=float32)
from lambeq import Dataset

train_dataset = Dataset(
            [train_circuit],
            [-1])

Initialize trainer

from lambeq import QuantumTrainer
from lambeq import RotosolveOptimizer
from tqdm import trange

EPOCHS = 5

losses = []

for i in trange(20):  # calculate results for 100 different seeds

    trainer = QuantumTrainer(
        model,
        loss_function=loss,
        epochs=EPOCHS,
        optimizer=RotosolveOptimizer,
        optim_hyperparams={},
        evaluate_on_train=True,
        verbose='suppress',
        seed=i
    )

    initial_loss = loss(trainer.model([train_circuit]), None)

    trainer.fit(train_dataset, log_interval=12)
    losses.append([initial_loss] + trainer.train_epoch_costs)
100%|██████████| 20/20 [00:02<00:00,  8.96it/s]

Show results

# get mean and std of the losses
import numpy as np

mean_losses = np.mean(losses, axis=0)
std_losses = np.std(losses, axis=0)
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(8, 6))

ax.set_title('Training set')
ax.set_xlabel('Epochs')
ax.set_ylabel('Loss')

colours = iter(plt.rcParams['axes.prop_cycle'].by_key()['color'])
ax.plot(mean_losses, color=next(colours));
ax.fill_between(range(len(mean_losses)), mean_losses-std_losses, mean_losses+std_losses, alpha=0.2, color=next(colours));
../_images/92456f227904e5718bbb6330122c088dbbc0445f45bb2cfb905f71274a8e28de.png

Final output

model([train_circuit]).round(3)
Array([[1., 0.]], dtype=float32)