NVIDIA/cuda-quantum

State Preparation Circuit Synthesis via Matrix Product State Decomposition

1tnguyen opened this issue ยท 24 comments

Required prerequisites

  • Search the issue tracker to check if your feature has already been mentioned or rejected in other issues.

Describe the feature

Background

State preparation is an important class of quantum circuits: given the complex amplitude state vector/wave function, synthesize a quantum circuit to prepare that state.

Exact methods exist for this decomposition, e.g., see [1, Sec. 4] or [2] (implemented in https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/cudaq/builder/kernels.h#L62)

An alternative approach is to decompose the input state vector into the Matrix Product State form. Then, there are protocols for converting those MPS tensors into shallow-depth circuits [3, 4].

Description

Develop an MPS-based state preparation module for CUDA-Q.

At a minimum viable prototype level, this can be implemented as a circuit builder module (similar to https://github.com/NVIDIA/cuda-quantum/blob/main/runtime/cudaq/builder/kernels.h#L62) to demonstrate the correctness of the protocol. Ideally, this will eventually be converted to a CUDA-Q MLIR circuit synthesis pass.

The implementation needs to be customizable by the target state preparation fidelity and/or the number of circuit layers.

The basic decomposition is described in [5]. The proposed optimization protocol in [3] can also be considered.

For example, Figure 1 from [4] depicts the high-level decomposition protocol

image

Resources

State vector to MPS tensor decomposition procedure.

References

[1] https://arxiv.org/abs/quant-ph/0406176v5

[2] https://arxiv.org/pdf/quant-ph/0407010

[3] https://arxiv.org/pdf/2209.00595

[4] https://arxiv.org/abs/2303.01562

[5] https://arxiv.org/pdf/1908.07958

Greetings there,

Hope you are well. Could you kindly expand a bit more on this?
"Ideally, this will eventually be converted to a CUDA-Q MLIR circuit synthesis pass."

Greetings there,

Hope you are well. Could you kindly expand a bit more on this? "Ideally, this will eventually be converted to a CUDA-Q MLIR circuit synthesis pass."

Hi @ACE07-Sev,

Glad that you're interested in this project.

As you may know, CUDA-Q uses MLIR as its internal IR representation. One of the high-level features that this project aims to support is quantum circuit synthesis for state preparation. Specifically, CUDA-Q would have an IR node representing quantum state initialization (e.g., see this init_state op in our development branch).

The algorithm developed in this project would eventually be implemented as a Quake IR decomposition/synthesis pass (e.g., very similar to the existing gate decomposition passes)

Please let us know if you have any other questions.

Thank you for the kind explanation. I guess my question is if this (beyond the UnitaryHack) is going to be only for state preparation (synthesis of statevectors, not arbitrary quantum circuits)?

Because gate decomposition is usually more for operator synthesis. Would this do operator synthesis as well?

Thank you for the kind explanation. I guess my question is if this (beyond the UnitaryHack) is going to be only for state preparation (synthesis of statevectors, not arbitrary quantum circuits)?

The immediate objective of this project is state preparation. It shares many commonalities with operator/unitary matrix synthesis (e.g., via the matrix product operator (MPO) representation of a unitary matrix, etc.) but operator synthesis is not part of the feature request.

Because gate decomposition is usually more for operator synthesis. Would this do operator synthesis as well?

We don't need to do generic operator synthesis in this project. The state preparation algorithm needs some specific matrix to gate sequence conversion as depicted in the attached figure.

I hope that it helps.

I see, I just wanted to have some context for whether you were also shooting for MPO synthesis since I'm also aiming to do that in the near future.

Cool, I have the code done using qiskit, but since I'm on Windows, I'm having some trouble setting up CUDAQ. I'll see if I can set it up ASAP and rewrite the code with CUDAQ.

Quick question, can I use quimb in my implementation?

Quick question, can I use quimb in my implementation?

Hi @ACE07-Sev,

We'd prefer to minimize third-party dependency as much as we can, especially if it is a Python package. This is because we'll need to support it in C++ as well.

If you have started working on this with quimb as a dependency, I recommend that you try to isolate it in some wrapper/helper module. For example, only expose the functionality you need from quimb as standalone helper functions. That way you can continue the implementation and reimplement those helpers without quimb at a later time.

Hi @ACE07-Sev,
How are you doing with this project?
If you have some code/partial implementation, please feel free to post a PR.
We can iterate over the PR.

Greetings there,

Hope you are well. I am currently translating the code I have to CUDAQ. At the moment, I'd like to know how I can resolve #1773. I'll do my best to make a PR very soon.

Greetings there,

Hope you are well. I am currently translating the code I have to CUDAQ. At the moment, I'd like to know how I can resolve #1773. I'll do my best to make a PR very soon.

Please see my reply in #1773 and let me know if you have any questions. Looking forward to seeing your contribution.

I saw it, thank you very much! I need one more thing before I can finish the translation. How can I do a unitary gate in CUDAQ? This is where one would pass a unitary matrix and qubit indices it would be applied to, and it would get transpiled to the gates. Equivalent of this in qiskit is

circuit = QuantumCircuit(3, 3)
unitary_matrix = SOME_UNITARY_MATRIX
circuit.unitary(unitary_matrix, [0, 1, 2])

Oh, and also how to add a U3 gate?

I saw it, thank you very much! I need one more thing before I can finish the translation. How can I do a unitary gate in CUDAQ? This is where one would pass a unitary matrix and qubit indices it would be applied to, and it would get transpiled to the gates. Equivalent of this in qiskit is

circuit = QuantumCircuit(3, 3)
unitary_matrix = SOME_UNITARY_MATRIX
circuit.unitary(unitary_matrix, [0, 1, 2])

Currently, we don't support arbitrary unitary matrix synthesis. You'd need to decompose it as gates.

Re: u3 gate. This is an example:

kernel.u3(np.pi, np.pi, np.pi / 2, q)

So, once I finish the translation, I'm going to first make a notebook in my PR to show the functional code, and then under your guidance I will implement it in the native style of cudaq. Is that feasible for you?

I see. Am I allowed to use qiskit to transpile it first then?

I saw it, thank you very much! I need one more thing before I can finish the translation. How can I do a unitary gate in CUDAQ? This is where one would pass a unitary matrix and qubit indices it would be applied to, and it would get transpiled to the gates. Equivalent of this in qiskit is

circuit = QuantumCircuit(3, 3)
unitary_matrix = SOME_UNITARY_MATRIX
circuit.unitary(unitary_matrix, [0, 1, 2])

Currently, we don't support arbitrary unitary matrix synthesis. You'd need to decompose it as gates.

Re: u3 gate. This is an example:

kernel.u3(np.pi, np.pi, np.pi / 2, q)

I can't seem to do it. When I run it, it gives this error

Cell In[5], [line 21](vscode-notebook-cell:?execution_count=5&line=21)
     [18](vscode-notebook-cell:?execution_count=5&line=18) def U3(self,
     [19](vscode-notebook-cell:?execution_count=5&line=19)        angles,
     [20](vscode-notebook-cell:?execution_count=5&line=20)        qubit_index: int) -> None:
---> [21](vscode-notebook-cell:?execution_count=5&line=21)     self.circuit.u3(angles[0], angles[1], angles[2], self.qr[qubit_index])

AttributeError: 'PyKernel' object has no attribute 'u3'

This U3 feature is scheduled for the next release (v0.8).
It's only available in the nightly docker container but not the Python wheel.
For your circuit builder model, you can use the decomposed form, if using 0.7.x wheels e.g.,

def U3(self, angles, qubit_index: int) -> None:
   self.circuit.rz(angles[2], self.qr[qubit_index])
   self.circuit.rx(np.pi/2, self.qr[qubit_index])
   self.circuit.rz(angles[0], self.qr[qubit_index])
   self.circuit.rx(-np.pi/2, self.qr[qubit_index])
   self.circuit.rz(angles[1], self.qr[qubit_index])

I see. I'll try this one out then.

FYI, unitary matrix decomposition is also in works (with perf optimizations for simulators). If you use the nightly docker image (nvcr.io/nvidia/nightly/cuda-quantum:latest) you may be able to use it already.

I see. I'll try to set it up then. It'll definitely help with your 3rd party dependency since it wouldn't need sth like qiskit's transpile. Much appreciate the heads up!

Ohh, quick question. Does your unitary decomposition (I imagine you're also transpiling?) introduce any global phase?

Ohh, quick question. Does your unitary decomposition (I imagine you're also transpiling?) introduce any global phase?

No, the unitary decomposition wouldn't introduce any global phase.
btw, we're closing in on the unitaryHACK. If you have some code and want to participate in this year's event, please feel free to post a PR.

Oh no, I'm not interested in the UnitaryHACK event. I am simply interested in the MPS approach and have been working on it for a few months now, so I'm just eager to contribute this feature to CUDAQ.

Greetings there,

Hope you are well. Apologies for the delay, I am currently trying to set up the nightly docker version. I'll notify the moment I get the notebook translated with cudaq.