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