Making a one layer neural network

Hey everyone,

I am using the CVNN at the moment and I would like to simplify it and just use a one layer neural network:

out_dim = 4
wires = 1
n_quantum_layers = 2

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

@qml.qnode(dev)
def layer(inputs, w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10):
    qml.templates.DisplacementEmbedding(inputs, wires=range(wires))
    qml.templates.CVNeuralNetLayers(w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, wires=range(wires))
    return [qml.expval(qml.X(wires=i)) for i in range(wires)]

weights = qml.init.cvqnn_layers_all(n_quantum_layers, wires)#, seed=0)
weight_shapes = {"w{}".format(i): w.shape for i, w in enumerate(weights)}
qlayer = qml.qnn.KerasLayer(layer, weight_shapes, output_dim=wires)
clayer_in = tf.keras.layers.Dense(wires)
clayer_out = tf.keras.layers.Dense(out_dim)
model = tf.keras.models.Sequential([clayer_in, qlayer, clayer_out])
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate = 0.00012), loss = 'mse')

And I would like to for example just use a simple displacement layer:

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

@qml.qnode(dev)
def layer(inputs, x):
    qml.templates.DisplacementEmbedding(inputs, wires=range(wires))
    qml.Displacement(x, 0, wires=range(wires)
    return [qml.expval(qml.X(wires=i)) for i in range(wires)]

but am unsure what to put for the weights and the weight_shapes for

qlayer = qml.qnn.KerasLayer(layer, weight_shapes, output_dim=wires)

Any insight is appreciated!

Hi Shawn,

As far as I understand, the variational parameters of your quantum circuit are the displacements, which you call x in the code. These are 1-dimensional parameters. Then you are applying a displacement for each wire. So if you have n wires, these parameters are just an array of dimension n.

I haven’t used the KerasLayer before, but reading the documentation, I believe in your case you should simply set weight_shapes = {"x": wires}.

Let me know if that works!

Thanks @jmarrazola it worked!

1 Like

Hi @jmarrazola one more question :slight_smile:

I’m trying to get this to work:

def layer(inputs, w, x, y, z):
    qml.templates.DisplacementEmbedding(inputs, wires=range(wires))
    qml.Interferometer(np.array([w]), wires=range(wires))
    qml.Displacement(x, 0, wires=range(wires))
    qml.Rotation(y, wires=range(wires))
    qml.Kerr(z, wires=range(wires))
    return [qml.expval(qml.X(wires=i)) for i in range(wires)]
a = ["x","y","z","w"]
weight_shapes = {a[i]: wires for i in range(len(a))}

But I keep getting errors. Can anyone tell me what’s wrong with the layer or the weight_shapes?

Hey @Shawn,

There are three things causing a problem here:

  • Inside the circuit, you are using single mode gates on multiple wires (e.g., Displacement, Rotation, Kerr).

  • You should use qml.templates.Interferometer, which applies the interferometer according to some trainable angles. The qml.Interferometer you were using requires an input unitary.

  • The interferometer should be fed three variables and the shape of each should be declared in weight_shapes. Their shapes are not simply wires, you can find them here.

Your code should look something more like this:

def layer(inputs, theta, phi, varphi, x, y, z):
    qml.templates.DisplacementEmbedding(inputs, wires=range(wires))
    qml.templates.Interferometer(theta, phi, varphi, wires=range(wires))
    for i in range(wires):
        qml.Displacement(x[i], 0, wires=i)
        qml.Rotation(y[i], wires=i)
        qml.Kerr(z[i], wires=i)
    return [qml.expval(qml.X(wires=i)) for i in range(wires)]

interferometer_shape = int(wires * (wires - 1) / 2)

weight_shapes = {
    "theta": interferometer_shape,
    "phi": interferometer_shape,
    "varphi": wires,
    "x": wires,
    "y": wires,
    "z": wires,
}

As a quick test, this makes a usable Keras layer:

dev = qml.device("strawberryfields.fock", wires=wires, cutoff_dim=4)
qnode = qml.QNode(layer, dev)
qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, wires)

import numpy as np
x = np.random.random((10, wires))
qlayer(x)

Hi @Tom_Bromley thanks a lot for the help. When running the code I am getting the following error:

WARNING:tensorflow:Layer keras_layer is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2.  The layer has dtype float32 because it's dtype defaults to floatx.

If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.

To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.

Traceback (most recent call last):
  File "froz_versuch.py", line 40, in <module>
    qlayer(x)
ValueError: wrong shape of weight input(s) detected

Hey @Shawn, that’s odd.

Here is the full code that I managed to get working:

import pennylane as qml

wires = 4

def layer(inputs, theta, phi, varphi, x, y, z):
    qml.templates.DisplacementEmbedding(inputs, wires=range(wires))
    qml.templates.Interferometer(theta, phi, varphi, wires=range(wires))
    for i in range(wires):
        qml.Displacement(x[i], 0, wires=i)
        qml.Rotation(y[i], wires=i)
        qml.Kerr(z[i], wires=i)
    return [qml.expval(qml.X(wires=i)) for i in range(wires)]

interferometer_shape = int(wires * (wires - 1) / 2)

weight_shapes = {
    "theta": interferometer_shape,
    "phi": interferometer_shape,
    "varphi": 4,
    "x": wires,
    "y": wires,
    "z": wires,
}

dev = qml.device("strawberryfields.fock", wires=wires, cutoff_dim=4)
qnode = qml.QNode(layer, dev)
qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, wires)

import numpy as np
x = np.random.random((10, wires))
qlayer(x)

If you copy/paste that, are you still getting an error?

Hi @Tom_Bromley, it seems to only work when wires = 3,4,5,6,7 (8 and beyond I get a space error so I would assume it works also for 8 and onward). For wires = 1,2 the error occurs. Could it be that interferometer_shape needs to be bigger than 1?

Yes, the MemoryError for increasing wire number should be due to representing the cutoff ** wires dimensional state in the Fock basis.

For wires=2, try replacing this line:
qml.templates.Interferometer([theta], [phi], varphi, wires=range(wires))
This is needed because the theta and phi are one-dimensional for wires=2 and end up getting internally flattened from an array to a float.

For wires=1, you could just swap out the interferometer with a qml.Rotation() gate.

Thank you @Tom_Bromley! Both of them are working.

1 Like