Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cirq-ionq/cirq_ionq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@
from cirq_ionq.serializer import Serializer, SerializedProgram

from cirq_ionq.service import Service

from cirq_ionq.ionq_native_gates import GPIGate, GPI2Gate, MSGate

from cirq.protocols.json_serialization import _register_resolver
from cirq_ionq.json_resolver_cache import _class_resolver_dictionary

_register_resolver(_class_resolver_dictionary)
7 changes: 2 additions & 5 deletions cirq-ionq/cirq_ionq/ionq_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import cirq_ionq
from cirq_ionq import ionq_exceptions


RETRIABLE_STATUS_CODES = {requests.codes.internal_server_error, requests.codes.service_unavailable}


Expand Down Expand Up @@ -117,11 +118,7 @@ def create_job(
"""
actual_target = self._target(target)

json: Dict[str, Any] = {
'target': actual_target,
'body': serialized_program.body,
'lang': 'json',
}
json: Dict[str, Any] = {'target': actual_target, 'body': serialized_program.body}
if name:
json['name'] = name
# We have to pass measurement keys through the metadata.
Expand Down
1 change: 0 additions & 1 deletion cirq-ionq/cirq_ionq/ionq_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def test_ionq_client_create_job(mock_post):
expected_json = {
'target': 'qpu',
'body': {'job': 'mine'},
'lang': 'json',
'name': 'bacon',
'shots': '200',
'metadata': {'shots': '200', 'a': '0,1'},
Expand Down
2 changes: 1 addition & 1 deletion cirq-ionq/cirq_ionq/ionq_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@


class IonQAPIDevice(cirq.Device):
"""A device that uses the gates exposed by the IonQ API.
"""A device that uses the QIS gates exposed by the IonQ API.

When using this device in constructing a circuit, it will convert one and two qubit gates
that are not supported by the API into those supported by the API if they have a unitary
Expand Down
199 changes: 199 additions & 0 deletions cirq-ionq/cirq_ionq/ionq_native_gates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Copyright 2019 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Native gates for IonQ hardware"""

from typing import Any, Dict, Sequence, Union

import cmath
import math
import cirq
from cirq import protocols
from cirq._doc import document
import numpy as np


@cirq.value.value_equality
class GPIGate(cirq.Gate):
"""The GPI gate is a single qubit gate.
The unitary of this gate is
[[0, e^{-i\\phi}],
[e^{-i\\phi}, 0]]
"""

def __init__(self, *, phi):
self.phi = phi

def _unitary_(self) -> np.ndarray:
top = cmath.exp(self.phi * 1j)
bot = cmath.exp(-self.phi * 1j)
return np.array([[0, top], [bot, 0]])

def __str__(self) -> str:
return 'GPI'

def _num_qubits_(self) -> int:
return 1

@property
def phase(self) -> float:
return self.phi

def __repr__(self) -> str:
return f'cirq_ionq.GPIGate(phi={self.phi!r})'

def _json_dict_(self) -> Dict[str, Any]:
return cirq.obj_to_dict_helper(self, ['phi'])

def _value_equality_values_(self) -> Any:
return self.phi

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> Union[str, 'protocols.CircuitDiagramInfo']:
return protocols.CircuitDiagramInfo(wire_symbols=('GPI',), exponent=self.phase)


GPI = GPIGate(phi=0)
document(
GPI,
"""The GPI gate is a single qubit gate.
The unitary of this gate is
[[0, e^{-i\\phi}],
[e^{-i\\phi}, 0]]
It is driven by Rabi laser.
https://ionq.com/best-practices
""",
)


@cirq.value.value_equality
class GPI2Gate(cirq.Gate):
"""The GPI2 gate is a single qubit gate.
The unitary of this gate is
\\frac{1}{/\\sqrt{2}}[[1, -i*e^{-i\\phi}],
[-i*e^{-i\\phi}, 1]]
"""

def __init__(self, *, phi):
self.phi = phi

def _unitary_(self) -> np.ndarray:
top = -1j * cmath.exp(self.phase * -1j)
bot = -1j * cmath.exp(self.phase * 1j)
return np.array([[1, top], [bot, 1]]) / math.sqrt(2)

@property
def phase(self) -> float:
return self.phi

def __str__(self) -> str:
return 'GPI2'

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> Union[str, 'protocols.CircuitDiagramInfo']:
return protocols.CircuitDiagramInfo(wire_symbols=('GPI2',), exponent=self.phase)

def _num_qubits_(self) -> int:
return 1

def __repr__(self) -> str:
return f'cirq_ionq.GPI2Gate(phi={self.phi!r})'

def _json_dict_(self) -> Dict[str, Any]:
return cirq.obj_to_dict_helper(self, ['phi'])

def _value_equality_values_(self) -> Any:
return self.phi


GPI2 = GPI2Gate(phi=0)
document(
GPI2,
"""The GPI2 gate is a single qubit gate.
The unitary of this gate is
\\frac{1}{/\\sqrt{2}}[[1, -i*e^{-i\\phi}],
[-i*e^{-i\\phi}, 1]]
It is driven by Rabi laser.
https://ionq.com/best-practices
""",
)


@cirq.value.value_equality
class MSGate(cirq.Gate):
"""The MS gate is a 2 qubit gate.
The unitary of this gate is
MS(\\phi_1 - \\phi_0) =
MS(t) =
\\frac{1}{\\sqrt{2}}
[[\\cos(t), 0, 0, -i*\\sin(t)],
[0, \\cos(t), -i*\\sin(t), 0],
[0, -i*\\sin(t), \\cos(t), 0],
[-i*\\sin(t), 0, 0, \\cos(t)]]
"""

def __init__(self, *, phi1, phi2):
self.phi1 = phi1
self.phi2 = phi2

def _unitary_(self) -> np.ndarray:
tee = self.phi2 - self.phi1
diag = math.cos(tee)
adiag = -1j * math.sin(tee)
return np.array(
[[diag, 0, 0, adiag], [0, diag, adiag, 0], [0, adiag, diag, 0], [adiag, 0, 0, diag]]
)

@property
def phases(self) -> Sequence[float]:
return [self.phi1, self.phi2]

def __str__(self) -> str:
return 'MS'

def _num_qubits_(self) -> int:
return 2

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> Union[str, 'protocols.CircuitDiagramInfo']:
return protocols.CircuitDiagramInfo(wire_symbols=('MS',), exponent=self.phases)

def __repr__(self) -> str:
return f'cirq_ionq.MSGate(phi1={self.phi1!r}, phi2={self.phi2!r})'

def _json_dict_(self) -> Dict[str, Any]:
return cirq.obj_to_dict_helper(self, ['phi1', 'phi2'])

def _value_equality_values_(self) -> Any:
return (self.phi1, self.phi2)


MS = MSGate(phi1=0, phi2=0)
document(
MS,
"""The MS gate is a 2 qubit gate.
The unitary of this gate is
MS(\\phi_1 - \\phi_0) =
MS(t) =
\\frac{1}{\\sqrt{2}}
[[\\cos(t), 0, 0, -i*\\sin(t)],
[0, \\cos(t), -i*\\sin(t), 0],
[0, -i*\\sin(t), \\cos(t), 0],
[-i*\\sin(t), 0, 0, \\cos(t)]]

https://ionq.com/best-practices
""",
)
65 changes: 65 additions & 0 deletions cirq-ionq/cirq_ionq/ionq_native_gates_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2019 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for IonQ native gates"""

import math
import cirq
import numpy
import pytest
from .ionq_native_gates import GPIGate, GPI2Gate, MSGate


@pytest.mark.parametrize(
"gate,nqubits", [(GPIGate(phi=0.1), 1), (GPI2Gate(phi=0.2), 1), (MSGate(phi1=0.1, phi2=0.2), 2)]
)
def test_gate_methods(gate, nqubits):
assert str(gate) != ""
assert repr(gate) != ""
assert gate.num_qubits() == nqubits
assert cirq.protocols.circuit_diagram_info(gate) is not None


@pytest.mark.parametrize("gate", [GPIGate(phi=0.1), GPI2Gate(phi=0.2), MSGate(phi1=0.1, phi2=0.2)])
def test_gate_json(gate):
g_json = cirq.to_json(gate)
assert cirq.read_json(json_text=g_json) == gate


@pytest.mark.parametrize("phase", [0, 0.1, 0.4, math.pi / 2, math.pi, 2 * math.pi])
def test_gpi_unitary(phase):
"""Tests that the GPI gate is unitary."""
gate = GPIGate(phi=phase)

mat = cirq.protocols.unitary(gate)
numpy.testing.assert_array_almost_equal(mat.dot(mat.conj().T), numpy.identity(2))


@pytest.mark.parametrize("phase", [0, 0.1, 0.4, math.pi / 2, math.pi, 2 * math.pi])
def test_gpi2_unitary(phase):
"""Tests that the GPI2 gate is unitary."""
gate = GPI2Gate(phi=phase)

mat = cirq.protocols.unitary(gate)
numpy.testing.assert_array_almost_equal(mat.dot(mat.conj().T), numpy.identity(2))


@pytest.mark.parametrize(
"phases", [(0, 1), (0.1, 1), (0.4, 1), (math.pi / 2, 0), (0, math.pi), (0.1, 2 * math.pi)]
)
def test_ms_unitary(phases):
"""Tests that the MS gate is unitary."""
gate = MSGate(phi1=phases[0], phi2=phases[1])

mat = cirq.protocols.unitary(gate)
numpy.testing.assert_array_almost_equal(mat.dot(mat.conj().T), numpy.identity(4))
11 changes: 8 additions & 3 deletions cirq-ionq/cirq_ionq/json_resolver_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@
import functools
from typing import Dict

import cirq_ionq
from cirq.protocols.json_serialization import ObjectFactory


@functools.lru_cache()
def _class_resolver_dictionary() -> Dict[str, ObjectFactory]:
return {}
@functools.lru_cache() # coverage: ignore
def _class_resolver_dictionary() -> Dict[str, ObjectFactory]: # coverage: ignore
return {
"GPIGate": cirq_ionq.GPIGate,
"GPI2Gate": cirq_ionq.GPI2Gate,
"MSGate": cirq_ionq.MSGate,
}
4 changes: 4 additions & 0 deletions cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"cirq_type": "GPI2Gate",
"phi": 0.1
}
1 change: 1 addition & 0 deletions cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.repr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq_ionq.GPI2Gate(phi=0.1)
4 changes: 4 additions & 0 deletions cirq-ionq/cirq_ionq/json_test_data/GPIGate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"cirq_type": "GPIGate",
"phi": 0.2
}
1 change: 1 addition & 0 deletions cirq-ionq/cirq_ionq/json_test_data/GPIGate.repr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq_ionq.GPIGate(phi=0.2)
5 changes: 5 additions & 0 deletions cirq-ionq/cirq_ionq/json_test_data/MSGate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cirq_type": "MSGate",
"phi1": 0.1,
"phi2": 0.55
}
1 change: 1 addition & 0 deletions cirq-ionq/cirq_ionq/json_test_data/MSGate.repr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq_ionq.MSGate(phi1=0.1, phi2=0.55)
Loading