bloqade-circuit 0.4.3__py3-none-any.whl → 0.4.4__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.

@@ -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
@@ -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.4
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,7 +115,7 @@ 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
120
  bloqade/squin/cirq/__init__.py,sha256=AptJlelH-KJoFKLnq6phq68SrV785zWzi2NOfLH62ms,5994
121
121
  bloqade/squin/cirq/lowering.py,sha256=4-kZFH_qbBbV-c3-C9KhIB5o_cp_D8oxJrS8KicD_A8,14382
@@ -128,26 +128,21 @@ bloqade/squin/noise/_dialect.py,sha256=2IR98J-lXm5Y3srP9g-FD4JC-qTq2seureM6mKKq1
128
128
  bloqade/squin/noise/_wrapper.py,sha256=0jD5va_go9jEW5rC6bZSWU30kjCha2-axFogPON3-V0,580
129
129
  bloqade/squin/noise/rewrite.py,sha256=SxIHgMDqYJXepiZDyukHWpe5yaFDSTG-yJ4JONNVr0o,3917
130
130
  bloqade/squin/noise/stmts.py,sha256=rktxkIdjdPUYek0MYh9uh83otkl-7UoADCoWHWf57J8,1678
131
- bloqade/squin/op/__init__.py,sha256=5OBgT4E44Cy0DNF3yRbXGXkiB8VAJtr48x8hDQEquH4,741
131
+ bloqade/squin/op/__init__.py,sha256=QLlvZlb2nDq-RTalRp7xe0v82YgXURsvyovvMA6j2mw,807
132
132
  bloqade/squin/op/_dialect.py,sha256=66G1IYqmsqUEaCTyUqn2shSHmGYduiTU8GfDXcoMvw4,55
133
- bloqade/squin/op/_wrapper.py,sha256=5pqbKGeNWoYQIPa1xkKBr8z5waxwmAm3AV4efGSRT_s,1714
133
+ bloqade/squin/op/_wrapper.py,sha256=rmQHXjRHFEr-bATbKEKL30w1wlDB7vmrQzDDRoLhRVw,1866
134
134
  bloqade/squin/op/number.py,sha256=yujWUqLrOAr8i8OBDsiS5M882wV7t08u345NgNA6TUc,95
135
135
  bloqade/squin/op/rewrite.py,sha256=Itxz_hTAPNLyLYeLS0PCVk143J1Z558UR7N9-urbnoU,1327
136
136
  bloqade/squin/op/stdlib.py,sha256=4UFK3wKImpums2v5a9OFKuVvz2TLYbYwidg3JYYEi2o,1073
137
- bloqade/squin/op/stmts.py,sha256=uBQeCWc1JjMTIVcssWkLWi6MyTLtQMoqTWoKgLsYgWQ,5262
137
+ bloqade/squin/op/stmts.py,sha256=atPrr12Bc7D5xu_xavVgME3FBNqHQXQSAtMuh7xL0Dw,5491
138
138
  bloqade/squin/op/traits.py,sha256=jjsnzWtPtmQK7K3H_D2fvc8XiW1Y3EMBcgeyPax2sjc,1065
139
139
  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
140
+ bloqade/squin/rewrite/U3_to_clifford.py,sha256=HpkFTqe-J0macb_aNs1QNs8wJDoUsmwf9Mtb0I3ZepI,5377
141
+ bloqade/squin/rewrite/__init__.py,sha256=mxP01A2IeOT5HKHKBRRMCIPN4BPKfpYSlQkSjY78ugQ,282
143
142
  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
143
+ bloqade/squin/rewrite/remove_dangling_qubits.py,sha256=iTuWV-03YW5wtYbSeKMlnnWjNzDj9SmflyqYPgoYGy8,469
144
+ bloqade/squin/rewrite/wrap_analysis.py,sha256=JUPS4OAYbDHOK0VIrdz1pprSizISUfN7osuoP_P-bIo,2256
145
+ bloqade/stim/__init__.py,sha256=-TkpVoISvZ-HERL2LJfIMHs3Y4k-WdrGWM-_Rwe9q0o,905
151
146
  bloqade/stim/_wrappers.py,sha256=KBIIOaeYPnhR3SJSNHcEzihLMcph2tL2mRKhEH47Je8,3939
152
147
  bloqade/stim/groups.py,sha256=Fx8G698BGO7hR8OwpPXGUEYdW4uCCPwbMp_3fJAqa8M,585
153
148
  bloqade/stim/dialects/__init__.py,sha256=A1Sq0jg8wi6MjRkzmuSBnHmO3EraD0pDFWz-dO6c6v8,89
@@ -159,7 +154,7 @@ bloqade/stim/dialects/auxiliary/lowering.py,sha256=4dxRZHGp_18EMVlYuZQFSzg4MIOBr
159
154
  bloqade/stim/dialects/auxiliary/types.py,sha256=1l2YdJ-u9y0wpBfxh4OAlvmRAUnrmcDjDagoW2GyVaQ,314
160
155
  bloqade/stim/dialects/auxiliary/stmts/__init__.py,sha256=VxLBnqlZngabs5vn3PiRNRXBBDueTCTmJ2wgWEertrA,371
161
156
  bloqade/stim/dialects/auxiliary/stmts/annotate.py,sha256=NZT4AAW0lmwyHR31cmZI8aQNRsp5soeE36s9nBrcOmU,1759
162
- bloqade/stim/dialects/auxiliary/stmts/const.py,sha256=nfx2aFTPBTCfKriANbzDUWjP9wgnWC5Phkfl2aUPyXY,3296
157
+ bloqade/stim/dialects/auxiliary/stmts/const.py,sha256=nxcpVylgjfKt8pDkcMzVBMGLjON2bQb99WSLv7frvkI,3298
163
158
  bloqade/stim/dialects/collapse/__init__.py,sha256=MhlEWJR_mJI3teoip1N8MlHk2C68seJELXwmyjyx_ns,305
164
159
  bloqade/stim/dialects/collapse/_dialect.py,sha256=F5uplH7WL9ZQatEp0ZMvgLWX1k9moAWW879LTNwnHH0,60
165
160
  bloqade/stim/dialects/collapse/emit_str.py,sha256=4Vlhtq8BKMMuZLQ-ayUGJTuOs4YJ1rGurPZBZxBQJcU,1879
@@ -170,7 +165,7 @@ bloqade/stim/dialects/collapse/stmts/reset.py,sha256=4fPSdM_ZiivuUn4uiZGiZQ9swol
170
165
  bloqade/stim/dialects/gate/__init__.py,sha256=5Y0BTlLOuW-Ud4LwAC_CUuMwxa04LKj4CTKgQbiL8yg,347
171
166
  bloqade/stim/dialects/gate/_dialect.py,sha256=78_dThliFZG9xSwYhTdibxP3vefHpEDUawtZqb9X3uA,56
172
167
  bloqade/stim/dialects/gate/emit.py,sha256=HGnko9_nV6jl0kOI_dBWWNQDjySo14jAFBsg-2lTVOU,2869
173
- bloqade/stim/dialects/gate/stmts/__init__.py,sha256=QJgmpArJwM9TXgB95AsEvet630gITtYOwRptnhj9a8U,293
168
+ bloqade/stim/dialects/gate/stmts/__init__.py,sha256=TbXjWYfCbZNuGEehvtF75LELLJJo_GzoXWRnuVZphv8,461
174
169
  bloqade/stim/dialects/gate/stmts/base.py,sha256=MK7Zu-Kys_64O0nXAkPzlHEnWYevBGm3FRHhJw33rrE,748
175
170
  bloqade/stim/dialects/gate/stmts/clifford_1q.py,sha256=Sj1Nf9AA_WWazGTg5kzFRTaLRIj3Edj657j0B8dybdI,896
176
171
  bloqade/stim/dialects/gate/stmts/clifford_2q.py,sha256=cb3BVSkOt5SiRq4D4WkbuUbEouK3MwDxQIhAF0cvZbo,239
@@ -184,6 +179,15 @@ bloqade/stim/emit/__init__.py,sha256=N2dPQY7OyqPwHAStDeOgYg2yfxqxMOz-N7pD5Z4JwlI
184
179
  bloqade/stim/emit/stim_str.py,sha256=JyEBoIhLQASogZcUWHI9tMD4JoXYrEqUr2qaZ30gZdc,1491
185
180
  bloqade/stim/parse/__init__.py,sha256=l2DjReB2KkgrDjP_4nP6RnoziiOewoSeZfTno1sVYTw,59
186
181
  bloqade/stim/parse/lowering.py,sha256=L-IcR_exlxsTVv4SQ0bhzIF4_L82P-GEdK6qRd6B86Y,23723
182
+ bloqade/stim/passes/__init__.py,sha256=KAWJPhZHf0cVDmr9o9LNRfZU8G9aS4adGC4EC7ci_E8,54
183
+ bloqade/stim/passes/squin_to_stim.py,sha256=4CSWAevECGxYgtojzlH92YwRUlz_lDtedWNEylXKd0A,2752
184
+ bloqade/stim/rewrite/__init__.py,sha256=S2sKUKtX4EGofhZRbewvyHN5s-CavPzseYufij3R5SQ,372
185
+ bloqade/stim/rewrite/py_constant_to_stim.py,sha256=PV8bHvn759-d_0JW4akaGSORW_oxigrlUBhAC51PJAU,1354
186
+ bloqade/stim/rewrite/qubit_to_stim.py,sha256=VggIAS6EQ4a01YplDGJUMDpQ_q-UYD8C4g1xMC_zbcY,2509
187
+ bloqade/stim/rewrite/squin_measure.py,sha256=1X3rcfGvVDSEXmvqlw4T0LQu2A8LFeNeqz1_zf_I8vA,2466
188
+ bloqade/stim/rewrite/util.py,sha256=vpBbTv00odpnnyHTMJZRStb1ZQYfcgbOW5PT5Frf4jw,5847
189
+ bloqade/stim/rewrite/wire_identity_elimination.py,sha256=Cscu8yaSslPuW04HvbXx4HJ3JzdUZNUMyFqcvuc4sxY,795
190
+ bloqade/stim/rewrite/wire_to_stim.py,sha256=vHtktd9pfjlEpInwN7pPhsIHCxTtHJJqJJhYpy0kIGg,1685
187
191
  bloqade/visual/__init__.py,sha256=Y7d0YgovKhUFzjMeDvt0wGRUZ3re3SY6iO3e8xHXrr4,37
188
192
  bloqade/visual/animation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
189
193
  bloqade/visual/animation/animate.py,sha256=NSKs3YDWgTZH2-Tpx3cP1bBcIEWTEPqK5UFkY_IF_3k,8927
@@ -195,7 +199,7 @@ bloqade/visual/animation/runtime/atoms.py,sha256=EmjxhujLiHHPS_HtH_B-7TiqeHgvW5u
195
199
  bloqade/visual/animation/runtime/ppoly.py,sha256=JB9IP53N1w6adBJEue6J5Nmj818Id9JvrlgrmiQTU1I,1385
196
200
  bloqade/visual/animation/runtime/qpustate.py,sha256=rlmxQeJSvaohXrTpXQL5y-NJcpvfW33xPaYM1slv7cc,4270
197
201
  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,,
202
+ bloqade_circuit-0.4.4.dist-info/METADATA,sha256=WNmsRDnheZ4YMfpb6skDmZ3tTRy_8eh8OwdyYXtCSwY,3683
203
+ bloqade_circuit-0.4.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
204
+ bloqade_circuit-0.4.4.dist-info/licenses/LICENSE,sha256=S5GIJwR6QCixPA9wryYb44ZEek0Nz4rt_zLUqP05UbU,13160
205
+ bloqade_circuit-0.4.4.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