bloqade-circuit 0.6.4__py3-none-any.whl → 0.9.1__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.
- bloqade/analysis/address/__init__.py +8 -4
- bloqade/analysis/address/analysis.py +123 -33
- bloqade/analysis/address/impls.py +293 -90
- bloqade/analysis/address/lattice.py +209 -24
- bloqade/analysis/fidelity/analysis.py +11 -23
- bloqade/analysis/measure_id/analysis.py +18 -20
- bloqade/analysis/measure_id/impls.py +31 -29
- bloqade/annotate/__init__.py +6 -0
- bloqade/annotate/_dialect.py +3 -0
- bloqade/annotate/_interface.py +22 -0
- bloqade/annotate/stmts.py +29 -0
- bloqade/annotate/types.py +13 -0
- bloqade/cirq_utils/__init__.py +4 -2
- bloqade/cirq_utils/emit/__init__.py +3 -0
- bloqade/cirq_utils/emit/base.py +246 -0
- bloqade/cirq_utils/emit/gate.py +104 -0
- bloqade/cirq_utils/emit/noise.py +90 -0
- bloqade/cirq_utils/emit/qubit.py +35 -0
- bloqade/cirq_utils/lowering.py +660 -0
- bloqade/cirq_utils/noise/__init__.py +0 -2
- bloqade/cirq_utils/noise/_two_zone_utils.py +7 -15
- bloqade/cirq_utils/noise/model.py +151 -191
- bloqade/cirq_utils/noise/transform.py +2 -2
- bloqade/cirq_utils/parallelize.py +9 -6
- bloqade/gemini/__init__.py +1 -0
- bloqade/gemini/analysis/__init__.py +3 -0
- bloqade/gemini/analysis/logical_validation/__init__.py +1 -0
- bloqade/gemini/analysis/logical_validation/analysis.py +17 -0
- bloqade/gemini/analysis/logical_validation/impls.py +101 -0
- bloqade/gemini/groups.py +67 -0
- bloqade/native/__init__.py +23 -0
- bloqade/native/_prelude.py +45 -0
- bloqade/native/dialects/__init__.py +0 -0
- bloqade/native/dialects/gate/__init__.py +2 -0
- bloqade/native/dialects/gate/_dialect.py +3 -0
- bloqade/native/dialects/gate/_interface.py +32 -0
- bloqade/native/dialects/gate/stmts.py +31 -0
- bloqade/native/stdlib/__init__.py +0 -0
- bloqade/native/stdlib/broadcast.py +246 -0
- bloqade/native/stdlib/simple.py +220 -0
- bloqade/native/upstream/__init__.py +4 -0
- bloqade/native/upstream/squin2native.py +79 -0
- bloqade/pyqrack/__init__.py +2 -2
- bloqade/pyqrack/base.py +7 -1
- bloqade/pyqrack/device.py +192 -18
- bloqade/pyqrack/native.py +49 -0
- bloqade/pyqrack/reg.py +6 -6
- bloqade/pyqrack/squin/gate/__init__.py +1 -0
- bloqade/pyqrack/squin/gate/gate.py +136 -0
- bloqade/pyqrack/squin/noise/native.py +120 -54
- bloqade/pyqrack/squin/qubit.py +39 -36
- bloqade/pyqrack/target.py +5 -4
- bloqade/pyqrack/task.py +114 -7
- bloqade/qasm2/_qasm_loading.py +3 -3
- bloqade/qasm2/dialects/core/address.py +21 -12
- bloqade/qasm2/dialects/expr/_emit.py +19 -8
- bloqade/qasm2/dialects/expr/stmts.py +7 -7
- bloqade/qasm2/dialects/noise/fidelity.py +4 -8
- bloqade/qasm2/dialects/noise/model.py +2 -1
- bloqade/qasm2/emit/base.py +16 -11
- bloqade/qasm2/emit/gate.py +11 -8
- bloqade/qasm2/emit/main.py +103 -3
- bloqade/qasm2/emit/target.py +9 -5
- bloqade/qasm2/groups.py +3 -2
- bloqade/qasm2/parse/lowering.py +0 -1
- bloqade/qasm2/passes/fold.py +14 -73
- bloqade/qasm2/passes/glob.py +2 -2
- bloqade/qasm2/passes/noise.py +1 -1
- bloqade/qasm2/passes/parallel.py +7 -5
- bloqade/qasm2/rewrite/__init__.py +0 -1
- bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
- bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
- bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
- bloqade/qasm2/rewrite/register.py +2 -2
- bloqade/qasm2/rewrite/uop_to_parallel.py +4 -2
- bloqade/qbraid/lowering.py +1 -0
- bloqade/qbraid/schema.py +2 -2
- bloqade/qubit/__init__.py +12 -0
- bloqade/qubit/_dialect.py +3 -0
- bloqade/qubit/_interface.py +49 -0
- bloqade/qubit/_prelude.py +45 -0
- bloqade/qubit/analysis/__init__.py +1 -0
- bloqade/qubit/analysis/address_impl.py +40 -0
- bloqade/qubit/stdlib/__init__.py +2 -0
- bloqade/qubit/stdlib/_new.py +34 -0
- bloqade/qubit/stdlib/broadcast.py +62 -0
- bloqade/qubit/stdlib/simple.py +59 -0
- bloqade/qubit/stmts.py +60 -0
- bloqade/rewrite/passes/__init__.py +6 -0
- bloqade/rewrite/passes/aggressive_unroll.py +103 -0
- bloqade/rewrite/passes/callgraph.py +116 -0
- bloqade/rewrite/passes/canonicalize_ilist.py +20 -14
- bloqade/rewrite/rules/split_ifs.py +18 -1
- bloqade/squin/__init__.py +47 -14
- bloqade/squin/analysis/__init__.py +0 -1
- bloqade/squin/analysis/schedule.py +10 -11
- bloqade/squin/gate/__init__.py +2 -0
- bloqade/squin/gate/_dialect.py +3 -0
- bloqade/squin/gate/_interface.py +98 -0
- bloqade/squin/gate/stmts.py +125 -0
- bloqade/squin/groups.py +5 -22
- bloqade/squin/noise/__init__.py +1 -10
- bloqade/squin/noise/_dialect.py +1 -1
- bloqade/squin/noise/_interface.py +45 -0
- bloqade/squin/noise/stmts.py +66 -28
- bloqade/squin/rewrite/U3_to_clifford.py +70 -51
- bloqade/squin/rewrite/__init__.py +0 -2
- bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
- bloqade/squin/rewrite/wrap_analysis.py +4 -35
- bloqade/squin/stdlib/__init__.py +0 -0
- bloqade/squin/stdlib/broadcast/__init__.py +34 -0
- bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
- bloqade/squin/stdlib/broadcast/gate.py +260 -0
- bloqade/squin/stdlib/broadcast/noise.py +144 -0
- bloqade/squin/stdlib/simple/__init__.py +33 -0
- bloqade/squin/stdlib/simple/gate.py +242 -0
- bloqade/squin/stdlib/simple/noise.py +126 -0
- bloqade/stim/__init__.py +1 -0
- bloqade/stim/_wrappers.py +6 -0
- bloqade/stim/dialects/auxiliary/emit.py +19 -18
- bloqade/stim/dialects/collapse/emit_str.py +7 -8
- bloqade/stim/dialects/gate/emit.py +9 -10
- bloqade/stim/dialects/noise/emit.py +17 -13
- bloqade/stim/dialects/noise/stmts.py +5 -3
- bloqade/stim/emit/__init__.py +1 -0
- bloqade/stim/emit/impls.py +16 -0
- bloqade/stim/emit/stim_str.py +48 -31
- bloqade/stim/groups.py +12 -2
- bloqade/stim/parse/lowering.py +14 -17
- bloqade/stim/passes/__init__.py +0 -2
- bloqade/stim/passes/flatten.py +26 -0
- bloqade/stim/passes/simplify_ifs.py +6 -1
- bloqade/stim/passes/squin_to_stim.py +9 -84
- bloqade/stim/rewrite/__init__.py +2 -4
- bloqade/stim/rewrite/get_record_util.py +24 -0
- bloqade/stim/rewrite/ifs_to_stim.py +24 -25
- bloqade/stim/rewrite/qubit_to_stim.py +90 -41
- bloqade/stim/rewrite/set_detector_to_stim.py +68 -0
- bloqade/stim/rewrite/set_observable_to_stim.py +52 -0
- bloqade/stim/rewrite/squin_measure.py +9 -18
- bloqade/stim/rewrite/squin_noise.py +134 -108
- bloqade/stim/rewrite/util.py +5 -192
- bloqade/test_utils.py +1 -1
- bloqade/types.py +10 -0
- bloqade/validation/__init__.py +2 -0
- bloqade/validation/analysis/__init__.py +5 -0
- bloqade/validation/analysis/analysis.py +41 -0
- bloqade/validation/analysis/lattice.py +58 -0
- bloqade/validation/kernel_validation.py +77 -0
- {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/METADATA +5 -6
- bloqade_circuit-0.9.1.dist-info/RECORD +265 -0
- bloqade/pyqrack/squin/op.py +0 -180
- bloqade/pyqrack/squin/runtime.py +0 -535
- bloqade/pyqrack/squin/wire.py +0 -51
- bloqade/rewrite/rules/flatten_ilist.py +0 -51
- bloqade/rewrite/rules/inline_getitem_ilist.py +0 -31
- bloqade/squin/_typeinfer.py +0 -20
- bloqade/squin/analysis/address_impl.py +0 -71
- bloqade/squin/analysis/nsites/__init__.py +0 -9
- bloqade/squin/analysis/nsites/analysis.py +0 -50
- bloqade/squin/analysis/nsites/impls.py +0 -92
- bloqade/squin/analysis/nsites/lattice.py +0 -49
- bloqade/squin/cirq/__init__.py +0 -280
- bloqade/squin/cirq/emit/emit_circuit.py +0 -109
- bloqade/squin/cirq/emit/noise.py +0 -49
- bloqade/squin/cirq/emit/op.py +0 -125
- bloqade/squin/cirq/emit/qubit.py +0 -60
- bloqade/squin/cirq/emit/runtime.py +0 -242
- bloqade/squin/cirq/lowering.py +0 -440
- bloqade/squin/lowering.py +0 -54
- bloqade/squin/noise/_wrapper.py +0 -40
- bloqade/squin/noise/rewrite.py +0 -111
- bloqade/squin/op/__init__.py +0 -41
- bloqade/squin/op/_dialect.py +0 -3
- bloqade/squin/op/_wrapper.py +0 -121
- bloqade/squin/op/number.py +0 -5
- bloqade/squin/op/rewrite.py +0 -46
- bloqade/squin/op/stdlib.py +0 -62
- bloqade/squin/op/stmts.py +0 -276
- bloqade/squin/op/traits.py +0 -43
- bloqade/squin/op/types.py +0 -26
- bloqade/squin/qubit.py +0 -184
- bloqade/squin/rewrite/canonicalize.py +0 -60
- bloqade/squin/rewrite/desugar.py +0 -124
- bloqade/squin/types.py +0 -8
- bloqade/squin/wire.py +0 -201
- bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
- bloqade/stim/rewrite/wire_to_stim.py +0 -57
- bloqade_circuit-0.6.4.dist-info/RECORD +0 -234
- {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/WHEEL +0 -0
- {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/licenses/LICENSE +0 -0
bloqade/pyqrack/squin/runtime.py
DELETED
|
@@ -1,535 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from dataclasses import field, dataclass
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
from kirin.dialects import ilist
|
|
6
|
-
|
|
7
|
-
from pyqrack.pauli import Pauli
|
|
8
|
-
from bloqade.pyqrack import PyQrackQubit
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@dataclass(frozen=True)
|
|
12
|
-
class OperatorRuntimeABC:
|
|
13
|
-
"""The number of sites the operator applies to (including controls)"""
|
|
14
|
-
|
|
15
|
-
@property
|
|
16
|
-
def n_sites(self) -> int: ...
|
|
17
|
-
|
|
18
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
19
|
-
raise NotImplementedError(
|
|
20
|
-
"Operator runtime base class should not be called directly, override the method"
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
def control_apply(
|
|
24
|
-
self,
|
|
25
|
-
controls: tuple[PyQrackQubit, ...],
|
|
26
|
-
targets: tuple[PyQrackQubit, ...],
|
|
27
|
-
adjoint: bool = False,
|
|
28
|
-
) -> None:
|
|
29
|
-
raise RuntimeError(f"Can't apply controlled version of {self}")
|
|
30
|
-
|
|
31
|
-
def broadcast_apply(self, qubits: ilist.IList[PyQrackQubit, Any], **kwargs) -> None:
|
|
32
|
-
n = self.n_sites
|
|
33
|
-
|
|
34
|
-
if len(qubits) % n != 0:
|
|
35
|
-
raise RuntimeError(
|
|
36
|
-
f"Cannot broadcast operator {self} that applies to {n} over {len(qubits)} qubits."
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
for qubit_index in range(0, len(qubits), n):
|
|
40
|
-
targets = qubits[qubit_index : qubit_index + n]
|
|
41
|
-
self.apply(*targets, **kwargs)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass(frozen=True)
|
|
45
|
-
class ResetRuntime(OperatorRuntimeABC):
|
|
46
|
-
"""Reset the qubit to the target state"""
|
|
47
|
-
|
|
48
|
-
target_state: bool
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def n_sites(self) -> int:
|
|
52
|
-
return 1
|
|
53
|
-
|
|
54
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
55
|
-
for qubit in qubits:
|
|
56
|
-
if not qubit.is_active():
|
|
57
|
-
continue
|
|
58
|
-
|
|
59
|
-
res: bool = qubit.sim_reg.m(qubit.addr)
|
|
60
|
-
if res != self.target_state:
|
|
61
|
-
qubit.sim_reg.x(qubit.addr)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@dataclass(frozen=True)
|
|
65
|
-
class OperatorRuntime(OperatorRuntimeABC):
|
|
66
|
-
method_name: str
|
|
67
|
-
|
|
68
|
-
@property
|
|
69
|
-
def n_sites(self) -> int:
|
|
70
|
-
return 1
|
|
71
|
-
|
|
72
|
-
def get_method_name(self, adjoint: bool, control: bool) -> str:
|
|
73
|
-
method_name = ""
|
|
74
|
-
if control:
|
|
75
|
-
method_name += "mc"
|
|
76
|
-
|
|
77
|
-
if adjoint and self.method_name in ("s", "t"):
|
|
78
|
-
method_name += "adj"
|
|
79
|
-
|
|
80
|
-
return method_name + self.method_name
|
|
81
|
-
|
|
82
|
-
def apply(self, qubit: PyQrackQubit, adjoint: bool = False) -> None:
|
|
83
|
-
if not qubit.is_active():
|
|
84
|
-
return
|
|
85
|
-
method_name = self.get_method_name(adjoint=adjoint, control=False)
|
|
86
|
-
getattr(qubit.sim_reg, method_name)(qubit.addr)
|
|
87
|
-
|
|
88
|
-
def control_apply(
|
|
89
|
-
self,
|
|
90
|
-
controls: tuple[PyQrackQubit, ...],
|
|
91
|
-
targets: tuple[PyQrackQubit],
|
|
92
|
-
adjoint: bool = False,
|
|
93
|
-
) -> None:
|
|
94
|
-
target = targets[0]
|
|
95
|
-
if not target.is_active():
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
ctrls: list[int] = []
|
|
99
|
-
for qbit in controls:
|
|
100
|
-
if not qbit.is_active():
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
ctrls.append(qbit.addr)
|
|
104
|
-
|
|
105
|
-
method_name = self.get_method_name(adjoint=adjoint, control=True)
|
|
106
|
-
getattr(target.sim_reg, method_name)(ctrls, target.addr)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
@dataclass(frozen=True)
|
|
110
|
-
class ControlRuntime(OperatorRuntimeABC):
|
|
111
|
-
op: OperatorRuntimeABC
|
|
112
|
-
n_controls: int
|
|
113
|
-
|
|
114
|
-
@property
|
|
115
|
-
def n_sites(self) -> int:
|
|
116
|
-
return self.op.n_sites + self.n_controls
|
|
117
|
-
|
|
118
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
119
|
-
ctrls = qubits[: self.n_controls]
|
|
120
|
-
targets = qubits[self.n_controls :]
|
|
121
|
-
|
|
122
|
-
if len(targets) != self.op.n_sites:
|
|
123
|
-
raise RuntimeError(
|
|
124
|
-
f"Cannot apply operator {self.op} to {len(targets)} qubits! It applies to {self.op.n_sites}, check your inputs!"
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
self.op.control_apply(controls=ctrls, targets=targets, adjoint=adjoint)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@dataclass(frozen=True)
|
|
131
|
-
class ProjectorRuntime(OperatorRuntimeABC):
|
|
132
|
-
to_state: bool
|
|
133
|
-
|
|
134
|
-
@property
|
|
135
|
-
def n_sites(self) -> int:
|
|
136
|
-
return 1
|
|
137
|
-
|
|
138
|
-
def apply(self, qubit: PyQrackQubit, adjoint: bool = False) -> None:
|
|
139
|
-
if not qubit.is_active():
|
|
140
|
-
return
|
|
141
|
-
qubit.sim_reg.force_m(qubit.addr, self.to_state)
|
|
142
|
-
|
|
143
|
-
def control_apply(
|
|
144
|
-
self,
|
|
145
|
-
controls: tuple[PyQrackQubit, ...],
|
|
146
|
-
targets: tuple[PyQrackQubit],
|
|
147
|
-
adjoint: bool = False,
|
|
148
|
-
) -> None:
|
|
149
|
-
target = targets[0]
|
|
150
|
-
if not target.is_active():
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
ctrls: list[int] = []
|
|
154
|
-
for qbit in controls:
|
|
155
|
-
if not qbit.is_active():
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
m = [not self.to_state, 0, 0, self.to_state]
|
|
159
|
-
target.sim_reg.mcmtrx(ctrls, m, target.addr)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@dataclass(frozen=True)
|
|
163
|
-
class IdentityRuntime(OperatorRuntimeABC):
|
|
164
|
-
# TODO: do we even need sites? The apply never does anything
|
|
165
|
-
sites: int
|
|
166
|
-
|
|
167
|
-
@property
|
|
168
|
-
def n_sites(self) -> int:
|
|
169
|
-
return self.sites
|
|
170
|
-
|
|
171
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
172
|
-
pass
|
|
173
|
-
|
|
174
|
-
def control_apply(
|
|
175
|
-
self,
|
|
176
|
-
controls: tuple[PyQrackQubit, ...],
|
|
177
|
-
targets: tuple[PyQrackQubit, ...],
|
|
178
|
-
adjoint: bool = False,
|
|
179
|
-
) -> None:
|
|
180
|
-
pass
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@dataclass(frozen=True)
|
|
184
|
-
class MultRuntime(OperatorRuntimeABC):
|
|
185
|
-
lhs: OperatorRuntimeABC
|
|
186
|
-
rhs: OperatorRuntimeABC
|
|
187
|
-
|
|
188
|
-
@property
|
|
189
|
-
def n_sites(self) -> int:
|
|
190
|
-
if self.lhs.n_sites != self.rhs.n_sites:
|
|
191
|
-
raise RuntimeError("Multiplication of operators with unequal size.")
|
|
192
|
-
|
|
193
|
-
return self.lhs.n_sites
|
|
194
|
-
|
|
195
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
196
|
-
if adjoint:
|
|
197
|
-
# NOTE: inverted order
|
|
198
|
-
self.lhs.apply(*qubits, adjoint=adjoint)
|
|
199
|
-
self.rhs.apply(*qubits, adjoint=adjoint)
|
|
200
|
-
else:
|
|
201
|
-
self.rhs.apply(*qubits)
|
|
202
|
-
self.lhs.apply(*qubits)
|
|
203
|
-
|
|
204
|
-
def control_apply(
|
|
205
|
-
self,
|
|
206
|
-
controls: tuple[PyQrackQubit, ...],
|
|
207
|
-
targets: tuple[PyQrackQubit, ...],
|
|
208
|
-
adjoint: bool = False,
|
|
209
|
-
) -> None:
|
|
210
|
-
if adjoint:
|
|
211
|
-
self.lhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
|
|
212
|
-
self.rhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
|
|
213
|
-
else:
|
|
214
|
-
self.rhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
|
|
215
|
-
self.lhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
@dataclass(frozen=True)
|
|
219
|
-
class KronRuntime(OperatorRuntimeABC):
|
|
220
|
-
lhs: OperatorRuntimeABC
|
|
221
|
-
rhs: OperatorRuntimeABC
|
|
222
|
-
|
|
223
|
-
@property
|
|
224
|
-
def n_sites(self) -> int:
|
|
225
|
-
return self.lhs.n_sites + self.rhs.n_sites
|
|
226
|
-
|
|
227
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
228
|
-
self.lhs.apply(*qubits[: self.lhs.n_sites], adjoint=adjoint)
|
|
229
|
-
self.rhs.apply(*qubits[self.lhs.n_sites :], adjoint=adjoint)
|
|
230
|
-
|
|
231
|
-
def control_apply(
|
|
232
|
-
self,
|
|
233
|
-
controls: tuple[PyQrackQubit, ...],
|
|
234
|
-
targets: tuple[PyQrackQubit, ...],
|
|
235
|
-
adjoint: bool = False,
|
|
236
|
-
) -> None:
|
|
237
|
-
self.lhs.control_apply(
|
|
238
|
-
controls=controls,
|
|
239
|
-
targets=tuple(targets[: self.lhs.n_sites]),
|
|
240
|
-
adjoint=adjoint,
|
|
241
|
-
)
|
|
242
|
-
self.rhs.control_apply(
|
|
243
|
-
controls=controls,
|
|
244
|
-
targets=tuple(targets[self.lhs.n_sites :]),
|
|
245
|
-
adjoint=adjoint,
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
@dataclass(frozen=True)
|
|
250
|
-
class ScaleRuntime(OperatorRuntimeABC):
|
|
251
|
-
op: OperatorRuntimeABC
|
|
252
|
-
factor: complex
|
|
253
|
-
|
|
254
|
-
@property
|
|
255
|
-
def n_sites(self) -> int:
|
|
256
|
-
return self.op.n_sites
|
|
257
|
-
|
|
258
|
-
@staticmethod
|
|
259
|
-
def mat(factor, adjoint: bool):
|
|
260
|
-
if adjoint:
|
|
261
|
-
return [np.conj(factor), 0, 0, factor]
|
|
262
|
-
else:
|
|
263
|
-
return [factor, 0, 0, factor]
|
|
264
|
-
|
|
265
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
266
|
-
self.op.apply(*qubits, adjoint=adjoint)
|
|
267
|
-
|
|
268
|
-
# NOTE: when applying to multiple qubits, we "spread" the factor evenly
|
|
269
|
-
applied_factor = self.factor ** (1.0 / len(qubits))
|
|
270
|
-
for qbit in qubits:
|
|
271
|
-
if not qbit.is_active():
|
|
272
|
-
continue
|
|
273
|
-
|
|
274
|
-
# NOTE: just factor * eye(2)
|
|
275
|
-
m = self.mat(applied_factor, adjoint)
|
|
276
|
-
|
|
277
|
-
# TODO: output seems to always be normalized -- no-op?
|
|
278
|
-
qbit.sim_reg.mtrx(m, qbit.addr)
|
|
279
|
-
|
|
280
|
-
def control_apply(
|
|
281
|
-
self,
|
|
282
|
-
controls: tuple[PyQrackQubit, ...],
|
|
283
|
-
targets: tuple[PyQrackQubit, ...],
|
|
284
|
-
adjoint: bool = False,
|
|
285
|
-
) -> None:
|
|
286
|
-
|
|
287
|
-
ctrls: list[int] = []
|
|
288
|
-
for qbit in controls:
|
|
289
|
-
if not qbit.is_active():
|
|
290
|
-
return
|
|
291
|
-
|
|
292
|
-
ctrls.append(qbit.addr)
|
|
293
|
-
|
|
294
|
-
self.op.control_apply(controls=controls, targets=targets, adjoint=adjoint)
|
|
295
|
-
|
|
296
|
-
applied_factor = self.factor ** (1.0 / len(targets))
|
|
297
|
-
for target in targets:
|
|
298
|
-
m = self.mat(applied_factor, adjoint=adjoint)
|
|
299
|
-
target.sim_reg.mcmtrx(ctrls, m, target.addr)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
@dataclass(frozen=True)
|
|
303
|
-
class MtrxOpRuntime(OperatorRuntimeABC):
|
|
304
|
-
def mat(self, adjoint: bool) -> list[complex]:
|
|
305
|
-
raise NotImplementedError("Override this method in the subclass!")
|
|
306
|
-
|
|
307
|
-
@property
|
|
308
|
-
def n_sites(self) -> int:
|
|
309
|
-
# NOTE: pyqrack only supports 2x2 matrices, i.e. single qubit applications
|
|
310
|
-
return 1
|
|
311
|
-
|
|
312
|
-
def apply(self, target: PyQrackQubit, adjoint: bool = False) -> None:
|
|
313
|
-
if not target.is_active():
|
|
314
|
-
return
|
|
315
|
-
|
|
316
|
-
m = self.mat(adjoint=adjoint)
|
|
317
|
-
target.sim_reg.mtrx(m, target.addr)
|
|
318
|
-
|
|
319
|
-
def control_apply(
|
|
320
|
-
self,
|
|
321
|
-
controls: tuple[PyQrackQubit, ...],
|
|
322
|
-
targets: tuple[PyQrackQubit, ...],
|
|
323
|
-
adjoint: bool = False,
|
|
324
|
-
) -> None:
|
|
325
|
-
target = targets[0]
|
|
326
|
-
if not target.is_active():
|
|
327
|
-
return
|
|
328
|
-
|
|
329
|
-
ctrls: list[int] = []
|
|
330
|
-
for qbit in controls:
|
|
331
|
-
if not qbit.is_active():
|
|
332
|
-
return
|
|
333
|
-
|
|
334
|
-
ctrls.append(qbit.addr)
|
|
335
|
-
|
|
336
|
-
m = self.mat(adjoint=adjoint)
|
|
337
|
-
target.sim_reg.mcmtrx(ctrls, m, target.addr)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
@dataclass(frozen=True)
|
|
341
|
-
class SpRuntime(MtrxOpRuntime):
|
|
342
|
-
def mat(self, adjoint: bool) -> list[complex]:
|
|
343
|
-
if adjoint:
|
|
344
|
-
return [0, 0, 0.5, 0]
|
|
345
|
-
else:
|
|
346
|
-
return [0, 0.5, 0, 0]
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
@dataclass(frozen=True)
|
|
350
|
-
class SnRuntime(MtrxOpRuntime):
|
|
351
|
-
def mat(self, adjoint: bool) -> list[complex]:
|
|
352
|
-
if adjoint:
|
|
353
|
-
return [0, 0.5, 0, 0]
|
|
354
|
-
else:
|
|
355
|
-
return [0, 0, 0.5, 0]
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
@dataclass(frozen=True)
|
|
359
|
-
class PhaseOpRuntime(MtrxOpRuntime):
|
|
360
|
-
theta: float
|
|
361
|
-
global_: bool
|
|
362
|
-
|
|
363
|
-
def mat(self, adjoint: bool) -> list[complex]:
|
|
364
|
-
sign = (-1) ** (not adjoint)
|
|
365
|
-
local_phase = np.exp(sign * 1j * self.theta)
|
|
366
|
-
|
|
367
|
-
# NOTE: this is just 1 if we want a local shift
|
|
368
|
-
global_phase = np.exp(sign * 1j * self.theta * self.global_)
|
|
369
|
-
|
|
370
|
-
return [global_phase, 0, 0, local_phase]
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
@dataclass(frozen=True)
|
|
374
|
-
class RotRuntime(OperatorRuntimeABC):
|
|
375
|
-
axis: OperatorRuntimeABC
|
|
376
|
-
angle: float
|
|
377
|
-
pyqrack_axis: Pauli = field(init=False)
|
|
378
|
-
|
|
379
|
-
@property
|
|
380
|
-
def n_sites(self) -> int:
|
|
381
|
-
return 1
|
|
382
|
-
|
|
383
|
-
def __post_init__(self):
|
|
384
|
-
if not isinstance(self.axis, OperatorRuntime):
|
|
385
|
-
raise RuntimeError(
|
|
386
|
-
f"Rotation only supported for Pauli operators! Got {self.axis}"
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
try:
|
|
390
|
-
axis = getattr(Pauli, "Pauli" + self.axis.method_name.upper())
|
|
391
|
-
except KeyError:
|
|
392
|
-
raise RuntimeError(
|
|
393
|
-
f"Rotation only supported for Pauli operators! Got {self.axis}"
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
# NOTE: weird setattr for frozen dataclasses
|
|
397
|
-
object.__setattr__(self, "pyqrack_axis", axis)
|
|
398
|
-
|
|
399
|
-
def apply(self, target: PyQrackQubit, adjoint: bool = False) -> None:
|
|
400
|
-
if not target.is_active():
|
|
401
|
-
return
|
|
402
|
-
|
|
403
|
-
sign = (-1) ** adjoint
|
|
404
|
-
angle = sign * self.angle
|
|
405
|
-
target.sim_reg.r(self.pyqrack_axis, angle, target.addr)
|
|
406
|
-
|
|
407
|
-
def control_apply(
|
|
408
|
-
self,
|
|
409
|
-
controls: tuple[PyQrackQubit, ...],
|
|
410
|
-
targets: tuple[PyQrackQubit, ...],
|
|
411
|
-
adjoint: bool = False,
|
|
412
|
-
) -> None:
|
|
413
|
-
target = targets[0]
|
|
414
|
-
if not target.is_active():
|
|
415
|
-
return
|
|
416
|
-
|
|
417
|
-
ctrls: list[int] = []
|
|
418
|
-
for qbit in controls:
|
|
419
|
-
if not qbit.is_active():
|
|
420
|
-
return
|
|
421
|
-
|
|
422
|
-
ctrls.append(qbit.addr)
|
|
423
|
-
|
|
424
|
-
sign = (-1) ** (not adjoint)
|
|
425
|
-
angle = sign * self.angle
|
|
426
|
-
target.sim_reg.mcr(self.pyqrack_axis, angle, ctrls, target.addr)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
@dataclass(frozen=True)
|
|
430
|
-
class AdjointRuntime(OperatorRuntimeABC):
|
|
431
|
-
op: OperatorRuntimeABC
|
|
432
|
-
|
|
433
|
-
@property
|
|
434
|
-
def n_sites(self) -> int:
|
|
435
|
-
return self.op.n_sites
|
|
436
|
-
|
|
437
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
|
|
438
|
-
# NOTE: to account for adjoint(adjoint(op))
|
|
439
|
-
passed_on_adjoint = not adjoint
|
|
440
|
-
|
|
441
|
-
self.op.apply(*qubits, adjoint=passed_on_adjoint)
|
|
442
|
-
|
|
443
|
-
def control_apply(
|
|
444
|
-
self,
|
|
445
|
-
controls: tuple[PyQrackQubit, ...],
|
|
446
|
-
targets: tuple[PyQrackQubit, ...],
|
|
447
|
-
adjoint: bool = False,
|
|
448
|
-
) -> None:
|
|
449
|
-
passed_on_adjoint = not adjoint
|
|
450
|
-
self.op.control_apply(
|
|
451
|
-
controls=controls, targets=targets, adjoint=passed_on_adjoint
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
@dataclass(frozen=True)
|
|
456
|
-
class U3Runtime(OperatorRuntimeABC):
|
|
457
|
-
theta: float
|
|
458
|
-
phi: float
|
|
459
|
-
lam: float
|
|
460
|
-
|
|
461
|
-
@property
|
|
462
|
-
def n_sites(self) -> int:
|
|
463
|
-
return 1
|
|
464
|
-
|
|
465
|
-
def angles(self, adjoint: bool) -> tuple[float, float, float]:
|
|
466
|
-
if adjoint:
|
|
467
|
-
# NOTE: adjoint(U(theta, phi, lam)) == U(-theta, -lam, -phi)
|
|
468
|
-
return -self.theta, -self.lam, -self.phi
|
|
469
|
-
else:
|
|
470
|
-
return self.theta, self.phi, self.lam
|
|
471
|
-
|
|
472
|
-
def apply(self, target: PyQrackQubit, adjoint: bool = False) -> None:
|
|
473
|
-
if not target.is_active():
|
|
474
|
-
return
|
|
475
|
-
|
|
476
|
-
angles = self.angles(adjoint=adjoint)
|
|
477
|
-
target.sim_reg.u(target.addr, *angles)
|
|
478
|
-
|
|
479
|
-
def control_apply(
|
|
480
|
-
self,
|
|
481
|
-
controls: tuple[PyQrackQubit, ...],
|
|
482
|
-
targets: tuple[PyQrackQubit, ...],
|
|
483
|
-
adjoint: bool = False,
|
|
484
|
-
) -> None:
|
|
485
|
-
target = targets[0]
|
|
486
|
-
if not target.is_active():
|
|
487
|
-
return
|
|
488
|
-
|
|
489
|
-
ctrls: list[int] = []
|
|
490
|
-
for qbit in controls:
|
|
491
|
-
if not qbit.is_active():
|
|
492
|
-
return
|
|
493
|
-
|
|
494
|
-
ctrls.append(qbit.addr)
|
|
495
|
-
|
|
496
|
-
angles = self.angles(adjoint=adjoint)
|
|
497
|
-
target.sim_reg.mcu(ctrls, target.addr, *angles)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
@dataclass(frozen=True)
|
|
501
|
-
class PauliStringRuntime(OperatorRuntimeABC):
|
|
502
|
-
string: str
|
|
503
|
-
ops: list[OperatorRuntime]
|
|
504
|
-
|
|
505
|
-
@property
|
|
506
|
-
def n_sites(self) -> int:
|
|
507
|
-
return sum((op.n_sites for op in self.ops))
|
|
508
|
-
|
|
509
|
-
def apply(self, *qubits: PyQrackQubit, adjoint: bool = False):
|
|
510
|
-
if len(qubits) != self.n_sites:
|
|
511
|
-
raise RuntimeError(
|
|
512
|
-
f"Cannot apply Pauli string {self.string} to {len(qubits)} qubits! Make sure the number of qubits matches."
|
|
513
|
-
)
|
|
514
|
-
|
|
515
|
-
qubit_index = 0
|
|
516
|
-
for op in self.ops:
|
|
517
|
-
next_qubit_index = qubit_index + op.n_sites
|
|
518
|
-
op.apply(*qubits[qubit_index:next_qubit_index], adjoint=adjoint)
|
|
519
|
-
qubit_index = next_qubit_index
|
|
520
|
-
|
|
521
|
-
def control_apply(
|
|
522
|
-
self,
|
|
523
|
-
controls: tuple[PyQrackQubit, ...],
|
|
524
|
-
targets: tuple[PyQrackQubit, ...],
|
|
525
|
-
adjoint: bool = False,
|
|
526
|
-
) -> None:
|
|
527
|
-
if len(targets) != self.n_sites:
|
|
528
|
-
raise RuntimeError(
|
|
529
|
-
f"Cannot apply Pauli string {self.string} to {len(targets)} qubits! Make sure the number of qubits matches."
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
for i, op in enumerate(self.ops):
|
|
533
|
-
# NOTE: this is fine as the size of each op is actually just 1 by definition
|
|
534
|
-
target = targets[i]
|
|
535
|
-
op.control_apply(controls=controls, targets=(target,))
|
bloqade/pyqrack/squin/wire.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from kirin import interp
|
|
2
|
-
|
|
3
|
-
from bloqade.squin import wire
|
|
4
|
-
from bloqade.pyqrack.reg import PyQrackWire, PyQrackQubit
|
|
5
|
-
from bloqade.pyqrack.base import PyQrackInterpreter
|
|
6
|
-
|
|
7
|
-
from .runtime import OperatorRuntimeABC
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@wire.dialect.register(key="pyqrack")
|
|
11
|
-
class PyQrackMethods(interp.MethodTable):
|
|
12
|
-
# @interp.impl(wire.Wrap)
|
|
13
|
-
# def wrap(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Wrap):
|
|
14
|
-
# traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
|
|
15
|
-
# wire: ir.SSAValue = info.argument(WireType)
|
|
16
|
-
# qubit: ir.SSAValue = info.argument(QubitType)
|
|
17
|
-
|
|
18
|
-
@interp.impl(wire.Unwrap)
|
|
19
|
-
def unwrap(
|
|
20
|
-
self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Unwrap
|
|
21
|
-
):
|
|
22
|
-
q: PyQrackQubit = frame.get(stmt.qubit)
|
|
23
|
-
return (PyQrackWire(q),)
|
|
24
|
-
|
|
25
|
-
@interp.impl(wire.Apply)
|
|
26
|
-
def apply(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Apply):
|
|
27
|
-
ws = stmt.inputs
|
|
28
|
-
assert isinstance(ws, tuple)
|
|
29
|
-
qubits: list[PyQrackQubit] = []
|
|
30
|
-
for w in ws:
|
|
31
|
-
assert isinstance(w, PyQrackWire)
|
|
32
|
-
qubits.append(w.qubit)
|
|
33
|
-
op: OperatorRuntimeABC = frame.get(stmt.operator)
|
|
34
|
-
|
|
35
|
-
op.apply(*qubits)
|
|
36
|
-
|
|
37
|
-
out_ws = [PyQrackWire(qbit) for qbit in qubits]
|
|
38
|
-
return (out_ws,)
|
|
39
|
-
|
|
40
|
-
@interp.impl(wire.Measure)
|
|
41
|
-
def measure(
|
|
42
|
-
self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Measure
|
|
43
|
-
):
|
|
44
|
-
w: PyQrackWire = frame.get(stmt.wire)
|
|
45
|
-
qbit = w.qubit
|
|
46
|
-
|
|
47
|
-
if not qbit.is_active():
|
|
48
|
-
return (interp.loss_m_result,)
|
|
49
|
-
|
|
50
|
-
res: bool = bool(qbit.sim_reg.m(qbit.addr))
|
|
51
|
-
return (res,)
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
from kirin import ir
|
|
4
|
-
from kirin.dialects import py, ilist
|
|
5
|
-
from kirin.rewrite.abc import RewriteRule, RewriteResult
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@dataclass
|
|
9
|
-
class FlattenAddOpIList(RewriteRule):
|
|
10
|
-
|
|
11
|
-
def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
|
|
12
|
-
if not isinstance(node, py.binop.Add):
|
|
13
|
-
return RewriteResult()
|
|
14
|
-
|
|
15
|
-
# check if we are adding two ilist.New objects
|
|
16
|
-
new_data = ()
|
|
17
|
-
|
|
18
|
-
# lhs:
|
|
19
|
-
if not isinstance(node.lhs.owner, ilist.New):
|
|
20
|
-
if not (
|
|
21
|
-
isinstance(node.lhs.owner, py.Constant)
|
|
22
|
-
and isinstance(
|
|
23
|
-
const_ilist := node.lhs.owner.value.unwrap(), ilist.IList
|
|
24
|
-
)
|
|
25
|
-
and len(const_ilist.data) == 0
|
|
26
|
-
):
|
|
27
|
-
return RewriteResult()
|
|
28
|
-
|
|
29
|
-
else:
|
|
30
|
-
new_data += node.lhs.owner.values
|
|
31
|
-
|
|
32
|
-
# rhs:
|
|
33
|
-
if not isinstance(node.rhs.owner, ilist.New):
|
|
34
|
-
if not (
|
|
35
|
-
isinstance(node.rhs.owner, py.Constant)
|
|
36
|
-
and isinstance(
|
|
37
|
-
const_ilist := node.rhs.owner.value.unwrap(), ilist.IList
|
|
38
|
-
)
|
|
39
|
-
and len(const_ilist.data) == 0
|
|
40
|
-
):
|
|
41
|
-
return RewriteResult()
|
|
42
|
-
|
|
43
|
-
else:
|
|
44
|
-
new_data += node.rhs.owner.values
|
|
45
|
-
|
|
46
|
-
new_stmt = ilist.New(values=new_data)
|
|
47
|
-
node.replace_by(new_stmt)
|
|
48
|
-
|
|
49
|
-
return RewriteResult(
|
|
50
|
-
has_done_something=True,
|
|
51
|
-
)
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
from kirin import ir
|
|
4
|
-
from kirin.analysis import const
|
|
5
|
-
from kirin.dialects import py, ilist
|
|
6
|
-
from kirin.rewrite.abc import RewriteRule, RewriteResult
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclass
|
|
10
|
-
class InlineGetItemFromIList(RewriteRule):
|
|
11
|
-
constprop_result: dict[ir.SSAValue, const.Result]
|
|
12
|
-
|
|
13
|
-
def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
|
|
14
|
-
if not isinstance(node, py.indexing.GetItem):
|
|
15
|
-
return RewriteResult()
|
|
16
|
-
|
|
17
|
-
if not isinstance(node.obj.owner, ilist.New):
|
|
18
|
-
return RewriteResult()
|
|
19
|
-
|
|
20
|
-
if not isinstance(
|
|
21
|
-
index_value := self.constprop_result.get(node.index), const.Value
|
|
22
|
-
):
|
|
23
|
-
return RewriteResult()
|
|
24
|
-
|
|
25
|
-
elem_ssa = node.obj.owner.values[index_value.data]
|
|
26
|
-
|
|
27
|
-
node.result.replace_by(elem_ssa)
|
|
28
|
-
|
|
29
|
-
return RewriteResult(
|
|
30
|
-
has_done_something=True,
|
|
31
|
-
)
|
bloqade/squin/_typeinfer.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from kirin import types, interp
|
|
2
|
-
from kirin.analysis import TypeInference, const
|
|
3
|
-
from kirin.dialects import ilist
|
|
4
|
-
|
|
5
|
-
from bloqade import squin
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@squin.qubit.dialect.register(key="typeinfer")
|
|
9
|
-
class TypeInfer(interp.MethodTable):
|
|
10
|
-
@interp.impl(squin.qubit.New)
|
|
11
|
-
def _call(self, interp: TypeInference, frame: interp.Frame, stmt: squin.qubit.New):
|
|
12
|
-
# based on Xiu-zhe (Roger) Luo's get_const_value function
|
|
13
|
-
|
|
14
|
-
if (hint := stmt.n_qubits.hints.get("const")) is None:
|
|
15
|
-
return (ilist.IListType[squin.qubit.QubitType, types.Any],)
|
|
16
|
-
|
|
17
|
-
if isinstance(hint, const.Value) and isinstance(hint.data, int):
|
|
18
|
-
return (ilist.IListType[squin.qubit.QubitType, types.Literal(hint.data)],)
|
|
19
|
-
|
|
20
|
-
return (ilist.IListType[squin.qubit.QubitType, types.Any],)
|