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,102 @@
|
|
|
1
|
+
from dataclasses import field, dataclass
|
|
2
|
+
|
|
3
|
+
from kirin import ir, types, interp
|
|
4
|
+
from kirin.dialects import py, func, ilist
|
|
5
|
+
from kirin.ir.dialect import Dialect as Dialect
|
|
6
|
+
|
|
7
|
+
from bloqade.types import QubitType
|
|
8
|
+
from bloqade.qasm2.parse import ast
|
|
9
|
+
|
|
10
|
+
from .base import EmitError, EmitQASM2Base, EmitQASM2Frame
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _default_dialect_group():
|
|
14
|
+
from bloqade.qasm2.groups import gate
|
|
15
|
+
|
|
16
|
+
return gate
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class EmitQASM2Gate(EmitQASM2Base[ast.UOp | ast.Barrier, ast.Gate]):
|
|
21
|
+
keys = ["emit.qasm2.gate"]
|
|
22
|
+
dialects: ir.DialectGroup = field(default_factory=_default_dialect_group)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@ilist.dialect.register(key="emit.qasm2.gate")
|
|
26
|
+
class Ilist(interp.MethodTable):
|
|
27
|
+
|
|
28
|
+
@interp.impl(ilist.New)
|
|
29
|
+
def emit_ilist(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: ilist.New):
|
|
30
|
+
return (ilist.IList(data=frame.get_values(stmt.values)),)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@py.constant.dialect.register(key="emit.qasm2.gate")
|
|
34
|
+
class Constant(interp.MethodTable):
|
|
35
|
+
|
|
36
|
+
@interp.impl(py.Constant)
|
|
37
|
+
def emit_constant(
|
|
38
|
+
self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: py.Constant
|
|
39
|
+
):
|
|
40
|
+
return (stmt.value,)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@func.dialect.register(key="emit.qasm2.gate")
|
|
44
|
+
class Func(interp.MethodTable):
|
|
45
|
+
|
|
46
|
+
@interp.impl(func.Call)
|
|
47
|
+
def emit_call(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: func.Call):
|
|
48
|
+
raise EmitError("cannot emit dynamic call")
|
|
49
|
+
|
|
50
|
+
@interp.impl(func.Invoke)
|
|
51
|
+
def emit_invoke(
|
|
52
|
+
self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: func.Invoke
|
|
53
|
+
):
|
|
54
|
+
ret = ()
|
|
55
|
+
if len(stmt.results) == 1 and stmt.results[0].type.is_subseteq(types.NoneType):
|
|
56
|
+
ret = (None,)
|
|
57
|
+
elif len(stmt.results) > 0:
|
|
58
|
+
raise EmitError(
|
|
59
|
+
"cannot emit invoke with results, this "
|
|
60
|
+
"is not compatible QASM2 gate routine"
|
|
61
|
+
" (consider pass qreg/creg by argument)"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
cparams, qparams = [], []
|
|
65
|
+
for arg in stmt.inputs:
|
|
66
|
+
if arg.type.is_subseteq(QubitType):
|
|
67
|
+
qparams.append(frame.get(arg))
|
|
68
|
+
else:
|
|
69
|
+
cparams.append(frame.get(arg))
|
|
70
|
+
|
|
71
|
+
frame.body.append(
|
|
72
|
+
ast.Instruction(
|
|
73
|
+
name=ast.Name(stmt.callee.sym_name),
|
|
74
|
+
params=cparams,
|
|
75
|
+
qargs=qparams,
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
return ret
|
|
79
|
+
|
|
80
|
+
@interp.impl(func.Lambda)
|
|
81
|
+
@interp.impl(func.GetField)
|
|
82
|
+
def emit_err(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt):
|
|
83
|
+
raise EmitError(f"illegal statement {stmt.name} for QASM2 gate routine")
|
|
84
|
+
|
|
85
|
+
@interp.impl(func.Return)
|
|
86
|
+
@interp.impl(func.ConstantNone)
|
|
87
|
+
def ignore(self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt):
|
|
88
|
+
return ()
|
|
89
|
+
|
|
90
|
+
@interp.impl(func.Function)
|
|
91
|
+
def emit_func(
|
|
92
|
+
self, emit: EmitQASM2Gate, frame: EmitQASM2Frame, stmt: func.Function
|
|
93
|
+
):
|
|
94
|
+
emit.run_ssacfg_region(frame, stmt.body)
|
|
95
|
+
cparams, qparams = [], []
|
|
96
|
+
for arg in stmt.args:
|
|
97
|
+
if arg.type.is_subseteq(QubitType):
|
|
98
|
+
qparams.append(frame.get(arg))
|
|
99
|
+
else:
|
|
100
|
+
cparams.append(frame.get(arg))
|
|
101
|
+
emit.output = ast.Gate(stmt.sym_name, cparams, qparams, frame.body)
|
|
102
|
+
return ()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from kirin import ir, interp
|
|
4
|
+
from kirin.dialects import cf, scf, func
|
|
5
|
+
from kirin.ir.dialect import Dialect as Dialect
|
|
6
|
+
|
|
7
|
+
from bloqade.qasm2.parse import ast
|
|
8
|
+
|
|
9
|
+
from .base import EmitQASM2Base, EmitQASM2Frame
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class EmitQASM2Main(EmitQASM2Base[ast.Statement, ast.MainProgram]):
|
|
14
|
+
keys = ["emit.qasm2.main", "emit.qasm2.gate"]
|
|
15
|
+
dialects: ir.DialectGroup
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@func.dialect.register(key="emit.qasm2.main")
|
|
19
|
+
class Func(interp.MethodTable):
|
|
20
|
+
|
|
21
|
+
@interp.impl(func.Function)
|
|
22
|
+
def emit_func(
|
|
23
|
+
self, emit: EmitQASM2Main, frame: EmitQASM2Frame, stmt: func.Function
|
|
24
|
+
):
|
|
25
|
+
from bloqade.qasm2.dialects import glob, noise, parallel
|
|
26
|
+
|
|
27
|
+
emit.run_ssacfg_region(frame, stmt.body)
|
|
28
|
+
if emit.dialects.data.intersection(
|
|
29
|
+
(parallel.dialect, glob.dialect, noise.dialect)
|
|
30
|
+
):
|
|
31
|
+
header = ast.Kirin([dialect.name for dialect in emit.dialects])
|
|
32
|
+
else:
|
|
33
|
+
header = ast.OPENQASM(ast.Version(2, 0))
|
|
34
|
+
|
|
35
|
+
emit.output = ast.MainProgram(header=header, statements=frame.body)
|
|
36
|
+
return ()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@cf.dialect.register(key="emit.qasm2.main")
|
|
40
|
+
class Cf(interp.MethodTable):
|
|
41
|
+
|
|
42
|
+
@interp.impl(cf.Branch)
|
|
43
|
+
def emit_branch(self, emit: EmitQASM2Main, frame: EmitQASM2Frame, stmt: cf.Branch):
|
|
44
|
+
frame.worklist.append(
|
|
45
|
+
interp.Successor(stmt.successor, frame.get_values(stmt.arguments))
|
|
46
|
+
)
|
|
47
|
+
return ()
|
|
48
|
+
|
|
49
|
+
@interp.impl(cf.ConditionalBranch)
|
|
50
|
+
def emit_conditional_branch(
|
|
51
|
+
self, emit: EmitQASM2Main, frame: EmitQASM2Frame, stmt: cf.ConditionalBranch
|
|
52
|
+
):
|
|
53
|
+
cond = emit.assert_node(ast.Cmp, frame.get(stmt.cond))
|
|
54
|
+
body_frame = emit.new_frame(stmt)
|
|
55
|
+
body_frame.entries.update(frame.entries)
|
|
56
|
+
body_frame.set_values(
|
|
57
|
+
stmt.then_successor.args, frame.get_values(stmt.then_arguments)
|
|
58
|
+
)
|
|
59
|
+
emit.emit_block(body_frame, stmt.then_successor)
|
|
60
|
+
frame.body.append(
|
|
61
|
+
ast.IfStmt(
|
|
62
|
+
cond,
|
|
63
|
+
body=body_frame.body, # type: ignore
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
frame.worklist.append(
|
|
67
|
+
interp.Successor(stmt.else_successor, frame.get_values(stmt.else_arguments))
|
|
68
|
+
)
|
|
69
|
+
return ()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@scf.dialect.register(key="emit.qasm2.main")
|
|
73
|
+
class Scf(interp.MethodTable):
|
|
74
|
+
|
|
75
|
+
@interp.impl(scf.Yield)
|
|
76
|
+
def emit_yield(self, emit: EmitQASM2Main, frame: EmitQASM2Frame, stmt: scf.Yield):
|
|
77
|
+
return frame.get_values(stmt.values)
|
|
78
|
+
|
|
79
|
+
@interp.impl(scf.IfElse)
|
|
80
|
+
def emit_if_else(
|
|
81
|
+
self, emit: EmitQASM2Main, frame: EmitQASM2Frame, stmt: scf.IfElse
|
|
82
|
+
):
|
|
83
|
+
else_stmts = stmt.else_body.blocks[0].stmts
|
|
84
|
+
if not (
|
|
85
|
+
len(else_stmts) == 0
|
|
86
|
+
or len(else_stmts) == 1
|
|
87
|
+
and isinstance(else_stmts.at(0), scf.Yield)
|
|
88
|
+
):
|
|
89
|
+
raise interp.InterpreterError(
|
|
90
|
+
"cannot lower if-else with non-empty else block"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
cond = emit.assert_node(ast.Cmp, frame.get(stmt.cond))
|
|
94
|
+
then_frame = emit.new_frame(stmt)
|
|
95
|
+
then_frame.entries.update(frame.entries)
|
|
96
|
+
emit.emit_block(then_frame, stmt.then_body.blocks[0])
|
|
97
|
+
frame.body.append(
|
|
98
|
+
ast.IfStmt(
|
|
99
|
+
cond,
|
|
100
|
+
body=then_frame.body, # type: ignore
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
term = stmt.then_body.blocks[0].last_stmt
|
|
104
|
+
if isinstance(term, scf.Yield):
|
|
105
|
+
return then_frame.get_values(term.values)
|
|
106
|
+
return ()
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
3
|
+
from kirin import ir
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from kirin.analysis import CallGraph
|
|
6
|
+
from kirin.dialects import ilist
|
|
7
|
+
|
|
8
|
+
from bloqade.qasm2.parse import ast, pprint
|
|
9
|
+
from bloqade.qasm2.passes.fold import QASM2Fold
|
|
10
|
+
from bloqade.qasm2.passes.glob import GlobalToParallel
|
|
11
|
+
from bloqade.qasm2.passes.py2qasm import Py2QASM
|
|
12
|
+
from bloqade.qasm2.passes.parallel import ParallelToUOp
|
|
13
|
+
|
|
14
|
+
from .gate import EmitQASM2Gate
|
|
15
|
+
from .main import EmitQASM2Main
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class QASM2:
|
|
19
|
+
"""QASM2 target for Bloqade kernels.
|
|
20
|
+
|
|
21
|
+
QASM2 target that accepts a Bloqade kernel and produces an AST that you can then obtain a string for printing or saving as a file.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
qelib1: bool = True,
|
|
27
|
+
allow_parallel: bool = False,
|
|
28
|
+
allow_global: bool = False,
|
|
29
|
+
custom_gate: bool = True,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Initialize the QASM2 target.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
allow_parallel (bool):
|
|
35
|
+
Allow parallel gate in the resulting QASM2 AST. Defaults to `False`.
|
|
36
|
+
In the case its False, and the input kernel uses parallel gates, they will get rewrite into uop gates.
|
|
37
|
+
|
|
38
|
+
allow_global (bool):
|
|
39
|
+
Allow global gate in the resulting QASM2 AST. Defaults to `False`.
|
|
40
|
+
In the case its False, and the input kernel uses global gates, they will get rewrite into parallel gates.
|
|
41
|
+
If both `allow_parallel` and `allow_global` are False, the input kernel will be rewritten to use uop gates.
|
|
42
|
+
|
|
43
|
+
qelib1 (bool):
|
|
44
|
+
Include the `include "qelib1.inc"` line in the resulting QASM2 AST that's
|
|
45
|
+
submitted to qBraid. Defaults to `True`.
|
|
46
|
+
custom_gate (bool):
|
|
47
|
+
Include the custom gate definitions in the resulting QASM2 AST. Defaults to `True`. If `False`, all the qasm2.gate will be inlined.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
from bloqade import qasm2
|
|
53
|
+
|
|
54
|
+
self.main_target = qasm2.main
|
|
55
|
+
self.gate_target = qasm2.gate
|
|
56
|
+
|
|
57
|
+
self.qelib1 = qelib1
|
|
58
|
+
self.custom_gate = custom_gate
|
|
59
|
+
self.allow_parallel = allow_parallel
|
|
60
|
+
self.allow_global = allow_global
|
|
61
|
+
|
|
62
|
+
if allow_parallel:
|
|
63
|
+
self.main_target = self.main_target.add(qasm2.dialects.parallel)
|
|
64
|
+
self.gate_target = self.gate_target.add(qasm2.dialects.parallel)
|
|
65
|
+
|
|
66
|
+
if allow_global:
|
|
67
|
+
self.main_target = self.main_target.add(qasm2.dialects.glob)
|
|
68
|
+
self.gate_target = self.gate_target.add(qasm2.dialects.glob)
|
|
69
|
+
|
|
70
|
+
if allow_global or allow_parallel:
|
|
71
|
+
self.main_target = self.main_target.add(ilist)
|
|
72
|
+
self.gate_target = self.gate_target.add(ilist)
|
|
73
|
+
|
|
74
|
+
def emit(self, entry: ir.Method) -> ast.MainProgram:
|
|
75
|
+
"""Emit a QASM2 AST from the Bloqade kernel.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
entry (ir.Method):
|
|
79
|
+
The Bloqade kernel to convert to the QASM2 AST
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
ast.MainProgram:
|
|
83
|
+
A QASM2 AST object
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
assert len(entry.args) == 0, "entry method should not have arguments"
|
|
87
|
+
|
|
88
|
+
# make a cloned instance of kernel
|
|
89
|
+
entry = entry.similar()
|
|
90
|
+
QASM2Fold(entry.dialects, inline_gate_subroutine=not self.custom_gate).fixpoint(
|
|
91
|
+
entry
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if not self.allow_global:
|
|
95
|
+
# rewrite global to parallel
|
|
96
|
+
GlobalToParallel(dialects=entry.dialects)(entry)
|
|
97
|
+
|
|
98
|
+
if not self.allow_parallel:
|
|
99
|
+
# rewrite parallel to uop
|
|
100
|
+
ParallelToUOp(dialects=entry.dialects)(entry)
|
|
101
|
+
|
|
102
|
+
Py2QASM(entry.dialects)(entry)
|
|
103
|
+
target_main = EmitQASM2Main(self.main_target)
|
|
104
|
+
target_main.run(
|
|
105
|
+
entry, tuple(ast.Name(name) for name in entry.arg_names[1:])
|
|
106
|
+
).expect()
|
|
107
|
+
|
|
108
|
+
main_program = target_main.output
|
|
109
|
+
assert main_program is not None, f"failed to emit {entry.sym_name}"
|
|
110
|
+
|
|
111
|
+
extra = []
|
|
112
|
+
if self.qelib1:
|
|
113
|
+
extra.append(ast.Include("qelib1.inc"))
|
|
114
|
+
|
|
115
|
+
if self.custom_gate:
|
|
116
|
+
cg = CallGraph(entry)
|
|
117
|
+
target_gate = EmitQASM2Gate(self.gate_target)
|
|
118
|
+
|
|
119
|
+
for _, fn in cg.defs.items():
|
|
120
|
+
if fn is entry:
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
fn = fn.similar()
|
|
124
|
+
QASM2Fold(fn.dialects).fixpoint(fn)
|
|
125
|
+
|
|
126
|
+
if not self.allow_global:
|
|
127
|
+
# rewrite global to parallel
|
|
128
|
+
GlobalToParallel(dialects=fn.dialects)(fn)
|
|
129
|
+
|
|
130
|
+
if not self.allow_parallel:
|
|
131
|
+
# rewrite parallel to uop
|
|
132
|
+
ParallelToUOp(dialects=fn.dialects)(fn)
|
|
133
|
+
|
|
134
|
+
Py2QASM(fn.dialects)(fn)
|
|
135
|
+
|
|
136
|
+
target_gate.run(
|
|
137
|
+
fn, tuple(ast.Name(name) for name in fn.arg_names[1:])
|
|
138
|
+
).expect()
|
|
139
|
+
assert target_gate.output is not None, f"failed to emit {fn.sym_name}"
|
|
140
|
+
extra.append(target_gate.output)
|
|
141
|
+
|
|
142
|
+
main_program.statements = extra + main_program.statements
|
|
143
|
+
return main_program
|
|
144
|
+
|
|
145
|
+
def emit_str(self, entry: ir.Method) -> str:
|
|
146
|
+
"""Emit a QASM2 AST from the Bloqade kernel.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
entry (ir.Method):
|
|
150
|
+
The Bloqade kernel to convert to the QASM2 AST
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
str:
|
|
154
|
+
A string with the QASM2 representation of the kernel
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
console = Console(
|
|
158
|
+
file=io.StringIO(),
|
|
159
|
+
force_terminal=False,
|
|
160
|
+
force_interactive=False,
|
|
161
|
+
force_jupyter=False,
|
|
162
|
+
record=True,
|
|
163
|
+
)
|
|
164
|
+
pprint(self.emit(entry), console=console)
|
|
165
|
+
return console.export_text()
|
bloqade/qasm2/glob.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""QASM2 extension for global gates."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from kirin.dialects import ilist
|
|
6
|
+
from kirin.lowering import wraps
|
|
7
|
+
|
|
8
|
+
from .types import QReg
|
|
9
|
+
from .dialects import glob
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@wraps(glob.UGate)
|
|
13
|
+
def u(
|
|
14
|
+
theta: float, phi: float, lam: float, registers: ilist.IList[QReg, Any] | list
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Apply a U gate to all qubits in the input registers.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
theta (float): The angle theta.
|
|
20
|
+
phi (float): The angle phi.
|
|
21
|
+
lam (float): The angle lam.
|
|
22
|
+
registers (IList[QReg] | list[QReg]): The registers to apply the gate to.
|
|
23
|
+
|
|
24
|
+
"""
|
bloqade/qasm2/groups.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from kirin import ir, passes
|
|
2
|
+
from kirin.prelude import structural_no_opt
|
|
3
|
+
from kirin.dialects import scf, func, ilist, lowering
|
|
4
|
+
|
|
5
|
+
from bloqade.qasm2.dialects import (
|
|
6
|
+
uop,
|
|
7
|
+
core,
|
|
8
|
+
expr,
|
|
9
|
+
glob,
|
|
10
|
+
noise,
|
|
11
|
+
inline,
|
|
12
|
+
indexing,
|
|
13
|
+
parallel,
|
|
14
|
+
)
|
|
15
|
+
from bloqade.qasm2.rewrite.desugar import IndexingDesugarPass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@ir.dialect_group([uop, func, expr, lowering.func, lowering.call])
|
|
19
|
+
def gate(self):
|
|
20
|
+
fold_pass = passes.Fold(self)
|
|
21
|
+
typeinfer_pass = passes.TypeInfer(self)
|
|
22
|
+
|
|
23
|
+
def run_pass(
|
|
24
|
+
method: ir.Method,
|
|
25
|
+
*,
|
|
26
|
+
fold: bool = True,
|
|
27
|
+
):
|
|
28
|
+
method.verify()
|
|
29
|
+
|
|
30
|
+
if isinstance(method.code, func.Function):
|
|
31
|
+
new_code = expr.GateFunction(
|
|
32
|
+
sym_name=method.code.sym_name,
|
|
33
|
+
signature=method.code.signature,
|
|
34
|
+
body=method.code.body,
|
|
35
|
+
)
|
|
36
|
+
method.code = new_code
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
"Gate Method code must be a Function, cannot be lambda/closure"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if fold:
|
|
43
|
+
fold_pass(method)
|
|
44
|
+
|
|
45
|
+
typeinfer_pass(method)
|
|
46
|
+
method.verify_type()
|
|
47
|
+
|
|
48
|
+
return run_pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@ir.dialect_group(
|
|
52
|
+
[
|
|
53
|
+
uop,
|
|
54
|
+
expr,
|
|
55
|
+
core,
|
|
56
|
+
scf,
|
|
57
|
+
indexing,
|
|
58
|
+
func,
|
|
59
|
+
lowering.func,
|
|
60
|
+
lowering.call,
|
|
61
|
+
]
|
|
62
|
+
)
|
|
63
|
+
def main(self):
|
|
64
|
+
fold_pass = passes.Fold(self)
|
|
65
|
+
typeinfer_pass = passes.TypeInfer(self)
|
|
66
|
+
|
|
67
|
+
def run_pass(
|
|
68
|
+
method: ir.Method,
|
|
69
|
+
*,
|
|
70
|
+
fold: bool = True,
|
|
71
|
+
):
|
|
72
|
+
method.verify()
|
|
73
|
+
# TODO make special Function rewrite
|
|
74
|
+
|
|
75
|
+
if fold:
|
|
76
|
+
fold_pass(method)
|
|
77
|
+
|
|
78
|
+
typeinfer_pass(method)
|
|
79
|
+
method.verify_type()
|
|
80
|
+
|
|
81
|
+
return run_pass
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@ir.dialect_group(
|
|
85
|
+
structural_no_opt.union(
|
|
86
|
+
[
|
|
87
|
+
inline,
|
|
88
|
+
uop,
|
|
89
|
+
glob,
|
|
90
|
+
noise,
|
|
91
|
+
parallel,
|
|
92
|
+
core,
|
|
93
|
+
]
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
def extended(self):
|
|
97
|
+
fold_pass = passes.Fold(self)
|
|
98
|
+
typeinfer_pass = passes.TypeInfer(self)
|
|
99
|
+
ilist_desugar_pass = ilist.IListDesugar(self)
|
|
100
|
+
indexing_desugar_pass = IndexingDesugarPass(self)
|
|
101
|
+
|
|
102
|
+
def run_pass(
|
|
103
|
+
mt: ir.Method,
|
|
104
|
+
*,
|
|
105
|
+
fold: bool = True,
|
|
106
|
+
typeinfer: bool = True,
|
|
107
|
+
):
|
|
108
|
+
mt.verify()
|
|
109
|
+
if fold:
|
|
110
|
+
fold_pass.fixpoint(mt)
|
|
111
|
+
|
|
112
|
+
if typeinfer:
|
|
113
|
+
typeinfer_pass(mt)
|
|
114
|
+
ilist_desugar_pass(mt)
|
|
115
|
+
indexing_desugar_pass(mt)
|
|
116
|
+
if typeinfer:
|
|
117
|
+
typeinfer_pass(mt) # fix types after desugaring
|
|
118
|
+
mt.verify_type()
|
|
119
|
+
|
|
120
|
+
return run_pass
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""QASM2 extension for parallel execution of gates."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from kirin.dialects import ilist
|
|
6
|
+
from kirin.lowering import wraps
|
|
7
|
+
|
|
8
|
+
from .types import Qubit
|
|
9
|
+
from .dialects import parallel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@wraps(parallel.CZ)
|
|
13
|
+
def cz(
|
|
14
|
+
ctrls: ilist.IList[Qubit, Any] | list, qargs: ilist.IList[Qubit, Any] | list
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Apply a controlled-Z gate to input qubits in parallel.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
ctrls (IList[Qubit] | list[Qubit]): The control qubits.
|
|
20
|
+
qargs (IList[Qubit] | list[Qubit]): The target qubits.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@wraps(parallel.UGate)
|
|
26
|
+
def u(
|
|
27
|
+
qargs: ilist.IList[Qubit, Any] | list, theta: float, phi: float, lam: float
|
|
28
|
+
) -> None:
|
|
29
|
+
"""Apply a U gate to input qubits in parallel.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
qargs (IList[Qubit] | list[Qubit]): The target qubits.
|
|
33
|
+
theta (float): The angle theta.
|
|
34
|
+
phi (float): The angle phi.
|
|
35
|
+
lam (float): The angle lam.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@wraps(parallel.RZ)
|
|
41
|
+
def rz(qargs: ilist.IList[Qubit, Any] | list, theta: float) -> None:
|
|
42
|
+
"""Apply a RZ gate to input qubits in parallel.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
qargs (IList[Qubit] | list[Qubit]): The target qubits.
|
|
46
|
+
theta (float): The angle theta.
|
|
47
|
+
|
|
48
|
+
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
from . import ast as ast
|
|
6
|
+
from .build import Build
|
|
7
|
+
from .print import Printer as Printer
|
|
8
|
+
from .parser import qasm2_parser as lark_parser
|
|
9
|
+
from .visitor import Visitor as Visitor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def loads(txt: str):
|
|
13
|
+
raw = lark_parser.parse(txt)
|
|
14
|
+
return Build().build_mainprogram(raw)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def loadfile(file: str | pathlib.Path):
|
|
18
|
+
with open(file) as f:
|
|
19
|
+
return loads(f.read())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def pprint(node: ast.Node, *, console: Console | None = None):
|
|
23
|
+
if console:
|
|
24
|
+
return Printer(console).visit(node)
|
|
25
|
+
else:
|
|
26
|
+
Printer().visit(node)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def spprint(node: ast.Node, *, console: Console | None = None):
|
|
30
|
+
if console:
|
|
31
|
+
printer = Printer(console)
|
|
32
|
+
else:
|
|
33
|
+
printer = Printer()
|
|
34
|
+
|
|
35
|
+
with printer.string_io() as stream:
|
|
36
|
+
printer.visit(node)
|
|
37
|
+
return stream.getvalue()
|