How to scale the QGAN example to more qubits

HI,

I am trying to scale the number of qubits for my experiments using Quantum GANs — PennyLane documentation . But when I run the script with num_qubits of more than 5, then the script fails with error -

Traceback (most recent call last):
File “qgan.py”, line 225, in
outD_fake = discriminator(fake_data.detach()).view(-1)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/nn/modules/module.py”, line 1501, in _call_impl
return forward_call(*args, **kwargs)
File “qgan.py”, line 93, in forward
return self.model(x)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/nn/modules/module.py”, line 1501, in _call_impl
return forward_call(*args, **kwargs)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/nn/modules/container.py”, line 217, in forward
input = module(input)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/nn/modules/module.py”, line 1501, in _call_impl
return forward_call(*args, **kwargs)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/nn/modules/linear.py”, line 114, in forward
return F.linear(input, self.weight, self.bias)
RuntimeError: mat1 and mat2 shapes cannot be multiplied (1x128 and 64x64)

Any thoughts on how we can make the script more generic, and pass the qubits as parameters? Is there any other channel, where we can ask these kind of questions and get quick answers?

Thanks

Hey @QuantumMan,

I believe you’re getting an error when you change the number of qubits here because the image sizes are 8 by 8 (8*8 = 64). There are a total of 5+1 qubits being used, which means 2^6 = 64 is the size of the quantum circuit state in the computational basis. The output of the circuit is qml.probs(wires=list(range(n_qubits))) which will output the proper dimension :slight_smile:

Hi @QuantumMan,

So to generate an image of 64 x 64, I must use 12 qubits (11 data qubits and 1 ancillary qubit). How many qubits can PennyLane support? Also, can I use it to generate RBG images instead of grayscale images?

Pennylane simulators are able to support up to 32 qubits :slight_smile:

Can QGAN generate RBG images instead of grayscale images? Will it be able to handle so many qubits? Is there a way around it?

Yep! In principle, any generative model that was made for grayscale images can be used for RGB images with suitable adjustments :slight_smile:. Usually all this means is that the output of the model is multinomial/continuous instead of binary.

Thanks @isaacdevlugt - sorry, i am still newbie to quantum and ML, so what should i be changing in the code to support 10 qubits or scale to 32 qubits? Thanks.

No worries at all! A GAN, effectively, has two parts:

  1. A generator that generates data similar to what’s in your training dataset but is fake
  2. A discriminator that discerns whether the generator’s fake data is real or not.

We want to train the discriminator to be good at telling us what is fake or not, and we want to train the generator to make it even better at making fake data seem real.

In this demo, the generator is quantum. So, the data that the quantum generator makes must have the same dimensions as the real data. The real data is an 8x8 image (8x8 = 64 units), so the quantum generator must generate 8x8 images, as well.

There’s a few layers of abstraction that I’m glossing over because the circuit is using a patching method. But, a quantum circuit can generate 8x8=64-length data by outputting a probability vector that is of length 2^6 = 64, where the “6” is the number of qubits. If you increase the number of qubits, you are generating an image that isn’t the right dimension.

Hope that makes sense!

so what should i be changing in the code to support 10 qubits or scale to 32 qubits?

You’d have to make some serious changes to the quantum generator for this in order for the dimensions to work out. There’s many ways to make it work, but ultimately anything should (in theory) work so long as your quantum generator can generate a 64-length vector that can be suitably interpreted as an image (e.g., a probability vector).

I think there’s a more important thing to highlight here in that simply increasing the number of qubits doesn’t always guarantee better performance, which is what I assume you’re after. There’s a lot of nuance and unknowns in machine learning, let alone quantum machine learning!

That helps. I do have a GPU VM, where i am trying to run some QGAN application where i can scale the Qubits and see how the resource utlization is varying. Do you have any other simple QGAN example i can use for above purpose?

AH! :man_facepalming: There is a way to simply increase the number of qubits. Sorry! Let me backpedal.

Everything I said about the dimensions needing to match (i.e., the generator needs to generate 8x8 = 64 unit features) is still correct. However, the patch method’s architecture can be moulded to your liking. The nuances I was glossing over are actually quite important :sweat_smile:.

Given N_G sub-generators that make up the entire generator, N qubits, and N_A ancillary qubits, the size of each sub-generator’s output (patch_size) is 2^{N - N_A}. So, when all of the sub-generators are recombined / concatenated together, that cumulative output needs to be 8x8.

In the tutorial, we have N_G = 4, N = 5, and N_A = 1. Each sub-generator will create a feature whose length is 2^{5 - 1} = 2^4 = 16. 4 groups of 16 gives us 64 — the dimensionality we need!

However, I could easily do N_G = 4, N = 6, and N_A = 2 and the math still checks out (you can try this). You could even do N_G = 16, N = 5, and N_A = 3.

Here’s the magic formula you can use: 2^{N - N_A} = \frac{64}{N_G}. If that equation holds, then those values of N_G, N, and N_A will work.

Sorry about that! Hopefully that clears things up, but please let me know if you’re still confused!

1 Like

Thank you , that actually helped to scale the qubits. I see the total time to run the whole application increased, but the Nvidia GPU utilization seems stagnated around 12% for 5, 10, 15 n_qubits. I am using ‘lightning.gpu’ plugin. Any thoughts on how to increase the gpu utlization?

Do you think the descriminators aren’t scaled as the generators are scaling, so would that be a problem. I see as the qubits scaled, the total runtime of the program is increasing.

Any thoughts on how to increase the gpu utlization?

My thoughts initially are that the calculation will use what it needs to use and nothing more.

Do you think the descriminators aren’t scaled as the generators are scaling, so would that be a problem. I see as the qubits scaled, the total runtime of the program is increasing.

I’m not sure I understand your question here. But, if you want to scale up the number of trainable parameters in the discriminator, you’d need to tweak the inner dimensions of the classical layers in Discriminator:

class Discriminator(nn.Module):
    """Fully connected classical discriminator"""

    def __init__(self):
        super().__init__()

        self.model = nn.Sequential(
            # Inputs to first hidden layer (num_input_features -> 64)
            nn.Linear(image_size * image_size, 64),
            nn.ReLU(),
            # First hidden layer (64 -> 16)
            nn.Linear(64, 16),
            nn.ReLU(),
            # Second hidden layer (16 -> output)
            nn.Linear(16, 1),
            nn.Sigmoid(),
        )

Regarding an increase in runtime as the number of qubits increases, that is to be expected :slight_smile:.

I do understand as the number of generators/number of qubits/ancillary qubits increase, we do have more runtime. But that increase in runtime is caused by more computation, which i believe should show up in the increase of GPU utilization(lightning.gpu) , but i see it as flat line. Thats what i was wondering. Even CPU/Memory seems normal. SO i am wondering number of generators aren’t either being leveraging whole GPU and is blocked by soemthing else.

The increase in run time won’t necessarily correlate to an increase in GPU usage across all regimes. For circuits with 5-15 qubits like what you have, lightning.gpu is probably consumed by computational overheads that you simply are not exceeding due to the small circuit size. For 20+ qubits and deep circuits, you should probably see that 12% number increase. You can also try using adjoint instead of parameter-shift in those regimes, as well. That will probably help!

If you’re sticking to regimes with <20 qubits, lightning gpu isn’t the best.

Thanks @isaacdevlugt - Can you explain what plugin should i be using. I am hoping we have GPU VM , so using lighting.gpu and also whats the computational overhead between 5-15%. The above script doesn’t seem to support adjoint diff_method.

Also when i use 30 qubits, 26 anciallary qubits. the script stuck… Any idea why?

time python3 qgan.py
^C^C^C

^C^C^C^C^CTraceback (most recent call last):
File “qgan.py”, line 220, in
fake_data = generator(noise)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/nn/modules/module.py”, line 1501, in _call_impl
return forward_call(*args, **kwargs)
File “qgan.py”, line 176, in forward
q_out = partial_measure(elem, params).float().unsqueeze(0)
File “qgan.py”, line 135, in partial_measure
probs = quantum_circuit(noise, weights)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/qnode.py”, line 889, in call
res = qml.execute(
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/interfaces/execution.py”, line 729, in execute
res = _execute(
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/interfaces/torch.py”, line 258, in execute
return ExecuteTapes.apply(kwargs, *parameters)
File “/home/exouser/.local/lib/python3.8/site-packages/torch/autograd/function.py”, line 506, in apply
return super().apply(*args, **kwargs) # type: ignore[misc]
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/interfaces/torch.py”, line 87, in forward
res, ctx.jacs = ctx.execute_fn(ctx.tapes, **ctx.gradient_kwargs)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/interfaces/execution.py”, line 205, in wrapper
res = fn(execution_tapes.values(), **kwargs)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/interfaces/execution.py”, line 131, in fn
return original_fn(tapes, **kwargs)
File “/usr/lib/python3.8/contextlib.py”, line 75, in inner
return func(*args, **kwds)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/_qubit_device.py”, line 591, in batch_execute
res = self.execute(circuit)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/_qubit_device.py”, line 381, in execute
results = self.statistics(circuit=circuit)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane_lightning_gpu/lightning_gpu.py”, line 408, in statistics
return super().statistics(circuit, shot_range, bin_size)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane/_qubit_device.py”, line 757, in statistics
self.probability(wires=obs.wires, shot_range=shot_range, bin_size=bin_size)
File “/home/exouser/.local/lib/python3.8/site-packages/pennylane_lightning_gpu/lightning_gpu.py”, line 769, in probability
self._gpu_state.Probability(device_wires)
KeyboardInterrupt

real 7m23.809s

Can you explain what plugin should i be using

I think using lightning.qubit in tandem with Pytorch / JAX is a good idea!

Also when i use 30 qubits, 26 anciallary qubits. the script stuck… Any idea why?

It’s tough to say, but it’s most likely the fact that it’s an extremely large calculation that you’re wanting to do so it takes a while!