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.
- bloqade/analysis/__init__.py +0 -0
- bloqade/analysis/address/__init__.py +11 -0
- bloqade/analysis/address/analysis.py +60 -0
- bloqade/analysis/address/impls.py +228 -0
- bloqade/analysis/address/lattice.py +85 -0
- bloqade/noise/__init__.py +1 -0
- bloqade/noise/native/__init__.py +20 -0
- bloqade/noise/native/_dialect.py +3 -0
- bloqade/noise/native/_wrappers.py +34 -0
- bloqade/noise/native/model.py +347 -0
- bloqade/noise/native/rewrite.py +35 -0
- bloqade/noise/native/stmts.py +46 -0
- bloqade/pyqrack/__init__.py +18 -0
- bloqade/pyqrack/base.py +131 -0
- bloqade/pyqrack/noise/__init__.py +0 -0
- bloqade/pyqrack/noise/native.py +100 -0
- bloqade/pyqrack/qasm2/__init__.py +0 -0
- bloqade/pyqrack/qasm2/core.py +79 -0
- bloqade/pyqrack/qasm2/parallel.py +46 -0
- bloqade/pyqrack/qasm2/uop.py +247 -0
- bloqade/pyqrack/reg.py +109 -0
- bloqade/pyqrack/target.py +112 -0
- bloqade/qasm2/__init__.py +19 -0
- bloqade/qasm2/_wrappers.py +674 -0
- bloqade/qasm2/dialects/__init__.py +10 -0
- bloqade/qasm2/dialects/core/__init__.py +3 -0
- bloqade/qasm2/dialects/core/_dialect.py +3 -0
- bloqade/qasm2/dialects/core/_emit.py +68 -0
- bloqade/qasm2/dialects/core/_typeinfer.py +23 -0
- bloqade/qasm2/dialects/core/address.py +38 -0
- bloqade/qasm2/dialects/core/stmts.py +94 -0
- bloqade/qasm2/dialects/expr/__init__.py +3 -0
- bloqade/qasm2/dialects/expr/_dialect.py +3 -0
- bloqade/qasm2/dialects/expr/_emit.py +103 -0
- bloqade/qasm2/dialects/expr/_from_python.py +86 -0
- bloqade/qasm2/dialects/expr/_interp.py +75 -0
- bloqade/qasm2/dialects/expr/stmts.py +262 -0
- bloqade/qasm2/dialects/glob.py +45 -0
- bloqade/qasm2/dialects/indexing.py +64 -0
- bloqade/qasm2/dialects/inline.py +76 -0
- bloqade/qasm2/dialects/noise.py +16 -0
- bloqade/qasm2/dialects/parallel.py +110 -0
- bloqade/qasm2/dialects/uop/__init__.py +4 -0
- bloqade/qasm2/dialects/uop/_dialect.py +3 -0
- bloqade/qasm2/dialects/uop/_emit.py +211 -0
- bloqade/qasm2/dialects/uop/schedule.py +89 -0
- bloqade/qasm2/dialects/uop/stmts.py +325 -0
- bloqade/qasm2/emit/__init__.py +1 -0
- bloqade/qasm2/emit/base.py +72 -0
- bloqade/qasm2/emit/gate.py +102 -0
- bloqade/qasm2/emit/main.py +106 -0
- bloqade/qasm2/emit/target.py +165 -0
- bloqade/qasm2/glob.py +24 -0
- bloqade/qasm2/groups.py +120 -0
- bloqade/qasm2/parallel.py +48 -0
- bloqade/qasm2/parse/__init__.py +37 -0
- bloqade/qasm2/parse/ast.py +235 -0
- bloqade/qasm2/parse/build.py +289 -0
- bloqade/qasm2/parse/lowering.py +553 -0
- bloqade/qasm2/parse/parser.py +5 -0
- bloqade/qasm2/parse/print.py +293 -0
- bloqade/qasm2/parse/qasm2.lark +75 -0
- bloqade/qasm2/parse/visitor.py +16 -0
- bloqade/qasm2/parse/visitor.pyi +39 -0
- bloqade/qasm2/passes/__init__.py +5 -0
- bloqade/qasm2/passes/fold.py +94 -0
- bloqade/qasm2/passes/glob.py +119 -0
- bloqade/qasm2/passes/noise.py +61 -0
- bloqade/qasm2/passes/parallel.py +176 -0
- bloqade/qasm2/passes/py2qasm.py +63 -0
- bloqade/qasm2/passes/qasm2py.py +61 -0
- bloqade/qasm2/rewrite/__init__.py +12 -0
- bloqade/qasm2/rewrite/desugar.py +28 -0
- bloqade/qasm2/rewrite/glob.py +103 -0
- bloqade/qasm2/rewrite/heuristic_noise.py +247 -0
- bloqade/qasm2/rewrite/native_gates.py +447 -0
- bloqade/qasm2/rewrite/parallel_to_uop.py +83 -0
- bloqade/qasm2/rewrite/register.py +45 -0
- bloqade/qasm2/rewrite/uop_to_parallel.py +395 -0
- bloqade/qasm2/types.py +39 -0
- bloqade/qbraid/__init__.py +2 -0
- bloqade/qbraid/lowering.py +324 -0
- bloqade/qbraid/schema.py +252 -0
- bloqade/qbraid/simulation_result.py +99 -0
- bloqade/qbraid/target.py +86 -0
- bloqade/squin/__init__.py +2 -0
- bloqade/squin/analysis/__init__.py +0 -0
- bloqade/squin/analysis/nsites/__init__.py +8 -0
- bloqade/squin/analysis/nsites/analysis.py +52 -0
- bloqade/squin/analysis/nsites/impls.py +69 -0
- bloqade/squin/analysis/nsites/lattice.py +49 -0
- bloqade/squin/analysis/schedule.py +244 -0
- bloqade/squin/groups.py +38 -0
- bloqade/squin/op/__init__.py +132 -0
- bloqade/squin/op/_dialect.py +3 -0
- bloqade/squin/op/complex.py +6 -0
- bloqade/squin/op/stmts.py +220 -0
- bloqade/squin/op/traits.py +43 -0
- bloqade/squin/op/types.py +10 -0
- bloqade/squin/qubit.py +118 -0
- bloqade/squin/wire.py +103 -0
- bloqade/stim/__init__.py +6 -0
- bloqade/stim/_wrappers.py +186 -0
- bloqade/stim/dialects/__init__.py +5 -0
- bloqade/stim/dialects/aux/__init__.py +11 -0
- bloqade/stim/dialects/aux/_dialect.py +3 -0
- bloqade/stim/dialects/aux/emit.py +102 -0
- bloqade/stim/dialects/aux/interp.py +39 -0
- bloqade/stim/dialects/aux/lowering.py +40 -0
- bloqade/stim/dialects/aux/stmts/__init__.py +14 -0
- bloqade/stim/dialects/aux/stmts/annotate.py +47 -0
- bloqade/stim/dialects/aux/stmts/const.py +95 -0
- bloqade/stim/dialects/aux/types.py +19 -0
- bloqade/stim/dialects/collapse/__init__.py +3 -0
- bloqade/stim/dialects/collapse/_dialect.py +3 -0
- bloqade/stim/dialects/collapse/emit.py +68 -0
- bloqade/stim/dialects/collapse/stmts/__init__.py +3 -0
- bloqade/stim/dialects/collapse/stmts/measure.py +45 -0
- bloqade/stim/dialects/collapse/stmts/pp_measure.py +14 -0
- bloqade/stim/dialects/collapse/stmts/reset.py +26 -0
- bloqade/stim/dialects/gate/__init__.py +3 -0
- bloqade/stim/dialects/gate/_dialect.py +3 -0
- bloqade/stim/dialects/gate/emit.py +87 -0
- bloqade/stim/dialects/gate/stmts/__init__.py +14 -0
- bloqade/stim/dialects/gate/stmts/base.py +31 -0
- bloqade/stim/dialects/gate/stmts/clifford_1q.py +53 -0
- bloqade/stim/dialects/gate/stmts/clifford_2q.py +11 -0
- bloqade/stim/dialects/gate/stmts/control_2q.py +21 -0
- bloqade/stim/dialects/gate/stmts/pp.py +15 -0
- bloqade/stim/dialects/noise/__init__.py +3 -0
- bloqade/stim/dialects/noise/_dialect.py +3 -0
- bloqade/stim/dialects/noise/emit.py +66 -0
- bloqade/stim/dialects/noise/stmts.py +77 -0
- bloqade/stim/emit/__init__.py +1 -0
- bloqade/stim/emit/stim.py +54 -0
- bloqade/stim/groups.py +26 -0
- bloqade/test_utils.py +35 -0
- bloqade/types.py +24 -0
- bloqade/visual/__init__.py +1 -0
- bloqade/visual/animation/__init__.py +0 -0
- bloqade/visual/animation/animate.py +267 -0
- bloqade/visual/animation/base.py +346 -0
- bloqade/visual/animation/gate_event.py +24 -0
- bloqade/visual/animation/runtime/__init__.py +0 -0
- bloqade/visual/animation/runtime/aod.py +36 -0
- bloqade/visual/animation/runtime/atoms.py +55 -0
- bloqade/visual/animation/runtime/ppoly.py +50 -0
- bloqade/visual/animation/runtime/qpustate.py +119 -0
- bloqade/visual/animation/runtime/utils.py +43 -0
- bloqade_circuit-0.1.0.dist-info/METADATA +70 -0
- bloqade_circuit-0.1.0.dist-info/RECORD +153 -0
- bloqade_circuit-0.1.0.dist-info/WHEEL +4 -0
- 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 ()
|