Open KilianPoirier opened 7 months ago
Hi @KilianPoirier! I would like to work on this issue of adding Cirq backend to OpenQAOA. Can you assign it to me?
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.
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.
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==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.
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
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.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)]
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})"
Hi @shubhamkaushal765 thanks for tackling this issue!
Let me address your points in order:
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?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.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
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.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!
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:
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:openqaoa-cirq
including all necessary components (e.g. setup.py, pyproject.toml, etc...).openqaoa-cirq/backend
equivalent, bridging the stack's internal representation to one compatible with Cirq Quantum SDK.