Skip to content
Closed
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
33 changes: 16 additions & 17 deletions qiskit_ionq/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@

from qiskit.circuit import controlledgate as q_cgates
from qiskit.circuit.library import standard_gates as q_gates
from qiskit.qobj import QasmQobj
from qiskit.assembler import disassemble

from . import exceptions

Expand All @@ -59,9 +61,7 @@
"id",
"mcp",
"mcphase",
"mct",
"mcx",
"mcx_gray",
# no mcx for now until terra issue 6271 is resolved
"measure",
"p",
"rx",
Expand Down Expand Up @@ -90,8 +90,6 @@
q_gates.x.CCXGate: "cx", # just one C for all mcx
q_gates.x.C3XGate: "cx", # just one C for all mcx
q_gates.x.C4XGate: "cx", # just one C for all mcx
q_gates.x.MCXGate: "cx", # just one C for all mcx
q_gates.x.MCXGrayCode: "cx", # just one C for all mcx
q_gates.t.TdgGate: "ti",
q_gates.p.PhaseGate: "z",
q_gates.RXXGate: "xx",
Expand Down Expand Up @@ -193,11 +191,7 @@ def qiskit_circ_to_ionq_circ(input_circuit):

# Update converted gate values.
converted.update(
{
"gate": gate,
"controls": controls,
"targets": targets,
}
{"gate": gate, "controls": controls, "targets": targets,}
)

# if there's a valid instruction after a measurement,
Expand Down Expand Up @@ -292,17 +286,25 @@ def decompress_metadata_string_to_dict(input_string): # pylint: disable=invalid
return json.loads(decompressed)


def qiskit_to_ionq(circuit, backend_name, passed_args=None):
"""Convert a Qiskit circuit to a IonQ compatible dict.
def qiskit_to_ionq(circuit_or_qobj, backend_name, passed_args=None):
"""Serialize a Qiskit circuit to a IonQ compatible dict.

Parameters:
circuit (:class:`qiskit.circuit.QuantumCircuit`): A Qiskit quantum circuit.
circuit_or_qobj (:class:`qiskit.circuit.QuantumCircuit` or :class:`qiskit.qobj.QasmQobj`):
A Qiskit quantum circuit or qobj containing a circuit in QASM.
backend_name (str): Backend name.
passed_args (dict): Dictionary containing additional passed arguments, eg. shots.

Returns:
dict: A dict with IonQ API compatible values.
"""
circuit = circuit_or_qobj
# if the submit job method gets passed a Qobj (possible via e.g. the Qiskit.execute method),
# pull the circuit off the Qobj. We don't need any of the rest of the context it provides.
if isinstance(circuit_or_qobj, QasmQobj):
circuit_list, _, _ = disassemble(circuit_or_qobj)
circuit = circuit_list.pop()

passed_args = passed_args or {}
ionq_circ, _, meas_map = qiskit_circ_to_ionq_circ(circuit)
creg_sizes, clbit_labels = get_register_sizes_and_labels(circuit.cregs)
Expand All @@ -324,10 +326,7 @@ def qiskit_to_ionq(circuit, backend_name, passed_args=None):
"lang": "json",
"target": backend_name[5:],
"shots": passed_args.get("shots"),
"body": {
"qubits": circuit.num_qubits,
"circuit": ionq_circ,
},
"body": {"qubits": circuit.num_qubits, "circuit": ionq_circ,},
"registers": {"meas_mapped": meas_map},
# store a couple of things we'll need later for result formatting
"metadata": {
Expand Down
19 changes: 2 additions & 17 deletions qiskit_ionq/ionq_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,7 @@ def run(self, circuit, **kwargs):
kwargs["shots"] = self.options.shots
passed_args = kwargs

job = ionq_job.IonQJob(
self,
None,
self.client,
circuit=circuit,
passed_args=passed_args,
)
job = ionq_job.IonQJob(self, None, self.client, circuit=circuit, passed_args=passed_args,)
job.submit()
return job

Expand All @@ -210,15 +204,6 @@ def retrieve_jobs(self, job_ids):

return [ionq_job.IonQJob(self, job_id, self.client) for job_id in job_ids]

# TODO: Implement backend status checks.
def status(self):
"""Not yet implemented.

Raises:
NotImplementedError: This behavior is not currently supported.
"""
raise NotImplementedError("Backend status check is not supported.")

def calibration(self):
"""Fetch the most recent calibration data for this backend.

Expand Down Expand Up @@ -290,7 +275,7 @@ def __init__(self, provider):
"memory": False,
"n_qubits": 29,
"conditional": False,
"max_shots": 1,
"max_shots": None,
"max_experiments": 1,
"open_pulse": False,
"gates": [{"name": "TODO", "parameters": [], "qasm_def": "TODO"}],
Expand Down
5 changes: 4 additions & 1 deletion qiskit_ionq/ionq_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def get_probabilities(self, circuit=None): # pylint: disable=unused-argument
"""
return self.result().get_probabilities()

def result(self):
def result(self, **kwargs):
"""Retrieve job result data.

.. NOTE::
Expand All @@ -239,6 +239,9 @@ def result(self):
Returns:
Result: A Qiskit :class:`Result <qiskit.result.Result>` representation of this job.
"""
# we ignore all result args like timeout etc, but consume them for compatibility reasons
_ = kwargs

# Short-circuit if we have already cached the result for this job.
if self._result is not None:
return self._result
Expand Down
6 changes: 0 additions & 6 deletions test/helpers/test_gate_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,6 @@
("i", [0], []),
("id", [0], []),
("mcp", [0.5, [0, 1], 2], [{"gate": "z", "rotation": 0.5, "targets": [2], "controls": [0, 1]}]),
("mct", [[0, 1], 2], [{"gate": "x", "targets": [2], "controls": [0, 1]}]),
("mcx", [[0, 1], 2], [{"gate": "x", "targets": [2], "controls": [0, 1]}]),
# make sure that multi-control can take any number of controls
("mcx", [[0, 1, 2], 3], [{"gate": "x", "targets": [3], "controls": [0, 1, 2]}]),
("mcx", [[0, 1, 2, 3], 4], [{"gate": "x", "targets": [4], "controls": [0, 1, 2, 3]}]),
("mcx", [[0, 1, 2, 3, 4], 5], [{"gate": "x", "targets": [5], "controls": [0, 1, 2, 3, 4]}]),
("measure", [0, 0], []),
("p", [0, 0], [{"gate": "z", "rotation": 0, "targets": [0]}]),
("p", [0.5, 0], [{"gate": "z", "rotation": 0.5, "targets": [0]}]),
Expand Down
56 changes: 56 additions & 0 deletions test/helpers/test_qiskit_to_ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import json

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.compiler import assemble

from qiskit_ionq.helpers import qiskit_to_ionq, decompress_metadata_string_to_dict

Expand Down Expand Up @@ -190,3 +191,58 @@ def test_full_circuit(simulator_backend):
assert actual_metadata_header == expected_metadata_header
assert actual_output_map == expected_output_map
assert actual == expected_rest_of_payload


def test_full_circuit_from_qobj(simulator_backend):
"""Test a full circuit

Args:
simulator_backend (IonQSimulatorBackend): A simulator backend fixture.
"""
qc = QuantumCircuit(2, 2, name="test_name")
qc.cnot(1, 0)
qc.h(1)
qc.measure(1, 0)
qc.measure(0, 1)
qobj = assemble(qc, backend=simulator_backend, shots=200)
ionq_json = qiskit_to_ionq(
qobj, simulator_backend.name(), passed_args={"shots": 200, "sampler_seed": 42}
)
expected_metadata_header = {
"memory_slots": 2,
"global_phase": 0,
"n_qubits": 2,
"name": "test_name",
"creg_sizes": [["c", 2]],
"clbit_labels": [["c", 0], ["c", 1]],
"qreg_sizes": [["q", 2]],
"qubit_labels": [["q", 0], ["q", 1]],
}
expected_output_map = [1, 0]
expected_metadata = {"shots": "200", "sampler_seed": "42"}
expected_rest_of_payload = {
"lang": "json",
"target": "simulator",
"shots": 200,
"body": {
"qubits": 2,
"circuit": [
{"gate": "x", "controls": [1], "targets": [0]},
{"gate": "h", "targets": [1]},
],
},
}

actual = json.loads(ionq_json)
actual_metadata = actual.pop("metadata") or {}
actual_metadata_header = decompress_metadata_string_to_dict(
actual_metadata.pop("qiskit_header") or None
)
actual_maps = actual.pop("registers") or {}
actual_output_map = actual_maps.pop("meas_mapped") or []

# check dict equality:
assert actual_metadata == expected_metadata
assert actual_metadata_header == expected_metadata_header
assert actual_output_map == expected_output_map
assert actual == expected_rest_of_payload
13 changes: 0 additions & 13 deletions test/ionq_backend/test_base_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,6 @@
from .. import conftest


def test_status_not_implemented(mock_backend):
"""Test that by default, `status` is not implemented.

Args:
mock_backend (MockBackend): A fake/mock IonQBackend.
"""

with pytest.raises(NotImplementedError) as exc_info:
mock_backend.status()

assert str(exc_info.value) == "Backend status check is not supported."


def test_client_property(mock_backend):
"""
Test that the client property is an IonQClient instance.
Expand Down