Problem on running quantumGAN

@CatalinaAlbarnoz,

I also tried with the cutting circuit that works both on the demo and on my code changing the number of qubits (wires on the device), but if I run the code with the entire dataset and 32 batch size, it gets the same problem running infinite time without any results. Can you try running my code that I shared with you on github?
The only way I found in order to get results is using a reduced dataset (30 instead of 300 images) and batch size=1. I trained the mode for 200 iterations but when I try to plot the images with this code I get an error:

fig = plt.figure(figsize=(10, 5))
outer = gridspec.GridSpec(5, 2, wspace=0.1)

for i, images in enumerate(results):
inner = gridspec.GridSpecFromSubplotSpec(1, images.size(0),
subplot_spec=outer[i])

images = torch.squeeze(images, dim=1)
for j, im in enumerate(images):

ax = plt.Subplot(fig, inner[j])

ax.imshow(im.numpy())
ax.set_xticks()
ax.set_yticks()
if j==0:
ax.set_title(f’Iteration {50+i*100}', loc=‘left’)
fig.add_subplot(ax)

plt.show()

Invalid shape (3, 64, 64) for image data

I tried to transpose or permute images:

for k in range(len(test_images)) :

fig, axs = plt.subplots(1, 1, sharey=False, tight_layout=True, figsize=(2,2), facecolor=‘white’)

#axs.matshow(np.squeeze(test_images[k].permute(1,2,0)))
axs.matshow(test_images[k].T)

But the result is this:
image.png

Do you think I made a mistake in visualizing the image correctly or there is a problem during the training?

The cutting circuit adapted to my code (13 qubits), is like this? I don’t understand how I can decide the number of remaining qubits…from 13 to…?? (The demo reduces from 3 to 2 qubits)
I added some RX(0.3) and RY(0.9) in order to reach 13 qubits, but I don’t know if it is correct because I don’t know which rotation of RX or RY selected?
Anyway, for the moment the training has the same problem (it is running for infinite time without showing any epochs).

image.png

Hi @CatalinaAlbarnoz, can you help me to understand what is the right structure for the cutting circuit adapted to my model? Should I reduce the 13 qubits circuits cutting to 7 qubits?
The following architecture is correct? I inserted more RX abd RY blocks until 13 like the number of qubits…but I don’t understand how to implement this in order to cut the circuit in 7 qubits.

dev = qml.device("lightning.qubit", wires=n_qubits)

@qml.qnode(dev)
def circuit(x):
    qml.RX(x, wires=0)
    qml.RY(0.9, wires=1)
    qml.RX(0.3, wires=2)
    qml.CZ(wires=[0, 1])
    qml.RY(-0.4, wires=0)
    qml.CZ(wires=[1, 2])
    qml.RY(0.9, wires=3)
    qml.CZ(wires=[2, 3])
    qml.RX(0.3, wires=4)
    qml.CZ(wires=[3, 4])
    qml.RY(0.9, wires=5)
    qml.CZ(wires=[4, 5])
    qml.RX(0.3, wires=6)
    qml.CZ(wires=[5, 6])
    qml.RY(0.9, wires=7)
    qml.CZ(wires=[6, 7])
    qml.RX(0.3, wires=8)
    qml.CZ(wires=[7, 8])
    qml.RY(0.9, wires=9)
    qml.CZ(wires=[8, 9])
    qml.RX(0.3, wires=10)
    qml.CZ(wires=[9, 10])
    qml.RY(0.9, wires=11)
    qml.CZ(wires=[10, 11])
    qml.RX(0.3, wires=12)
    qml.CZ(wires=[11, 12])
    return qml.expval(qml.pauli.string_to_pauli_word("ZZZZZZZZZZZZZ"))

cut_circuit = qml.cut_circuit(circuit, auto_cutter=True)

x = pnp.array(0.531, requires_grad=True)

cut_circuit(x)

Hi @Eleonora_Panini ,

Great question!

The idea behind circuit cutting is simply that you keep your circuit as-is but then run it on smaller devices. The circuit is then run more times, but needing less RAM each time.

I looked at the circuit in the Quantum GANs demo and this is actually not a good one for circuit cutting since it has a lot of entanglement between qubits. However, below is a small example of circuit cutting on a similar circuit:

import torch.nn as nn
import pennylane as qml
import torch
import math

# We define our constants
n_qubits = 8
q_depth = 2 
n_generators = 1
q_delta = 1
batch_size = 1

# We define our CUDA device (if available)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# We define our quantum function (without a decorator)
def quantum_function(noise, weights):
  weights = weights.reshape(q_depth, n_qubits)
  # Initialise latent vectors
  for i in range(n_qubits):
    qml.RY(noise[i], wires=i)

  # Repeated layer
  for i in range(q_depth):
    # Parameterised layer
    for y in range(n_qubits):
      qml.RY(weights[i][y], wires=y)

    # Control Z gates
    for y in range(n_qubits - 1):
      qml.CZ(wires=[y, y + 1])

  return qml.expval(qml.PauliZ(0))

# We define our parameters
params = nn.Parameter(q_delta * torch.rand(q_depth * n_qubits), requires_grad=True)
noise = torch.rand(batch_size, n_qubits, device=device) * math.pi / 2

# We create a device with the number of qubits required to run our circuit
dev = qml.device("lightning.qubit", wires=n_qubits)

# We create a QNode
quantum_circuit = qml.QNode(quantum_function, dev)

# We run the circuit for a specific value of the noise and the parameters just to test
output = quantum_circuit(noise[0], params)
print(output)
qml.draw_mpl(quantum_circuit)(noise[0], params);

Below is where the circuit cutting part actually starts

"""
Now let's imagine that we only have access to a 5-qubit device instead of the 8-qubit device 
that we need in order to run the previous circuit 
"""
# We create a 5-qubit device
dev_small = qml.device("lightning.qubit", wires=5)

# We create a QNode with the original quantum function but with the smaller device
quantum_circuit2 = qml.QNode(quantum_function, dev_small)

# We use our circuit cutter in "auto_cutter" mode
cut_circuit = qml.cut_circuit(quantum_circuit2, auto_cutter=True)

# We run the cut circuit for a specific value of the noise and the parameters just to test
output = cut_circuit(noise[0], params)
print(output)
qml.draw_mpl(cut_circuit)(noise[0], params);

Notice that circuit cutting is not something that should be used in every situation, but it sometimes can help you. In this case, for the original quantumGAN circuit it’s not a great help, so you may instead want to reduce the depth of your circuit to reduce your RAM issues.

I hope this helps!

1 Like

Thank you for your suggestion, I am trying to apply this implementation for cutting circuit but I get this error when I run this cell:

“”"
Now let’s imagine that we only have access to a 5-qubit device instead of the 8-qubit device
that we need in order to run the previous circuit
“”"

We create a 5-qubit device

dev_small = qml.device(“lightning.qubit”, wires=7)

We create a QNode with the original quantum function but with the smaller device

quantum_circuit2 = qml.QNode(quantum_function, dev_small)

We use our circuit cutter in “auto_cutter” mode

cut_circuit = qml.cut_circuit(quantum_circuit2, auto_cutter=True)

We run the cut circuit for a specific value of the noise and the parameters just to test

output = cut_circuit(noise[0], params)
print(output)
qml.draw_mpl(cut_circuit)(noise[0], params);

TypeError                                 Traceback (most recent call last)

<ipython-input-17-291b130a18a5> in <cell line: 15>()
     13 
     14 # We run the cut circuit for a specific value of the noise and the parameters just to test
---> 15 output = cut_circ(noise[0], params)
     16 print(output)
     17 qml.draw_mpl(cut_circ)(noise[0], params);

Hi @Eleonora_Panini , did you modify anything in the code I shared?
Can you please post the output of qml.about() and your full traceback error? Thanks!

I noticed that if I set the parameters as:
n_qubits = 8-> cutting to 5 q_depth = 2 n_generators = 1 q_delta = 1 batch_size = 1

OR

n_qubits = 12-> cutting to 7 q_depth = 2 n_generators = 4 q_delta = 1 batch_size = 16

It works!

If I set the parameters as my model:
n_qubits = 13 → cutting to 7 q_depth = 2 n_generators = 6 q_delta = 1 batch_size = 16
Whatever destination qubits I choose , I get the error:

Also is it normal that the draw of the cutting circuit is the same as the original?
Original with 12 qubits:

Cutted to 7 qubits:

Hi @Eleonora_Panini ,

I don’t know why your system isn’t being able to handle 13 qubits and 6 generators. Can you please post your full error traceback?

Yes, it’s ok that the drawing is the same because the drawing represents the circuit you want to run, not all of the pieces that are actually run on the device. You can think of this as part of the compilation kind of, which isn’t visible when you draw the original circuit you want to run.