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,79 @@
1
+ from kirin import interp
2
+
3
+ from bloqade.pyqrack.reg import (
4
+ CBitRef,
5
+ CRegister,
6
+ PyQrackReg,
7
+ QubitState,
8
+ Measurement,
9
+ PyQrackQubit,
10
+ )
11
+ from bloqade.pyqrack.base import PyQrackInterpreter
12
+ from bloqade.qasm2.dialects import core
13
+
14
+
15
+ @core.dialect.register(key="pyqrack")
16
+ class PyQrackMethods(interp.MethodTable):
17
+
18
+ @interp.impl(core.QRegNew)
19
+ def qreg_new(
20
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.QRegNew
21
+ ):
22
+ n_qubits: int = frame.get(stmt.n_qubits)
23
+ return (
24
+ PyQrackReg(
25
+ size=n_qubits,
26
+ sim_reg=interp.memory.sim_reg,
27
+ addrs=interp.memory.allocate(n_qubits),
28
+ qubit_state=[QubitState.Active] * n_qubits,
29
+ ),
30
+ )
31
+
32
+ @interp.impl(core.CRegNew)
33
+ def creg_new(
34
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.CRegNew
35
+ ):
36
+ n_bits: int = frame.get(stmt.n_bits)
37
+ return (CRegister(size=n_bits),)
38
+
39
+ @interp.impl(core.QRegGet)
40
+ def qreg_get(
41
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.QRegGet
42
+ ):
43
+ return (PyQrackQubit(ref=frame.get(stmt.reg), pos=frame.get(stmt.idx)),)
44
+
45
+ @interp.impl(core.CRegGet)
46
+ def creg_get(
47
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.CRegGet
48
+ ):
49
+ return (CBitRef(ref=frame.get(stmt.reg), pos=frame.get(stmt.idx)),)
50
+
51
+ @interp.impl(core.Measure)
52
+ def measure(
53
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.Measure
54
+ ):
55
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
56
+ carg: CBitRef = frame.get(stmt.carg)
57
+ if qarg.is_active():
58
+ carg.set_value(Measurement(qarg.sim_reg.m(qarg.addr)))
59
+ else:
60
+ carg.set_value(interp.loss_m_result)
61
+
62
+ return ()
63
+
64
+ @interp.impl(core.Reset)
65
+ def reset(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.Reset):
66
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
67
+ qarg.sim_reg.force_m(qarg.addr, 0)
68
+ return ()
69
+
70
+ @interp.impl(core.CRegEq)
71
+ def creg_eq(
72
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.CRegEq
73
+ ):
74
+ lhs: CRegister = frame.get(stmt.lhs)
75
+ rhs: CRegister = frame.get(stmt.rhs)
76
+ if len(lhs) != len(rhs):
77
+ return (False,)
78
+
79
+ return (all(left is right for left, right in zip(lhs, rhs)),)
@@ -0,0 +1,46 @@
1
+ from typing import Any
2
+
3
+ from kirin import interp
4
+ from kirin.dialects import ilist
5
+
6
+ from bloqade.pyqrack.reg import PyQrackQubit
7
+ from bloqade.pyqrack.base import PyQrackInterpreter
8
+ from bloqade.qasm2.dialects import parallel
9
+
10
+
11
+ @parallel.dialect.register(key="pyqrack")
12
+ class PyQrackMethods(interp.MethodTable):
13
+
14
+ @interp.impl(parallel.CZ)
15
+ def cz(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: parallel.CZ):
16
+
17
+ qargs: ilist.IList[PyQrackQubit, Any] = frame.get(stmt.qargs)
18
+ ctrls: ilist.IList[PyQrackQubit, Any] = frame.get(stmt.ctrls)
19
+ for qarg, ctrl in zip(qargs, ctrls):
20
+ if qarg.is_active() and ctrl.is_active():
21
+ interp.memory.sim_reg.mcz([ctrl.addr], qarg.addr)
22
+ return ()
23
+
24
+ @interp.impl(parallel.UGate)
25
+ def ugate(
26
+ self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: parallel.UGate
27
+ ):
28
+ qargs: ilist.IList[PyQrackQubit, Any] = frame.get(stmt.qargs)
29
+ theta, phi, lam = (
30
+ frame.get(stmt.theta),
31
+ frame.get(stmt.phi),
32
+ frame.get(stmt.lam),
33
+ )
34
+ for qarg in qargs:
35
+ if qarg.is_active():
36
+ interp.memory.sim_reg.u(qarg.addr, theta, phi, lam)
37
+ return ()
38
+
39
+ @interp.impl(parallel.RZ)
40
+ def rz(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: parallel.RZ):
41
+ qargs: ilist.IList[PyQrackQubit, Any] = frame.get(stmt.qargs)
42
+ phi = frame.get(stmt.theta)
43
+ for qarg in qargs:
44
+ if qarg.is_active():
45
+ interp.memory.sim_reg.r(3, phi, qarg.addr)
46
+ return ()
@@ -0,0 +1,247 @@
1
+ import math
2
+
3
+ from kirin import interp
4
+
5
+ from bloqade.pyqrack.reg import PyQrackQubit
6
+ from bloqade.qasm2.dialects import uop
7
+
8
+
9
+ @uop.dialect.register(key="pyqrack")
10
+ class PyQrackMethods(interp.MethodTable):
11
+ GATE_TO_METHOD = {
12
+ "x": "x",
13
+ "y": "y",
14
+ "z": "z",
15
+ "h": "h",
16
+ "s": "s",
17
+ "t": "t",
18
+ "cx": "mcx",
19
+ "CX": "mcx",
20
+ "cz": "mcz",
21
+ "cy": "mcy",
22
+ "ch": "mch",
23
+ "sdag": "adjs",
24
+ "sdg": "adjs",
25
+ "tdag": "adjt",
26
+ "tdg": "adjt",
27
+ }
28
+
29
+ AXIS_MAP = {"rx": 1, "ry": 2, "rz": 3, "crx": 1, "cry": 2, "crz": 3}
30
+
31
+ @interp.impl(uop.Barrier)
32
+ def barrier(
33
+ self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.Barrier
34
+ ):
35
+ return ()
36
+
37
+ @interp.impl(uop.X)
38
+ @interp.impl(uop.Y)
39
+ @interp.impl(uop.Z)
40
+ @interp.impl(uop.H)
41
+ @interp.impl(uop.S)
42
+ @interp.impl(uop.Sdag)
43
+ @interp.impl(uop.T)
44
+ @interp.impl(uop.Tdag)
45
+ def single_qubit_gate(
46
+ self,
47
+ interp: interp.Interpreter,
48
+ frame: interp.Frame,
49
+ stmt: uop.SingleQubitGate,
50
+ ):
51
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
52
+ if qarg.is_active():
53
+ getattr(qarg.sim_reg, self.GATE_TO_METHOD[stmt.name])(qarg.addr)
54
+ return ()
55
+
56
+ @interp.impl(uop.UGate)
57
+ def ugate(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.UGate):
58
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
59
+ if qarg.is_active():
60
+ qarg.sim_reg.u(
61
+ qarg.addr,
62
+ frame.get(stmt.theta),
63
+ frame.get(stmt.phi),
64
+ frame.get(stmt.lam),
65
+ )
66
+ return ()
67
+
68
+ @interp.impl(uop.Id)
69
+ def id(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.Id):
70
+ return ()
71
+
72
+ @interp.impl(uop.SX)
73
+ def sx(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.SX):
74
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
75
+ if qarg.is_active():
76
+ qarg.sim_reg.u(qarg.addr, math.pi / 2, math.pi / 2, -math.pi / 2)
77
+ return ()
78
+
79
+ @interp.impl(uop.SXdag)
80
+ def sx_dag(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.SX):
81
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
82
+ if qarg.is_active():
83
+ qarg.sim_reg.u(qarg.addr, math.pi * (1.5), math.pi / 2, math.pi / 2)
84
+ return ()
85
+
86
+ @interp.impl(uop.CSX)
87
+ def csx(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CSX):
88
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
89
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
90
+ if qarg.is_active() and ctrl.is_active():
91
+ qarg.sim_reg.mcu(
92
+ [ctrl.addr], qarg.addr, math.pi / 2, math.pi / 2, -math.pi / 2
93
+ )
94
+ return ()
95
+
96
+ @interp.impl(uop.Swap)
97
+ def swap(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.Swap):
98
+ qarg1: PyQrackQubit = frame.get(stmt.ctrl)
99
+ qarg2: PyQrackQubit = frame.get(stmt.qarg)
100
+ if qarg1.is_active() and qarg2.is_active():
101
+ qarg1.sim_reg.swap(qarg1.addr, qarg2.addr)
102
+ return ()
103
+
104
+ @interp.impl(uop.CSwap)
105
+ def cswap(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CSwap):
106
+ qarg1: PyQrackQubit = frame.get(stmt.qarg1)
107
+ qarg2: PyQrackQubit = frame.get(stmt.qarg2)
108
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
109
+ if qarg1.is_active() and qarg2.is_active():
110
+ qarg1.sim_reg.cswap([ctrl.addr], qarg1.addr, qarg2.addr)
111
+ return ()
112
+
113
+ @interp.impl(uop.CX)
114
+ @interp.impl(uop.CZ)
115
+ @interp.impl(uop.CY)
116
+ @interp.impl(uop.CH)
117
+ def control_gate(
118
+ self,
119
+ interp: interp.Interpreter,
120
+ frame: interp.Frame,
121
+ stmt: uop.CX | uop.CZ | uop.CY,
122
+ ):
123
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
124
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
125
+ if ctrl.is_active() and qarg.is_active():
126
+ getattr(qarg.sim_reg, self.GATE_TO_METHOD[stmt.name])(
127
+ [ctrl.addr], qarg.addr
128
+ )
129
+ return ()
130
+
131
+ @interp.impl(uop.CCX)
132
+ def ccx(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CCX):
133
+ ctrl1: PyQrackQubit = frame.get(stmt.ctrl1)
134
+ ctrl2: PyQrackQubit = frame.get(stmt.ctrl2)
135
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
136
+ if ctrl1.is_active() and ctrl2.is_active() and qarg.is_active():
137
+ qarg.sim_reg.mcx([ctrl1.addr, ctrl2.addr], qarg.addr)
138
+ return ()
139
+
140
+ @interp.impl(uop.RX)
141
+ @interp.impl(uop.RY)
142
+ @interp.impl(uop.RZ)
143
+ def rotation(
144
+ self,
145
+ interp: interp.Interpreter,
146
+ frame: interp.Frame,
147
+ stmt: uop.RX | uop.RY | uop.RZ,
148
+ ):
149
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
150
+ if qarg.is_active():
151
+ qarg.sim_reg.r(self.AXIS_MAP[stmt.name], frame.get(stmt.theta), qarg.addr)
152
+ return ()
153
+
154
+ @interp.impl(uop.U1)
155
+ def u1(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.U1):
156
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
157
+ if qarg.is_active():
158
+ qarg.sim_reg.u(qarg.addr, 0, 0, frame.get(stmt.lam))
159
+ return ()
160
+
161
+ @interp.impl(uop.U2)
162
+ def u2(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.U2):
163
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
164
+ if qarg.is_active():
165
+ qarg.sim_reg.u(
166
+ qarg.addr, math.pi / 2, frame.get(stmt.phi), frame.get(stmt.lam)
167
+ )
168
+ return ()
169
+
170
+ @interp.impl(uop.CRX)
171
+ @interp.impl(uop.CRY)
172
+ @interp.impl(uop.CRZ)
173
+ def crx(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CRX):
174
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
175
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
176
+ if qarg.is_active() and ctrl.is_active():
177
+ qarg.sim_reg.mcr(
178
+ self.AXIS_MAP[stmt.name], frame.get(stmt.lam), [ctrl.addr], qarg.addr
179
+ )
180
+ return ()
181
+
182
+ @interp.impl(uop.CU1)
183
+ def cu1(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CU1):
184
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
185
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
186
+ if qarg.is_active() and ctrl.is_active():
187
+ qarg.sim_reg.mcu([ctrl.addr], qarg.addr, 0, 0, frame.get(stmt.lam))
188
+ return ()
189
+
190
+ @interp.impl(uop.CU3)
191
+ def cu3(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CU3):
192
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
193
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
194
+ if qarg.is_active() and ctrl.is_active():
195
+ qarg.sim_reg.mcu(
196
+ [ctrl.addr],
197
+ qarg.addr,
198
+ frame.get(stmt.theta),
199
+ frame.get(stmt.phi),
200
+ frame.get(stmt.lam),
201
+ )
202
+ return ()
203
+
204
+ @interp.impl(uop.CU)
205
+ def cu(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.CU):
206
+ ctrl: PyQrackQubit = frame.get(stmt.ctrl)
207
+ qarg: PyQrackQubit = frame.get(stmt.qarg)
208
+ if qarg.is_active() and ctrl.is_active():
209
+ ctrl.sim_reg.u(ctrl.addr, 0, 0, frame.get(stmt.gamma))
210
+ qarg.sim_reg.mcu(
211
+ [ctrl.addr],
212
+ qarg.addr,
213
+ frame.get(stmt.theta),
214
+ frame.get(stmt.phi),
215
+ frame.get(stmt.lam),
216
+ )
217
+ return ()
218
+
219
+ @interp.impl(uop.RXX)
220
+ def rxx(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.RXX):
221
+ a: PyQrackQubit = frame.get(stmt.qarg)
222
+ b: PyQrackQubit = frame.get(stmt.ctrl)
223
+ theta = frame.get(stmt.theta)
224
+ sim_reg = a.sim_reg
225
+ if a.is_active() and b.is_active():
226
+ sim_reg.u(a.addr, math.pi / 2, theta, 0)
227
+ sim_reg.h(b.addr)
228
+ sim_reg.mcx([a.addr], b.addr)
229
+ sim_reg.u(b.addr, 0, 0, -theta)
230
+ sim_reg.mcx([a.addr], b.addr)
231
+ sim_reg.h(b.addr)
232
+ sim_reg.u(a.addr, math.pi / 2, -math.pi, math.pi - theta)
233
+
234
+ return ()
235
+
236
+ @interp.impl(uop.RZZ)
237
+ def rzz(self, interp: interp.Interpreter, frame: interp.Frame, stmt: uop.RZZ):
238
+ a: PyQrackQubit = frame.get(stmt.qarg)
239
+ b: PyQrackQubit = frame.get(stmt.ctrl)
240
+ theta = frame.get(stmt.theta)
241
+ sim_reg = a.sim_reg
242
+ if a.is_active() and b.is_active():
243
+ sim_reg.mcx([a.addr], b.addr)
244
+ sim_reg.u(b.addr, 0, 0, theta)
245
+ sim_reg.mcx([a.addr], b.addr)
246
+
247
+ return ()
bloqade/pyqrack/reg.py ADDED
@@ -0,0 +1,109 @@
1
+ import enum
2
+ from typing import TYPE_CHECKING, List
3
+ from dataclasses import dataclass
4
+
5
+ from bloqade.qasm2.types import QReg, Qubit
6
+
7
+ if TYPE_CHECKING:
8
+ from pyqrack import QrackSimulator
9
+
10
+
11
+ class Measurement(enum.IntEnum):
12
+ """Enumeration of measurement results."""
13
+
14
+ Zero = 0
15
+ One = 1
16
+ Lost = enum.auto()
17
+
18
+
19
+ class CRegister(list[Measurement]):
20
+ """Runtime representation of a classical register."""
21
+
22
+ def __init__(self, size: int):
23
+ super().__init__(Measurement.Zero for _ in range(size))
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class CBitRef:
28
+ """Object representing a reference to a classical bit."""
29
+
30
+ ref: CRegister
31
+ """The classical register that is holding this bit."""
32
+
33
+ pos: int
34
+ """The position of this bit in the classical register."""
35
+
36
+ def set_value(self, value: bool):
37
+ self.ref[self.pos] = value
38
+
39
+ def get_value(self):
40
+ return self.ref[self.pos]
41
+
42
+
43
+ class QubitState(enum.Enum):
44
+ Active = enum.auto()
45
+ Lost = enum.auto()
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class PyQrackReg(QReg):
50
+ """Simulation runtime value of a quantum register."""
51
+
52
+ size: int
53
+ """The number of qubits in this register."""
54
+
55
+ sim_reg: "QrackSimulator"
56
+ """The register of the simulator."""
57
+
58
+ addrs: tuple[int, ...]
59
+ """The global addresses of the qubits in this register."""
60
+
61
+ qubit_state: List[QubitState]
62
+ """The state of each qubit in this register."""
63
+
64
+ def drop(self, pos: int):
65
+ """Drop the qubit at the given position in-place.
66
+
67
+ Args
68
+ pos (int): The position of the qubit to drop.
69
+
70
+ """
71
+ assert self.qubit_state[pos] is QubitState.Active, "Qubit already lost"
72
+ self.qubit_state[pos] = QubitState.Lost
73
+
74
+ def __getitem__(self, pos: int):
75
+ return PyQrackQubit(self, pos)
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class PyQrackQubit(Qubit):
80
+ """The runtime representation of a qubit reference."""
81
+
82
+ ref: PyQrackReg
83
+ """The quantum register that is holding this qubit."""
84
+
85
+ pos: int
86
+ """The position of this qubit in the quantum register."""
87
+
88
+ @property
89
+ def sim_reg(self):
90
+ """The register of the simulator."""
91
+ return self.ref.sim_reg
92
+
93
+ @property
94
+ def addr(self) -> int:
95
+ """The global address of the qubit."""
96
+ return self.ref.addrs[self.pos]
97
+
98
+ def is_active(self) -> bool:
99
+ """Check if the qubit is active.
100
+
101
+ Returns
102
+ True if the qubit is active, False otherwise.
103
+
104
+ """
105
+ return self.ref.qubit_state[self.pos] is QubitState.Active
106
+
107
+ def drop(self):
108
+ """Drop the qubit in-place."""
109
+ self.ref.drop(self.pos)
@@ -0,0 +1,112 @@
1
+ from typing import List, TypeVar, ParamSpec
2
+ from dataclasses import field, dataclass
3
+
4
+ from kirin import ir
5
+ from kirin.passes import Fold
6
+
7
+ from bloqade.pyqrack.base import (
8
+ StackMemory,
9
+ DynamicMemory,
10
+ PyQrackOptions,
11
+ PyQrackInterpreter,
12
+ _default_pyqrack_args,
13
+ )
14
+ from bloqade.analysis.address import AnyAddress, AddressAnalysis
15
+
16
+ Params = ParamSpec("Params")
17
+ RetType = TypeVar("RetType")
18
+
19
+
20
+ @dataclass
21
+ class PyQrack:
22
+ """PyQrack target runtime for Bloqade."""
23
+
24
+ min_qubits: int = 0
25
+ """Minimum number of qubits required for the PyQrack simulator.
26
+ Useful when address analysis fails to determine the number of qubits.
27
+ """
28
+ dynamic_qubits: bool = False
29
+ """Whether to use dynamic qubit allocation. Cannot use with tensor network simulations."""
30
+
31
+ pyqrack_options: PyQrackOptions = field(default_factory=_default_pyqrack_args)
32
+ """Options to pass to the QrackSimulator object, node `qubitCount` will be overwritten."""
33
+
34
+ def __post_init__(self):
35
+ self.pyqrack_options = PyQrackOptions(
36
+ {**_default_pyqrack_args(), **self.pyqrack_options}
37
+ )
38
+
39
+ def _get_interp(self, mt: ir.Method[Params, RetType]):
40
+ if self.dynamic_qubits:
41
+
42
+ options = self.pyqrack_options.copy()
43
+ options["qubitCount"] = -1
44
+ return PyQrackInterpreter(mt.dialects, memory=DynamicMemory(options))
45
+ else:
46
+ address_analysis = AddressAnalysis(mt.dialects)
47
+ frame, _ = address_analysis.run_analysis(mt)
48
+ if self.min_qubits == 0 and any(
49
+ isinstance(a, AnyAddress) for a in frame.entries.values()
50
+ ):
51
+ raise ValueError(
52
+ "All addresses must be resolved. Or set min_qubits to a positive integer."
53
+ )
54
+
55
+ num_qubits = max(address_analysis.qubit_count, self.min_qubits)
56
+ options = self.pyqrack_options.copy()
57
+ options["qubitCount"] = num_qubits
58
+ memory = StackMemory(
59
+ options,
60
+ total=num_qubits,
61
+ )
62
+
63
+ return PyQrackInterpreter(mt.dialects, memory=memory)
64
+
65
+ def run(
66
+ self,
67
+ mt: ir.Method[Params, RetType],
68
+ *args: Params.args,
69
+ **kwargs: Params.kwargs,
70
+ ) -> RetType:
71
+ """Run the given kernel method on the PyQrack simulator.
72
+
73
+ Args
74
+ mt (Method):
75
+ The kernel method to run.
76
+
77
+ Returns
78
+ The result of the kernel method, if any.
79
+
80
+ """
81
+ fold = Fold(mt.dialects)
82
+ fold(mt)
83
+ return self._get_interp(mt).run(mt, args, kwargs).expect()
84
+
85
+ def multi_run(
86
+ self,
87
+ mt: ir.Method[Params, RetType],
88
+ _shots: int,
89
+ *args: Params.args,
90
+ **kwargs: Params.kwargs,
91
+ ) -> List[RetType]:
92
+ """Run the given kernel method on the PyQrack `_shots` times, caching analysis results.
93
+
94
+ Args
95
+ mt (Method):
96
+ The kernel method to run.
97
+ _shots (int):
98
+ The number of times to run the kernel method.
99
+
100
+ Returns
101
+ List of results of the kernel method, one for each shot.
102
+
103
+ """
104
+ fold = Fold(mt.dialects)
105
+ fold(mt)
106
+
107
+ interpreter = self._get_interp(mt)
108
+ batched_results = []
109
+ for _ in range(_shots):
110
+ batched_results.append(interpreter.run(mt, args, kwargs).expect())
111
+
112
+ return batched_results
@@ -0,0 +1,19 @@
1
+ from bloqade.types import Qubit as Qubit, QubitType as QubitType
2
+
3
+ from . import (
4
+ emit as emit,
5
+ glob as glob,
6
+ parse as parse,
7
+ dialects as dialects,
8
+ parallel as parallel,
9
+ )
10
+ from .types import (
11
+ Bit as Bit,
12
+ CReg as CReg,
13
+ QReg as QReg,
14
+ BitType as BitType,
15
+ CRegType as CRegType,
16
+ QRegType as QRegType,
17
+ )
18
+ from .groups import gate as gate, main as main, extended as extended
19
+ from ._wrappers import * # noqa: F403