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
10 changes: 8 additions & 2 deletions azure-quantum/azure/quantum/cirq/targets/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class AzureGenericQirCirqTarget(AzureTarget, CirqTarget):
that do not have dedicated Cirq target classes.

Translation pipeline:
- Cirq circuit -> OpenQASM (via `cirq.Circuit.to_qasm()`)
- Cirq circuit -> OpenQASM 3 (via `cirq.Circuit.to_qasm(version="3.0")`)
- OpenQASM -> QIR (via `qsharp.openqasm.compile`)

Dependencies: requires `qsharp` to be installed.
Expand Down Expand Up @@ -168,7 +168,13 @@ def _translate_cirq_circuit(circuit: "cirq.Circuit") -> QirRepresentable:
"Install with: pip install azure-quantum[cirq,qsharp]"
) from exc

qasm = circuit.to_qasm()
if cirq.is_parameterized(circuit):
raise ValueError(
"Cannot serialize a parameterized Cirq circuit to OpenQASM 3. "
"Resolve parameters first (e.g. via cirq.resolve_parameters)."
)

qasm = circuit.to_qasm(version="3.0")

return compile(qasm)

Expand Down
26 changes: 23 additions & 3 deletions azure-quantum/azure/quantum/cirq/targets/ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
##
from typing import TYPE_CHECKING, Any, Dict, Union, Optional
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union

try:
import cirq
Expand Down Expand Up @@ -117,7 +119,13 @@ def _translate_cirq_circuit(circuit) -> Dict[str, Any]:
"""Translate Cirq circuit to IonQ JSON. If dependencies \
are not installed, throw error with installation instructions."""
from cirq_ionq import Serializer
return Serializer().serialize(circuit)

serializer = Serializer()
if hasattr(serializer, "serialize_single_circuit"):
return serializer.serialize_single_circuit(circuit)

# Backward-compat for older cirq_ionq.
return serializer.serialize(circuit)

def _to_cirq_job(self, azure_job: "AzureJob") -> "CirqIonqJob":
"""Convert Azure job to Cirq job"""
Expand Down Expand Up @@ -160,10 +168,18 @@ def submit(

@staticmethod
def _to_cirq_result(
result: Union[QPUResult, SimulatorResult],
result: "IonQResultLike",
param_resolver: cirq.ParamResolverOrSimilarType = cirq.ParamResolver({}),
seed: cirq.RANDOM_STATE_OR_SEED_LIKE = None,
) -> "cirq.Result":
# cirq_ionq>=1.6 returns a list of results even for a single circuit.
if isinstance(result, (list, tuple)):
if len(result) != 1:
raise ValueError(
f"Expected a single IonQ result for a single circuit, got {len(result)} results."
)
result = result[0]

if isinstance(result, QPUResult):
return result.to_cirq_result(params=cirq.ParamResolver(param_resolver))
elif isinstance(result, SimulatorResult):
Expand All @@ -172,4 +188,8 @@ def _to_cirq_result(
raise ValueError("Result {result} not supported. \
Expecting either a cirq_ionq.results.QPUResult \
or cirq_ionq.results.SimulatorResult.")


IonQSingleResult = Union[QPUResult, SimulatorResult]
IonQResultLike = Union[IonQSingleResult, Sequence[IonQSingleResult]]

17 changes: 15 additions & 2 deletions azure-quantum/azure/quantum/cirq/targets/quantinuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,21 @@ def __init__(

@staticmethod
def _translate_cirq_circuit(circuit) -> str:
"""Translate `cirq` circuit to OpenQASM 2.0."""
return circuit.to_qasm()
"""Translate `cirq` circuit to OpenQASM 2.0.

Note: The Quantinuum targets in this SDK default to
`input_data_format="honeywell.openqasm.v1"`, which corresponds to
OpenQASM 2.0.
"""
import cirq

if cirq.is_parameterized(circuit):
raise ValueError(
"Cannot serialize a parameterized Cirq circuit to OpenQASM 2.0. "
"Resolve parameters first (e.g. via cirq.resolve_parameters)."
)

return circuit.to_qasm(version="2.0")

@staticmethod
def _to_cirq_result(result: Dict[str, Any], param_resolver, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions azure-quantum/requirements-cirq.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cirq-core>=1.3.0,<=1.4.1
cirq-ionq>=1.3.0,<=1.4.1
cirq-core>=1.6.1,<1.7; python_version >= "3.10"
cirq-ionq>=1.6.1,<1.7; python_version >= "3.10"
58 changes: 58 additions & 0 deletions azure-quantum/tests/test_cirq.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,61 @@ def test_cirq_job_results_converts_generic_target_shots(
result.measurements["m"],
np.asarray([[0, 1], [1, 0], [0, 0]], dtype=np.int8),
)


def test_cirq_to_qasm_supports_openqasm3_program():
cirq = pytest.importorskip("cirq")

q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.H(q0),
cirq.CNOT(q0, q1),
cirq.measure(q0, q1, key="m"),
)

qasm = circuit.to_qasm(version="3.0")

# Cirq's exporter may include a leading comment header.
assert "OPENQASM 3.0;" in qasm
assert 'include "stdgates.inc";' in qasm
assert "qubit[2] q;" in qasm
assert "h q[0];" in qasm
assert "cx q[0],q[1];" in qasm
assert "measure q[0];" in qasm
assert "measure q[1];" in qasm


def test_cirq_ionq_serializer_api_compatibility():
cirq = pytest.importorskip("cirq")
pytest.importorskip("cirq_ionq")

from azure.quantum.cirq.targets.ionq import IonQTarget

q0 = cirq.LineQubit(0)
circuit = cirq.Circuit(cirq.X(q0), cirq.measure(q0, key="m"))

serialized = IonQTarget._translate_cirq_circuit(circuit)

assert hasattr(serialized, "body")
assert isinstance(serialized.body, dict)
assert "qubits" in serialized.body


def test_cirq_ionq_to_cirq_result_accepts_singleton_list():
cirq = pytest.importorskip("cirq")
pytest.importorskip("cirq_ionq")

from cirq_ionq.results import SimulatorResult
from azure.quantum.cirq.targets.ionq import IonQTarget

sim_result = SimulatorResult(
probabilities={0: 0.5, 1: 0.5},
num_qubits=1,
measurement_dict={"m": [0]},
repetitions=10,
)

cirq_result = IonQTarget._to_cirq_result(
[sim_result], param_resolver=cirq.ParamResolver({})
)
assert hasattr(cirq_result, "measurements")
5 changes: 5 additions & 0 deletions azure_quantum_manual_tests.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,11 @@
" target_name = futures[future]\n",
" try:\n",
" result = future.result()\n",
" # cirq_ionq>=1.6 returns a list of results even for a single circuit.\n",
" if isinstance(result, (list, tuple)):\n",
" if len(result) != 1:\n",
" raise ValueError(f\"[{target_name}] Expected a single result, got {len(result)}\")\n",
" result = result[0]\n",
" # The IonQ provider wrapper (cirq_ionq.Job) returns a SimulatorResult or\n",
" # QPUResult rather than a cirq.Result. Normalize by calling to_cirq_result().\n",
" if hasattr(result, \"to_cirq_result\"):\n",
Expand Down
Loading