Skip to content

Commit 0b12c35

Browse files
committed
Implement lowering a cirq.Circuit to squin (#294)
Addresses #283 For now, I went with the syntax `squin.load_circuit(circuit: cirq.Circuit)`. So you can e.g. do ```python q0 = cirq.NamedQubit("q0") q1 = cirq.NamedQubit("q1") circuit = cirq.Circuit( cirq.H(q1), cirq.X(q0).controlled_by(q1), cirq.Rx(rads=math.pi / 4).on(q0).controlled_by(q1), cirq.measure(q0, q1), ) main = squin.load_circuit(circuit) ``` ~~@Roger-luo @weinbe58 one open question here: cirq support arbitrary powers of gates, e.g. `cirq.H(q0)**0.123`. Should we add a `Pow` statement to squin to properly support this? I think pyqrack can also handle it, but I'll have to check again. Also, not sure what this should look like when running on hardware, but we can still error in the runtime.~~ **Edit**: turns out arbitrary powers are quite easy since we can just use the cirq helper `in_su2` to get the corresponding rotation and lower that. Also, for multi-qubit gates they usually mean "apply a controlled single qubit gate with an exponent", which again corresponds to a rotation. Thanks @kaihsin for pointing to the helper method. ## TODOs: - [x] support (arbitrary) powers of operators? - ~~[ ] Lowering of noisy circuits~~ done, except #301 - [x] Add missing gates (e.g. three-qubit controls are still missing) - [ ] Make sure the set of supported gates is complete - ~~ [ ] Optionally return the quantum register (and possibly other things) ~~ see #302 - ~~ [ ] Optionally pass in the quantum register as argument to the method ~~ see #302 - ~~[ ] Support classical control by lowering to `if` ~~ see #303
1 parent 7ae0f1f commit 0b12c35

File tree

5 files changed

+563
-12
lines changed

5 files changed

+563
-12
lines changed

src/bloqade/squin/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,12 @@
66
lowering as lowering,
77
)
88
from .groups import wired as wired, kernel as kernel
9+
10+
try:
11+
# NOTE: make sure optional cirq dependency is installed
12+
import cirq as cirq_package # noqa: F401
13+
except ImportError:
14+
pass
15+
else:
16+
from . import cirq as cirq
17+
from .cirq import load_circuit as load_circuit

src/bloqade/squin/cirq/__init__.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from typing import Any
2+
3+
import cirq
4+
from kirin import ir, types
5+
from kirin.dialects import func
6+
7+
from . import lowering as lowering
8+
from .. import kernel
9+
from .lowering import Squin
10+
11+
12+
def load_circuit(
13+
circuit: cirq.Circuit,
14+
kernel_name: str = "main",
15+
dialects: ir.DialectGroup = kernel,
16+
globals: dict[str, Any] | None = None,
17+
file: str | None = None,
18+
lineno_offset: int = 0,
19+
col_offset: int = 0,
20+
compactify: bool = True,
21+
):
22+
"""Converts a cirq.Circuit object into a squin kernel.
23+
24+
Args:
25+
circuit (cirq.Circuit): The circuit to load.
26+
27+
Keyword Args:
28+
kernel_name (str): The name of the kernel to load. Defaults to "main".
29+
dialects (ir.DialectGroup | None): The dialects to use. Defaults to `squin.kernel`.
30+
globals (dict[str, Any] | None): The global variables to use. Defaults to None.
31+
file (str | None): The file name for error reporting. Defaults to None.
32+
lineno_offset (int): The line number offset for error reporting. Defaults to 0.
33+
col_offset (int): The column number offset for error reporting. Defaults to 0.
34+
compactify (bool): Whether to compactify the output. Defaults to True.
35+
36+
Example:
37+
38+
```python
39+
# from cirq's "hello qubit" example
40+
import cirq
41+
from bloqade import squin
42+
43+
# Pick a qubit.
44+
qubit = cirq.GridQubit(0, 0)
45+
46+
# Create a circuit.
47+
circuit = cirq.Circuit(
48+
cirq.X(qubit)**0.5, # Square root of NOT.
49+
cirq.measure(qubit, key='m') # Measurement.
50+
)
51+
52+
# load the circuit as squin
53+
main = squin.load_circuit(circuit)
54+
55+
# print the resulting IR
56+
main.print()
57+
```
58+
"""
59+
60+
target = Squin(dialects=dialects, circuit=circuit)
61+
body = target.run(
62+
circuit,
63+
source=str(circuit), # TODO: proper source string
64+
file=file,
65+
globals=globals,
66+
lineno_offset=lineno_offset,
67+
col_offset=col_offset,
68+
compactify=compactify,
69+
)
70+
71+
# NOTE: no return value
72+
return_value = func.ConstantNone()
73+
body.blocks[0].stmts.append(return_value)
74+
body.blocks[0].stmts.append(func.Return(value_or_stmt=return_value))
75+
76+
code = func.Function(
77+
sym_name=kernel_name,
78+
signature=func.Signature((), types.NoneType),
79+
body=body,
80+
)
81+
82+
return ir.Method(
83+
mod=None,
84+
py_func=None,
85+
sym_name=kernel_name,
86+
arg_names=[],
87+
dialects=dialects,
88+
code=code,
89+
)

0 commit comments

Comments
 (0)