Skip to content

Commit 1989f3b

Browse files
committed
update cirq to 1.6.1
1 parent 1bfca2e commit 1989f3b

6 files changed

Lines changed: 111 additions & 9 deletions

File tree

azure-quantum/azure/quantum/cirq/targets/generic.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class AzureGenericQirCirqTarget(AzureTarget, CirqTarget):
111111
that do not have dedicated Cirq target classes.
112112
113113
Translation pipeline:
114-
- Cirq circuit -> OpenQASM (via `cirq.Circuit.to_qasm()`)
114+
- Cirq circuit -> OpenQASM 3 (via `cirq.Circuit.to_qasm(version="3.0")`)
115115
- OpenQASM -> QIR (via `qsharp.openqasm.compile`)
116116
117117
Dependencies: requires `qsharp` to be installed.
@@ -168,7 +168,13 @@ def _translate_cirq_circuit(circuit: "cirq.Circuit") -> QirRepresentable:
168168
"Install with: pip install azure-quantum[cirq,qsharp]"
169169
) from exc
170170

171-
qasm = circuit.to_qasm()
171+
if cirq.is_parameterized(circuit):
172+
raise ValueError(
173+
"Cannot serialize a parameterized Cirq circuit to OpenQASM 3. "
174+
"Resolve parameters first (e.g. via cirq.resolve_parameters)."
175+
)
176+
177+
qasm = circuit.to_qasm(version="3.0")
172178

173179
return compile(qasm)
174180

azure-quantum/azure/quantum/cirq/targets/ionq.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
# Copyright (c) Microsoft Corporation.
33
# Licensed under the MIT License.
44
##
5-
from typing import TYPE_CHECKING, Any, Dict, Union, Optional
5+
from __future__ import annotations
6+
7+
from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union
68

79
try:
810
import cirq
@@ -117,7 +119,13 @@ def _translate_cirq_circuit(circuit) -> Dict[str, Any]:
117119
"""Translate Cirq circuit to IonQ JSON. If dependencies \
118120
are not installed, throw error with installation instructions."""
119121
from cirq_ionq import Serializer
120-
return Serializer().serialize(circuit)
122+
123+
serializer = Serializer()
124+
if hasattr(serializer, "serialize_single_circuit"):
125+
return serializer.serialize_single_circuit(circuit)
126+
127+
# Backward-compat for older cirq_ionq.
128+
return serializer.serialize(circuit)
121129

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

161169
@staticmethod
162170
def _to_cirq_result(
163-
result: Union[QPUResult, SimulatorResult],
171+
result: "IonQResultLike",
164172
param_resolver: cirq.ParamResolverOrSimilarType = cirq.ParamResolver({}),
165173
seed: cirq.RANDOM_STATE_OR_SEED_LIKE = None,
166174
) -> "cirq.Result":
175+
# cirq_ionq>=1.6 returns a list of results even for a single circuit.
176+
if isinstance(result, (list, tuple)):
177+
if len(result) != 1:
178+
raise ValueError(
179+
f"Expected a single IonQ result for a single circuit, got {len(result)} results."
180+
)
181+
result = result[0]
182+
167183
if isinstance(result, QPUResult):
168184
return result.to_cirq_result(params=cirq.ParamResolver(param_resolver))
169185
elif isinstance(result, SimulatorResult):
@@ -172,4 +188,8 @@ def _to_cirq_result(
172188
raise ValueError("Result {result} not supported. \
173189
Expecting either a cirq_ionq.results.QPUResult \
174190
or cirq_ionq.results.SimulatorResult.")
191+
192+
193+
IonQSingleResult = Union[QPUResult, SimulatorResult]
194+
IonQResultLike = Union[IonQSingleResult, Sequence[IonQSingleResult]]
175195

azure-quantum/azure/quantum/cirq/targets/quantinuum.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,21 @@ def __init__(
4444

4545
@staticmethod
4646
def _translate_cirq_circuit(circuit) -> str:
47-
"""Translate `cirq` circuit to OpenQASM 2.0."""
48-
return circuit.to_qasm()
47+
"""Translate `cirq` circuit to OpenQASM 2.0.
48+
49+
Note: The Quantinuum targets in this SDK default to
50+
`input_data_format="honeywell.openqasm.v1"`, which corresponds to
51+
OpenQASM 2.0.
52+
"""
53+
import cirq
54+
55+
if cirq.is_parameterized(circuit):
56+
raise ValueError(
57+
"Cannot serialize a parameterized Cirq circuit to OpenQASM 2.0. "
58+
"Resolve parameters first (e.g. via cirq.resolve_parameters)."
59+
)
60+
61+
return circuit.to_qasm(version="2.0")
4962

5063
@staticmethod
5164
def _to_cirq_result(result: Dict[str, Any], param_resolver, **kwargs):
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
cirq-core>=1.3.0,<=1.4.1
2-
cirq-ionq>=1.3.0,<=1.4.1
1+
cirq-core==1.6.1; python_version >= "3.10"
2+
cirq-ionq==1.6.1; python_version >= "3.10"

azure-quantum/tests/test_cirq.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,61 @@ def test_cirq_job_results_converts_generic_target_shots(
411411
result.measurements["m"],
412412
np.asarray([[0, 1], [1, 0], [0, 0]], dtype=np.int8),
413413
)
414+
415+
416+
def test_cirq_to_qasm_supports_openqasm3_program():
417+
cirq = pytest.importorskip("cirq")
418+
419+
q0, q1 = cirq.LineQubit.range(2)
420+
circuit = cirq.Circuit(
421+
cirq.H(q0),
422+
cirq.CNOT(q0, q1),
423+
cirq.measure(q0, q1, key="m"),
424+
)
425+
426+
qasm = circuit.to_qasm(version="3.0")
427+
428+
# Cirq's exporter may include a leading comment header.
429+
assert "OPENQASM 3.0;" in qasm
430+
assert 'include "stdgates.inc";' in qasm
431+
assert "qubit[2] q;" in qasm
432+
assert "h q[0];" in qasm
433+
assert "cx q[0],q[1];" in qasm
434+
assert "measure q[0];" in qasm
435+
assert "measure q[1];" in qasm
436+
437+
438+
def test_cirq_ionq_serializer_api_compatibility():
439+
cirq = pytest.importorskip("cirq")
440+
pytest.importorskip("cirq_ionq")
441+
442+
from azure.quantum.cirq.targets.ionq import IonQTarget
443+
444+
q0 = cirq.LineQubit(0)
445+
circuit = cirq.Circuit(cirq.X(q0), cirq.measure(q0, key="m"))
446+
447+
serialized = IonQTarget._translate_cirq_circuit(circuit)
448+
449+
assert hasattr(serialized, "body")
450+
assert isinstance(serialized.body, dict)
451+
assert "qubits" in serialized.body
452+
453+
454+
def test_cirq_ionq_to_cirq_result_accepts_singleton_list():
455+
cirq = pytest.importorskip("cirq")
456+
pytest.importorskip("cirq_ionq")
457+
458+
from cirq_ionq.results import SimulatorResult
459+
from azure.quantum.cirq.targets.ionq import IonQTarget
460+
461+
sim_result = SimulatorResult(
462+
probabilities={0: 0.5, 1: 0.5},
463+
num_qubits=1,
464+
measurement_dict={"m": [0]},
465+
repetitions=10,
466+
)
467+
468+
cirq_result = IonQTarget._to_cirq_result(
469+
[sim_result], param_resolver=cirq.ParamResolver({})
470+
)
471+
assert hasattr(cirq_result, "measurements")

azure_quantum_manual_tests.ipynb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,11 @@
567567
" target_name = futures[future]\n",
568568
" try:\n",
569569
" result = future.result()\n",
570+
" # cirq_ionq>=1.6 returns a list of results even for a single circuit.\n",
571+
" if isinstance(result, (list, tuple)):\n",
572+
" if len(result) != 1:\n",
573+
" raise ValueError(f\"[{target_name}] Expected a single result, got {len(result)}\")\n",
574+
" result = result[0]\n",
570575
" # The IonQ provider wrapper (cirq_ionq.Job) returns a SimulatorResult or\n",
571576
" # QPUResult rather than a cirq.Result. Normalize by calling to_cirq_result().\n",
572577
" if hasattr(result, \"to_cirq_result\"):\n",

0 commit comments

Comments
 (0)