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,553 @@
1
+ from typing import Any
2
+ from dataclasses import field, dataclass
3
+
4
+ from kirin import ir, types, lowering
5
+ from kirin.dialects import cf, func, ilist
6
+
7
+ from bloqade.qasm2.types import CRegType, QRegType
8
+ from bloqade.qasm2.dialects import uop, core, expr, glob, noise, parallel
9
+
10
+ from . import ast
11
+
12
+
13
+ @dataclass
14
+ class QASM2(lowering.LoweringABC[ast.Node]):
15
+ max_lines: int = field(default=3, kw_only=True)
16
+ hint_indent: int = field(default=2, kw_only=True)
17
+ hint_show_lineno: bool = field(default=True, kw_only=True)
18
+ stacktrace: bool = field(default=True, kw_only=True)
19
+
20
+ def run(
21
+ self,
22
+ stmt: ast.Node,
23
+ *,
24
+ source: str | None = None,
25
+ globals: dict[str, Any] | None = None,
26
+ file: str | None = None,
27
+ lineno_offset: int = 0,
28
+ col_offset: int = 0,
29
+ compactify: bool = True,
30
+ ) -> ir.Region:
31
+ # TODO: add source info
32
+ state = lowering.State(
33
+ self,
34
+ file=file,
35
+ lineno_offset=lineno_offset,
36
+ col_offset=col_offset,
37
+ )
38
+ with state.frame(
39
+ [stmt],
40
+ globals=globals,
41
+ ) as frame:
42
+ try:
43
+ self.visit(state, stmt)
44
+ except lowering.BuildError as e:
45
+ hint = state.error_hint(
46
+ e,
47
+ max_lines=self.max_lines,
48
+ indent=self.hint_indent,
49
+ show_lineno=self.hint_show_lineno,
50
+ )
51
+ if self.stacktrace:
52
+ raise Exception(
53
+ f"{e.args[0]}\n\n{hint}",
54
+ *e.args[1:],
55
+ ) from e
56
+ else:
57
+ e.args = (hint,)
58
+ raise e
59
+
60
+ region = frame.curr_region
61
+
62
+ if compactify:
63
+ from kirin.rewrite import Walk, CFGCompactify
64
+
65
+ Walk(CFGCompactify()).rewrite(region)
66
+ return region
67
+
68
+ def visit(self, state: lowering.State[ast.Node], node: ast.Node) -> lowering.Result:
69
+ name = node.__class__.__name__
70
+ return getattr(self, f"visit_{name}", self.generic_visit)(state, node)
71
+
72
+ def generic_visit(
73
+ self, state: lowering.State[ast.Node], node: ast.Node
74
+ ) -> lowering.Result:
75
+ if isinstance(node, ast.Node):
76
+ raise lowering.BuildError(
77
+ f"Cannot lower {node.__class__.__name__} node: {node}"
78
+ )
79
+ raise lowering.BuildError(
80
+ f"Unexpected `{node.__class__.__name__}` node: {repr(node)} is not an AST node"
81
+ )
82
+
83
+ def lower_literal(self, state: lowering.State[ast.Node], value) -> ir.SSAValue:
84
+ if isinstance(value, int):
85
+ stmt = expr.ConstInt(value=value)
86
+ elif isinstance(value, float):
87
+ stmt = expr.ConstFloat(value=value)
88
+ state.current_frame.push(stmt)
89
+ return stmt.result
90
+
91
+ def lower_global(
92
+ self, state: lowering.State[ast.Node], node: ast.Node
93
+ ) -> lowering.LoweringABC.Result:
94
+ raise lowering.BuildError("Global variables are not supported in QASM 2.0")
95
+
96
+ def visit_MainProgram(self, state: lowering.State[ast.Node], node: ast.MainProgram):
97
+ allowed = {dialect.name for dialect in self.dialects}
98
+ if isinstance(node.header, ast.OPENQASM) and node.header.version.major == 2:
99
+ dialects = ["qasm2.core", "qasm2.uop", "qasm2.expr"]
100
+ elif isinstance(node.header, ast.Kirin):
101
+ dialects = node.header.dialects
102
+
103
+ for dialect in dialects:
104
+ if dialect not in allowed:
105
+ raise lowering.BuildError(
106
+ f"Dialect {dialect} not found, allowed: {', '.join(allowed)}"
107
+ )
108
+
109
+ for stmt in node.statements:
110
+ state.lower(stmt)
111
+
112
+ def visit_QReg(self, state: lowering.State[ast.Node], node: ast.QReg):
113
+ reg = core.QRegNew(
114
+ state.current_frame.push(expr.ConstInt(value=node.size)).result
115
+ )
116
+ state.current_frame.push(reg)
117
+ state.current_frame.defs[node.name] = reg.result
118
+
119
+ def visit_CReg(self, state: lowering.State[ast.Node], node: ast.CReg):
120
+ reg = core.CRegNew(
121
+ state.current_frame.push(expr.ConstInt(value=node.size)).result
122
+ )
123
+ state.current_frame.push(reg)
124
+ state.current_frame.defs[node.name] = reg.result
125
+
126
+ def visit_Barrier(self, state: lowering.State[ast.Node], node: ast.Barrier):
127
+ state.current_frame.push(
128
+ uop.Barrier(
129
+ qargs=tuple(state.lower(qarg).expect_one() for qarg in node.qargs)
130
+ )
131
+ )
132
+
133
+ def visit_CXGate(self, state: lowering.State[ast.Node], node: ast.CXGate):
134
+ state.current_frame.push(
135
+ uop.CX(
136
+ ctrl=state.lower(node.ctrl).expect_one(),
137
+ qarg=state.lower(node.qarg).expect_one(),
138
+ )
139
+ )
140
+
141
+ def visit_Measure(self, state: lowering.State[ast.Node], node: ast.Measure):
142
+ state.current_frame.push(
143
+ core.Measure(
144
+ qarg=state.lower(node.qarg).expect_one(),
145
+ carg=state.lower(node.carg).expect_one(),
146
+ )
147
+ )
148
+
149
+ def visit_UGate(self, state: lowering.State[ast.Node], node: ast.UGate):
150
+ state.current_frame.push(
151
+ uop.UGate(
152
+ theta=state.lower(node.theta).expect_one(),
153
+ phi=state.lower(node.phi).expect_one(),
154
+ lam=state.lower(node.lam).expect_one(),
155
+ qarg=state.lower(node.qarg).expect_one(),
156
+ )
157
+ )
158
+
159
+ def visit_Reset(self, state: lowering.State[ast.Node], node: ast.Reset):
160
+ state.current_frame.push(core.Reset(qarg=state.lower(node.qarg).expect_one()))
161
+
162
+ # TODO: clean this up? copied from cf dialect with a small modification
163
+ def visit_IfStmt(self, state: lowering.State[ast.Node], node: ast.IfStmt):
164
+ cond_stmt = core.CRegEq(
165
+ lhs=state.lower(node.cond.lhs).expect_one(),
166
+ rhs=state.lower(node.cond.rhs).expect_one(),
167
+ )
168
+ cond = state.current_frame.push(cond_stmt).result
169
+ frame = state.current_frame
170
+ before_block = frame.curr_block
171
+
172
+ with state.frame(node.body, region=frame.curr_region) as if_frame:
173
+ true_cond = if_frame.entr_block.args.append_from(types.Bool, cond.name)
174
+ if cond.name:
175
+ if_frame.defs[cond.name] = true_cond
176
+
177
+ if_frame.exhaust()
178
+ self.branch_next_if_not_terminated(if_frame)
179
+
180
+ with state.frame([], region=frame.curr_region) as else_frame:
181
+ true_cond = else_frame.entr_block.args.append_from(types.Bool, cond.name)
182
+ if cond.name:
183
+ else_frame.defs[cond.name] = true_cond
184
+ else_frame.exhaust()
185
+ self.branch_next_if_not_terminated(else_frame)
186
+
187
+ with state.frame(frame.stream.split(), region=frame.curr_region) as after_frame:
188
+ after_frame.defs.update(frame.defs)
189
+ phi: set[str] = set()
190
+ for name in if_frame.defs.keys():
191
+ if frame.get(name):
192
+ phi.add(name)
193
+ elif name in else_frame.defs:
194
+ phi.add(name)
195
+
196
+ for name in else_frame.defs.keys():
197
+ if frame.get(name): # not defined in if_frame
198
+ phi.add(name)
199
+
200
+ for name in phi:
201
+ after_frame.defs[name] = after_frame.entr_block.args.append_from(
202
+ types.Any, name
203
+ )
204
+
205
+ after_frame.exhaust()
206
+ self.branch_next_if_not_terminated(after_frame)
207
+ after_frame.next_block.stmts.append(
208
+ cf.Branch(arguments=(), successor=frame.next_block)
209
+ )
210
+
211
+ if_args = []
212
+ for name in phi:
213
+ if value := if_frame.get(name):
214
+ if_args.append(value)
215
+ else:
216
+ raise lowering.BuildError(f"undefined variable {name} in if branch")
217
+
218
+ else_args = []
219
+ for name in phi:
220
+ if value := else_frame.get(name):
221
+ else_args.append(value)
222
+ else:
223
+ raise lowering.BuildError(f"undefined variable {name} in else branch")
224
+
225
+ if_frame.next_block.stmts.append(
226
+ cf.Branch(
227
+ arguments=tuple(if_args),
228
+ successor=after_frame.entr_block,
229
+ )
230
+ )
231
+ else_frame.next_block.stmts.append(
232
+ cf.Branch(
233
+ arguments=tuple(else_args),
234
+ successor=after_frame.entr_block,
235
+ )
236
+ )
237
+ before_block.stmts.append(
238
+ cf.ConditionalBranch(
239
+ cond=cond,
240
+ then_arguments=(cond,),
241
+ then_successor=if_frame.entr_block,
242
+ else_arguments=(cond,),
243
+ else_successor=else_frame.entr_block,
244
+ )
245
+ )
246
+ frame.defs.update(after_frame.defs)
247
+ frame.jump_next_block()
248
+
249
+ def branch_next_if_not_terminated(self, frame: lowering.Frame):
250
+ """Branch to the next block if the current block is not terminated.
251
+
252
+ This must be used after exhausting the current frame and before popping the frame.
253
+ """
254
+ if not frame.curr_block.last_stmt or not frame.curr_block.last_stmt.has_trait(
255
+ ir.IsTerminator
256
+ ):
257
+ frame.curr_block.stmts.append(
258
+ cf.Branch(arguments=(), successor=frame.next_block)
259
+ )
260
+
261
+ def visit_BinOp(self, state: lowering.State[ast.Node], node: ast.BinOp):
262
+ if node.op == "+":
263
+ stmt_type = expr.Add
264
+ elif node.op == "-":
265
+ stmt_type = expr.Sub
266
+ elif node.op == "*":
267
+ stmt_type = expr.Mul
268
+ else:
269
+ stmt_type = expr.Div
270
+
271
+ return state.current_frame.push(
272
+ stmt_type(
273
+ lhs=state.lower(node.lhs).expect_one(),
274
+ rhs=state.lower(node.rhs).expect_one(),
275
+ )
276
+ )
277
+
278
+ def visit_UnaryOp(self, state: lowering.State[ast.Node], node: ast.UnaryOp):
279
+ if node.op == "-":
280
+ stmt = expr.Neg(value=state.lower(node.operand).expect_one())
281
+ return stmt.result
282
+ else:
283
+ return state.lower(node.operand).expect_one()
284
+
285
+ def visit_Bit(self, state: lowering.State[ast.Node], node: ast.Bit):
286
+ if node.name.id not in state.current_frame.defs:
287
+ raise ValueError(f"Bit {node.name} not found")
288
+
289
+ addr = state.current_frame.push(expr.ConstInt(value=node.addr))
290
+ reg = state.current_frame.get_local(node.name.id)
291
+ if reg is None:
292
+ raise lowering.BuildError(f"{node.name.id} is not defined")
293
+
294
+ if reg.type.is_subseteq(QRegType):
295
+ stmt = core.QRegGet(reg, addr.result)
296
+ elif reg.type.is_subseteq(CRegType):
297
+ stmt = core.CRegGet(reg, addr.result)
298
+ return state.current_frame.push(stmt).result
299
+
300
+ def visit_Call(self, state: lowering.State[ast.Node], node: ast.Call):
301
+ if node.name == "cos":
302
+ stmt = expr.Cos(state.lower(node.args[0]).expect_one())
303
+ elif node.name == "sin":
304
+ stmt = expr.Sin(state.lower(node.args[0]).expect_one())
305
+ elif node.name == "tan":
306
+ stmt = expr.Tan(state.lower(node.args[0]).expect_one())
307
+ elif node.name == "exp":
308
+ stmt = expr.Exp(state.lower(node.args[0]).expect_one())
309
+ elif node.name == "log":
310
+ stmt = expr.Log(state.lower(node.args[0]).expect_one())
311
+ elif node.name == "sqrt":
312
+ stmt = expr.Sqrt(state.lower(node.args[0]).expect_one())
313
+ else:
314
+ raise ValueError(f"Unknown function {node.name}")
315
+ state.current_frame.push(stmt)
316
+ return stmt.result
317
+
318
+ def visit_Name(self, state: lowering.State[ast.Node], node: ast.Name):
319
+ if (value := state.current_frame.get_local(node.id)) is not None:
320
+ return value
321
+ raise ValueError(f"name {node.id} not found")
322
+
323
+ def visit_ParaCZGate(self, state: lowering.State[ast.Node], node: ast.ParaCZGate):
324
+ ctrls: list[ir.SSAValue] = []
325
+ qargs: list[ir.SSAValue] = []
326
+ for pair in node.qargs:
327
+ if len(pair) != 2:
328
+ raise ValueError("CZ gate requires exactly two qargs")
329
+ ctrl, qarg = pair
330
+ ctrls.append(state.lower(ctrl).expect_one())
331
+ qargs.append(state.lower(qarg).expect_one())
332
+
333
+ ctrls_stmt = ilist.New(values=ctrls)
334
+ qargs_stmt = ilist.New(values=qargs)
335
+ state.current_frame.push(ctrls_stmt)
336
+ state.current_frame.push(qargs_stmt)
337
+ state.current_frame.push(
338
+ parallel.CZ(ctrls=ctrls_stmt.result, qargs=qargs_stmt.result)
339
+ )
340
+
341
+ def visit_ParaRZGate(self, state: lowering.State[ast.Node], node: ast.ParaRZGate):
342
+ qargs: list[ir.SSAValue] = []
343
+ for pair in node.qargs:
344
+ if len(pair) != 1:
345
+ raise ValueError("Rz gate requires exactly one qarg")
346
+ qargs.append(state.lower(pair[0]).expect_one())
347
+
348
+ qargs_stmt = ilist.New(values=qargs)
349
+ state.current_frame.push(qargs_stmt)
350
+ state.current_frame.push(
351
+ parallel.RZ(
352
+ theta=state.lower(node.theta).expect_one(),
353
+ qargs=qargs_stmt.result,
354
+ )
355
+ )
356
+
357
+ def visit_ParaU3Gate(self, state: lowering.State[ast.Node], node: ast.ParaU3Gate):
358
+ qargs: list[ir.SSAValue] = []
359
+ for pair in node.qargs:
360
+ if len(pair) != 1:
361
+ raise ValueError("U3 gate requires exactly one qarg")
362
+ qargs.append(state.lower(pair[0]).expect_one())
363
+
364
+ qargs_stmt = ilist.New(values=qargs)
365
+ state.current_frame.push(qargs_stmt)
366
+ state.current_frame.push(
367
+ parallel.UGate(
368
+ theta=state.lower(node.theta).expect_one(),
369
+ phi=state.lower(node.phi).expect_one(),
370
+ lam=state.lower(node.lam).expect_one(),
371
+ qargs=qargs_stmt.result,
372
+ )
373
+ )
374
+
375
+ def visit_GlobUGate(self, state: lowering.State[ast.Node], node: ast.GlobUGate):
376
+ registers: list[ir.SSAValue] = []
377
+
378
+ for register in node.registers: # These will all be ast.Names
379
+ registers.append(state.lower(register).expect_one())
380
+
381
+ registers_stmt = ilist.New(values=registers)
382
+ state.current_frame.push(registers_stmt)
383
+ state.current_frame.push(
384
+ # all the stuff going into the args should be SSA values
385
+ glob.UGate(
386
+ registers=registers_stmt.result, # expect_one = a singular SSA value
387
+ theta=state.lower(node.theta).expect_one(),
388
+ phi=state.lower(node.phi).expect_one(),
389
+ lam=state.lower(node.lam).expect_one(),
390
+ )
391
+ )
392
+
393
+ def visit_NoisePAULI1(self, state: lowering.State[ast.Node], node: ast.NoisePAULI1):
394
+ state.current_frame.push(
395
+ noise.Pauli1(
396
+ px=state.lower(node.px).expect_one(),
397
+ py=state.lower(node.py).expect_one(),
398
+ pz=state.lower(node.pz).expect_one(),
399
+ qarg=state.lower(node.qarg).expect_one(),
400
+ )
401
+ )
402
+
403
+ def visit_Number(self, state: lowering.State[ast.Node], node: ast.Number):
404
+ if isinstance(node.value, int):
405
+ stmt = expr.ConstInt(value=node.value)
406
+ else:
407
+ stmt = expr.ConstFloat(value=node.value)
408
+ state.current_frame.push(stmt)
409
+ return stmt
410
+
411
+ def visit_Pi(self, state: lowering.State[ast.Node], node: ast.Pi):
412
+ return state.current_frame.push(expr.ConstPI()).result
413
+
414
+ def visit_Include(self, state: lowering.State[ast.Node], node: ast.Include):
415
+ if node.filename not in ["qelib1.inc"]:
416
+ raise lowering.BuildError(f"Include {node.filename} not found")
417
+
418
+ def visit_Gate(self, state: lowering.State[ast.Node], node: ast.Gate):
419
+ raise NotImplementedError("Gate lowering not supported")
420
+
421
+ def visit_Instruction(self, state: lowering.State[ast.Node], node: ast.Instruction):
422
+ params = [state.lower(param).expect_one() for param in node.params]
423
+ qargs = [state.lower(qarg).expect_one() for qarg in node.qargs]
424
+ visit_inst = getattr(self, "visit_Instruction_" + node.name.id, None)
425
+ if visit_inst is not None:
426
+ state.current_frame.push(visit_inst(params, qargs))
427
+ else:
428
+ value = state.get_global(node.name).expect(ir.Method)
429
+ # NOTE: QASM expects the return type to be known at call site
430
+ if value.return_type is None:
431
+ raise ValueError(f"Unknown return type for {node.name.id}")
432
+ state.current_frame.push(
433
+ func.Invoke(
434
+ callee=value,
435
+ inputs=tuple(params + qargs),
436
+ kwargs=tuple(),
437
+ )
438
+ )
439
+
440
+ def visit_Instruction_id(self, params, qargs):
441
+ return uop.Id(qarg=qargs[0])
442
+
443
+ def visit_Instruction_x(self, params, qargs):
444
+ return uop.X(qarg=qargs[0])
445
+
446
+ def visit_Instruction_y(self, params, qargs):
447
+ return uop.Y(qarg=qargs[0])
448
+
449
+ def visit_Instruction_z(self, params, qargs):
450
+ return uop.Z(qarg=qargs[0])
451
+
452
+ def visit_Instruction_h(self, params, qargs):
453
+ return uop.H(qarg=qargs[0])
454
+
455
+ def visit_Instruction_s(self, params, qargs):
456
+ return uop.S(qarg=qargs[0])
457
+
458
+ def visit_Instruction_sdg(self, params, qargs):
459
+ return uop.Sdag(qarg=qargs[0])
460
+
461
+ def visit_Instruction_sx(self, params, qargs):
462
+ return uop.SX(qarg=qargs[0])
463
+
464
+ def visit_Instruction_sxdg(self, params, qargs):
465
+ return uop.SXdag(qarg=qargs[0])
466
+
467
+ def visit_Instruction_t(self, params, qargs):
468
+ return uop.T(qarg=qargs[0])
469
+
470
+ def visit_Instruction_tdg(self, params, qargs):
471
+ return uop.Tdag(qarg=qargs[0])
472
+
473
+ def visit_Instruction_rx(self, params, qargs):
474
+ return uop.RX(theta=params[0], qarg=qargs[0])
475
+
476
+ def visit_Instruction_ry(self, params, qargs):
477
+ return uop.RY(theta=params[0], qarg=qargs[0])
478
+
479
+ def visit_Instruction_rz(self, params, qargs):
480
+ return uop.RZ(theta=params[0], qarg=qargs[0])
481
+
482
+ def visit_Instruction_p(self, params, qargs):
483
+ return uop.U1(lam=params[0], qarg=qargs[0])
484
+
485
+ def visit_Instruction_u(self, params, qargs):
486
+ return uop.UGate(theta=params[0], phi=params[1], lam=params[2], qarg=qargs[0])
487
+
488
+ def visit_Instruction_u1(self, params, qargs):
489
+ return uop.U1(lam=params[0], qarg=qargs[0])
490
+
491
+ def visit_Instruction_u2(self, params, qargs):
492
+ return uop.U2(phi=params[0], lam=params[1], qarg=qargs[0])
493
+
494
+ def visit_Instruction_u3(self, params, qargs):
495
+ return uop.UGate(theta=params[0], phi=params[1], lam=params[2], qarg=qargs[0])
496
+
497
+ def visit_Instruction_CX(self, params, qargs):
498
+ return uop.CX(ctrl=qargs[0], qarg=qargs[1])
499
+
500
+ def visit_Instruction_cx(self, params, qargs):
501
+ return uop.CX(ctrl=qargs[0], qarg=qargs[1])
502
+
503
+ def visit_Instruction_cy(self, params, qargs):
504
+ return uop.CY(ctrl=qargs[0], qarg=qargs[1])
505
+
506
+ def visit_Instruction_cz(self, params, qargs):
507
+ return uop.CZ(ctrl=qargs[0], qarg=qargs[1])
508
+
509
+ def visit_Instruction_ch(self, params, qargs):
510
+ return uop.CH(ctrl=qargs[0], qarg=qargs[1])
511
+
512
+ def visit_Instruction_crx(self, params, qargs):
513
+ return uop.CRX(lam=params[0], ctrl=qargs[0], qarg=qargs[1])
514
+
515
+ def visit_Instruction_cry(self, params, qargs):
516
+ return uop.CRY(lam=params[0], ctrl=qargs[0], qarg=qargs[1])
517
+
518
+ def visit_Instruction_crz(self, params, qargs):
519
+ return uop.CRZ(lam=params[0], ctrl=qargs[0], qarg=qargs[1])
520
+
521
+ def visit_Instruction_ccx(self, params, qargs):
522
+ return uop.CCX(ctrl1=qargs[0], ctrl2=qargs[1], qarg=qargs[2])
523
+
524
+ def visit_Instruction_csx(self, params, qargs):
525
+ return uop.CSX(ctrl=qargs[0], qarg=qargs[1])
526
+
527
+ def visit_Instruction_cswap(self, params, qargs):
528
+ return uop.CSwap(ctrl=qargs[0], qarg1=qargs[1], qarg2=qargs[2])
529
+
530
+ def visit_Instruction_cp(self, params, qargs):
531
+ return uop.CU1(lam=params[0], ctrl=qargs[0], qarg=qargs[1])
532
+
533
+ def visit_Instruction_cu1(self, params, qargs):
534
+ return uop.CU1(lam=params[0], ctrl=qargs[0], qarg=qargs[1])
535
+
536
+ def visit_Instruction_cu3(self, params, qargs):
537
+ return uop.CU3(
538
+ theta=params[0], phi=params[1], lam=params[2], ctrl=qargs[0], qarg=qargs[1]
539
+ )
540
+
541
+ def visit_Instruction_cu(self, params, qargs):
542
+ return uop.CU3(
543
+ theta=params[0], phi=params[1], lam=params[2], ctrl=qargs[0], qarg=qargs[1]
544
+ )
545
+
546
+ def visit_Instruction_rxx(self, params, qargs):
547
+ return uop.RXX(theta=params[0], ctrl=qargs[0], qarg=qargs[1])
548
+
549
+ def visit_Instruction_rzz(self, params, qargs):
550
+ return uop.RZZ(theta=params[0], ctrl=qargs[0], qarg=qargs[1])
551
+
552
+ def visit_Instruction_swap(self, params, qargs):
553
+ return uop.Swap(ctrl=qargs[0], qarg=qargs[1])
@@ -0,0 +1,5 @@
1
+ from lark import Lark
2
+
3
+ qasm2_parser = Lark.open(
4
+ "qasm2.lark", rel_to=__file__, parser="lalr", start="mainprogram"
5
+ )