Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate the Cirq backend with OpenQAOA #306

Open
KilianPoirier opened this issue Apr 22, 2024 · 4 comments
Open

Integrate the Cirq backend with OpenQAOA #306

KilianPoirier opened this issue Apr 22, 2024 · 4 comments

Comments

@KilianPoirier
Copy link
Collaborator

KilianPoirier commented Apr 22, 2024

Issue Description

Can we add the Cirq backend to the OpenQAOA stack?
Cirq provides a SDK to simulate and execute quantum circuits on multiple backends:

  • Simulate using different methods: exact simulation, noisy simulation, parameter sweep, etc...
  • Access multiple QPU backends.

Note: someone already tackled this issue but couldn't finish the task. You can find the details of their attempt in this closed PR.

Changes to be made

In the same way we implemented different backends (physical QPU or simulators), implement a plugin package openqaoa-cirq that allows execution on Cirq's backend. More specifically, changes include:

  • Creation of a new plugin openqaoa-cirq including all necessary components (e.g. setup.py, pyproject.toml, etc...).
  • Creation of a openqaoa-cirq/backend equivalent, bridging the stack's internal representation to one compatible with Cirq Quantum SDK.
  • Creation of unit tests to make sure all features are correctly supported.
@shubhamkaushal765
Copy link

Hi @KilianPoirier!
I would like to work on this issue of adding Cirq backend to OpenQAOA. Can you assign it to me?

@KilianPoirier
Copy link
Collaborator Author

Hi @shubhamkaushal765 , thanks for looking into openqaoa!
You can go ahead and tackle the issue, it will be assigned when the PR has been accepted.

@shubhamkaushal765
Copy link

Hi @KilianPoirier, Thanks! OpenQAOA looks great and I really wanted to contribute to it.

First of all, sorry for the long post, therefore added a TLDR.

TLDR:

  • Cirq 1.4.0 conflicts with other packages due to older pydantic, attrs, httpx dependencies
  • Added placeholder code for Google's QPU (limited access), plan to add IONQ's QPU
  • Cirq lacks direct 2-qubit rotation gates, using ZZPowGate with converted exponents
  • Cirq's exponent approximation leads to errors in wavefunctions and expectation values
  • Tried custom ZZ gates but faced sympy incompatibility issues
  • Seeking suggestions for handling 2-qubit-rotation gate implementations, and approximation errors

So, I had been tackling this issue, and I have run into some creative challenges, some of which I had solved, and for others I wanted your suggestion on how to proceed. They are:

Cirq dependency conflicts

cirq==1.4.0: the latest cirq version is dependent on pydantic==1.10.16, attrs==21.4.0 and httpx==0.23.3. It conflicts with other packages installed which have much updated versions of these sub-packages. Downgrading cirq will only lead to much older versions of these sub-packages, hence the only option remains is to downgrade other packages. We can also try to find a balance, but I am not sure how good it will work.
I have more forward with cirq==1.4.0 and built the openqaoa-cirq on it. If later we decided to change the version, I am sure only small modifications will be needed to suit the version.

Dependency Conflicts
amazon-braket-default-simulator 1.23.2 requires pydantic>2, but pydantic 1.10.16 is installed which is incompatible.
amazon-braket-schemas 1.22.0 requires pydantic>2, but pydantic 1.10.16 is installed which is incompatible.
    cirq-rigetti==1.4.0 -> pyquil<4.0.0,>=3.2.0 -> qcs-api-client<0.22.0,>=0.21.0 -> pydantic 1.10.16

jsonschema 4.22.0 requires attrs>=22.2.0, but attrs 21.4.0 is installed which is incompatible.
referencing 0.35.1 requires attrs>=22.2.0, but attrs 21.4.0 is installed which is incompatible.
    cirq==1.4.0 -> cirq-core==1.4.0 -> attrs 21.4.0

jupyterlab 4.2.2 requires httpx>=0.25.0, but httpx 0.23.3 is installed which is incompatible.
    cirq-rigetti==1.4.0 -> pyquil<4.0.0,>=3.2.0 -> qcs-api-client<0.22.0,>=0.21.0 -> httpx 0.23.3

QPU availability by cirq

This link says

Access is currently only granted to those on an approved list. No public access to the service is available at this time.

So, I have added a boiler plate code which can be modified as required. Google also offers IONQ's QPU, which I plan to add in the coming few days.

QAOACirqBackend*Simulator

  1. cirq has no direct implementation of 2-qubit rotation gates such as RZZ, RZX, RXX, etc. We have to use the cirq.ZZPowGate which takes exponent as a keyword argument. So to make the rotation angles compatible with this format we can do rotation_angle / np.pi to get the exponent.
  2. cirq takes the exponent and approximates the representative fraction, which leads to small errors. This trickles down errors to wavefunction and expectation methods. The expectation values usually have an error of 1e-6, but the wavefunctions are going wild, with very different real and imaginary values.
# function: src/openqaoa-cirq/tests/test_sim_cirq.py:test_qaoa_circuit_wavefunction_expectation_equivalence_1
# expectation value
>           self.assertAlmostEqual(
                cirq_expectation, vector_expectation, places=ASSERT_ALMOST_EQUAL_PLACES
            )
E           AssertionError: -0.302997738123 != -0.30299750696 within 7 places (2.3116300001957413e-07 difference)
==========================================================================

# wavefunction
Cirq WaveFunction: [ 0.11789444+0.2546382j  -0.33760953+0.31751314j -0.17041622+0.21223776j -0.26151618+0.252959j -0.26151618+0.252959j   -0.17041622+0.21223776j -0.33760953+0.31751314j  0.11789444+0.2546382j ]
Vector Wavefunction [(-0.11338235549177214+0.25667908366165665j), (-0.36265249809327543-0.029362249318695896j), (-0.27191651336185224+0.01216375954595944j), (-0.4613775050174799-0.04388124877541683j), (-0.4613775050174799-0.04388124877541683j), (-0.27191651336185224+0.01216375954595944j), (-0.36265249809327543-0.029362249318695896j), (-0.11338235549177214+0.25667908366165665j)]

Comparing circuit by both Qiskit and Cirq

Keep in mind that qiskit shows rotation_angle with ZZ, whereas cirq shows exponent.

# Qiskit circuit
      ┌───┐                          ┌────────┐
q1_0: ┤ H ├─■────────────────■───────┤ Rx(-2) ├
      ├───┤ │ZZ(6)           │       ├────────┤
q1_1: ┤ H ├─■───────■────────┼───────┤ Rx(-2) ├
      ├───┤         │ZZ(12)  │ZZ(18) ├────────┤
q1_2: ┤ H ├─────────■────────■───────┤ Rx(-2) ├
      └───┘                          └────────┘

wavefunction = [-0.11338236+0.25667908j -0.3626525 -0.02936225j -0.27191651+0.01216376j
 -0.46137751-0.04388125j -0.46137751-0.04388125j -0.27191651+0.01216376j
 -0.3626525 -0.02936225j -0.11338236+0.25667908j]
================================================

# Cirq circuit
                                  ┌───────────────────┐
0: ───H───ZZ───────────────────────ZZ─────────────────────Rx(-0.637π)───
          │                        │
1: ───H───ZZ^(-1/11)───ZZ──────────┼───────Rx(-0.637π)──────────────────
                       │           │
2: ───H────────────────ZZ^-0.18────ZZ^-0.27───────────────Rx(-0.637π)───
                                  └───────────────────┘
wavefunction = [ 0.11789444+0.2546382j  -0.33760953+0.31751314j -0.17041622+0.21223776j
 -0.26151618+0.252959j   -0.26151618+0.252959j   -0.17041622+0.21223776j
 -0.33760953+0.31751314j  0.11789444+0.2546382j ]

I tried building custom ZZ and other gates to overcome the approximation challenge, but as numpy function does not support sympy.Symbol, it is proving to be a challenge. I can explore more along this line, but frankly, I wanted your suggestion on this problem.

My implementation of ZZGate is as follows:

# self.theta is of type sympy.Symbol which is incompatible with np.exp
class RZZGate(cirq.Gate):
    def __init__(self, theta):
        super().__init__()
        self.theta = theta

    def _unitary_(self):
        return np.array(
            [
                [np.exp(-1j * self.theta / 2), 0, 0, 0],
                [0, np.exp(1j * self.theta / 2), 0, 0],
                [0, 0, np.exp(1j * self.theta / 2), 0],
                [0, 0, 0, np.exp(-1j * self.theta / 2)],
            ]
        )

    def _num_qubits_(self):
        return 2

    def _circuit_diagram_info_(self, args):
        return f"RZZ({self.theta})", f"RZZ({self.theta})"

@KilianPoirier
Copy link
Collaborator Author

Hi @shubhamkaushal765 thanks for tackling this issue!

Let me address your points in order:

  • Concerning the dependency conflicts, it seems like the cirq-rigetti extension is causing somme issues since we support pyquil>=4.0 while cirq-rigetti limits the version to the previous major release. Would removing cirq-rigetti fix some of these conflicts?
  • Concerning Google's QPU you're right, there is no possible public access at the moment and I doubt there will be for a while. I believe that a placeholder would be enough for now. I believe both IonQ and Pasqal are available through cirq but I don't know how much you would be able to interface. Let me know if you can make it work or need any help on that front.
  • When it comes to specifying non-native gates, one must be very careful since different libraries sometimes use different conventions. I believe this is the case with cirq.ZZPowGate, it uses two arguments exponent and global_phase that needs to be tuned to match the standard description used with the other supported packages. For example, the following expressions are equivalent:
a = cirq.ZZPowGate(exponent=1.0, global_shift=0.5)
b = RZZGate(theta=-np.pi)
np.all(cirq.unitary(a) == np.round(b.to_matrix())) # returns True
  • I believe that the approximation made in cirq for rotation angles is reasonable and it shouldn't change the value of the wave function significantly. If the matrix description of the gate doesn't match the ones already implemented, it is probably due to the difference in convention. Make sure that the gates themselves are the same before comparing circuits.
  • I think using ZZPowGate should be a reasonable choice to replicate the behaviour of RZZGate or equivalent gates.

I hope this helps. Let me know if you have any additional questions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants