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,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])
|