CV neural network training

Hi,

I am trying to develop a CV neural network for function fitting, so I am basing my code on this tutorial

However, I am interested in using more than one qumode. I am using qml.CVNeuralNetLayers to create the layers. When I define the circuit, to give an initial value for the parameters, I am doing something like this

shapes = qml.CVNeuralNetLayers.shape(n_layers=num_layers, n_wires=num_wires)
var_init = [np.random.random(shape, requires_grad=True) for shape in shapes]
 
def circuit(var, x):
    for i,j in enumerate(x):
        qml.Displacement(j, 0.0, wires=i)
    qml.CVNeuralNetLayers(*var, wires=range(num_wires))
    return qml.expval(qml.X(0)),qml.expval(qml.X(1)),qml.expval(qml.X(2))

var_init_array = np.array(var_init)

I have noted that the last part in which I define var_init_array is neccesary to make the circuit trainable (otherwise it just keeps using the same parameters for each iteration). The problem is that qml.CVNeuralNetLayers requires several tensors with different shapes as inputs, so I get an error when trying to define the aforementioned array (except when num_wires=3, because all of those tensors have the same shape and then everything works):

could not broadcast input array from shape (4,6) into shape (4,)

I don’t get why I can’t train the circuit with a list of tensors instead of having to convert it to an array, but anyway the arguments in qml.CVNeuralNetLayers are not generally equal in shape so I can’t define that array and train my network. I don’t know which is the best solution to this, so any help is welcome. Thank you in advance!

Hey @Pablo_Vinas! Great question. I usually find that it’s best, in these cases, to be explicit about each differentiable parameter. Specifically, what I mean is that you should allocate a unique variable to each parameter that needs to be differentiated with respect to, especially when they’re shaped differently (as you noted).

This code works:

import pennylane as qml
from pennylane import numpy as np

num_wires = 4
n_layers = 2

shapes = qml.CVNeuralNetLayers.shape(n_layers=n_layers, n_wires=num_wires)
var = [np.random.random(shape, requires_grad=True) for shape in shapes]

theta_1 = var[0]
phi_1 = var[1]
varphi_1 = var[2]
r = var[3]
phi_r = var[4]
theta_2 = var[5]
phi_2 = var[6]
varphi_2 = var[7]
a = var[8]
phi_a = var[9]
k = var[10]

x = [0.1, 0.2, 0.3]

dev = qml.device("strawberryfields.fock", wires=num_wires, cutoff_dim=3)


@qml.qnode(dev)
def circuit(
    theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, x
):
    for i, j in enumerate(x):
        qml.Displacement(j, 0.0, wires=i)
    qml.CVNeuralNetLayers(
        theta_1,
        phi_1,
        varphi_1,
        r,
        phi_r,
        theta_2,
        phi_2,
        varphi_2,
        a,
        phi_a,
        k,
        wires=range(num_wires),
    )
    return [qml.expval(qml.X(0)), qml.expval(qml.X(1)), qml.expval(qml.X(2))]

print(
    circuit(
        theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, x
    )
)

def loss(theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, x):
    result = circuit(
        theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, x
    )
    return np.sum(result)

opt = qml.GradientDescentOptimizer(0.1)

for _ in range(3):
    (
        theta_1,
        phi_1,
        varphi_1,
        r,
        phi_r,
        theta_2,
        phi_2,
        varphi_2,
        a,
        phi_a,
        k,
        _,
    ), l = opt.step_and_cost(
        loss,
        theta_1,
        phi_1,
        varphi_1,
        r,
        phi_r,
        theta_2,
        phi_2,
        varphi_2,
        a,
        phi_a,
        k,
        x,
    )
    print(l)

It looks kinda wacky due to all of the different variables, but, hey — qml.CVNeuralNetLayers has a lot of arguments :sweat_smile:. Let me know if this helps!

1 Like

Hi @isaacdevlugt. Thank you for the answer, it works fine! Have a good day :smiley:

1 Like

Awesome! I’m glad that worked :rocket:

1 Like