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