bloqade-circuit 0.4.3__py3-none-any.whl → 0.4.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bloqade-circuit might be problematic. Click here for more details.

Files changed (32) hide show
  1. bloqade/squin/analysis/nsites/impls.py +7 -0
  2. bloqade/squin/cirq/__init__.py +74 -9
  3. bloqade/squin/cirq/emit/noise.py +49 -0
  4. bloqade/squin/cirq/emit/runtime.py +9 -1
  5. bloqade/squin/cirq/lowering.py +27 -21
  6. bloqade/squin/noise/_wrapper.py +9 -2
  7. bloqade/squin/noise/rewrite.py +3 -3
  8. bloqade/squin/op/__init__.py +3 -0
  9. bloqade/squin/op/_wrapper.py +12 -0
  10. bloqade/squin/op/stmts.py +19 -1
  11. bloqade/squin/rewrite/U3_to_clifford.py +149 -0
  12. bloqade/squin/rewrite/__init__.py +3 -7
  13. bloqade/squin/rewrite/remove_dangling_qubits.py +19 -0
  14. bloqade/squin/rewrite/wrap_analysis.py +34 -19
  15. bloqade/stim/__init__.py +1 -1
  16. bloqade/stim/dialects/auxiliary/stmts/const.py +1 -1
  17. bloqade/stim/dialects/gate/stmts/__init__.py +6 -0
  18. bloqade/stim/passes/__init__.py +1 -0
  19. bloqade/stim/passes/squin_to_stim.py +86 -0
  20. bloqade/stim/rewrite/__init__.py +7 -0
  21. bloqade/stim/rewrite/py_constant_to_stim.py +42 -0
  22. bloqade/{squin → stim}/rewrite/qubit_to_stim.py +18 -3
  23. bloqade/{squin → stim}/rewrite/squin_measure.py +2 -2
  24. bloqade/{squin/rewrite/stim_rewrite_util.py → stim/rewrite/util.py} +36 -17
  25. bloqade/{squin → stim}/rewrite/wire_to_stim.py +1 -1
  26. {bloqade_circuit-0.4.3.dist-info → bloqade_circuit-0.4.5.dist-info}/METADATA +1 -1
  27. {bloqade_circuit-0.4.3.dist-info → bloqade_circuit-0.4.5.dist-info}/RECORD +30 -25
  28. bloqade/squin/passes/__init__.py +0 -1
  29. bloqade/squin/passes/stim.py +0 -68
  30. /bloqade/{squin → stim}/rewrite/wire_identity_elimination.py +0 -0
  31. {bloqade_circuit-0.4.3.dist-info → bloqade_circuit-0.4.5.dist-info}/WHEEL +0 -0
  32. {bloqade_circuit-0.4.3.dist-info → bloqade_circuit-0.4.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,6 @@
1
1
  from kirin import interp
2
+ from kirin.dialects import scf
3
+ from kirin.dialects.scf.typeinfer import TypeInfer as ScfTypeInfer
2
4
 
3
5
  from bloqade.squin import op, wire
4
6
 
@@ -78,3 +80,8 @@ class SquinOp(interp.MethodTable):
78
80
  def scale(self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.Scale):
79
81
  op_sites = frame.get(stmt.op)
80
82
  return (op_sites,)
83
+
84
+
85
+ @scf.dialect.register(key="op.nsites")
86
+ class ScfSquinOp(ScfTypeInfer):
87
+ pass
@@ -9,8 +9,9 @@ from . import lowering as lowering
9
9
  from .. import kernel
10
10
 
11
11
  # NOTE: just to register methods
12
- from .emit import op as op, qubit as qubit
12
+ from .emit import op as op, noise as noise, qubit as qubit
13
13
  from .lowering import Squin
14
+ from ..noise.rewrite import RewriteNoiseStmts
14
15
  from .emit.emit_circuit import EmitCirq
15
16
 
16
17
 
@@ -18,6 +19,9 @@ def load_circuit(
18
19
  circuit: cirq.Circuit,
19
20
  kernel_name: str = "main",
20
21
  dialects: ir.DialectGroup = kernel,
22
+ register_as_argument: bool = False,
23
+ return_register: bool = False,
24
+ register_argument_name: str = "q",
21
25
  globals: dict[str, Any] | None = None,
22
26
  file: str | None = None,
23
27
  lineno_offset: int = 0,
@@ -32,13 +36,23 @@ def load_circuit(
32
36
  Keyword Args:
33
37
  kernel_name (str): The name of the kernel to load. Defaults to "main".
34
38
  dialects (ir.DialectGroup | None): The dialects to use. Defaults to `squin.kernel`.
39
+ register_as_argument (bool): Determine whether the resulting kernel function should accept
40
+ a single `ilist.IList[Qubit, Any]` argument that is a list of qubits used within the
41
+ function. This allows you to compose kernel functions generated from circuits.
42
+ Defaults to `False`.
43
+ return_register (bool): Determine whether the resulting kernel functionr returns a
44
+ single value of type `ilist.IList[Qubit, Any]` that is the list of qubits used
45
+ in the kernel function. Useful when you want to compose multiple kernel functions
46
+ generated from circuits. Defaults to `False`.
47
+ register_argument_name (str): The name of the argument that represents the qubit register.
48
+ Only used when `register_as_argument=True`. Defaults to "q".
35
49
  globals (dict[str, Any] | None): The global variables to use. Defaults to None.
36
50
  file (str | None): The file name for error reporting. Defaults to None.
37
51
  lineno_offset (int): The line number offset for error reporting. Defaults to 0.
38
52
  col_offset (int): The column number offset for error reporting. Defaults to 0.
39
53
  compactify (bool): Whether to compactify the output. Defaults to True.
40
54
 
41
- Example:
55
+ ## Usage Examples:
42
56
 
43
57
  ```python
44
58
  # from cirq's "hello qubit" example
@@ -60,6 +74,30 @@ def load_circuit(
60
74
  # print the resulting IR
61
75
  main.print()
62
76
  ```
77
+
78
+ You can also compose kernel functions generated from circuits by passing in
79
+ and / or returning the respective quantum registers:
80
+
81
+ ```python
82
+ q = cirq.LineQubit.range(2)
83
+ circuit = cirq.Circuit(cirq.H(q[0]), cirq.CX(*q))
84
+
85
+ get_entangled_qubits = squin.cirq.load_circuit(
86
+ circuit, return_register=True, kernel_name="get_entangled_qubits"
87
+ )
88
+ get_entangled_qubits.print()
89
+
90
+ entangle_qubits = squin.cirq.load_circuit(
91
+ circuit, register_as_argument=True, kernel_name="entangle_qubits"
92
+ )
93
+
94
+ @squin.kernel
95
+ def main():
96
+ qreg = get_entangled_qubits()
97
+ qreg2 = squin.qubit.new(1)
98
+ entangle_qubits([qreg[1], qreg2[0]])
99
+ return squin.qubit.measure(qreg2)
100
+ ```
63
101
  """
64
102
 
65
103
  target = Squin(dialects=dialects, circuit=circuit)
@@ -71,16 +109,38 @@ def load_circuit(
71
109
  lineno_offset=lineno_offset,
72
110
  col_offset=col_offset,
73
111
  compactify=compactify,
112
+ register_as_argument=register_as_argument,
113
+ register_argument_name=register_argument_name,
74
114
  )
75
115
 
76
- # NOTE: no return value
77
- return_value = func.ConstantNone()
78
- body.blocks[0].stmts.append(return_value)
79
- body.blocks[0].stmts.append(func.Return(value_or_stmt=return_value))
116
+ if return_register:
117
+ return_value = target.qreg
118
+ else:
119
+ return_value = func.ConstantNone()
120
+ body.blocks[0].stmts.append(return_value)
121
+
122
+ return_node = func.Return(value_or_stmt=return_value)
123
+ body.blocks[0].stmts.append(return_node)
124
+
125
+ self_arg_name = kernel_name + "_self"
126
+ arg_names = [self_arg_name]
127
+ if register_as_argument:
128
+ args = (target.qreg.type,)
129
+ arg_names.append(register_argument_name)
130
+ else:
131
+ args = ()
132
+
133
+ # NOTE: add _self as argument; need to know signature before so do it after lowering
134
+ signature = func.Signature(args, return_node.value.type)
135
+ body.blocks[0].args.insert_from(
136
+ 0,
137
+ types.Generic(ir.Method, types.Tuple.where(signature.inputs), signature.output),
138
+ self_arg_name,
139
+ )
80
140
 
81
141
  code = func.Function(
82
142
  sym_name=kernel_name,
83
- signature=func.Signature((), types.NoneType),
143
+ signature=signature,
84
144
  body=body,
85
145
  )
86
146
 
@@ -88,7 +148,7 @@ def load_circuit(
88
148
  mod=None,
89
149
  py_func=None,
90
150
  sym_name=kernel_name,
91
- arg_names=[],
151
+ arg_names=arg_names,
92
152
  dialects=dialects,
93
153
  code=code,
94
154
  )
@@ -176,7 +236,12 @@ def emit_circuit(
176
236
  )
177
237
 
178
238
  emitter = EmitCirq(qubits=qubits)
179
- return emitter.run(mt, args=())
239
+
240
+ # Rewrite noise statements
241
+ mt_ = mt.similar(mt.dialects)
242
+ RewriteNoiseStmts(mt_.dialects)(mt_)
243
+
244
+ return emitter.run(mt_, args=())
180
245
 
181
246
 
182
247
  def dump_circuit(mt: ir.Method, qubits: Sequence[cirq.Qid] | None = None, **kwargs):
@@ -0,0 +1,49 @@
1
+ import cirq
2
+ from kirin.emit import EmitError
3
+ from kirin.interp import MethodTable, impl
4
+
5
+ from ... import noise
6
+ from .runtime import (
7
+ KronRuntime,
8
+ BasicOpRuntime,
9
+ OperatorRuntimeABC,
10
+ PauliStringRuntime,
11
+ )
12
+ from .emit_circuit import EmitCirq, EmitCirqFrame
13
+
14
+
15
+ @noise.dialect.register(key="emit.cirq")
16
+ class EmitCirqNoiseMethods(MethodTable):
17
+
18
+ @impl(noise.stmts.StochasticUnitaryChannel)
19
+ def stochastic_unitary_channel(
20
+ self,
21
+ emit: EmitCirq,
22
+ frame: EmitCirqFrame,
23
+ stmt: noise.stmts.StochasticUnitaryChannel,
24
+ ):
25
+ ops = frame.get(stmt.operators)
26
+ ps = frame.get(stmt.probabilities)
27
+
28
+ error_probabilities = {self._op_to_key(op_): p for op_, p in zip(ops, ps)}
29
+ cirq_op = cirq.asymmetric_depolarize(error_probabilities=error_probabilities)
30
+ return (BasicOpRuntime(cirq_op),)
31
+
32
+ @staticmethod
33
+ def _op_to_key(operator: OperatorRuntimeABC) -> str:
34
+ match operator:
35
+ case KronRuntime():
36
+ key_lhs = EmitCirqNoiseMethods._op_to_key(operator.lhs)
37
+ key_rhs = EmitCirqNoiseMethods._op_to_key(operator.rhs)
38
+ return key_lhs + key_rhs
39
+
40
+ case BasicOpRuntime():
41
+ return str(operator.gate)
42
+
43
+ case PauliStringRuntime():
44
+ return operator.string
45
+
46
+ case _:
47
+ raise EmitError(
48
+ f"Unexpected operator runtime in StochasticUnitaryChannel of type {type(operator).__name__} encountered!"
49
+ )
@@ -21,7 +21,10 @@ class OperatorRuntimeABC:
21
21
 
22
22
  def unsafe_apply(
23
23
  self, qubits: Sequence[cirq.Qid], adjoint: bool = False
24
- ) -> list[cirq.Operation]: ...
24
+ ) -> list[cirq.Operation]:
25
+ raise NotImplementedError(
26
+ f"Apply method needs to be implemented in {self.__class__.__name__}"
27
+ )
25
28
 
26
29
 
27
30
  @dataclass
@@ -38,6 +41,11 @@ class BasicOpRuntime(UnsafeOperatorRuntimeABC):
38
41
  def num_qubits(self) -> int:
39
42
  return self.gate.num_qubits()
40
43
 
44
+ def unsafe_apply(
45
+ self, qubits: Sequence[cirq.Qid], adjoint: bool = False
46
+ ) -> list[cirq.Operation]:
47
+ return [self.gate(*qubits)]
48
+
41
49
 
42
50
  @dataclass
43
51
  class UnitaryRuntime(BasicOpRuntime):
@@ -3,7 +3,7 @@ from typing import Any
3
3
  from dataclasses import field, dataclass
4
4
 
5
5
  import cirq
6
- from kirin import ir, lowering
6
+ from kirin import ir, types, lowering
7
7
  from kirin.rewrite import Walk, CFGCompactify
8
8
  from kirin.dialects import py, scf, ilist
9
9
 
@@ -25,27 +25,26 @@ class Squin(lowering.LoweringABC[CirqNode]):
25
25
  """Lower a cirq.Circuit object to a squin kernel"""
26
26
 
27
27
  circuit: cirq.Circuit
28
- qreg: qubit.New = field(init=False)
28
+ qreg: ir.SSAValue = field(init=False)
29
29
  qreg_index: dict[cirq.Qid, int] = field(init=False, default_factory=dict)
30
30
  next_qreg_index: int = field(init=False, default=0)
31
31
 
32
- def lower_qubit_getindex(self, state: lowering.State[CirqNode], qid: cirq.Qid):
33
- index = self.qreg_index.get(qid)
34
-
35
- if index is None:
36
- index = self.next_qreg_index
37
- self.qreg_index[qid] = index
38
- self.next_qreg_index += 1
32
+ def __post_init__(self):
33
+ # TODO: sort by cirq ordering
34
+ qbits = sorted(self.circuit.all_qubits())
35
+ self.qreg_index = {qid: idx for (idx, qid) in enumerate(qbits)}
39
36
 
37
+ def lower_qubit_getindex(self, state: lowering.State[CirqNode], qid: cirq.Qid):
38
+ index = self.qreg_index[qid]
40
39
  index_ssa = state.current_frame.push(py.Constant(index)).result
41
- qbit_getitem = state.current_frame.push(py.GetItem(self.qreg.result, index_ssa))
40
+ qbit_getitem = state.current_frame.push(py.GetItem(self.qreg, index_ssa))
42
41
  return qbit_getitem.result
43
42
 
44
43
  def lower_qubit_getindices(
45
44
  self, state: lowering.State[CirqNode], qids: list[cirq.Qid]
46
45
  ):
47
46
  qbits_getitem = [self.lower_qubit_getindex(state, qid) for qid in qids]
48
- qbits_stmt = ilist.New(values=qbits_getitem)
47
+ qbits_stmt = ilist.New(values=qbits_getitem, elem_type=qubit.QubitType)
49
48
  qbits_result = state.current_frame.get(qbits_stmt.name)
50
49
 
51
50
  if qbits_result is not None:
@@ -64,6 +63,8 @@ class Squin(lowering.LoweringABC[CirqNode]):
64
63
  lineno_offset: int = 0,
65
64
  col_offset: int = 0,
66
65
  compactify: bool = True,
66
+ register_as_argument: bool = False,
67
+ register_argument_name: str = "q",
67
68
  ) -> ir.Region:
68
69
 
69
70
  state = lowering.State(
@@ -73,16 +74,21 @@ class Squin(lowering.LoweringABC[CirqNode]):
73
74
  col_offset=col_offset,
74
75
  )
75
76
 
76
- with state.frame(
77
- [stmt],
78
- globals=globals,
79
- finalize_next=False,
80
- ) as frame:
81
- # NOTE: create a global register of qubits first
82
- # TODO: can there be a circuit without qubits?
83
- n_qubits = cirq.num_qubits(self.circuit)
84
- n = frame.push(py.Constant(n_qubits))
85
- self.qreg = frame.push(qubit.New(n_qubits=n.result))
77
+ with state.frame([stmt], globals=globals, finalize_next=False) as frame:
78
+
79
+ # NOTE: need a register of qubits before lowering statements
80
+ if register_as_argument:
81
+ # NOTE: register as argument to the kernel; we have freedom of choice for the name here
82
+ frame.curr_block.args.append_from(
83
+ ilist.IListType[qubit.QubitType, types.Any],
84
+ name=register_argument_name,
85
+ )
86
+ self.qreg = frame.curr_block.args[0]
87
+ else:
88
+ # NOTE: create a new register of appropriate size
89
+ n_qubits = len(self.qreg_index)
90
+ n = frame.push(py.Constant(n_qubits))
91
+ self.qreg = frame.push(qubit.New(n_qubits=n.result)).result
86
92
 
87
93
  self.visit(state, stmt)
88
94
 
@@ -1,3 +1,6 @@
1
+ from typing import Literal
2
+
3
+ from kirin.dialects import ilist
1
4
  from kirin.lowering import wraps
2
5
 
3
6
  from bloqade.squin.op.types import Op
@@ -18,11 +21,15 @@ def depolarize(p: float) -> Op: ...
18
21
 
19
22
 
20
23
  @wraps(stmts.SingleQubitPauliChannel)
21
- def single_qubit_pauli_channel(params: tuple[float, float, float]) -> Op: ...
24
+ def single_qubit_pauli_channel(
25
+ params: ilist.IList[float, Literal[3]] | list[float] | tuple[float, float, float],
26
+ ) -> Op: ...
22
27
 
23
28
 
24
29
  @wraps(stmts.TwoQubitPauliChannel)
25
- def two_qubit_pauli_channel(params: tuple[float, ...]) -> Op: ...
30
+ def two_qubit_pauli_channel(
31
+ params: ilist.IList[float, Literal[15]] | list[float] | tuple[float, ...],
32
+ ) -> Op: ...
26
33
 
27
34
 
28
35
  @wraps(stmts.QubitLoss)
@@ -58,12 +58,12 @@ class _RewriteNoiseStmts(RewriteRule):
58
58
  def rewrite_two_qubit_pauli_channel(
59
59
  self, node: TwoQubitPauliChannel
60
60
  ) -> RewriteResult:
61
- paulis = (X(), Y(), Z(), Identity(sites=1))
61
+ paulis = (Identity(sites=1), X(), Y(), Z())
62
62
  for op in paulis:
63
63
  op.insert_before(node)
64
64
 
65
- # NOTE: collect list so we can skip the last entry, which will be two identities
66
- combinations = list(itertools.product(paulis, repeat=2))[:-1]
65
+ # NOTE: collect list so we can skip the first entry, which will be two identities
66
+ combinations = list(itertools.product(paulis, repeat=2))[1:]
67
67
  operators: list[ir.SSAValue] = []
68
68
  for pauli_1, pauli_2 in combinations:
69
69
  op = Kron(pauli_1.result, pauli_2.result)
@@ -30,6 +30,9 @@ from ._wrapper import (
30
30
  shift as shift,
31
31
  spin_n as spin_n,
32
32
  spin_p as spin_p,
33
+ sqrt_x as sqrt_x,
34
+ sqrt_y as sqrt_y,
35
+ sqrt_z as sqrt_z,
33
36
  adjoint as adjoint,
34
37
  control as control,
35
38
  identity as identity,
@@ -69,6 +69,18 @@ def y() -> types.Op: ...
69
69
  def z() -> types.Op: ...
70
70
 
71
71
 
72
+ @wraps(stmts.SqrtX)
73
+ def sqrt_x() -> types.Op: ...
74
+
75
+
76
+ @wraps(stmts.SqrtY)
77
+ def sqrt_y() -> types.Op: ...
78
+
79
+
80
+ @wraps(stmts.S)
81
+ def sqrt_z() -> types.Op: ...
82
+
83
+
72
84
  @wraps(stmts.H)
73
85
  def h() -> types.Op: ...
74
86
 
bloqade/squin/op/stmts.py CHANGED
@@ -142,7 +142,12 @@ class Reset(PrimitiveOp):
142
142
 
143
143
 
144
144
  @statement
145
- class PauliOp(ConstantUnitary):
145
+ class CliffordOp(ConstantUnitary):
146
+ pass
147
+
148
+
149
+ @statement
150
+ class PauliOp(CliffordOp):
146
151
  pass
147
152
 
148
153
 
@@ -173,6 +178,19 @@ class Z(PauliOp):
173
178
  pass
174
179
 
175
180
 
181
+ @statement(dialect=dialect)
182
+ class SqrtX(ConstantUnitary):
183
+ pass
184
+
185
+
186
+ @statement(dialect=dialect)
187
+ class SqrtY(ConstantUnitary):
188
+ pass
189
+
190
+
191
+ # NOTE no SqrtZ since its equal to S
192
+
193
+
176
194
  @statement(dialect=dialect)
177
195
  class H(ConstantUnitary):
178
196
  pass
@@ -0,0 +1,149 @@
1
+ # create rewrite rule name SquinMeasureToStim using kirin
2
+ import math
3
+ from typing import List, Tuple, Callable
4
+
5
+ import numpy as np
6
+ from kirin import ir
7
+ from kirin.dialects import py
8
+ from kirin.rewrite.abc import RewriteRule, RewriteResult
9
+
10
+ from bloqade.squin import op, qubit
11
+
12
+
13
+ def sdag() -> list[ir.Statement]:
14
+ return [_op := op.stmts.S(), op.stmts.Adjoint(op=_op.result, is_unitary=True)]
15
+
16
+
17
+ # (theta, phi, lam)
18
+ U3_HALF_PI_ANGLE_TO_GATES: dict[
19
+ tuple[int, int, int], Callable[[], Tuple[List[ir.Statement], ...]]
20
+ ] = {
21
+ (0, 0, 0): lambda: ([op.stmts.Identity(sites=1)],),
22
+ (0, 0, 1): lambda: ([op.stmts.S()],),
23
+ (0, 0, 2): lambda: ([op.stmts.Z()],),
24
+ (0, 0, 3): lambda: (sdag(),),
25
+ (1, 0, 0): lambda: ([op.stmts.SqrtY()],),
26
+ (1, 0, 1): lambda: ([op.stmts.S()], [op.stmts.SqrtY()]),
27
+ (1, 0, 2): lambda: ([op.stmts.H()],),
28
+ (1, 0, 3): lambda: (sdag(), [op.stmts.SqrtY()]),
29
+ (1, 1, 0): lambda: ([op.stmts.SqrtY()], [op.stmts.S()]),
30
+ (1, 1, 1): lambda: ([op.stmts.S()], [op.stmts.SqrtY()], [op.stmts.S()]),
31
+ (1, 1, 2): lambda: ([op.stmts.Z()], [op.stmts.SqrtY()], [op.stmts.S()]),
32
+ (1, 1, 3): lambda: (sdag(), [op.stmts.SqrtY()], [op.stmts.S()]),
33
+ (1, 2, 0): lambda: ([op.stmts.SqrtY()], [op.stmts.Z()]),
34
+ (1, 2, 1): lambda: ([op.stmts.S()], [op.stmts.SqrtY()], [op.stmts.Z()]),
35
+ (1, 2, 2): lambda: ([op.stmts.Z()], [op.stmts.SqrtY()], [op.stmts.Z()]),
36
+ (1, 2, 3): lambda: (sdag(), [op.stmts.SqrtY()], [op.stmts.Z()]),
37
+ (1, 3, 0): lambda: ([op.stmts.SqrtY()], sdag()),
38
+ (1, 3, 1): lambda: ([op.stmts.S()], [op.stmts.SqrtY()], sdag()),
39
+ (1, 3, 2): lambda: ([op.stmts.Z()], [op.stmts.SqrtY()], sdag()),
40
+ (1, 3, 3): lambda: (sdag(), [op.stmts.SqrtY()], sdag()),
41
+ (2, 0, 0): lambda: ([op.stmts.Y()],),
42
+ (2, 0, 1): lambda: ([op.stmts.S()], [op.stmts.Y()]),
43
+ (2, 0, 2): lambda: ([op.stmts.Z()], [op.stmts.Y()]),
44
+ (2, 0, 3): lambda: (sdag(), [op.stmts.Y()]),
45
+ }
46
+
47
+
48
+ def equivalent_u3_para(
49
+ theta_half_pi: int, phi_half_pi: int, lam_half_pi: int
50
+ ) -> tuple[int, int, int]:
51
+ """
52
+ 1. Assume all three angles are in the range [0, 4].
53
+ 2. U3(theta, phi, lam) = -U3(2pi-theta, phi+pi, lam+pi).
54
+ """
55
+ return ((4 - theta_half_pi) % 4, (phi_half_pi + 2) % 4, (lam_half_pi + 2) % 4)
56
+
57
+
58
+ class SquinU3ToClifford(RewriteRule):
59
+ """
60
+ Rewrite squin U3 statements to clifford when possible.
61
+ """
62
+
63
+ def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
64
+ if isinstance(node, (qubit.Apply, qubit.Broadcast)):
65
+ return self.rewrite_ApplyOrBroadcast_onU3(node)
66
+ else:
67
+ return RewriteResult()
68
+
69
+ def get_constant(self, node: ir.SSAValue) -> float | None:
70
+ if isinstance(node.owner, py.Constant):
71
+ # node.value is a PyAttr, need to get the wrapped value out
72
+ return node.owner.value.unwrap()
73
+ else:
74
+ return None
75
+
76
+ def resolve_angle(self, angle: float) -> int | None:
77
+ """
78
+ Normalize the angle to be in the range [0, 2π).
79
+ """
80
+ # convert to 0.0~1.0, in unit of pi/2
81
+ angle_half_pi = angle / math.pi * 2.0
82
+
83
+ mod = angle_half_pi % 1.0
84
+ if not (np.isclose(mod, 0.0) or np.isclose(mod, 1.0)):
85
+ return None
86
+
87
+ else:
88
+ return round((angle / math.tau) % 1 * 4) % 4
89
+
90
+ def rewrite_ApplyOrBroadcast_onU3(
91
+ self, node: qubit.Apply | qubit.Broadcast
92
+ ) -> RewriteResult:
93
+ """
94
+ Rewrite Apply and Broadcast nodes to their clifford equivalent statements.
95
+ """
96
+ if not isinstance(node.operator.owner, op.stmts.U3):
97
+ return RewriteResult()
98
+
99
+ gates = self.decompose_U3_gates(node.operator.owner)
100
+
101
+ if len(gates) == 0:
102
+ return RewriteResult()
103
+
104
+ for stmt_list in gates:
105
+ for gate_stmt in stmt_list[:-1]:
106
+ gate_stmt.insert_before(node)
107
+
108
+ oper = stmt_list[-1]
109
+ oper.insert_before(node)
110
+ new_node = node.__class__(operator=oper.result, qubits=node.qubits)
111
+ new_node.insert_before(node)
112
+
113
+ node.delete()
114
+
115
+ # rewrite U3 to clifford gates
116
+ return RewriteResult(has_done_something=True)
117
+
118
+ def decompose_U3_gates(self, node: op.stmts.U3) -> Tuple[List[ir.Statement], ...]:
119
+ """
120
+ Rewrite U3 statements to clifford gates if possible.
121
+ """
122
+ theta = self.get_constant(node.theta)
123
+ phi = self.get_constant(node.phi)
124
+ lam = self.get_constant(node.lam)
125
+
126
+ if theta is None or phi is None or lam is None:
127
+ return ()
128
+
129
+ theta_half_pi: int | None = self.resolve_angle(theta)
130
+ phi_half_pi: int | None = self.resolve_angle(phi)
131
+ lam_half_pi: int | None = self.resolve_angle(lam)
132
+
133
+ if theta_half_pi is None or phi_half_pi is None or lam_half_pi is None:
134
+ return ()
135
+
136
+ angles_key = (theta_half_pi, phi_half_pi, lam_half_pi)
137
+ if angles_key not in U3_HALF_PI_ANGLE_TO_GATES:
138
+ angles_key = equivalent_u3_para(*angles_key)
139
+ if angles_key not in U3_HALF_PI_ANGLE_TO_GATES:
140
+ return ()
141
+
142
+ gates_stmts = U3_HALF_PI_ANGLE_TO_GATES.get(angles_key)
143
+
144
+ # no consistent gates, then:
145
+ assert (
146
+ gates_stmts is not None
147
+ ), "internal error, U3 gates not found for angles: {}".format(angles_key)
148
+
149
+ return gates_stmts()
@@ -1,11 +1,7 @@
1
- from .wire_to_stim import SquinWireToStim as SquinWireToStim
2
- from .qubit_to_stim import SquinQubitToStim as SquinQubitToStim
3
- from .squin_measure import SquinMeasureToStim as SquinMeasureToStim
4
1
  from .wrap_analysis import (
5
2
  SitesAttribute as SitesAttribute,
6
3
  AddressAttribute as AddressAttribute,
7
- WrapSquinAnalysis as WrapSquinAnalysis,
8
- )
9
- from .wire_identity_elimination import (
10
- SquinWireIdentityElimination as SquinWireIdentityElimination,
4
+ WrapOpSiteAnalysis as WrapOpSiteAnalysis,
5
+ WrapAddressAnalysis as WrapAddressAnalysis,
11
6
  )
7
+ from .remove_dangling_qubits import RemoveDeadRegister as RemoveDeadRegister
@@ -0,0 +1,19 @@
1
+ from kirin import ir
2
+ from kirin.rewrite.abc import RewriteRule, RewriteResult
3
+
4
+ from bloqade.squin import qubit
5
+
6
+
7
+ class RemoveDeadRegister(RewriteRule):
8
+
9
+ def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
10
+
11
+ if not isinstance(node, qubit.New):
12
+ return RewriteResult()
13
+
14
+ if bool(node.result.uses):
15
+ return RewriteResult()
16
+ else:
17
+ node.delete()
18
+
19
+ return RewriteResult(has_done_something=True)
@@ -1,3 +1,4 @@
1
+ from abc import abstractmethod
1
2
  from dataclasses import dataclass
2
3
 
3
4
  from kirin import ir
@@ -40,33 +41,47 @@ class SitesAttribute(ir.Attribute):
40
41
 
41
42
 
42
43
  @dataclass
43
- class WrapSquinAnalysis(RewriteRule):
44
+ class WrapAnalysis(RewriteRule):
44
45
 
46
+ @abstractmethod
47
+ def wrap(self, value: ir.SSAValue) -> bool:
48
+ pass
49
+
50
+ def rewrite_Block(self, node: ir.Block) -> RewriteResult:
51
+ has_done_something = any(self.wrap(arg) for arg in node.args)
52
+ return RewriteResult(has_done_something=has_done_something)
53
+
54
+ def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
55
+ has_done_something = any(self.wrap(result) for result in node.results)
56
+ return RewriteResult(has_done_something=has_done_something)
57
+
58
+
59
+ @dataclass
60
+ class WrapAddressAnalysis(WrapAnalysis):
45
61
  address_analysis: dict[ir.SSAValue, Address]
46
- op_site_analysis: dict[ir.SSAValue, Sites]
47
62
 
48
63
  def wrap(self, value: ir.SSAValue) -> bool:
49
64
  address_analysis_result = self.address_analysis[value]
50
- op_site_analysis_result = self.op_site_analysis[value]
51
65
 
52
- if value.hints.get("address") and value.hints.get("sites"):
66
+ if value.hints.get("address") is not None:
53
67
  return False
54
- else:
55
- value.hints["address"] = AddressAttribute(address_analysis_result)
56
- value.hints["sites"] = SitesAttribute(op_site_analysis_result)
68
+
69
+ value.hints["address"] = AddressAttribute(address_analysis_result)
57
70
 
58
71
  return True
59
72
 
60
- def rewrite_Block(self, node: ir.Block) -> RewriteResult:
61
- has_done_something = False
62
- for arg in node.args:
63
- if self.wrap(arg):
64
- has_done_something = True
65
- return RewriteResult(has_done_something=has_done_something)
66
73
 
67
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
68
- has_done_something = False
69
- for result in node.results:
70
- if self.wrap(result):
71
- has_done_something = True
72
- return RewriteResult(has_done_something=has_done_something)
74
+ @dataclass
75
+ class WrapOpSiteAnalysis(WrapAnalysis):
76
+
77
+ op_site_analysis: dict[ir.SSAValue, Sites]
78
+
79
+ def wrap(self, value: ir.SSAValue) -> bool:
80
+ op_site_analysis_result = self.op_site_analysis[value]
81
+
82
+ if value.hints.get("sites") is not None:
83
+ return False
84
+
85
+ value.hints["sites"] = SitesAttribute(op_site_analysis_result)
86
+
87
+ return True
bloqade/stim/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- from . import emit as emit, parse as parse, dialects as dialects
1
+ from . import emit as emit, parse as parse, passes as passes, dialects as dialects
2
2
  from .groups import main as main
3
3
  from ._wrappers import (
4
4
  h as h,
@@ -47,7 +47,7 @@ class ConstFloat(ir.Statement):
47
47
 
48
48
  @statement(dialect=dialect)
49
49
  class ConstBool(ir.Statement):
50
- """IR Statement representing a constant float value."""
50
+ """IR Statement representing a constant boolean value."""
51
51
 
52
52
  name = "constant.bool"
53
53
  traits = frozenset({ir.Pure(), ir.ConstantLike(), lowering.FromPythonCall()})
@@ -1,4 +1,10 @@
1
1
  from .pp import SPP as SPP
2
+ from .base import (
3
+ Gate as Gate,
4
+ TwoQubitGate as TwoQubitGate,
5
+ SingleQubitGate as SingleQubitGate,
6
+ ControlledTwoQubitGate as ControlledTwoQubitGate,
7
+ )
2
8
  from .control_2q import CX as CX, CY as CY, CZ as CZ
3
9
  from .clifford_1q import (
4
10
  H as H,
@@ -0,0 +1 @@
1
+ from .squin_to_stim import SquinToStim as SquinToStim
@@ -0,0 +1,86 @@
1
+ from dataclasses import dataclass
2
+
3
+ from kirin.passes import Fold
4
+ from kirin.rewrite import (
5
+ Walk,
6
+ Chain,
7
+ Fixpoint,
8
+ DeadCodeElimination,
9
+ CommonSubexpressionElimination,
10
+ )
11
+ from kirin.ir.method import Method
12
+ from kirin.passes.abc import Pass
13
+ from kirin.rewrite.abc import RewriteResult
14
+
15
+ from bloqade.stim.groups import main as stim_main_group
16
+ from bloqade.stim.rewrite import (
17
+ SquinWireToStim,
18
+ PyConstantToStim,
19
+ SquinQubitToStim,
20
+ SquinMeasureToStim,
21
+ SquinWireIdentityElimination,
22
+ )
23
+ from bloqade.squin.rewrite import RemoveDeadRegister
24
+
25
+
26
+ @dataclass
27
+ class SquinToStim(Pass):
28
+
29
+ def unsafe_run(self, mt: Method) -> RewriteResult:
30
+ fold_pass = Fold(mt.dialects)
31
+ # propagate constants
32
+ rewrite_result = fold_pass(mt)
33
+
34
+ # Assume that address analysis and
35
+ # wrapping has been done before this pass!
36
+
37
+ # Wrap Rewrite + SquinToStim can happen w/ standard walk
38
+ rewrite_result = (
39
+ Walk(
40
+ Chain(
41
+ SquinQubitToStim(),
42
+ SquinWireToStim(),
43
+ SquinMeasureToStim(), # reduce duplicated logic, can split out even more rules later
44
+ SquinWireIdentityElimination(),
45
+ )
46
+ )
47
+ .rewrite(mt.code)
48
+ .join(rewrite_result)
49
+ )
50
+
51
+ # Convert all PyConsts to Stim Constants
52
+ rewrite_result = (
53
+ Walk(Chain(PyConstantToStim())).rewrite(mt.code).join(rewrite_result)
54
+ )
55
+
56
+ # remove any squin.qubit.new that's left around
57
+ ## Not considered pure so DCE won't touch it but
58
+ ## it isn't being used anymore considering everything is a
59
+ ## stim dialect statement
60
+ rewrite_result = (
61
+ Fixpoint(
62
+ Walk(
63
+ Chain(
64
+ DeadCodeElimination(),
65
+ CommonSubexpressionElimination(),
66
+ RemoveDeadRegister(),
67
+ )
68
+ )
69
+ )
70
+ .rewrite(mt.code)
71
+ .join(rewrite_result)
72
+ )
73
+
74
+ # do program verification here,
75
+ # ideally use built-in .verify() to catch any
76
+ # incompatible statements as the full rewrite process should not
77
+ # leave statements from any other dialects (other than the stim main group)
78
+ mt_verification_clone = mt.similar(stim_main_group)
79
+
80
+ # suggested by Kai, will work for now
81
+ for stmt in mt_verification_clone.code.walk():
82
+ assert (
83
+ stmt.dialect in stim_main_group
84
+ ), "Statements detected that are not part of the stim dialect, please verify the original code is valid for rewrite!"
85
+
86
+ return rewrite_result
@@ -0,0 +1,7 @@
1
+ from .wire_to_stim import SquinWireToStim as SquinWireToStim
2
+ from .qubit_to_stim import SquinQubitToStim as SquinQubitToStim
3
+ from .squin_measure import SquinMeasureToStim as SquinMeasureToStim
4
+ from .py_constant_to_stim import PyConstantToStim as PyConstantToStim
5
+ from .wire_identity_elimination import (
6
+ SquinWireIdentityElimination as SquinWireIdentityElimination,
7
+ )
@@ -0,0 +1,42 @@
1
+ from kirin import ir
2
+ from kirin.dialects import py
3
+ from kirin.rewrite.abc import RewriteRule, RewriteResult
4
+
5
+ from bloqade.stim.dialects import auxiliary
6
+
7
+ # py.constant.int -> stim.const.ConstInt
8
+ # py.constant.float -> stimt.const.ConstFloat
9
+ # py.constant -> stim.const.ConstBool
10
+ #
11
+
12
+
13
+ class PyConstantToStim(RewriteRule):
14
+
15
+ def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
16
+
17
+ match node:
18
+ case py.constant.Constant():
19
+ return self.rewrite_PyConstant(node)
20
+ case _:
21
+ return RewriteResult()
22
+
23
+ def rewrite_PyConstant(self, node: py.constant.Constant) -> RewriteResult:
24
+
25
+ # node.value is a PyAttr, need to get the
26
+ # wrapped value out
27
+ wrapped_value = node.value.unwrap()
28
+
29
+ if isinstance(wrapped_value, int):
30
+ stim_const = auxiliary.ConstInt(value=wrapped_value)
31
+ elif isinstance(wrapped_value, float):
32
+ stim_const = auxiliary.ConstFloat(value=wrapped_value)
33
+ elif isinstance(wrapped_value, bool):
34
+ stim_const = auxiliary.ConstBool(value=wrapped_value)
35
+ elif isinstance(wrapped_value, str):
36
+ stim_const = auxiliary.ConstStr(value=wrapped_value)
37
+ else:
38
+ return RewriteResult()
39
+
40
+ node.replace_by(stim_const)
41
+
42
+ return RewriteResult(has_done_something=True)
@@ -2,8 +2,9 @@ from kirin import ir
2
2
  from kirin.rewrite.abc import RewriteRule, RewriteResult
3
3
 
4
4
  from bloqade.squin import op, qubit
5
- from bloqade.squin.rewrite.wrap_analysis import AddressAttribute
6
- from bloqade.squin.rewrite.stim_rewrite_util import (
5
+ from bloqade.squin.rewrite import AddressAttribute
6
+ from bloqade.stim.dialects import gate
7
+ from bloqade.stim.rewrite.util import (
7
8
  SQUIN_STIM_GATE_MAPPING,
8
9
  rewrite_Control,
9
10
  insert_qubit_idx_from_address,
@@ -34,6 +35,15 @@ class SquinQubitToStim(RewriteRule):
34
35
  if isinstance(applied_op, op.stmts.Control):
35
36
  return rewrite_Control(stmt)
36
37
 
38
+ # check if its adjoint, assume its canonicalized so no nested adjoints.
39
+ is_conj = False
40
+ if isinstance(applied_op, op.stmts.Adjoint):
41
+ if not applied_op.is_unitary:
42
+ return RewriteResult()
43
+
44
+ is_conj = True
45
+ applied_op = applied_op.op.owner
46
+
37
47
  # need to handle Control through separate means
38
48
  # but we can handle X, Y, Z, H, and S here just fine
39
49
  stim_1q_op = SQUIN_STIM_GATE_MAPPING.get(type(applied_op))
@@ -41,9 +51,11 @@ class SquinQubitToStim(RewriteRule):
41
51
  return RewriteResult()
42
52
 
43
53
  address_attr = stmt.qubits.hints.get("address")
54
+
44
55
  if address_attr is None:
45
56
  return RewriteResult()
46
57
 
58
+ # sometimes you can get a whole AddressReg...
47
59
  assert isinstance(address_attr, AddressAttribute)
48
60
  qubit_idx_ssas = insert_qubit_idx_from_address(
49
61
  address=address_attr, stmt_to_insert_before=stmt
@@ -52,7 +64,10 @@ class SquinQubitToStim(RewriteRule):
52
64
  if qubit_idx_ssas is None:
53
65
  return RewriteResult()
54
66
 
55
- stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas))
67
+ if isinstance(stim_1q_op, gate.stmts.Gate):
68
+ stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas), dagger=is_conj)
69
+ else:
70
+ stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas))
56
71
  stmt.replace_by(stim_1q_stmt)
57
72
 
58
73
  return RewriteResult(has_done_something=True)
@@ -4,9 +4,9 @@ from kirin.dialects import py
4
4
  from kirin.rewrite.abc import RewriteRule, RewriteResult
5
5
 
6
6
  from bloqade.squin import wire, qubit
7
+ from bloqade.squin.rewrite import AddressAttribute
7
8
  from bloqade.stim.dialects import collapse
8
- from bloqade.squin.rewrite.wrap_analysis import AddressAttribute
9
- from bloqade.squin.rewrite.stim_rewrite_util import (
9
+ from bloqade.stim.rewrite.util import (
10
10
  is_measure_result_used,
11
11
  insert_qubit_idx_from_address,
12
12
  )
@@ -3,9 +3,9 @@ from kirin.dialects import py
3
3
  from kirin.rewrite.abc import RewriteResult
4
4
 
5
5
  from bloqade.squin import op, wire, qubit
6
+ from bloqade.squin.rewrite import AddressAttribute
6
7
  from bloqade.stim.dialects import gate, collapse
7
- from bloqade.analysis.address import AddressWire, AddressQubit, AddressTuple
8
- from bloqade.squin.rewrite.wrap_analysis import AddressAttribute
8
+ from bloqade.analysis.address import AddressReg, AddressWire, AddressQubit, AddressTuple
9
9
 
10
10
  SQUIN_STIM_GATE_MAPPING = {
11
11
  op.stmts.X: gate.X,
@@ -13,10 +13,28 @@ SQUIN_STIM_GATE_MAPPING = {
13
13
  op.stmts.Z: gate.Z,
14
14
  op.stmts.H: gate.H,
15
15
  op.stmts.S: gate.S,
16
+ op.stmts.SqrtX: gate.SqrtX,
17
+ op.stmts.SqrtY: gate.SqrtY,
16
18
  op.stmts.Identity: gate.Identity,
17
19
  op.stmts.Reset: collapse.RZ,
18
20
  }
19
21
 
22
+ # Squin allows creation of control gates where the gate can be any operator,
23
+ # but Stim only supports CX, CY, and CZ as control gates.
24
+ SQUIN_STIM_CONTROL_GATE_MAPPING = {
25
+ op.stmts.X: gate.CX,
26
+ op.stmts.Y: gate.CY,
27
+ op.stmts.Z: gate.CZ,
28
+ }
29
+
30
+
31
+ def create_and_insert_qubit_idx_stmt(
32
+ qubit_idx, stmt_to_insert_before: ir.Statement, qubit_idx_ssas: list
33
+ ):
34
+ qubit_idx_stmt = py.Constant(qubit_idx)
35
+ qubit_idx_stmt.insert_before(stmt_to_insert_before)
36
+ qubit_idx_ssas.append(qubit_idx_stmt.result)
37
+
20
38
 
21
39
  def insert_qubit_idx_from_address(
22
40
  address: AddressAttribute, stmt_to_insert_before: ir.Statement
@@ -31,16 +49,23 @@ def insert_qubit_idx_from_address(
31
49
  for address_qubit in address_data.data:
32
50
  if not isinstance(address_qubit, AddressQubit):
33
51
  return
34
- qubit_idx = address_qubit.data
35
- qubit_idx_stmt = py.Constant(qubit_idx)
36
- qubit_idx_stmt.insert_before(stmt_to_insert_before)
37
- qubit_idx_ssas.append(qubit_idx_stmt.result)
52
+ create_and_insert_qubit_idx_stmt(
53
+ address_qubit.data, stmt_to_insert_before, qubit_idx_ssas
54
+ )
55
+ elif isinstance(address_data, AddressReg):
56
+ for qubit_idx in address_data.data:
57
+ create_and_insert_qubit_idx_stmt(
58
+ qubit_idx, stmt_to_insert_before, qubit_idx_ssas
59
+ )
60
+ elif isinstance(address_data, AddressQubit):
61
+ create_and_insert_qubit_idx_stmt(
62
+ address_data.data, stmt_to_insert_before, qubit_idx_ssas
63
+ )
38
64
  elif isinstance(address_data, AddressWire):
39
65
  address_qubit = address_data.origin_qubit
40
- qubit_idx = address_qubit.data
41
- qubit_idx_stmt = py.Constant(qubit_idx)
42
- qubit_idx_stmt.insert_before(stmt_to_insert_before)
43
- qubit_idx_ssas.append(qubit_idx_stmt.result)
66
+ create_and_insert_qubit_idx_stmt(
67
+ address_qubit.data, stmt_to_insert_before, qubit_idx_ssas
68
+ )
44
69
  else:
45
70
  return
46
71
 
@@ -119,13 +144,7 @@ def rewrite_Control(
119
144
  target_qubits = tuple(target_qubits)
120
145
  ctrl_qubits = tuple(ctrl_qubits)
121
146
 
122
- supported_gate_mapping = {
123
- op.stmts.X: gate.CX,
124
- op.stmts.Y: gate.CY,
125
- op.stmts.Z: gate.CZ,
126
- }
127
-
128
- stim_gate = supported_gate_mapping.get(type(ctrl_op_target_gate))
147
+ stim_gate = SQUIN_STIM_CONTROL_GATE_MAPPING.get(type(ctrl_op_target_gate))
129
148
  if stim_gate is None:
130
149
  return RewriteResult()
131
150
 
@@ -2,7 +2,7 @@ from kirin import ir
2
2
  from kirin.rewrite.abc import RewriteRule, RewriteResult
3
3
 
4
4
  from bloqade.squin import op, wire
5
- from bloqade.squin.rewrite.stim_rewrite_util import (
5
+ from bloqade.stim.rewrite.util import (
6
6
  SQUIN_STIM_GATE_MAPPING,
7
7
  rewrite_Control,
8
8
  insert_qubit_idx_from_wire_ssa,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bloqade-circuit
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: The software development toolkit for neutral atom arrays.
5
5
  Author-email: Roger-luo <rluo@quera.com>, kaihsin <khwu@quera.com>, weinbe58 <pweinberg@quera.com>, johnzl-777 <jlong@quera.com>
6
6
  License-File: LICENSE
@@ -115,39 +115,35 @@ bloqade/squin/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
115
115
  bloqade/squin/analysis/schedule.py,sha256=buuC4bFuLuaSDK2BZfkRkh8ZdNicz9HkEv3FAnsDViE,7880
116
116
  bloqade/squin/analysis/nsites/__init__.py,sha256=RlQg7ivczXCXG5lMeL3ipYKj2oJKC4THu8orYf5PBYs,263
117
117
  bloqade/squin/analysis/nsites/analysis.py,sha256=rIe1RU1MZRItcE2aB8DYahLrv73HfD3IHCX3E_EGQ1c,1773
118
- bloqade/squin/analysis/nsites/impls.py,sha256=OaKuAoZ0EAorStYDZxzgc6Dk42kuj19MLkqHWG1MEQM,2592
118
+ bloqade/squin/analysis/nsites/impls.py,sha256=Q2buVBmUX1ghj48_SoMO-_0BASGkfnILZZOOFRnwzIQ,2772
119
119
  bloqade/squin/analysis/nsites/lattice.py,sha256=ruh0808SHtj3ecuT-C3AZTsLY2j3DRhtezGiTZvcuVs,942
120
- bloqade/squin/cirq/__init__.py,sha256=AptJlelH-KJoFKLnq6phq68SrV785zWzi2NOfLH62ms,5994
121
- bloqade/squin/cirq/lowering.py,sha256=4-kZFH_qbBbV-c3-C9KhIB5o_cp_D8oxJrS8KicD_A8,14382
120
+ bloqade/squin/cirq/__init__.py,sha256=fxvBvwX5VNfDmqkeM4GHguLQh53k-PJVsz89Eu0wRXw,8552
121
+ bloqade/squin/cirq/lowering.py,sha256=ZrdulFJgzuOJHunCmPn9mpUEO1U2xq4gDTTNgSA4cSU,14914
122
122
  bloqade/squin/cirq/emit/emit_circuit.py,sha256=7puJ3eCFwE9VdPb9NAiSdyRNkoQPwo_uVykz9Yv7c14,3761
123
+ bloqade/squin/cirq/emit/noise.py,sha256=rESjGC_66s2Y4FwwYda4rY3mYHYjbqLlKE_vnqpZDYI,1534
123
124
  bloqade/squin/cirq/emit/op.py,sha256=z54NP5KqMxffXeFGWamEzvunpTNrxmYuluurk4j2-ps,4000
124
125
  bloqade/squin/cirq/emit/qubit.py,sha256=Z2HUsZmJ5F2uHCPGru81ux2usoX77KwtS97_cgeJRMI,1910
125
- bloqade/squin/cirq/emit/runtime.py,sha256=6_oHod-WK5yv0ae9xziQn-eh4Hn3MZNNqu4kJtOzPeY,6543
126
+ bloqade/squin/cirq/emit/runtime.py,sha256=dH7JSMt2mALPhVFjmZETQzvnTUQ3BFY5poe0YZpM5vQ,6819
126
127
  bloqade/squin/noise/__init__.py,sha256=HQl3FE0SZAGEX3qdveapCaMX391lgLvWeWnoE6Z2pYw,332
127
128
  bloqade/squin/noise/_dialect.py,sha256=2IR98J-lXm5Y3srP9g-FD4JC-qTq2seureM6mKKq1xg,63
128
- bloqade/squin/noise/_wrapper.py,sha256=0jD5va_go9jEW5rC6bZSWU30kjCha2-axFogPON3-V0,580
129
- bloqade/squin/noise/rewrite.py,sha256=SxIHgMDqYJXepiZDyukHWpe5yaFDSTG-yJ4JONNVr0o,3917
129
+ bloqade/squin/noise/_wrapper.py,sha256=b2HymlFi1BTgAZRaXvRnujJsoXkowmxQFPRBgZso82g,750
130
+ bloqade/squin/noise/rewrite.py,sha256=-IqFfDGnhuaFI-9b6PXjhSuiXFM1C5Qu0ibL5GvZldI,3917
130
131
  bloqade/squin/noise/stmts.py,sha256=rktxkIdjdPUYek0MYh9uh83otkl-7UoADCoWHWf57J8,1678
131
- bloqade/squin/op/__init__.py,sha256=5OBgT4E44Cy0DNF3yRbXGXkiB8VAJtr48x8hDQEquH4,741
132
+ bloqade/squin/op/__init__.py,sha256=QLlvZlb2nDq-RTalRp7xe0v82YgXURsvyovvMA6j2mw,807
132
133
  bloqade/squin/op/_dialect.py,sha256=66G1IYqmsqUEaCTyUqn2shSHmGYduiTU8GfDXcoMvw4,55
133
- bloqade/squin/op/_wrapper.py,sha256=5pqbKGeNWoYQIPa1xkKBr8z5waxwmAm3AV4efGSRT_s,1714
134
+ bloqade/squin/op/_wrapper.py,sha256=rmQHXjRHFEr-bATbKEKL30w1wlDB7vmrQzDDRoLhRVw,1866
134
135
  bloqade/squin/op/number.py,sha256=yujWUqLrOAr8i8OBDsiS5M882wV7t08u345NgNA6TUc,95
135
136
  bloqade/squin/op/rewrite.py,sha256=Itxz_hTAPNLyLYeLS0PCVk143J1Z558UR7N9-urbnoU,1327
136
137
  bloqade/squin/op/stdlib.py,sha256=4UFK3wKImpums2v5a9OFKuVvz2TLYbYwidg3JYYEi2o,1073
137
- bloqade/squin/op/stmts.py,sha256=uBQeCWc1JjMTIVcssWkLWi6MyTLtQMoqTWoKgLsYgWQ,5262
138
+ bloqade/squin/op/stmts.py,sha256=atPrr12Bc7D5xu_xavVgME3FBNqHQXQSAtMuh7xL0Dw,5491
138
139
  bloqade/squin/op/traits.py,sha256=jjsnzWtPtmQK7K3H_D2fvc8XiW1Y3EMBcgeyPax2sjc,1065
139
140
  bloqade/squin/op/types.py,sha256=ozUT0Bv9NuUxPjB2vAeqJ9cpdvUaBfP9trB5mybYxgc,663
140
- bloqade/squin/passes/__init__.py,sha256=Bhog-wZBtToNJXfhlYa6S7tE6OoppyRibjMl5JBfY58,45
141
- bloqade/squin/passes/stim.py,sha256=VWv3hhfizCWz5sIwwdFt3flWHLzG428evLGIcX8E36Y,1992
142
- bloqade/squin/rewrite/__init__.py,sha256=0-9m1cbvFRgjZpQ700NEjW1uKvwZPPbrmUwylhgOjUw,457
141
+ bloqade/squin/rewrite/U3_to_clifford.py,sha256=HpkFTqe-J0macb_aNs1QNs8wJDoUsmwf9Mtb0I3ZepI,5377
142
+ bloqade/squin/rewrite/__init__.py,sha256=mxP01A2IeOT5HKHKBRRMCIPN4BPKfpYSlQkSjY78ugQ,282
143
143
  bloqade/squin/rewrite/desugar.py,sha256=fnxchVHIbLx96cv-g_jK7NCVV8t8mo52rs00JaPG7FM,1846
144
- bloqade/squin/rewrite/qubit_to_stim.py,sha256=fZC9voSAgjNwZv4wYdJ1Qkd55049n8rc7gpujvdQHEc,1976
145
- bloqade/squin/rewrite/squin_measure.py,sha256=E5HVkLaBcsT-4dy1MN3BjdkOmqXzeTO4clZcwKRd66k,2494
146
- bloqade/squin/rewrite/stim_rewrite_util.py,sha256=y6CsosZXgAXc0enWPcRUt1uwxm09LBoR6mvl2Y5bHbs,5161
147
- bloqade/squin/rewrite/wire_identity_elimination.py,sha256=Cscu8yaSslPuW04HvbXx4HJ3JzdUZNUMyFqcvuc4sxY,795
148
- bloqade/squin/rewrite/wire_to_stim.py,sha256=PbXjwaF-Z2JioXALKx76uGkYr6-xy20zZFotPeDX-lY,1699
149
- bloqade/squin/rewrite/wrap_analysis.py,sha256=OoKoS0zFLjfCnn4_fK4bgpQ_ueAEiHl1UmfNWhruxMc,2069
150
- bloqade/stim/__init__.py,sha256=BbMeLjeIW29xuD6JB0cWG_g0qaYIy4xXbloDqvqbT7A,887
144
+ bloqade/squin/rewrite/remove_dangling_qubits.py,sha256=iTuWV-03YW5wtYbSeKMlnnWjNzDj9SmflyqYPgoYGy8,469
145
+ bloqade/squin/rewrite/wrap_analysis.py,sha256=JUPS4OAYbDHOK0VIrdz1pprSizISUfN7osuoP_P-bIo,2256
146
+ bloqade/stim/__init__.py,sha256=-TkpVoISvZ-HERL2LJfIMHs3Y4k-WdrGWM-_Rwe9q0o,905
151
147
  bloqade/stim/_wrappers.py,sha256=KBIIOaeYPnhR3SJSNHcEzihLMcph2tL2mRKhEH47Je8,3939
152
148
  bloqade/stim/groups.py,sha256=Fx8G698BGO7hR8OwpPXGUEYdW4uCCPwbMp_3fJAqa8M,585
153
149
  bloqade/stim/dialects/__init__.py,sha256=A1Sq0jg8wi6MjRkzmuSBnHmO3EraD0pDFWz-dO6c6v8,89
@@ -159,7 +155,7 @@ bloqade/stim/dialects/auxiliary/lowering.py,sha256=4dxRZHGp_18EMVlYuZQFSzg4MIOBr
159
155
  bloqade/stim/dialects/auxiliary/types.py,sha256=1l2YdJ-u9y0wpBfxh4OAlvmRAUnrmcDjDagoW2GyVaQ,314
160
156
  bloqade/stim/dialects/auxiliary/stmts/__init__.py,sha256=VxLBnqlZngabs5vn3PiRNRXBBDueTCTmJ2wgWEertrA,371
161
157
  bloqade/stim/dialects/auxiliary/stmts/annotate.py,sha256=NZT4AAW0lmwyHR31cmZI8aQNRsp5soeE36s9nBrcOmU,1759
162
- bloqade/stim/dialects/auxiliary/stmts/const.py,sha256=nfx2aFTPBTCfKriANbzDUWjP9wgnWC5Phkfl2aUPyXY,3296
158
+ bloqade/stim/dialects/auxiliary/stmts/const.py,sha256=nxcpVylgjfKt8pDkcMzVBMGLjON2bQb99WSLv7frvkI,3298
163
159
  bloqade/stim/dialects/collapse/__init__.py,sha256=MhlEWJR_mJI3teoip1N8MlHk2C68seJELXwmyjyx_ns,305
164
160
  bloqade/stim/dialects/collapse/_dialect.py,sha256=F5uplH7WL9ZQatEp0ZMvgLWX1k9moAWW879LTNwnHH0,60
165
161
  bloqade/stim/dialects/collapse/emit_str.py,sha256=4Vlhtq8BKMMuZLQ-ayUGJTuOs4YJ1rGurPZBZxBQJcU,1879
@@ -170,7 +166,7 @@ bloqade/stim/dialects/collapse/stmts/reset.py,sha256=4fPSdM_ZiivuUn4uiZGiZQ9swol
170
166
  bloqade/stim/dialects/gate/__init__.py,sha256=5Y0BTlLOuW-Ud4LwAC_CUuMwxa04LKj4CTKgQbiL8yg,347
171
167
  bloqade/stim/dialects/gate/_dialect.py,sha256=78_dThliFZG9xSwYhTdibxP3vefHpEDUawtZqb9X3uA,56
172
168
  bloqade/stim/dialects/gate/emit.py,sha256=HGnko9_nV6jl0kOI_dBWWNQDjySo14jAFBsg-2lTVOU,2869
173
- bloqade/stim/dialects/gate/stmts/__init__.py,sha256=QJgmpArJwM9TXgB95AsEvet630gITtYOwRptnhj9a8U,293
169
+ bloqade/stim/dialects/gate/stmts/__init__.py,sha256=TbXjWYfCbZNuGEehvtF75LELLJJo_GzoXWRnuVZphv8,461
174
170
  bloqade/stim/dialects/gate/stmts/base.py,sha256=MK7Zu-Kys_64O0nXAkPzlHEnWYevBGm3FRHhJw33rrE,748
175
171
  bloqade/stim/dialects/gate/stmts/clifford_1q.py,sha256=Sj1Nf9AA_WWazGTg5kzFRTaLRIj3Edj657j0B8dybdI,896
176
172
  bloqade/stim/dialects/gate/stmts/clifford_2q.py,sha256=cb3BVSkOt5SiRq4D4WkbuUbEouK3MwDxQIhAF0cvZbo,239
@@ -184,6 +180,15 @@ bloqade/stim/emit/__init__.py,sha256=N2dPQY7OyqPwHAStDeOgYg2yfxqxMOz-N7pD5Z4JwlI
184
180
  bloqade/stim/emit/stim_str.py,sha256=JyEBoIhLQASogZcUWHI9tMD4JoXYrEqUr2qaZ30gZdc,1491
185
181
  bloqade/stim/parse/__init__.py,sha256=l2DjReB2KkgrDjP_4nP6RnoziiOewoSeZfTno1sVYTw,59
186
182
  bloqade/stim/parse/lowering.py,sha256=L-IcR_exlxsTVv4SQ0bhzIF4_L82P-GEdK6qRd6B86Y,23723
183
+ bloqade/stim/passes/__init__.py,sha256=KAWJPhZHf0cVDmr9o9LNRfZU8G9aS4adGC4EC7ci_E8,54
184
+ bloqade/stim/passes/squin_to_stim.py,sha256=4CSWAevECGxYgtojzlH92YwRUlz_lDtedWNEylXKd0A,2752
185
+ bloqade/stim/rewrite/__init__.py,sha256=S2sKUKtX4EGofhZRbewvyHN5s-CavPzseYufij3R5SQ,372
186
+ bloqade/stim/rewrite/py_constant_to_stim.py,sha256=PV8bHvn759-d_0JW4akaGSORW_oxigrlUBhAC51PJAU,1354
187
+ bloqade/stim/rewrite/qubit_to_stim.py,sha256=VggIAS6EQ4a01YplDGJUMDpQ_q-UYD8C4g1xMC_zbcY,2509
188
+ bloqade/stim/rewrite/squin_measure.py,sha256=1X3rcfGvVDSEXmvqlw4T0LQu2A8LFeNeqz1_zf_I8vA,2466
189
+ bloqade/stim/rewrite/util.py,sha256=vpBbTv00odpnnyHTMJZRStb1ZQYfcgbOW5PT5Frf4jw,5847
190
+ bloqade/stim/rewrite/wire_identity_elimination.py,sha256=Cscu8yaSslPuW04HvbXx4HJ3JzdUZNUMyFqcvuc4sxY,795
191
+ bloqade/stim/rewrite/wire_to_stim.py,sha256=vHtktd9pfjlEpInwN7pPhsIHCxTtHJJqJJhYpy0kIGg,1685
187
192
  bloqade/visual/__init__.py,sha256=Y7d0YgovKhUFzjMeDvt0wGRUZ3re3SY6iO3e8xHXrr4,37
188
193
  bloqade/visual/animation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
189
194
  bloqade/visual/animation/animate.py,sha256=NSKs3YDWgTZH2-Tpx3cP1bBcIEWTEPqK5UFkY_IF_3k,8927
@@ -195,7 +200,7 @@ bloqade/visual/animation/runtime/atoms.py,sha256=EmjxhujLiHHPS_HtH_B-7TiqeHgvW5u
195
200
  bloqade/visual/animation/runtime/ppoly.py,sha256=JB9IP53N1w6adBJEue6J5Nmj818Id9JvrlgrmiQTU1I,1385
196
201
  bloqade/visual/animation/runtime/qpustate.py,sha256=rlmxQeJSvaohXrTpXQL5y-NJcpvfW33xPaYM1slv7cc,4270
197
202
  bloqade/visual/animation/runtime/utils.py,sha256=ju9IzOWX-vKwfpqUjlUKu3Ssr_UFPFFq-tzH_Nqyo_c,1212
198
- bloqade_circuit-0.4.3.dist-info/METADATA,sha256=ZBwC4D0_sLjDceh6og0S4tzzFghL5gTxP76QQrMsvv4,3683
199
- bloqade_circuit-0.4.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
200
- bloqade_circuit-0.4.3.dist-info/licenses/LICENSE,sha256=S5GIJwR6QCixPA9wryYb44ZEek0Nz4rt_zLUqP05UbU,13160
201
- bloqade_circuit-0.4.3.dist-info/RECORD,,
203
+ bloqade_circuit-0.4.5.dist-info/METADATA,sha256=8glVTUO-kiKWRlb-6bKJBAf--1A6rjOJgI6o1BJGL6w,3683
204
+ bloqade_circuit-0.4.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
205
+ bloqade_circuit-0.4.5.dist-info/licenses/LICENSE,sha256=S5GIJwR6QCixPA9wryYb44ZEek0Nz4rt_zLUqP05UbU,13160
206
+ bloqade_circuit-0.4.5.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- from .stim import SquinToStim as SquinToStim
@@ -1,68 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
- from kirin.passes import Fold
4
- from kirin.rewrite import (
5
- Walk,
6
- Chain,
7
- Fixpoint,
8
- DeadCodeElimination,
9
- CommonSubexpressionElimination,
10
- )
11
- from kirin.ir.method import Method
12
- from kirin.passes.abc import Pass
13
- from kirin.rewrite.abc import RewriteResult
14
-
15
- from bloqade.squin.rewrite import (
16
- SquinWireToStim,
17
- SquinQubitToStim,
18
- WrapSquinAnalysis,
19
- SquinMeasureToStim,
20
- SquinWireIdentityElimination,
21
- )
22
- from bloqade.analysis.address import AddressAnalysis
23
- from bloqade.squin.analysis.nsites import (
24
- NSitesAnalysis,
25
- )
26
-
27
-
28
- @dataclass
29
- class SquinToStim(Pass):
30
-
31
- def unsafe_run(self, mt: Method) -> RewriteResult:
32
- fold_pass = Fold(mt.dialects)
33
- # propagate constants
34
- rewrite_result = fold_pass(mt)
35
-
36
- # Get necessary analysis results to plug into hints
37
- address_analysis = AddressAnalysis(mt.dialects)
38
- address_frame, _ = address_analysis.run_analysis(mt)
39
- site_analysis = NSitesAnalysis(mt.dialects)
40
- sites_frame, _ = site_analysis.run_analysis(mt)
41
-
42
- # Wrap Rewrite + SquinToStim can happen w/ standard walk
43
- rewrite_result = (
44
- Walk(
45
- Chain(
46
- WrapSquinAnalysis(
47
- address_analysis=address_frame.entries,
48
- op_site_analysis=sites_frame.entries,
49
- ),
50
- SquinQubitToStim(),
51
- SquinWireToStim(),
52
- SquinMeasureToStim(), # reduce duplicated logic, can split out even more rules later
53
- SquinWireIdentityElimination(),
54
- )
55
- )
56
- .rewrite(mt.code)
57
- .join(rewrite_result)
58
- )
59
-
60
- rewrite_result = (
61
- Fixpoint(
62
- Walk(Chain(DeadCodeElimination(), CommonSubexpressionElimination()))
63
- )
64
- .rewrite(mt.code)
65
- .join(rewrite_result)
66
- )
67
-
68
- return rewrite_result