QAOA with High Number of Qubits

Hello everyone,

I would like to use QAOA to solve an optimisation problem. I have already achieved to build a prototype with a 10-qubit quantum circuit. However, when I try to scale the circuit up, to for example 30 qubits, I get the following error when I define the cost function method, try to optimise the circuit parameters with gradient descent or try to compute the probability distribution of the solutions:

MemoryError: Unable to allocate 16.0 GiB for an array with shape (1073741824,) and data type complex128

The complete error messages are long (I can post them if needed), but they all have the following in common:

--> 219     state = np.zeros(2**self.num_wires, dtype=np.complex128)
    220     state[index] = 1
    221     state = self._asarray(state, dtype=self.C_DTYPE)

MemoryError: Unable to allocate 16.0 GiB for an array with shape (1073741824,) and data type complex128

That means that a vector with all possible qubit “collapsed” states (0 or 1) has to be created (correct me if I’m wrong). Is there any way of scaling up QAOA on pennylane without having to define such a vector and using only 8GB to 32GB of memory?

Are there other ways of working with QAOA and a high number of qubits (for example in the range of 200 qubits)?

Thank you very much in advance.

Hi @pormelrog,

On a laptop you can usually only run circuits with up to 20 qubits. Remember that the compute becomes exponentially harder with each extra qubit, so 30 qubits is a big ask for a classical computer. However, we have implemented a nice trick in PennyLane that can, in some cases, allow you to run bigger circuits. This nice trick is circuit cutting.

The PennyLane functionality for this is qml.cut_circuit(). You can learn more about how to use it in this blog post, and do a deeper dive into it with this demo. The key thing here is that we’re breaking apart our circuit into smaller-sized circuits that we can run with a lower memory requirement. There is a classical overhead so your circuit will take longer to run, but you may be able to run larger circuits.

I encourage you to try it out and let us know if this worked for you!

Hello @CatalinaAlbornoz,

thank you very much for your answer. You are right and I understand that the memory and computational cost rises exponentially in the number of qubits. But maybe there is some technique or method unknown to me to achieve what I need.

I have also tried the circuit cutting method. However, it still does not help, because I keep getting an error for exceeding the maximum allowed dimension when defining the qml.device, i.e., before any circuit cutting can be performed:

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

returns:

ValueError                                Traceback (most recent call last)
c:\Users\pormelrog\Documents\Code\Prototyping.ipynb Cell 57 in 1
----> 1 dev = qml.device("lightning.qubit", wires=range(100))

File ~\AppData\Roaming\Python\Python39\site-packages\pennylane\__init__.py:325, in device(name, *args, **kwargs)
    319     raise DeviceError(
    320         f"The {name} plugin requires PennyLane versions {plugin_device_class.pennylane_requires}, "
    321         f"however PennyLane version {__version__} is installed."
    322     )
    324 # Construct the device
--> 325 dev = plugin_device_class(*args, **options)
    327 # Once the device is constructed, we set its custom expansion function if
    328 # any custom decompositions were specified.
    329 if custom_decomps is not None:

File ~\AppData\Roaming\Python\Python39\site-packages\pennylane_lightning\lightning_qubit.py:190, in LightningQubit.__init__(self, wires, c_dtype, shots, batch_obs, analytic)
    186 self._batch_obs = batch_obs
    188 # Create the initial state. Internally, we store the
    189 # state as an array of dimension [2]*wires.
--> 190 self._state = self._create_basis_state(0)
    191 self._pre_rotated_state = self._state

File ~\AppData\Roaming\Python\Python39\site-packages\pennylane_lightning\lightning_qubit.py:219, in LightningQubit._create_basis_state(self, index)
    210 def _create_basis_state(self, index):
    211     """Return a computational basis state over all wires.
    212     Args:
    213         index (int): integer representing the computational basis state
   (...)
    217     Note: This function does not support broadcasted inputs yet.
    218     """
--> 219     state = np.zeros(2**self.num_wires, dtype=np.complex128)
    220     state[index] = 1
    221     state = self._asarray(state, dtype=self.C_DTYPE)

ValueError: Maximum allowed dimension exceeded

What is the maximum allowed dimension? Is there any way of increasing it? Is there some other device which is better suited for this?

The error is caused by the fact that an 2^N dimensional vector, for N \in \mathbb{N} number of qubits, must be defined to save the probabilities of observing each of the possible solutions. Is there any way of avoiding this? Is there maybe some way of working with a certain set of possible solutions only or to work with collapsed/size-reduced probabilities, without having to split the circuit manually/myself?

Kind regards,

pormelrog