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,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
+ """
@@ -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()