diff --git a/cirq-ionq/cirq_ionq/__init__.py b/cirq-ionq/cirq_ionq/__init__.py index 28d7179dc18..728174c6d61 100644 --- a/cirq-ionq/cirq_ionq/__init__.py +++ b/cirq-ionq/cirq_ionq/__init__.py @@ -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) diff --git a/cirq-ionq/cirq_ionq/ionq_client.py b/cirq-ionq/cirq_ionq/ionq_client.py index ebaf00e6e08..c9df11b5e44 100644 --- a/cirq-ionq/cirq_ionq/ionq_client.py +++ b/cirq-ionq/cirq_ionq/ionq_client.py @@ -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} @@ -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. diff --git a/cirq-ionq/cirq_ionq/ionq_client_test.py b/cirq-ionq/cirq_ionq/ionq_client_test.py index 5fcb9ac0078..ce054a227a4 100644 --- a/cirq-ionq/cirq_ionq/ionq_client_test.py +++ b/cirq-ionq/cirq_ionq/ionq_client_test.py @@ -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'}, diff --git a/cirq-ionq/cirq_ionq/ionq_devices.py b/cirq-ionq/cirq_ionq/ionq_devices.py index 28092c4eb40..c2fdd2a56f5 100644 --- a/cirq-ionq/cirq_ionq/ionq_devices.py +++ b/cirq-ionq/cirq_ionq/ionq_devices.py @@ -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 diff --git a/cirq-ionq/cirq_ionq/ionq_native_gates.py b/cirq-ionq/cirq_ionq/ionq_native_gates.py new file mode 100644 index 00000000000..e9313b0c43a --- /dev/null +++ b/cirq-ionq/cirq_ionq/ionq_native_gates.py @@ -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 + """, +) diff --git a/cirq-ionq/cirq_ionq/ionq_native_gates_test.py b/cirq-ionq/cirq_ionq/ionq_native_gates_test.py new file mode 100644 index 00000000000..7e7dd425c7a --- /dev/null +++ b/cirq-ionq/cirq_ionq/ionq_native_gates_test.py @@ -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)) diff --git a/cirq-ionq/cirq_ionq/json_resolver_cache.py b/cirq-ionq/cirq_ionq/json_resolver_cache.py index 69115dc8979..c056a2bb77f 100644 --- a/cirq-ionq/cirq_ionq/json_resolver_cache.py +++ b/cirq-ionq/cirq_ionq/json_resolver_cache.py @@ -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, + } diff --git a/cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.json b/cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.json new file mode 100644 index 00000000000..ec5358981cc --- /dev/null +++ b/cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.json @@ -0,0 +1,4 @@ +{ + "cirq_type": "GPI2Gate", + "phi": 0.1 +} diff --git a/cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.repr b/cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.repr new file mode 100644 index 00000000000..bd270116fd2 --- /dev/null +++ b/cirq-ionq/cirq_ionq/json_test_data/GPI2Gate.repr @@ -0,0 +1 @@ +cirq_ionq.GPI2Gate(phi=0.1) diff --git a/cirq-ionq/cirq_ionq/json_test_data/GPIGate.json b/cirq-ionq/cirq_ionq/json_test_data/GPIGate.json new file mode 100644 index 00000000000..0e6d77a97b6 --- /dev/null +++ b/cirq-ionq/cirq_ionq/json_test_data/GPIGate.json @@ -0,0 +1,4 @@ +{ + "cirq_type": "GPIGate", + "phi": 0.2 +} diff --git a/cirq-ionq/cirq_ionq/json_test_data/GPIGate.repr b/cirq-ionq/cirq_ionq/json_test_data/GPIGate.repr new file mode 100644 index 00000000000..cc2d37df7f4 --- /dev/null +++ b/cirq-ionq/cirq_ionq/json_test_data/GPIGate.repr @@ -0,0 +1 @@ +cirq_ionq.GPIGate(phi=0.2) diff --git a/cirq-ionq/cirq_ionq/json_test_data/MSGate.json b/cirq-ionq/cirq_ionq/json_test_data/MSGate.json new file mode 100644 index 00000000000..4b2268733a4 --- /dev/null +++ b/cirq-ionq/cirq_ionq/json_test_data/MSGate.json @@ -0,0 +1,5 @@ +{ + "cirq_type": "MSGate", + "phi1": 0.1, + "phi2": 0.55 +} diff --git a/cirq-ionq/cirq_ionq/json_test_data/MSGate.repr b/cirq-ionq/cirq_ionq/json_test_data/MSGate.repr new file mode 100644 index 00000000000..1e1116467e1 --- /dev/null +++ b/cirq-ionq/cirq_ionq/json_test_data/MSGate.repr @@ -0,0 +1 @@ +cirq_ionq.MSGate(phi1=0.1, phi2=0.55) diff --git a/cirq-ionq/cirq_ionq/serializer.py b/cirq-ionq/cirq_ionq/serializer.py index bd784ad4fa7..8ad7abb110a 100644 --- a/cirq-ionq/cirq_ionq/serializer.py +++ b/cirq-ionq/cirq_ionq/serializer.py @@ -21,6 +21,11 @@ import cirq from cirq.devices import line_qubit from cirq.ops import common_gates, parity_gates +from .ionq_native_gates import GPIGate, GPI2Gate, MSGate + +_NATIVE_GATES = cirq.Gateset( + GPIGate, GPI2Gate, MSGate, cirq.MeasurementGate, unroll_circuit_op=False +) @dataclasses.dataclass @@ -63,6 +68,11 @@ def __init__(self, atol: float = 1e-8): common_gates.HPowGate: self._serialize_h_pow_gate, common_gates.SwapPowGate: self._serialize_swap_gate, common_gates.MeasurementGate: self._serialize_measurement_gate, + # These gates can't be used with any of the non-measurement gates above + # Rather than validating this here, we rely on the IonQ API to report failure. + GPIGate: self._serialize_gpi_gate, + GPI2Gate: self._serialize_gpi2_gate, + MSGate: self._serialize_ms_gate, } def serialize(self, circuit: cirq.AbstractCircuit) -> SerializedProgram: @@ -76,13 +86,17 @@ def serialize(self, circuit: cirq.AbstractCircuit) -> SerializedProgram: serialized_ops = self._serialize_circuit(circuit) + gateset = "qis" if not _NATIVE_GATES.validate(circuit) else "native" + # IonQ API does not support measurements, so we pass the measurement keys through # the metadata field. Here we split these out of the serialized ops. body = { + 'gateset': gateset, 'qubits': num_qubits, 'circuit': [op for op in serialized_ops if op['gate'] != 'meas'], } metadata = self._serialize_measurements(op for op in serialized_ops if op['gate'] == 'meas') + return SerializedProgram(body=body, metadata=metadata) def _validate_circuit(self, circuit: cirq.AbstractCircuit): @@ -183,6 +197,16 @@ def _serialize_h_pow_gate(self, gate: cirq.HPowGate, targets: Sequence[int]) -> return {'gate': 'h', 'targets': targets} return None + # These could potentially be using serialize functions on the gates themselves. + def _serialize_gpi_gate(self, gate: GPIGate, targets: Sequence[int]) -> Optional[dict]: + return {'gate': 'gpi', 'target': targets[0], 'phase': gate.phase} + + def _serialize_gpi2_gate(self, gate: GPI2Gate, targets: Sequence[int]) -> Optional[dict]: + return {'gate': 'gpi2', 'target': targets[0], 'phase': gate.phase} + + def _serialize_ms_gate(self, gate: MSGate, targets: Sequence[int]) -> Optional[dict]: + return {'gate': 'ms', 'targets': targets, 'phases': gate.phases} + def _serialize_cnot_pow_gate( self, gate: cirq.CNotPowGate, targets: Sequence[int] ) -> Optional[dict]: diff --git a/cirq-ionq/cirq_ionq/serializer_test.py b/cirq-ionq/cirq_ionq/serializer_test.py index 9b2c615fbd2..3ddca1146b3 100644 --- a/cirq-ionq/cirq_ionq/serializer_test.py +++ b/cirq-ionq/cirq_ionq/serializer_test.py @@ -19,6 +19,8 @@ import cirq import cirq_ionq as ionq +from .ionq_native_gates import GPIGate, GPI2Gate, MSGate + def test_serialize_empty_circuit_invalid(): empty = cirq.Circuit() @@ -84,6 +86,7 @@ def test_serialize_pow_gates(): result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( body={ + 'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': name, 'targets': [0], 'rotation': exponent * np.pi}], }, @@ -98,7 +101,8 @@ def test_serialize_pauli_gates(): circuit = cirq.Circuit(gate(q0)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': name, 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': name, 'targets': [0]}]}, + metadata={}, ) @@ -108,12 +112,14 @@ def test_serialize_sqrt_x_gate(): circuit = cirq.Circuit(cirq.X(q0) ** (0.5)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 'v', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 'v', 'targets': [0]}]}, + metadata={}, ) circuit = cirq.Circuit(cirq.X(q0) ** (-0.5)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 'vi', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 'vi', 'targets': [0]}]}, + metadata={}, ) @@ -123,12 +129,14 @@ def test_serialize_s_gate(): circuit = cirq.Circuit(cirq.Z(q0) ** (0.5)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 's', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 's', 'targets': [0]}]}, + metadata={}, ) circuit = cirq.Circuit(cirq.Z(q0) ** (-0.5)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 'si', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 'si', 'targets': [0]}]}, + metadata={}, ) @@ -138,7 +146,8 @@ def test_serialize_h_gate(): circuit = cirq.Circuit(cirq.H(q0)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 'h', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 'h', 'targets': [0]}]}, + metadata={}, ) with pytest.raises(ValueError, match=r'H\*\*0.5'): @@ -152,12 +161,14 @@ def test_serialize_t_gate(): circuit = cirq.Circuit(cirq.Z(q0) ** (0.25)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 't', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 't', 'targets': [0]}]}, + metadata={}, ) circuit = cirq.Circuit(cirq.Z(q0) ** (-0.25)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': [{'gate': 'ti', 'targets': [0]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 1, 'circuit': [{'gate': 'ti', 'targets': [0]}]}, + metadata={}, ) @@ -170,6 +181,7 @@ def test_serialize_parity_pow_gate(): result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( body={ + 'gateset': 'qis', 'qubits': 2, 'circuit': [{'gate': name, 'targets': [0, 1], 'rotation': exponent * np.pi}], }, @@ -183,7 +195,12 @@ def test_serialize_cnot_gate(): circuit = cirq.Circuit(cirq.CNOT(q0, q1)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 2, 'circuit': [{'gate': 'cnot', 'control': 0, 'target': 1}]}, metadata={} + body={ + 'gateset': 'qis', + 'qubits': 2, + 'circuit': [{'gate': 'cnot', 'control': 0, 'target': 1}], + }, + metadata={}, ) with pytest.raises(ValueError, match=r'CNOT\*\*0.5'): @@ -197,7 +214,8 @@ def test_serialize_swap_gate(): circuit = cirq.Circuit(cirq.SWAP(q0, q1)) result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 2, 'circuit': [{'gate': 'swap', 'targets': [0, 1]}]}, metadata={} + body={'gateset': 'qis', 'qubits': 2, 'circuit': [{'gate': 'swap', 'targets': [0, 1]}]}, + metadata={}, ) with pytest.raises(ValueError, match=r'SWAP\*\*0.5'): @@ -211,7 +229,8 @@ def test_serialize_measurement_gate(): serializer = ionq.Serializer() result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 1, 'circuit': []}, metadata={'measurement0': f'tomyheart{chr(31)}0'} + body={'gateset': 'native', 'qubits': 1, 'circuit': []}, + metadata={'measurement0': f'tomyheart{chr(31)}0'}, ) @@ -221,7 +240,8 @@ def test_serialize_measurement_gate_target_order(): serializer = ionq.Serializer() result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 3, 'circuit': []}, metadata={'measurement0': f'tomyheart{chr(31)}2,0'} + body={'gateset': 'native', 'qubits': 3, 'circuit': []}, + metadata={'measurement0': f'tomyheart{chr(31)}2,0'}, ) @@ -234,13 +254,35 @@ def test_serialize_measurement_gate_split_across_dict(): assert result.metadata['measurement1'] == 'a' * 20 + f'{chr(31)}0' +def test_serialize_native_gates(): + q0, q1, q2 = cirq.LineQubit.range(3) + gpi = GPIGate(phi=0.1).on(q0) + gpi2 = GPI2Gate(phi=0.2).on(q1) + ms = MSGate(phi1=0.3, phi2=0.4).on(q1, q2) + circuit = cirq.Circuit([gpi, gpi2, ms]) + serializer = ionq.Serializer() + result = serializer.serialize(circuit) + assert result == ionq.SerializedProgram( + body={ + 'gateset': 'native', + 'qubits': 3, + 'circuit': [ + {'gate': 'gpi', 'target': 0, 'phase': 0.1}, + {'gate': 'gpi2', 'target': 1, 'phase': 0.2}, + {'gate': 'ms', 'targets': [1, 2], 'phases': [0.3, 0.4]}, + ], + }, + metadata={}, + ) + + def test_serialize_measurement_gate_multiple_keys(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.measure(q0, key='a'), cirq.measure(q1, key='b')) serializer = ionq.Serializer() result = serializer.serialize(circuit) assert result == ionq.SerializedProgram( - body={'qubits': 2, 'circuit': []}, + body={'gateset': 'native', 'qubits': 2, 'circuit': []}, metadata={'measurement0': f'a{chr(31)}0{chr(30)}b{chr(31)}1'}, )