bloqade-circuit 0.6.4__py3-none-any.whl → 0.9.1__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.
Files changed (191) hide show
  1. bloqade/analysis/address/__init__.py +8 -4
  2. bloqade/analysis/address/analysis.py +123 -33
  3. bloqade/analysis/address/impls.py +293 -90
  4. bloqade/analysis/address/lattice.py +209 -24
  5. bloqade/analysis/fidelity/analysis.py +11 -23
  6. bloqade/analysis/measure_id/analysis.py +18 -20
  7. bloqade/analysis/measure_id/impls.py +31 -29
  8. bloqade/annotate/__init__.py +6 -0
  9. bloqade/annotate/_dialect.py +3 -0
  10. bloqade/annotate/_interface.py +22 -0
  11. bloqade/annotate/stmts.py +29 -0
  12. bloqade/annotate/types.py +13 -0
  13. bloqade/cirq_utils/__init__.py +4 -2
  14. bloqade/cirq_utils/emit/__init__.py +3 -0
  15. bloqade/cirq_utils/emit/base.py +246 -0
  16. bloqade/cirq_utils/emit/gate.py +104 -0
  17. bloqade/cirq_utils/emit/noise.py +90 -0
  18. bloqade/cirq_utils/emit/qubit.py +35 -0
  19. bloqade/cirq_utils/lowering.py +660 -0
  20. bloqade/cirq_utils/noise/__init__.py +0 -2
  21. bloqade/cirq_utils/noise/_two_zone_utils.py +7 -15
  22. bloqade/cirq_utils/noise/model.py +151 -191
  23. bloqade/cirq_utils/noise/transform.py +2 -2
  24. bloqade/cirq_utils/parallelize.py +9 -6
  25. bloqade/gemini/__init__.py +1 -0
  26. bloqade/gemini/analysis/__init__.py +3 -0
  27. bloqade/gemini/analysis/logical_validation/__init__.py +1 -0
  28. bloqade/gemini/analysis/logical_validation/analysis.py +17 -0
  29. bloqade/gemini/analysis/logical_validation/impls.py +101 -0
  30. bloqade/gemini/groups.py +67 -0
  31. bloqade/native/__init__.py +23 -0
  32. bloqade/native/_prelude.py +45 -0
  33. bloqade/native/dialects/__init__.py +0 -0
  34. bloqade/native/dialects/gate/__init__.py +2 -0
  35. bloqade/native/dialects/gate/_dialect.py +3 -0
  36. bloqade/native/dialects/gate/_interface.py +32 -0
  37. bloqade/native/dialects/gate/stmts.py +31 -0
  38. bloqade/native/stdlib/__init__.py +0 -0
  39. bloqade/native/stdlib/broadcast.py +246 -0
  40. bloqade/native/stdlib/simple.py +220 -0
  41. bloqade/native/upstream/__init__.py +4 -0
  42. bloqade/native/upstream/squin2native.py +79 -0
  43. bloqade/pyqrack/__init__.py +2 -2
  44. bloqade/pyqrack/base.py +7 -1
  45. bloqade/pyqrack/device.py +192 -18
  46. bloqade/pyqrack/native.py +49 -0
  47. bloqade/pyqrack/reg.py +6 -6
  48. bloqade/pyqrack/squin/gate/__init__.py +1 -0
  49. bloqade/pyqrack/squin/gate/gate.py +136 -0
  50. bloqade/pyqrack/squin/noise/native.py +120 -54
  51. bloqade/pyqrack/squin/qubit.py +39 -36
  52. bloqade/pyqrack/target.py +5 -4
  53. bloqade/pyqrack/task.py +114 -7
  54. bloqade/qasm2/_qasm_loading.py +3 -3
  55. bloqade/qasm2/dialects/core/address.py +21 -12
  56. bloqade/qasm2/dialects/expr/_emit.py +19 -8
  57. bloqade/qasm2/dialects/expr/stmts.py +7 -7
  58. bloqade/qasm2/dialects/noise/fidelity.py +4 -8
  59. bloqade/qasm2/dialects/noise/model.py +2 -1
  60. bloqade/qasm2/emit/base.py +16 -11
  61. bloqade/qasm2/emit/gate.py +11 -8
  62. bloqade/qasm2/emit/main.py +103 -3
  63. bloqade/qasm2/emit/target.py +9 -5
  64. bloqade/qasm2/groups.py +3 -2
  65. bloqade/qasm2/parse/lowering.py +0 -1
  66. bloqade/qasm2/passes/fold.py +14 -73
  67. bloqade/qasm2/passes/glob.py +2 -2
  68. bloqade/qasm2/passes/noise.py +1 -1
  69. bloqade/qasm2/passes/parallel.py +7 -5
  70. bloqade/qasm2/rewrite/__init__.py +0 -1
  71. bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
  72. bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
  73. bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
  74. bloqade/qasm2/rewrite/register.py +2 -2
  75. bloqade/qasm2/rewrite/uop_to_parallel.py +4 -2
  76. bloqade/qbraid/lowering.py +1 -0
  77. bloqade/qbraid/schema.py +2 -2
  78. bloqade/qubit/__init__.py +12 -0
  79. bloqade/qubit/_dialect.py +3 -0
  80. bloqade/qubit/_interface.py +49 -0
  81. bloqade/qubit/_prelude.py +45 -0
  82. bloqade/qubit/analysis/__init__.py +1 -0
  83. bloqade/qubit/analysis/address_impl.py +40 -0
  84. bloqade/qubit/stdlib/__init__.py +2 -0
  85. bloqade/qubit/stdlib/_new.py +34 -0
  86. bloqade/qubit/stdlib/broadcast.py +62 -0
  87. bloqade/qubit/stdlib/simple.py +59 -0
  88. bloqade/qubit/stmts.py +60 -0
  89. bloqade/rewrite/passes/__init__.py +6 -0
  90. bloqade/rewrite/passes/aggressive_unroll.py +103 -0
  91. bloqade/rewrite/passes/callgraph.py +116 -0
  92. bloqade/rewrite/passes/canonicalize_ilist.py +20 -14
  93. bloqade/rewrite/rules/split_ifs.py +18 -1
  94. bloqade/squin/__init__.py +47 -14
  95. bloqade/squin/analysis/__init__.py +0 -1
  96. bloqade/squin/analysis/schedule.py +10 -11
  97. bloqade/squin/gate/__init__.py +2 -0
  98. bloqade/squin/gate/_dialect.py +3 -0
  99. bloqade/squin/gate/_interface.py +98 -0
  100. bloqade/squin/gate/stmts.py +125 -0
  101. bloqade/squin/groups.py +5 -22
  102. bloqade/squin/noise/__init__.py +1 -10
  103. bloqade/squin/noise/_dialect.py +1 -1
  104. bloqade/squin/noise/_interface.py +45 -0
  105. bloqade/squin/noise/stmts.py +66 -28
  106. bloqade/squin/rewrite/U3_to_clifford.py +70 -51
  107. bloqade/squin/rewrite/__init__.py +0 -2
  108. bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
  109. bloqade/squin/rewrite/wrap_analysis.py +4 -35
  110. bloqade/squin/stdlib/__init__.py +0 -0
  111. bloqade/squin/stdlib/broadcast/__init__.py +34 -0
  112. bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
  113. bloqade/squin/stdlib/broadcast/gate.py +260 -0
  114. bloqade/squin/stdlib/broadcast/noise.py +144 -0
  115. bloqade/squin/stdlib/simple/__init__.py +33 -0
  116. bloqade/squin/stdlib/simple/gate.py +242 -0
  117. bloqade/squin/stdlib/simple/noise.py +126 -0
  118. bloqade/stim/__init__.py +1 -0
  119. bloqade/stim/_wrappers.py +6 -0
  120. bloqade/stim/dialects/auxiliary/emit.py +19 -18
  121. bloqade/stim/dialects/collapse/emit_str.py +7 -8
  122. bloqade/stim/dialects/gate/emit.py +9 -10
  123. bloqade/stim/dialects/noise/emit.py +17 -13
  124. bloqade/stim/dialects/noise/stmts.py +5 -3
  125. bloqade/stim/emit/__init__.py +1 -0
  126. bloqade/stim/emit/impls.py +16 -0
  127. bloqade/stim/emit/stim_str.py +48 -31
  128. bloqade/stim/groups.py +12 -2
  129. bloqade/stim/parse/lowering.py +14 -17
  130. bloqade/stim/passes/__init__.py +0 -2
  131. bloqade/stim/passes/flatten.py +26 -0
  132. bloqade/stim/passes/simplify_ifs.py +6 -1
  133. bloqade/stim/passes/squin_to_stim.py +9 -84
  134. bloqade/stim/rewrite/__init__.py +2 -4
  135. bloqade/stim/rewrite/get_record_util.py +24 -0
  136. bloqade/stim/rewrite/ifs_to_stim.py +24 -25
  137. bloqade/stim/rewrite/qubit_to_stim.py +90 -41
  138. bloqade/stim/rewrite/set_detector_to_stim.py +68 -0
  139. bloqade/stim/rewrite/set_observable_to_stim.py +52 -0
  140. bloqade/stim/rewrite/squin_measure.py +9 -18
  141. bloqade/stim/rewrite/squin_noise.py +134 -108
  142. bloqade/stim/rewrite/util.py +5 -192
  143. bloqade/test_utils.py +1 -1
  144. bloqade/types.py +10 -0
  145. bloqade/validation/__init__.py +2 -0
  146. bloqade/validation/analysis/__init__.py +5 -0
  147. bloqade/validation/analysis/analysis.py +41 -0
  148. bloqade/validation/analysis/lattice.py +58 -0
  149. bloqade/validation/kernel_validation.py +77 -0
  150. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/METADATA +5 -6
  151. bloqade_circuit-0.9.1.dist-info/RECORD +265 -0
  152. bloqade/pyqrack/squin/op.py +0 -180
  153. bloqade/pyqrack/squin/runtime.py +0 -535
  154. bloqade/pyqrack/squin/wire.py +0 -51
  155. bloqade/rewrite/rules/flatten_ilist.py +0 -51
  156. bloqade/rewrite/rules/inline_getitem_ilist.py +0 -31
  157. bloqade/squin/_typeinfer.py +0 -20
  158. bloqade/squin/analysis/address_impl.py +0 -71
  159. bloqade/squin/analysis/nsites/__init__.py +0 -9
  160. bloqade/squin/analysis/nsites/analysis.py +0 -50
  161. bloqade/squin/analysis/nsites/impls.py +0 -92
  162. bloqade/squin/analysis/nsites/lattice.py +0 -49
  163. bloqade/squin/cirq/__init__.py +0 -280
  164. bloqade/squin/cirq/emit/emit_circuit.py +0 -109
  165. bloqade/squin/cirq/emit/noise.py +0 -49
  166. bloqade/squin/cirq/emit/op.py +0 -125
  167. bloqade/squin/cirq/emit/qubit.py +0 -60
  168. bloqade/squin/cirq/emit/runtime.py +0 -242
  169. bloqade/squin/cirq/lowering.py +0 -440
  170. bloqade/squin/lowering.py +0 -54
  171. bloqade/squin/noise/_wrapper.py +0 -40
  172. bloqade/squin/noise/rewrite.py +0 -111
  173. bloqade/squin/op/__init__.py +0 -41
  174. bloqade/squin/op/_dialect.py +0 -3
  175. bloqade/squin/op/_wrapper.py +0 -121
  176. bloqade/squin/op/number.py +0 -5
  177. bloqade/squin/op/rewrite.py +0 -46
  178. bloqade/squin/op/stdlib.py +0 -62
  179. bloqade/squin/op/stmts.py +0 -276
  180. bloqade/squin/op/traits.py +0 -43
  181. bloqade/squin/op/types.py +0 -26
  182. bloqade/squin/qubit.py +0 -184
  183. bloqade/squin/rewrite/canonicalize.py +0 -60
  184. bloqade/squin/rewrite/desugar.py +0 -124
  185. bloqade/squin/types.py +0 -8
  186. bloqade/squin/wire.py +0 -201
  187. bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
  188. bloqade/stim/rewrite/wire_to_stim.py +0 -57
  189. bloqade_circuit-0.6.4.dist-info/RECORD +0 -234
  190. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/WHEEL +0 -0
  191. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/licenses/LICENSE +0 -0
bloqade/squin/qubit.py DELETED
@@ -1,184 +0,0 @@
1
- """qubit dialect for squin language.
2
-
3
- This dialect defines the operations that can be performed on qubits.
4
-
5
- Depends on:
6
- - `bloqade.squin.op`: provides the `OpType` type and semantics for operators applied to qubits.
7
- - `kirin.dialects.ilist`: provides the `ilist.IListType` type for lists of qubits.
8
- """
9
-
10
- from typing import Any, overload
11
-
12
- from kirin import ir, types, lowering
13
- from kirin.decl import info, statement
14
- from kirin.dialects import ilist
15
- from kirin.lowering import wraps
16
-
17
- from bloqade.types import Qubit, QubitType
18
- from bloqade.squin.op.types import Op, OpType
19
-
20
- from .types import MeasurementResult, MeasurementResultType
21
- from .lowering import ApplyAnyCallLowering
22
-
23
- dialect = ir.Dialect("squin.qubit")
24
-
25
-
26
- @statement(dialect=dialect)
27
- class New(ir.Statement):
28
- traits = frozenset({lowering.FromPythonCall()})
29
- n_qubits: ir.SSAValue = info.argument(types.Int)
30
- result: ir.ResultValue = info.result(ilist.IListType[QubitType, types.Any])
31
-
32
-
33
- @statement(dialect=dialect)
34
- class Apply(ir.Statement):
35
- traits = frozenset({lowering.FromPythonCall()})
36
- operator: ir.SSAValue = info.argument(OpType)
37
- qubits: ir.SSAValue = info.argument(ilist.IListType[QubitType])
38
-
39
-
40
- @statement(dialect=dialect)
41
- class ApplyAny(ir.Statement):
42
- # NOTE: custom lowering to deal with vararg calls
43
- traits = frozenset({ApplyAnyCallLowering()})
44
- operator: ir.SSAValue = info.argument(OpType)
45
- qubits: tuple[ir.SSAValue, ...] = info.argument()
46
-
47
-
48
- @statement(dialect=dialect)
49
- class Broadcast(ir.Statement):
50
- traits = frozenset({lowering.FromPythonCall()})
51
- operator: ir.SSAValue = info.argument(OpType)
52
- qubits: ir.SSAValue = info.argument(ilist.IListType[QubitType])
53
-
54
-
55
- @statement(dialect=dialect)
56
- class MeasureAny(ir.Statement):
57
- name = "measure"
58
-
59
- traits = frozenset({lowering.FromPythonCall()})
60
- input: ir.SSAValue = info.argument(types.Any)
61
- result: ir.ResultValue = info.result(types.Any)
62
-
63
-
64
- @statement(dialect=dialect)
65
- class MeasureQubit(ir.Statement):
66
- name = "measure.qubit"
67
-
68
- traits = frozenset({lowering.FromPythonCall()})
69
- qubit: ir.SSAValue = info.argument(QubitType)
70
- result: ir.ResultValue = info.result(MeasurementResultType)
71
-
72
-
73
- @statement(dialect=dialect)
74
- class MeasureQubitList(ir.Statement):
75
- name = "measure.qubit.list"
76
-
77
- traits = frozenset({lowering.FromPythonCall()})
78
- qubits: ir.SSAValue = info.argument(ilist.IListType[QubitType])
79
- result: ir.ResultValue = info.result(ilist.IListType[MeasurementResultType])
80
-
81
-
82
- # NOTE: no dependent types in Python, so we have to mark it Any...
83
- @wraps(New)
84
- def new(n_qubits: int) -> ilist.IList[Qubit, Any]:
85
- """Create a new list of qubits.
86
-
87
- Args:
88
- n_qubits(int): The number of qubits to create.
89
-
90
- Returns:
91
- (ilist.IList[Qubit, n_qubits]) A list of qubits.
92
- """
93
- ...
94
-
95
-
96
- @overload
97
- def apply(operator: Op, qubits: ilist.IList[Qubit, Any] | list[Qubit]) -> None:
98
- """Apply an operator to a list of qubits.
99
-
100
- Note, that when considering atom loss, lost qubits will be skipped.
101
-
102
- Args:
103
- operator: The operator to apply.
104
- qubits: The list of qubits to apply the operator to. The size of the list
105
- must be inferable and match the number of qubits expected by the operator.
106
-
107
- Returns:
108
- None
109
- """
110
- ...
111
-
112
-
113
- @overload
114
- def apply(operator: Op, *qubits: Qubit) -> None:
115
- """Apply and operator to any number of qubits.
116
-
117
- Note, that when considering atom loss, lost qubits will be skipped.
118
-
119
- Args:
120
- operator: The operator to apply.
121
- *qubits: The qubits to apply the operator to. The number of qubits must
122
- match the size of the operator.
123
-
124
- Returns:
125
- None
126
- """
127
- ...
128
-
129
-
130
- @wraps(ApplyAny)
131
- def apply(operator: Op, *qubits) -> None: ...
132
-
133
-
134
- @overload
135
- def measure(input: Qubit) -> MeasurementResult: ...
136
- @overload
137
- def measure(
138
- input: ilist.IList[Qubit, Any] | list[Qubit],
139
- ) -> ilist.IList[MeasurementResult, Any]: ...
140
-
141
-
142
- @wraps(MeasureAny)
143
- def measure(input: Any) -> Any:
144
- """Measure a qubit or qubits in the list.
145
-
146
- Args:
147
- input: A qubit or a list of qubits to measure.
148
-
149
- Returns:
150
- bool | list[bool]: The result of the measurement. If a single qubit is measured,
151
- a single boolean is returned. If a list of qubits is measured, a list of booleans
152
- is returned.
153
- """
154
- ...
155
-
156
-
157
- @wraps(Broadcast)
158
- def broadcast(operator: Op, qubits: ilist.IList[Qubit, Any] | list[Qubit]) -> None:
159
- """Broadcast and apply an operator to a list of qubits. For example, an operator
160
- that expects 2 qubits can be applied to a list of 2n qubits, where n is an integer > 0.
161
-
162
- For controlled operators, the list of qubits is interpreted as sets of (controls, targets).
163
- For example
164
-
165
- ```
166
- apply(CX, [q0, q1, q2, q3])
167
- ```
168
-
169
- is equivalent to
170
-
171
- ```
172
- apply(CX, [q0, q1])
173
- apply(CX, [q2, q3])
174
- ```
175
-
176
- Args:
177
- operator: The operator to broadcast and apply.
178
- qubits: The list of qubits to broadcast and apply the operator to. The size of the list
179
- must be inferable and match the number of qubits expected by the operator.
180
-
181
- Returns:
182
- None
183
- """
184
- ...
@@ -1,60 +0,0 @@
1
- from typing import cast
2
-
3
- from kirin import ir
4
- from kirin.rewrite import abc
5
- from kirin.dialects import cf
6
-
7
- from .. import wire
8
-
9
-
10
- class CanonicalizeWired(abc.RewriteRule):
11
- def rewrite_Statement(self, node: ir.Statement) -> abc.RewriteResult:
12
-
13
- if (
14
- not isinstance(node, wire.Wired)
15
- or len(node.qubits) != 0
16
- or (parent_region := node.parent_region) is None
17
- ):
18
- return abc.RewriteResult()
19
-
20
- parent_block = cast(ir.Block, node.parent_block)
21
-
22
- # the body doesn't contain any quantum operations so we can safely inline the
23
- # body into the parent block
24
-
25
- # move all statements after `node` in the current block into another block
26
- after_block = ir.Block()
27
-
28
- stmt = node.next_stmt
29
- while stmt is not None:
30
- stmt.detach()
31
- after_block.stmts.append(stmt)
32
- stmt = node.next_stmt
33
-
34
- # remap all results of the node to the arguments of the after_block
35
- for result in node.results:
36
- arg = after_block.args.append_from(result.type, result.name)
37
- result.replace_by(arg)
38
-
39
- parent_block_idx = parent_region._block_idx[parent_block]
40
- # insert goto of parent block to the body block of the node.
41
- parent_region.blocks.insert(parent_block_idx + 1, after_block)
42
- # insert all blocks of the body of the node after the parent region
43
- # making sure to convert any yield statements to jump statements to the after_block
44
- parent_block.stmts.append(
45
- cf.Branch(
46
- arguments=(),
47
- successor=node.body.blocks[0],
48
- )
49
- )
50
- for block in reversed(node.body.blocks):
51
- block.detach()
52
- if isinstance((yield_stmt := block.last_stmt), wire.Yield):
53
- yield_stmt.replace_by(
54
- cf.Branch(yield_stmt.values, successor=after_block)
55
- )
56
-
57
- parent_region.blocks.insert(parent_block_idx + 1, block)
58
-
59
- node.delete()
60
- return abc.RewriteResult(has_done_something=True)
@@ -1,124 +0,0 @@
1
- from kirin import ir, types
2
- from kirin.dialects import py, ilist
3
- from kirin.rewrite.abc import RewriteRule, RewriteResult
4
-
5
- from bloqade.squin.qubit import (
6
- Apply,
7
- ApplyAny,
8
- QubitType,
9
- MeasureAny,
10
- MeasureQubit,
11
- MeasureQubitList,
12
- )
13
-
14
-
15
- class MeasureDesugarRule(RewriteRule):
16
- """
17
- Desugar measure operations in the circuit.
18
- """
19
-
20
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
21
-
22
- if not isinstance(node, MeasureAny):
23
- return RewriteResult()
24
-
25
- if node.input.type.is_subseteq(QubitType):
26
- node.replace_by(
27
- MeasureQubit(
28
- qubit=node.input,
29
- )
30
- )
31
- return RewriteResult(has_done_something=True)
32
- elif node.input.type.is_subseteq(ilist.IListType[QubitType, types.Any]):
33
- node.replace_by(
34
- MeasureQubitList(
35
- qubits=node.input,
36
- )
37
- )
38
- return RewriteResult(has_done_something=True)
39
-
40
- return RewriteResult()
41
-
42
-
43
- class ApplyDesugarRule(RewriteRule):
44
- """
45
- Desugar apply operators in the kernel.
46
- """
47
-
48
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
49
-
50
- if not isinstance(node, ApplyAny):
51
- return RewriteResult()
52
-
53
- op = node.operator
54
- qubits = node.qubits
55
-
56
- if len(qubits) > 1 and all(q.type.is_subseteq(QubitType) for q in qubits):
57
- (qubits_ilist_stmt := ilist.New(qubits)).insert_before(
58
- node
59
- ) # qubits is just a tuple of SSAValues
60
- qubits_ilist = qubits_ilist_stmt.result
61
-
62
- elif len(qubits) == 1 and qubits[0].type.is_subseteq(QubitType):
63
- (qubits_ilist_stmt := ilist.New(qubits)).insert_before(node)
64
- qubits_ilist = qubits_ilist_stmt.result
65
-
66
- elif len(qubits) == 1 and qubits[0].type.is_subseteq(
67
- ilist.IListType[QubitType, types.Any]
68
- ):
69
- qubits_ilist = qubits[0]
70
-
71
- elif len(qubits) == 1:
72
- # TODO: remove this elif clause once we're at kirin v0.18
73
- # NOTE: this is a temporary workaround for kirin#408
74
- # currently type inference fails here in for loops since the loop var
75
- # is an IList for some reason
76
-
77
- if not isinstance(qubits[0], ir.ResultValue):
78
- return RewriteResult()
79
-
80
- is_ilist = isinstance(qbit_stmt := qubits[0].stmt, ilist.New)
81
-
82
- if is_ilist:
83
-
84
- if not all(
85
- isinstance(qbit_getindex_result, ir.ResultValue)
86
- for qbit_getindex_result in qbit_stmt.values
87
- ):
88
- return RewriteResult()
89
-
90
- # Get the parent statement that the qubit came from
91
- # (should be a GetItem instance, see logic below)
92
- qbit_getindices = [
93
- qbit_getindex_result.stmt
94
- for qbit_getindex_result in qbit_stmt.values
95
- ]
96
- else:
97
- qbit_getindices = [qubit.stmt for qubit in qubits]
98
-
99
- if any(
100
- not isinstance(qbit_getindex, py.indexing.GetItem)
101
- for qbit_getindex in qbit_getindices
102
- ):
103
- return RewriteResult()
104
-
105
- # The GetItem should have been applied on something that returns an IList of Qubits
106
- if any(
107
- not qbit_getindex.obj.type.is_subseteq(
108
- ilist.IListType[QubitType, types.Any]
109
- )
110
- for qbit_getindex in qbit_getindices
111
- ):
112
- return RewriteResult()
113
-
114
- if is_ilist:
115
- qubits_ilist = qbit_stmt.result
116
- else:
117
- (qubits_ilist_stmt := ilist.New(values=[qubits[0]])).insert_before(node)
118
- qubits_ilist = qubits_ilist_stmt.result
119
- else:
120
- return RewriteResult()
121
-
122
- stmt = Apply(operator=op, qubits=qubits_ilist)
123
- node.replace_by(stmt)
124
- return RewriteResult(has_done_something=True)
bloqade/squin/types.py DELETED
@@ -1,8 +0,0 @@
1
- from kirin import types
2
-
3
-
4
- class MeasurementResult:
5
- pass
6
-
7
-
8
- MeasurementResultType = types.PyClass(MeasurementResult)
bloqade/squin/wire.py DELETED
@@ -1,201 +0,0 @@
1
- """A NVIDIA QUAKE-like wire dialect.
2
-
3
- This dialect is expected to be used in combination with the operator dialect
4
- as an intermediate representation for analysis and optimization of quantum
5
- circuits. Thus we do not define wrapping functions for the statements in this
6
- dialect.
7
- """
8
-
9
- from kirin import ir, types, lowering, exception
10
- from kirin.decl import info, statement
11
- from kirin.dialects import func
12
- from kirin.lowering import wraps
13
- from kirin.ir.attrs.types import TypeAttribute
14
-
15
- from bloqade.types import Qubit, QubitType
16
-
17
- from .types import MeasurementResultType
18
- from .op.types import Op, OpType
19
-
20
- # from kirin.lowering import wraps
21
-
22
- # from .op.types import Op, OpType
23
-
24
- dialect = ir.Dialect("squin.wire")
25
-
26
-
27
- class WireTerminator(ir.StmtTrait):
28
- pass
29
-
30
-
31
- class Wire:
32
- pass
33
-
34
-
35
- WireType = types.PyClass(Wire)
36
-
37
-
38
- # no return value for `wrap`
39
- @statement(dialect=dialect)
40
- class Wrap(ir.Statement):
41
- traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
42
- wire: ir.SSAValue = info.argument(WireType)
43
- qubit: ir.SSAValue = info.argument(QubitType)
44
-
45
-
46
- # "Unwrap the quantum references to expose wires" -> From Quake Dialect documentation
47
- # Unwrap(Qubit) -> Wire
48
- @statement(dialect=dialect)
49
- class Unwrap(ir.Statement):
50
- traits = frozenset({lowering.FromPythonCall(), ir.Pure()})
51
- qubit: ir.SSAValue = info.argument(QubitType)
52
- result: ir.ResultValue = info.result(WireType)
53
-
54
-
55
- @statement(dialect=dialect)
56
- class Wired(ir.Statement):
57
- traits = frozenset()
58
-
59
- qubits: tuple[ir.SSAValue, ...] = info.argument(QubitType)
60
- memory_zone: str = info.attribute()
61
- body: ir.Region = info.region(multi=True)
62
-
63
- def __init__(
64
- self,
65
- body: ir.Region,
66
- *qubits: ir.SSAValue,
67
- memory_zone: str,
68
- result_types: tuple[TypeAttribute, ...] | None = None,
69
- ):
70
- if result_types is None:
71
- for block in body.blocks:
72
- if isinstance(block.last_stmt, Yield):
73
- result_types = tuple(arg.type for arg in block.last_stmt.values)
74
- break
75
-
76
- if result_types is None:
77
- result_types = ()
78
-
79
- super().__init__(
80
- args=qubits,
81
- args_slice={
82
- "qubits": slice(0, None),
83
- },
84
- regions=[body],
85
- attributes={
86
- "memory_zone": ir.PyAttr(memory_zone)
87
- }, # body of the wired statement
88
- result_types=result_types,
89
- )
90
-
91
- def check(self):
92
- entry_block = self.body.blocks[0]
93
-
94
- if len(entry_block.args) != len(self.qubits):
95
- raise exception.StaticCheckError(
96
- f"Expected {len(self.qubits)} arguments, got {len(entry_block.args)}."
97
- )
98
- for arg in entry_block.args:
99
- if not arg.type.is_subseteq(WireType):
100
- raise exception.StaticCheckError(
101
- f"Expected argument of type {WireType}, got {arg.type}."
102
- )
103
- for block in self.body.blocks:
104
- last_stmt = block.last_stmt
105
- if isinstance(last_stmt, func.Return):
106
- raise exception.StaticCheckError(
107
- "Return statements are not allowed in the body of a Wired statement."
108
- )
109
- elif isinstance(last_stmt, Yield) and len(last_stmt.values) != len(
110
- self.results
111
- ):
112
- raise exception.StaticCheckError(
113
- f"Expected {len(self.results)} return values, got {len(last_stmt.values)}."
114
- )
115
-
116
-
117
- @statement(dialect=dialect)
118
- class Yield(ir.Statement):
119
- traits = frozenset({})
120
- values: tuple[ir.SSAValue, ...] = info.argument(WireType)
121
-
122
- def __init__(self, *args: ir.SSAValue):
123
- super().__init__(
124
- args=args,
125
- args_slice={
126
- "values": slice(0, None),
127
- },
128
- )
129
-
130
-
131
- # In Quake, you put a wire in and get a wire out when you "apply" an operator
132
- # In this case though we just need to indicate that an operator is applied to list[wires]
133
- @statement(dialect=dialect)
134
- class Apply(ir.Statement): # apply(op, w1, w2, ...)
135
- traits = frozenset({lowering.FromPythonCall()})
136
- operator: ir.SSAValue = info.argument(OpType)
137
- inputs: tuple[ir.SSAValue, ...] = info.argument(WireType)
138
-
139
- def __init__(self, operator: ir.SSAValue, *args: ir.SSAValue):
140
- result_types = tuple(WireType for _ in args)
141
- super().__init__(
142
- args=(operator,) + args,
143
- result_types=result_types, # result types of the Apply statement, should all be WireTypes
144
- args_slice={
145
- "operator": 0,
146
- "inputs": slice(1, None),
147
- }, # pretty printing + syntax sugar
148
- ) # custom lowering required for wrapper to work here
149
-
150
-
151
- # Carry over from Qubit dialect
152
- @statement(dialect=dialect)
153
- class Broadcast(ir.Statement):
154
- traits = frozenset({lowering.FromPythonCall(), ir.Pure()})
155
- operator: ir.SSAValue = info.argument(OpType)
156
- inputs: tuple[ir.SSAValue, ...] = info.argument(WireType)
157
-
158
- def __init__(self, operator: ir.SSAValue, *args: ir.SSAValue):
159
- result_types = tuple(WireType for _ in args)
160
- super().__init__(
161
- args=(operator,) + args,
162
- result_types=result_types,
163
- args_slice={
164
- "operator": 0,
165
- "inputs": slice(1, None),
166
- }, # pretty printing + syntax sugar
167
- ) # custom lowering required for wrapper to work here
168
-
169
-
170
- @statement(dialect=dialect)
171
- class RegionMeasure(ir.Statement):
172
- traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
173
- wire: ir.SSAValue = info.argument(WireType)
174
- result: ir.ResultValue = info.result(MeasurementResultType)
175
-
176
-
177
- # NOTE: measurement cannot be pure because they will collapse the state
178
- # of the qubit. The state is a hidden state that is not visible to
179
- # the user in the wire dialect.
180
- @statement(dialect=dialect)
181
- class Measure(ir.Statement):
182
- traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
183
- wire: ir.SSAValue = info.argument(WireType)
184
- qubit: ir.SSAValue = info.argument(QubitType)
185
- result: ir.ResultValue = info.result(MeasurementResultType)
186
-
187
-
188
- @statement(dialect=dialect)
189
- class LossResolvingMeasure(ir.Statement):
190
- traits = frozenset({lowering.FromPythonCall()})
191
- input_wire: ir.SSAValue = info.argument(WireType)
192
- result: ir.ResultValue = info.result(MeasurementResultType)
193
- out_wire: ir.ResultValue = info.result(WireType)
194
-
195
-
196
- @wraps(Unwrap)
197
- def unwrap(qubit: Qubit) -> Wire: ...
198
-
199
-
200
- @wraps(Apply)
201
- def apply(op: Op, w: Wire) -> Wire: ...
@@ -1,24 +0,0 @@
1
- from kirin import ir
2
- from kirin.rewrite.abc import RewriteRule, RewriteResult
3
-
4
- from bloqade.squin import wire
5
-
6
-
7
- class SquinWireIdentityElimination(RewriteRule):
8
-
9
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
10
- """
11
- Handle the case where an unwrap feeds a wire directly into a wrap,
12
- equivalent to nothing happening/identity operation
13
-
14
- w = unwrap(qubit)
15
- wrap(qubit, w)
16
- """
17
- if isinstance(node, wire.Wrap):
18
- wire_origin_stmt = node.wire.owner
19
- if isinstance(wire_origin_stmt, wire.Unwrap):
20
- node.delete() # get rid of wrap
21
- wire_origin_stmt.delete() # get rid of the unwrap
22
- return RewriteResult(has_done_something=True)
23
-
24
- return RewriteResult()
@@ -1,57 +0,0 @@
1
- from kirin import ir
2
- from kirin.rewrite.abc import RewriteRule, RewriteResult
3
-
4
- from bloqade.squin import op, wire, noise
5
- from bloqade.stim.rewrite.util import (
6
- SQUIN_STIM_OP_MAPPING,
7
- rewrite_Control,
8
- rewrite_QubitLoss,
9
- insert_qubit_idx_from_wire_ssa,
10
- )
11
-
12
-
13
- class SquinWireToStim(RewriteRule):
14
-
15
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
16
- match node:
17
- case wire.Apply() | wire.Broadcast():
18
- return self.rewrite_Apply_and_Broadcast(node)
19
- case _:
20
- return RewriteResult()
21
-
22
- def rewrite_Apply_and_Broadcast(
23
- self, stmt: wire.Apply | wire.Broadcast
24
- ) -> RewriteResult:
25
-
26
- # this is an SSAValue, need it to be the actual operator
27
- applied_op = stmt.operator.owner
28
-
29
- if isinstance(applied_op, noise.stmts.QubitLoss):
30
- return rewrite_QubitLoss(stmt)
31
-
32
- assert isinstance(applied_op, op.stmts.Operator)
33
-
34
- if isinstance(applied_op, op.stmts.Control):
35
- return rewrite_Control(stmt)
36
-
37
- stim_1q_op = SQUIN_STIM_OP_MAPPING.get(type(applied_op))
38
- if stim_1q_op is None:
39
- return RewriteResult()
40
-
41
- qubit_idx_ssas = insert_qubit_idx_from_wire_ssa(
42
- wire_ssas=stmt.inputs, stmt_to_insert_before=stmt
43
- )
44
- if qubit_idx_ssas is None:
45
- return RewriteResult()
46
-
47
- stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas))
48
-
49
- # Get the wires from the inputs of Apply or Broadcast,
50
- # then put those as the result of the current stmt
51
- # before replacing it entirely
52
- for input_wire, output_wire in zip(stmt.inputs, stmt.results):
53
- output_wire.replace_by(input_wire)
54
-
55
- stmt.replace_by(stim_1q_stmt)
56
-
57
- return RewriteResult(has_done_something=True)