bloqade-circuit 0.1.0__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 (153) hide show
  1. bloqade/analysis/__init__.py +0 -0
  2. bloqade/analysis/address/__init__.py +11 -0
  3. bloqade/analysis/address/analysis.py +60 -0
  4. bloqade/analysis/address/impls.py +228 -0
  5. bloqade/analysis/address/lattice.py +85 -0
  6. bloqade/noise/__init__.py +1 -0
  7. bloqade/noise/native/__init__.py +20 -0
  8. bloqade/noise/native/_dialect.py +3 -0
  9. bloqade/noise/native/_wrappers.py +34 -0
  10. bloqade/noise/native/model.py +347 -0
  11. bloqade/noise/native/rewrite.py +35 -0
  12. bloqade/noise/native/stmts.py +46 -0
  13. bloqade/pyqrack/__init__.py +18 -0
  14. bloqade/pyqrack/base.py +131 -0
  15. bloqade/pyqrack/noise/__init__.py +0 -0
  16. bloqade/pyqrack/noise/native.py +100 -0
  17. bloqade/pyqrack/qasm2/__init__.py +0 -0
  18. bloqade/pyqrack/qasm2/core.py +79 -0
  19. bloqade/pyqrack/qasm2/parallel.py +46 -0
  20. bloqade/pyqrack/qasm2/uop.py +247 -0
  21. bloqade/pyqrack/reg.py +109 -0
  22. bloqade/pyqrack/target.py +112 -0
  23. bloqade/qasm2/__init__.py +19 -0
  24. bloqade/qasm2/_wrappers.py +674 -0
  25. bloqade/qasm2/dialects/__init__.py +10 -0
  26. bloqade/qasm2/dialects/core/__init__.py +3 -0
  27. bloqade/qasm2/dialects/core/_dialect.py +3 -0
  28. bloqade/qasm2/dialects/core/_emit.py +68 -0
  29. bloqade/qasm2/dialects/core/_typeinfer.py +23 -0
  30. bloqade/qasm2/dialects/core/address.py +38 -0
  31. bloqade/qasm2/dialects/core/stmts.py +94 -0
  32. bloqade/qasm2/dialects/expr/__init__.py +3 -0
  33. bloqade/qasm2/dialects/expr/_dialect.py +3 -0
  34. bloqade/qasm2/dialects/expr/_emit.py +103 -0
  35. bloqade/qasm2/dialects/expr/_from_python.py +86 -0
  36. bloqade/qasm2/dialects/expr/_interp.py +75 -0
  37. bloqade/qasm2/dialects/expr/stmts.py +262 -0
  38. bloqade/qasm2/dialects/glob.py +45 -0
  39. bloqade/qasm2/dialects/indexing.py +64 -0
  40. bloqade/qasm2/dialects/inline.py +76 -0
  41. bloqade/qasm2/dialects/noise.py +16 -0
  42. bloqade/qasm2/dialects/parallel.py +110 -0
  43. bloqade/qasm2/dialects/uop/__init__.py +4 -0
  44. bloqade/qasm2/dialects/uop/_dialect.py +3 -0
  45. bloqade/qasm2/dialects/uop/_emit.py +211 -0
  46. bloqade/qasm2/dialects/uop/schedule.py +89 -0
  47. bloqade/qasm2/dialects/uop/stmts.py +325 -0
  48. bloqade/qasm2/emit/__init__.py +1 -0
  49. bloqade/qasm2/emit/base.py +72 -0
  50. bloqade/qasm2/emit/gate.py +102 -0
  51. bloqade/qasm2/emit/main.py +106 -0
  52. bloqade/qasm2/emit/target.py +165 -0
  53. bloqade/qasm2/glob.py +24 -0
  54. bloqade/qasm2/groups.py +120 -0
  55. bloqade/qasm2/parallel.py +48 -0
  56. bloqade/qasm2/parse/__init__.py +37 -0
  57. bloqade/qasm2/parse/ast.py +235 -0
  58. bloqade/qasm2/parse/build.py +289 -0
  59. bloqade/qasm2/parse/lowering.py +553 -0
  60. bloqade/qasm2/parse/parser.py +5 -0
  61. bloqade/qasm2/parse/print.py +293 -0
  62. bloqade/qasm2/parse/qasm2.lark +75 -0
  63. bloqade/qasm2/parse/visitor.py +16 -0
  64. bloqade/qasm2/parse/visitor.pyi +39 -0
  65. bloqade/qasm2/passes/__init__.py +5 -0
  66. bloqade/qasm2/passes/fold.py +94 -0
  67. bloqade/qasm2/passes/glob.py +119 -0
  68. bloqade/qasm2/passes/noise.py +61 -0
  69. bloqade/qasm2/passes/parallel.py +176 -0
  70. bloqade/qasm2/passes/py2qasm.py +63 -0
  71. bloqade/qasm2/passes/qasm2py.py +61 -0
  72. bloqade/qasm2/rewrite/__init__.py +12 -0
  73. bloqade/qasm2/rewrite/desugar.py +28 -0
  74. bloqade/qasm2/rewrite/glob.py +103 -0
  75. bloqade/qasm2/rewrite/heuristic_noise.py +247 -0
  76. bloqade/qasm2/rewrite/native_gates.py +447 -0
  77. bloqade/qasm2/rewrite/parallel_to_uop.py +83 -0
  78. bloqade/qasm2/rewrite/register.py +45 -0
  79. bloqade/qasm2/rewrite/uop_to_parallel.py +395 -0
  80. bloqade/qasm2/types.py +39 -0
  81. bloqade/qbraid/__init__.py +2 -0
  82. bloqade/qbraid/lowering.py +324 -0
  83. bloqade/qbraid/schema.py +252 -0
  84. bloqade/qbraid/simulation_result.py +99 -0
  85. bloqade/qbraid/target.py +86 -0
  86. bloqade/squin/__init__.py +2 -0
  87. bloqade/squin/analysis/__init__.py +0 -0
  88. bloqade/squin/analysis/nsites/__init__.py +8 -0
  89. bloqade/squin/analysis/nsites/analysis.py +52 -0
  90. bloqade/squin/analysis/nsites/impls.py +69 -0
  91. bloqade/squin/analysis/nsites/lattice.py +49 -0
  92. bloqade/squin/analysis/schedule.py +244 -0
  93. bloqade/squin/groups.py +38 -0
  94. bloqade/squin/op/__init__.py +132 -0
  95. bloqade/squin/op/_dialect.py +3 -0
  96. bloqade/squin/op/complex.py +6 -0
  97. bloqade/squin/op/stmts.py +220 -0
  98. bloqade/squin/op/traits.py +43 -0
  99. bloqade/squin/op/types.py +10 -0
  100. bloqade/squin/qubit.py +118 -0
  101. bloqade/squin/wire.py +103 -0
  102. bloqade/stim/__init__.py +6 -0
  103. bloqade/stim/_wrappers.py +186 -0
  104. bloqade/stim/dialects/__init__.py +5 -0
  105. bloqade/stim/dialects/aux/__init__.py +11 -0
  106. bloqade/stim/dialects/aux/_dialect.py +3 -0
  107. bloqade/stim/dialects/aux/emit.py +102 -0
  108. bloqade/stim/dialects/aux/interp.py +39 -0
  109. bloqade/stim/dialects/aux/lowering.py +40 -0
  110. bloqade/stim/dialects/aux/stmts/__init__.py +14 -0
  111. bloqade/stim/dialects/aux/stmts/annotate.py +47 -0
  112. bloqade/stim/dialects/aux/stmts/const.py +95 -0
  113. bloqade/stim/dialects/aux/types.py +19 -0
  114. bloqade/stim/dialects/collapse/__init__.py +3 -0
  115. bloqade/stim/dialects/collapse/_dialect.py +3 -0
  116. bloqade/stim/dialects/collapse/emit.py +68 -0
  117. bloqade/stim/dialects/collapse/stmts/__init__.py +3 -0
  118. bloqade/stim/dialects/collapse/stmts/measure.py +45 -0
  119. bloqade/stim/dialects/collapse/stmts/pp_measure.py +14 -0
  120. bloqade/stim/dialects/collapse/stmts/reset.py +26 -0
  121. bloqade/stim/dialects/gate/__init__.py +3 -0
  122. bloqade/stim/dialects/gate/_dialect.py +3 -0
  123. bloqade/stim/dialects/gate/emit.py +87 -0
  124. bloqade/stim/dialects/gate/stmts/__init__.py +14 -0
  125. bloqade/stim/dialects/gate/stmts/base.py +31 -0
  126. bloqade/stim/dialects/gate/stmts/clifford_1q.py +53 -0
  127. bloqade/stim/dialects/gate/stmts/clifford_2q.py +11 -0
  128. bloqade/stim/dialects/gate/stmts/control_2q.py +21 -0
  129. bloqade/stim/dialects/gate/stmts/pp.py +15 -0
  130. bloqade/stim/dialects/noise/__init__.py +3 -0
  131. bloqade/stim/dialects/noise/_dialect.py +3 -0
  132. bloqade/stim/dialects/noise/emit.py +66 -0
  133. bloqade/stim/dialects/noise/stmts.py +77 -0
  134. bloqade/stim/emit/__init__.py +1 -0
  135. bloqade/stim/emit/stim.py +54 -0
  136. bloqade/stim/groups.py +26 -0
  137. bloqade/test_utils.py +35 -0
  138. bloqade/types.py +24 -0
  139. bloqade/visual/__init__.py +1 -0
  140. bloqade/visual/animation/__init__.py +0 -0
  141. bloqade/visual/animation/animate.py +267 -0
  142. bloqade/visual/animation/base.py +346 -0
  143. bloqade/visual/animation/gate_event.py +24 -0
  144. bloqade/visual/animation/runtime/__init__.py +0 -0
  145. bloqade/visual/animation/runtime/aod.py +36 -0
  146. bloqade/visual/animation/runtime/atoms.py +55 -0
  147. bloqade/visual/animation/runtime/ppoly.py +50 -0
  148. bloqade/visual/animation/runtime/qpustate.py +119 -0
  149. bloqade/visual/animation/runtime/utils.py +43 -0
  150. bloqade_circuit-0.1.0.dist-info/METADATA +70 -0
  151. bloqade_circuit-0.1.0.dist-info/RECORD +153 -0
  152. bloqade_circuit-0.1.0.dist-info/WHEEL +4 -0
  153. bloqade_circuit-0.1.0.dist-info/licenses/LICENSE +234 -0
@@ -0,0 +1,262 @@
1
+ from kirin import ir, types, lowering
2
+ from kirin.decl import info, statement
3
+ from kirin.print.printer import Printer
4
+ from kirin.dialects.func.attrs import Signature
5
+
6
+ from ._dialect import dialect
7
+
8
+
9
+ class GateFuncOpCallableInterface(ir.CallableStmtInterface["GateFunction"]):
10
+
11
+ @classmethod
12
+ def get_callable_region(cls, stmt: "GateFunction") -> ir.Region:
13
+ return stmt.body
14
+
15
+
16
+ @statement(dialect=dialect)
17
+ class GateFunction(ir.Statement):
18
+ """Special Function for qasm2 gate subroutine."""
19
+
20
+ name = "gate.func"
21
+ traits = frozenset(
22
+ {
23
+ ir.IsolatedFromAbove(),
24
+ ir.SymbolOpInterface(),
25
+ ir.HasSignature(),
26
+ GateFuncOpCallableInterface(),
27
+ }
28
+ )
29
+ sym_name: str = info.attribute()
30
+ signature: Signature = info.attribute()
31
+ body: ir.Region = info.region(multi=True)
32
+
33
+ def print_impl(self, printer: Printer) -> None:
34
+ with printer.rich(style="red"):
35
+ printer.plain_print(self.name + " ")
36
+
37
+ with printer.rich(style="cyan"):
38
+ printer.plain_print(self.sym_name)
39
+
40
+ self.signature.print_impl(printer)
41
+ printer.plain_print(" ")
42
+ self.body.print_impl(printer)
43
+
44
+ with printer.rich(style="black"):
45
+ printer.plain_print(f" // gate.func {self.sym_name}")
46
+
47
+
48
+ @statement(dialect=dialect)
49
+ class ConstInt(ir.Statement):
50
+ """IR Statement representing a constant integer value."""
51
+
52
+ name = "constant.int"
53
+ traits = frozenset({ir.Pure(), ir.ConstantLike(), lowering.FromPythonCall()})
54
+ value: int = info.attribute(types.Int)
55
+ """value (int): The constant integer value."""
56
+ result: ir.ResultValue = info.result(types.Int)
57
+ """result (Int): The result value."""
58
+
59
+ def print_impl(self, printer: Printer) -> None:
60
+ printer.print_name(self)
61
+ printer.plain_print(" ")
62
+ printer.plain_print(repr(self.value))
63
+ with printer.rich(style="comment"):
64
+ printer.plain_print(" : ")
65
+ printer.print(self.result.type)
66
+
67
+
68
+ @statement(dialect=dialect)
69
+ class ConstFloat(ir.Statement):
70
+ """IR Statement representing a constant float value."""
71
+
72
+ name = "constant.float"
73
+ traits = frozenset({ir.Pure(), ir.ConstantLike(), lowering.FromPythonCall()})
74
+ value: float = info.attribute(types.Float)
75
+ """value (float): The constant float value."""
76
+ result: ir.ResultValue = info.result(types.Float)
77
+ """result (Float): The result value."""
78
+
79
+ def print_impl(self, printer: Printer) -> None:
80
+ printer.print_name(self)
81
+ printer.plain_print(" ")
82
+ printer.plain_print(repr(self.value))
83
+ with printer.rich(style="comment"):
84
+ printer.plain_print(" : ")
85
+ printer.print(self.result.type)
86
+
87
+
88
+ @statement(dialect=dialect)
89
+ class ConstPI(ir.Statement):
90
+ """The constant value of PI."""
91
+
92
+ # this is marked as constant but not pure.
93
+ name = "constant.pi"
94
+ traits = frozenset({ir.ConstantLike(), lowering.FromPythonCall()})
95
+ result: ir.ResultValue = info.result(types.Float)
96
+ """result (ConstPI): The result value."""
97
+
98
+ def print_impl(self, printer: Printer) -> None:
99
+ printer.print_name(self)
100
+ printer.plain_print(" ")
101
+ printer.plain_print("PI")
102
+ with printer.rich(style="comment"):
103
+ printer.plain_print(" : ")
104
+ printer.print(self.result.type)
105
+
106
+
107
+ # QASM 2.0 arithmetic operations
108
+ PyNum = types.Union(types.Int, types.Float)
109
+
110
+
111
+ @statement(dialect=dialect)
112
+ class Neg(ir.Statement):
113
+ """Negate a number."""
114
+
115
+ name = "neg"
116
+ traits = frozenset({lowering.FromPythonCall()})
117
+ value: ir.SSAValue = info.argument(PyNum)
118
+ """value (Union[int, float]): The number to negate."""
119
+ result: ir.ResultValue = info.result(PyNum)
120
+ """result (Union[int, float]): The negated number."""
121
+
122
+
123
+ @statement(dialect=dialect)
124
+ class Sin(ir.Statement):
125
+ """Take the sine of a number."""
126
+
127
+ name = "sin"
128
+ traits = frozenset({lowering.FromPythonCall()})
129
+ value: ir.SSAValue = info.argument(PyNum)
130
+ """value (Union[int, float]): The number to take the sine of."""
131
+ result: ir.ResultValue = info.result(PyNum)
132
+ """result (float): The sine of the number."""
133
+
134
+
135
+ @statement(dialect=dialect)
136
+ class Cos(ir.Statement):
137
+ """Take the cosine of a number."""
138
+
139
+ name = "cos"
140
+ traits = frozenset({lowering.FromPythonCall()})
141
+ value: ir.SSAValue = info.argument(PyNum)
142
+ """value (Union[int, float]): The number to take the cosine of."""
143
+ result: ir.ResultValue = info.result(PyNum)
144
+ """result (float): The cosine of the number."""
145
+
146
+
147
+ @statement(dialect=dialect)
148
+ class Tan(ir.Statement):
149
+ """Take the tangent of a number."""
150
+
151
+ name = "tan"
152
+ traits = frozenset({lowering.FromPythonCall()})
153
+ value: ir.SSAValue = info.argument(PyNum)
154
+ """value (Union[int, float]): The number to take the tangent of."""
155
+ result: ir.ResultValue = info.result(PyNum)
156
+ """result (float): The tangent of the number."""
157
+
158
+
159
+ @statement(dialect=dialect)
160
+ class Exp(ir.Statement):
161
+ """Take the exponential of a number."""
162
+
163
+ name = "exp"
164
+ traits = frozenset({lowering.FromPythonCall()})
165
+ value: ir.SSAValue = info.argument(PyNum)
166
+ """value (Union[int, float]): The number to take the exponential of."""
167
+ result: ir.ResultValue = info.result(PyNum)
168
+ """result (float): The exponential of the number."""
169
+
170
+
171
+ @statement(dialect=dialect)
172
+ class Log(ir.Statement):
173
+ """Take the natural log of a number."""
174
+
175
+ name = "ln"
176
+ traits = frozenset({lowering.FromPythonCall()})
177
+ value: ir.SSAValue = info.argument(PyNum)
178
+ """value (Union[int, float]): The number to take the natural log of."""
179
+ result: ir.ResultValue = info.result(PyNum)
180
+ """result (float): The natural log of the number."""
181
+
182
+
183
+ @statement(dialect=dialect)
184
+ class Sqrt(ir.Statement):
185
+ """Take the square root of a number."""
186
+
187
+ name = "sqrt"
188
+ traits = frozenset({lowering.FromPythonCall()})
189
+ value: ir.SSAValue = info.argument(PyNum)
190
+ """value (Union[int, float]): The number to take the square root of."""
191
+ result: ir.ResultValue = info.result(PyNum)
192
+ """result (float): The square root of the number."""
193
+
194
+
195
+ @statement(dialect=dialect)
196
+ class Add(ir.Statement):
197
+ """Add two numbers."""
198
+
199
+ name = "add"
200
+ traits = frozenset({lowering.FromPythonCall()})
201
+ lhs: ir.SSAValue = info.argument(PyNum)
202
+ """lhs (Union[int, float]): The left-hand side of the addition."""
203
+ rhs: ir.SSAValue = info.argument(PyNum)
204
+ """rhs (Union[int, float]): The right-hand side of the addition."""
205
+ result: ir.ResultValue = info.result(PyNum)
206
+ """result (Union[int, float]): The result of the addition."""
207
+
208
+
209
+ @statement(dialect=dialect)
210
+ class Sub(ir.Statement):
211
+ """Subtract two numbers."""
212
+
213
+ name = "sub"
214
+ traits = frozenset({lowering.FromPythonCall()})
215
+ lhs: ir.SSAValue = info.argument(PyNum)
216
+ """lhs (Union[int, float]): The left-hand side of the subtraction."""
217
+ rhs: ir.SSAValue = info.argument(PyNum)
218
+ """rhs (Union[int, float]): The right-hand side of the subtraction."""
219
+ result: ir.ResultValue = info.result(PyNum)
220
+ """result (Union[int, float]): The result of the subtraction."""
221
+
222
+
223
+ @statement(dialect=dialect)
224
+ class Mul(ir.Statement):
225
+ """Multiply two numbers."""
226
+
227
+ name = "mul"
228
+ traits = frozenset({lowering.FromPythonCall()})
229
+ lhs: ir.SSAValue = info.argument(PyNum)
230
+ """lhs (Union[int, float]): The left-hand side of the multiplication."""
231
+ rhs: ir.SSAValue = info.argument(PyNum)
232
+ """rhs (Union[int, float]): The right-hand side of the multiplication."""
233
+ result: ir.ResultValue = info.result(PyNum)
234
+ """result (Union[int, float]): The result of the multiplication."""
235
+
236
+
237
+ @statement(dialect=dialect)
238
+ class Pow(ir.Statement):
239
+ """Take the power of a number."""
240
+
241
+ name = "pow"
242
+ traits = frozenset({lowering.FromPythonCall()})
243
+ lhs: ir.SSAValue = info.argument(PyNum)
244
+ """lhs (Union[int, float]): The base."""
245
+ rhs: ir.SSAValue = info.argument(PyNum)
246
+ """rhs (Union[int, float]): The exponent."""
247
+ result: ir.ResultValue = info.result(PyNum)
248
+ """result (Union[int, float]): The result of the power operation."""
249
+
250
+
251
+ @statement(dialect=dialect)
252
+ class Div(ir.Statement):
253
+ """Divide two numbers."""
254
+
255
+ name = "div"
256
+ traits = frozenset({lowering.FromPythonCall()})
257
+ lhs: ir.SSAValue = info.argument(PyNum)
258
+ """lhs (Union[int, float]): The numerator."""
259
+ rhs: ir.SSAValue = info.argument(PyNum)
260
+ """rhs (Union[int, float]): The denominator."""
261
+ result: ir.ResultValue = info.result(PyNum)
262
+ """result (Union[int, float]): The result of the division."""
@@ -0,0 +1,45 @@
1
+ from kirin import ir, types, interp, lowering
2
+ from kirin.decl import info, statement
3
+ from kirin.dialects import ilist
4
+
5
+ from bloqade.qasm2.parse import ast
6
+ from bloqade.qasm2.types import QRegType
7
+ from bloqade.qasm2.emit.gate import EmitQASM2Gate, EmitQASM2Frame
8
+ from bloqade.squin.analysis.schedule import DagScheduleAnalysis
9
+
10
+ dialect = ir.Dialect("qasm2.glob")
11
+
12
+
13
+ @statement(dialect=dialect)
14
+ class UGate(ir.Statement):
15
+ name = "ugate"
16
+ traits = frozenset({lowering.FromPythonCall()})
17
+ registers: ir.SSAValue = info.argument(ilist.IListType[QRegType])
18
+ theta: ir.SSAValue = info.argument(types.Float)
19
+ phi: ir.SSAValue = info.argument(types.Float)
20
+ lam: ir.SSAValue = info.argument(types.Float)
21
+
22
+
23
+ @dialect.register(key="qasm2.schedule.dag")
24
+ class Glob(interp.MethodTable):
25
+ @interp.impl(UGate)
26
+ def ugate(self, interp: DagScheduleAnalysis, frame: interp.Frame, stmt: UGate):
27
+ interp.update_dag(stmt, [stmt.registers])
28
+ return ()
29
+
30
+
31
+ @dialect.register(key="emit.qasm2.gate")
32
+ class GlobEmit(interp.MethodTable):
33
+ @interp.impl(UGate)
34
+ def ugate(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: UGate):
35
+ registers = [
36
+ emit.assert_node(ast.Name, reg)
37
+ for reg in frame.get_casted(stmt.registers, ilist.IList)
38
+ ]
39
+ theta = emit.assert_node(ast.Expr, frame.get(stmt.theta))
40
+ phi = emit.assert_node(ast.Expr, frame.get(stmt.phi))
41
+ lam = emit.assert_node(ast.Expr, frame.get(stmt.lam))
42
+ frame.body.append(
43
+ ast.GlobUGate(theta=theta, phi=phi, lam=lam, registers=registers)
44
+ )
45
+ return ()
@@ -0,0 +1,64 @@
1
+ """This dialect provides the indexing syntax in Python lowering
2
+ for QASM2 dialects. The dialect itself does not contain new statements.
3
+
4
+ Using this dialect will be conflict with Python semantics provided by
5
+ `kirin.dialects.py.binop` and `kirin.dialects.py.indexing` dialects.
6
+ """
7
+
8
+ import ast
9
+
10
+ from kirin import ir, types, lowering
11
+
12
+ from bloqade.qasm2.types import BitType, CRegType, QRegType, QubitType
13
+ from bloqade.qasm2.dialects import core
14
+
15
+ dialect = ir.Dialect("qasm2.indexing")
16
+
17
+
18
+ @dialect.register
19
+ class QASMCoreLowering(lowering.FromPythonAST):
20
+ def lower_Compare(self, state: lowering.State, node: ast.Compare):
21
+ lhs = state.lower(node.left).expect_one()
22
+ if len(node.ops) != 1:
23
+ raise lowering.BuildError(
24
+ "only one comparison operator and == is supported for qasm2 lowering"
25
+ )
26
+ rhs = state.lower(node.comparators[0]).expect_one()
27
+ if isinstance(node.ops[0], ast.Eq):
28
+ stmt = core.CRegEq(lhs, rhs)
29
+ else:
30
+ raise lowering.BuildError(
31
+ f"unsupported comparison operator {node.ops[0]} only Eq is supported."
32
+ )
33
+
34
+ return state.current_frame.push(stmt)
35
+
36
+ def lower_Subscript(self, state: lowering.State, node: ast.Subscript):
37
+ value = state.lower(node.value).expect_one()
38
+ index = state.lower(node.slice).expect_one()
39
+
40
+ if not index.type.is_subseteq(types.Int):
41
+ raise lowering.BuildError(
42
+ f"unsupported subscript index type {index.type},"
43
+ " only integer indices are supported in QASM 2.0"
44
+ )
45
+
46
+ if not isinstance(node.ctx, ast.Load):
47
+ raise lowering.BuildError(
48
+ f"unsupported subscript context {node.ctx},"
49
+ " cannot write to subscript in QASM 2.0"
50
+ )
51
+
52
+ if value.type.is_subseteq(QRegType):
53
+ stmt = core.QRegGet(reg=value, idx=index)
54
+ stmt.result.type = QubitType
55
+ elif value.type.is_subseteq(CRegType):
56
+ stmt = core.CRegGet(reg=value, idx=index)
57
+ stmt.result.type = BitType
58
+ else:
59
+ raise lowering.BuildError(
60
+ f"unsupported subscript value type {value.type},"
61
+ " only QReg and CReg are supported in QASM 2.0"
62
+ )
63
+
64
+ return state.current_frame.push(stmt)
@@ -0,0 +1,76 @@
1
+ """Inline QASM dialect.
2
+
3
+ This dialect allows users to use QASM string as part of a `@qasm2.main` kernel.
4
+ """
5
+
6
+ import ast
7
+ import textwrap
8
+ from dataclasses import dataclass
9
+
10
+ from kirin import ir, types, lowering
11
+ from kirin.decl import info, statement
12
+ from kirin.print import Printer
13
+
14
+ dialect = ir.Dialect("qasm2.inline")
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class InlineQASMLowering(lowering.FromPythonCall):
19
+
20
+ def lower(
21
+ self, stmt: type, state: lowering.State, node: ast.Call
22
+ ) -> lowering.Result:
23
+ from bloqade.qasm2.parse import loads
24
+ from bloqade.qasm2.parse.lowering import QASM2
25
+
26
+ if len(node.args) != 1 or node.keywords:
27
+ raise lowering.BuildError("InlineQASM takes 1 positional argument")
28
+ text = node.args[0]
29
+ # 1. string literal
30
+ if isinstance(text, ast.Constant) and isinstance(text.value, str):
31
+ value = text.value
32
+ elif isinstance(text, ast.Name) and isinstance(text.ctx, ast.Load):
33
+ value = state.get_global(text).expect(str)
34
+ else:
35
+ raise lowering.BuildError(
36
+ "InlineQASM takes a string literal or global string"
37
+ )
38
+
39
+ from kirin.dialects import ilist
40
+
41
+ from bloqade.qasm2.groups import main
42
+ from bloqade.qasm2.dialects import glob, noise, parallel
43
+
44
+ raw = textwrap.dedent(value)
45
+ qasm_lowering = QASM2(main.union([ilist, glob, noise, parallel]))
46
+ region = qasm_lowering.run(loads(raw))
47
+ for qasm_stmt in region.blocks[0].stmts:
48
+ qasm_stmt.detach()
49
+ state.current_frame.push(qasm_stmt)
50
+
51
+ for block in region.blocks:
52
+ for qasm_stmt in block.stmts:
53
+ qasm_stmt.detach()
54
+ state.current_frame.push(qasm_stmt)
55
+ state.current_frame.jump_next_block()
56
+
57
+
58
+ # NOTE: this is a dummy statement that won't appear in IR.
59
+ # TODO: maybe we should save the string in IR then rewrite?
60
+ # what would be the use case?
61
+ @statement(dialect=dialect)
62
+ class InlineQASM(ir.Statement):
63
+ name = "text"
64
+ traits = frozenset({InlineQASMLowering()})
65
+ text: str = info.attribute(types.String)
66
+
67
+ def __init__(self, text: str) -> None:
68
+ super().__init__(attributes={"text": ir.PyAttr(text)})
69
+
70
+ def print_impl(self, printer: Printer) -> None:
71
+ printer.print_name(self)
72
+ printer.plain_print('"""')
73
+ for line in self.text.splitlines():
74
+ printer.plain_print(line)
75
+ printer.print_newline()
76
+ printer.plain_print('"""')
@@ -0,0 +1,16 @@
1
+ from kirin import ir, types, lowering
2
+ from kirin.decl import info, statement
3
+
4
+ from bloqade.qasm2.types import QubitType
5
+
6
+ dialect = ir.Dialect("qasm2.noise")
7
+
8
+
9
+ @statement(dialect=dialect)
10
+ class Pauli1(ir.Statement):
11
+ name = "pauli_1"
12
+ traits = frozenset({lowering.FromPythonCall()})
13
+ px: ir.SSAValue = info.argument(types.Float)
14
+ py: ir.SSAValue = info.argument(types.Float)
15
+ pz: ir.SSAValue = info.argument(types.Float)
16
+ qarg: ir.SSAValue = info.argument(QubitType)
@@ -0,0 +1,110 @@
1
+ from typing import Any
2
+
3
+ from kirin import ir, types, interp, lowering
4
+ from kirin.decl import info, statement
5
+ from kirin.analysis import ForwardFrame
6
+ from kirin.dialects import ilist
7
+
8
+ from bloqade.qasm2.parse import ast
9
+ from bloqade.qasm2.types import QubitType
10
+ from bloqade.qasm2.emit.gate import EmitQASM2Gate, EmitQASM2Frame
11
+ from bloqade.squin.analysis.schedule import DagScheduleAnalysis
12
+
13
+ dialect = ir.Dialect("qasm2.parallel")
14
+
15
+ N = types.TypeVar("N")
16
+
17
+
18
+ @statement(dialect=dialect)
19
+ class CZ(ir.Statement):
20
+ name = "cz"
21
+ traits = frozenset({lowering.FromPythonCall()})
22
+ ctrls: ir.SSAValue = info.argument(ilist.IListType[QubitType, N])
23
+ qargs: ir.SSAValue = info.argument(ilist.IListType[QubitType, N])
24
+
25
+
26
+ @statement(dialect=dialect)
27
+ class UGate(ir.Statement):
28
+ name = "u"
29
+ traits = frozenset({lowering.FromPythonCall()})
30
+ qargs: ir.SSAValue = info.argument(ilist.IListType[QubitType])
31
+ theta: ir.SSAValue = info.argument(types.Float)
32
+ phi: ir.SSAValue = info.argument(types.Float)
33
+ lam: ir.SSAValue = info.argument(types.Float)
34
+
35
+
36
+ @statement(dialect=dialect)
37
+ class RZ(ir.Statement):
38
+ name = "rz"
39
+ traits = frozenset({lowering.FromPythonCall()})
40
+ qargs: ir.SSAValue = info.argument(ilist.IListType[QubitType])
41
+ theta: ir.SSAValue = info.argument(types.Float)
42
+
43
+
44
+ @dialect.register(key="emit.qasm2.gate")
45
+ class Parallel(interp.MethodTable):
46
+
47
+ def _emit_parallel_qargs(
48
+ self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, qargs: ir.SSAValue
49
+ ):
50
+ qargs_: ilist.IList[ast.Node, Any] = frame.get(qargs) # type: ignore
51
+ return [(emit.assert_node((ast.Name, ast.Bit), qarg),) for qarg in qargs_]
52
+
53
+ @interp.impl(UGate)
54
+ def ugate(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: UGate):
55
+ qargs = self._emit_parallel_qargs(emit, frame, stmt.qargs)
56
+ theta = emit.assert_node(ast.Expr, frame.get(stmt.theta))
57
+ phi = emit.assert_node(ast.Expr, frame.get(stmt.phi))
58
+ lam = emit.assert_node(ast.Expr, frame.get(stmt.lam))
59
+ frame.body.append(
60
+ ast.ParaU3Gate(
61
+ theta=theta, phi=phi, lam=lam, qargs=ast.ParallelQArgs(qargs=qargs)
62
+ )
63
+ )
64
+ return ()
65
+
66
+ @interp.impl(RZ)
67
+ def rz(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: RZ):
68
+ qargs = self._emit_parallel_qargs(emit, frame, stmt.qargs)
69
+ theta = emit.assert_node(ast.Expr, frame.get(stmt.theta))
70
+ frame.body.append(
71
+ ast.ParaRZGate(theta=theta, qargs=ast.ParallelQArgs(qargs=qargs))
72
+ )
73
+ return ()
74
+
75
+ @interp.impl(CZ)
76
+ def cz(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: CZ):
77
+ ctrls = self._emit_parallel_qargs(emit, frame, stmt.ctrls)
78
+ qargs = self._emit_parallel_qargs(emit, frame, stmt.qargs)
79
+ frame.body.append(
80
+ ast.ParaCZGate(
81
+ qargs=ast.ParallelQArgs(
82
+ qargs=[ctrl + qarg for ctrl, qarg in zip(ctrls, qargs)]
83
+ )
84
+ )
85
+ )
86
+ return ()
87
+
88
+
89
+ @dialect.register(key="qasm2.schedule.dag")
90
+ class ParallelDag(interp.MethodTable):
91
+
92
+ @interp.impl(CZ)
93
+ def parallel_cz(self, interp: DagScheduleAnalysis, frame: ForwardFrame, stmt: CZ):
94
+ interp.update_dag(stmt, [stmt.qargs, stmt.ctrls])
95
+ return ()
96
+
97
+ @interp.impl(UGate)
98
+ def parallel_ugate(
99
+ self,
100
+ interp: DagScheduleAnalysis,
101
+ frame: ForwardFrame,
102
+ stmt: UGate,
103
+ ):
104
+ interp.update_dag(stmt, [stmt.qargs])
105
+ return ()
106
+
107
+ @interp.impl(RZ)
108
+ def parallel_rz(self, interp: DagScheduleAnalysis, frame: ForwardFrame, stmt: RZ):
109
+ interp.update_dag(stmt, [stmt.qargs])
110
+ return ()
@@ -0,0 +1,4 @@
1
+ from . import _emit as _emit, stmts as stmts
2
+ from .stmts import * # noqa: F403
3
+ from ._dialect import dialect as dialect
4
+ from .schedule import * # noqa: F403
@@ -0,0 +1,3 @@
1
+ from kirin import ir
2
+
3
+ dialect = ir.Dialect("qasm2.uop")