bloqade-circuit 0.7.12__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 +90 -41
- 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.12.dist-info → bloqade_circuit-0.8.0.dist-info}/METADATA +2 -2
- {bloqade_circuit-0.7.12.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.12.dist-info → bloqade_circuit-0.8.0.dist-info}/WHEEL +0 -0
- {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/licenses/LICENSE +0 -0
bloqade/squin/stdlib/channel.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
from bloqade.types import Qubit
|
|
2
|
-
from bloqade.squin.op.types import PauliOp
|
|
3
|
-
|
|
4
|
-
from .. import noise, qubit as _qubit, kernel
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@kernel
|
|
8
|
-
def pauli_error(basis: PauliOp, p: float, qubit: Qubit) -> None:
|
|
9
|
-
"""Apply the pauli operator given as basis with a probability p to qubit.
|
|
10
|
-
|
|
11
|
-
NOTE: the `PauliError` actually supports multiple qubits and multi-site bases,
|
|
12
|
-
it's only the short-hand definition here that is limited to a single qubit.
|
|
13
|
-
If you want to define a pauli error for multiple qubits, you can do so using
|
|
14
|
-
the statement, for example
|
|
15
|
-
|
|
16
|
-
```python
|
|
17
|
-
q = squin.qubit.new(3)
|
|
18
|
-
multi_qubit_pauli = squin.op.pauli_string(string="XYZ")
|
|
19
|
-
pauli_error = squin.noise.pauli_error(basis=multi_qubit_pauli, p=0.1)
|
|
20
|
-
squin.qubit.apply(pauli_error, q[0], q[1], q[2])
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
|
-
op = noise.pauli_error(basis, p)
|
|
25
|
-
_qubit.apply(op, qubit)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@kernel
|
|
29
|
-
def depolarize(p: float, qubit: Qubit) -> None:
|
|
30
|
-
"""Apply depolarization error with probability p to qubit.
|
|
31
|
-
|
|
32
|
-
This means, that with a probability p/3 one of the three Pauli operators is applied.
|
|
33
|
-
Which Pauli operator is applied is chosen at random with equal probability.
|
|
34
|
-
"""
|
|
35
|
-
op = noise.depolarize(p)
|
|
36
|
-
_qubit.apply(op, qubit)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@kernel
|
|
40
|
-
def depolarize2(p: float, qubit1: Qubit, qubit2: Qubit) -> None:
|
|
41
|
-
"""Apply the correlated two-qubit depolarization error with probability p to qubits.
|
|
42
|
-
|
|
43
|
-
This means, that with a probability of p/15 one of the Pauli products {XX, XY, XZ, XI, YX, YY, ...}
|
|
44
|
-
is applied to the qubit pair. Which Pauli product is applied is chosen at random with equal probability.
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
op = noise.depolarize2(p)
|
|
48
|
-
_qubit.apply(op, qubit1, qubit2)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@kernel
|
|
52
|
-
def single_qubit_pauli_channel(params: list[float], qubit: Qubit) -> None:
|
|
53
|
-
"""Apply the single qubit Pauli error with probabilities px, py, pz, respectively, to the qubit.
|
|
54
|
-
|
|
55
|
-
Similar to `depolarize`, but with distinct probabilities. An error occurs with the probability `px + py + pz`.
|
|
56
|
-
Which operator is applied is chosen at random, but weighed with the respective probabilities.
|
|
57
|
-
"""
|
|
58
|
-
op = noise.single_qubit_pauli_channel(params)
|
|
59
|
-
_qubit.apply(op, qubit)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@kernel
|
|
63
|
-
def two_qubit_pauli_channel(params: list[float], qubit1: Qubit, qubit2: Qubit) -> None:
|
|
64
|
-
"""Apply the two-qubit correlated Pauli error with probabilities given in the list above.
|
|
65
|
-
|
|
66
|
-
This means one of the operator products
|
|
67
|
-
|
|
68
|
-
{IX, IY, IZ, XI, XX, XY, XZ, YI, YX, YY, YZ, ZI, ZX, ZY, ZZ}
|
|
69
|
-
|
|
70
|
-
is applied. The choice of which is weighed by the given probabilities.
|
|
71
|
-
|
|
72
|
-
NOTE: the given parameters are ordered as given in the list above!
|
|
73
|
-
|
|
74
|
-
"""
|
|
75
|
-
op = noise.two_qubit_pauli_channel(params)
|
|
76
|
-
_qubit.apply(op, qubit1, qubit2)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@kernel
|
|
80
|
-
def qubit_loss(p: float, qubit: Qubit) -> None:
|
|
81
|
-
"""Apply a qubit loss channel with probability p.
|
|
82
|
-
|
|
83
|
-
When a loss channel is applied, the qubit is marked as lost by setting its state accordingly.
|
|
84
|
-
"""
|
|
85
|
-
op = noise.qubit_loss(p)
|
|
86
|
-
_qubit.apply(op, qubit)
|
bloqade/squin/stdlib/gate.py
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
from bloqade.types import Qubit
|
|
2
|
-
|
|
3
|
-
from ..groups import kernel
|
|
4
|
-
|
|
5
|
-
from .. import op as _op, qubit as _qubit # isort: skip
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@kernel
|
|
9
|
-
def x(qubit: Qubit) -> None:
|
|
10
|
-
"""x gate applied to qubit."""
|
|
11
|
-
op = _op.x()
|
|
12
|
-
_qubit.apply(op, qubit)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@kernel
|
|
16
|
-
def y(qubit: Qubit) -> None:
|
|
17
|
-
"""y gate applied to qubit."""
|
|
18
|
-
op = _op.y()
|
|
19
|
-
_qubit.apply(op, qubit)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@kernel
|
|
23
|
-
def z(qubit: Qubit) -> None:
|
|
24
|
-
"""z gate applied to qubit."""
|
|
25
|
-
op = _op.z()
|
|
26
|
-
_qubit.apply(op, qubit)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@kernel
|
|
30
|
-
def sqrt_x(qubit: Qubit) -> None:
|
|
31
|
-
"""Square root x gate applied to qubit."""
|
|
32
|
-
op = _op.sqrt_x()
|
|
33
|
-
_qubit.apply(op, qubit)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@kernel
|
|
37
|
-
def sqrt_x_adj(qubit: Qubit) -> None:
|
|
38
|
-
"""Adjoint sqrt_x gate applied to qubit."""
|
|
39
|
-
op = _op.sqrt_x()
|
|
40
|
-
_qubit.apply(_op.adjoint(op), qubit)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@kernel
|
|
44
|
-
def sqrt_y(qubit: Qubit) -> None:
|
|
45
|
-
"""Square root y gate applied to qubit."""
|
|
46
|
-
op = _op.sqrt_y()
|
|
47
|
-
_qubit.apply(op, qubit)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@kernel
|
|
51
|
-
def sqrt_y_adj(qubit: Qubit) -> None:
|
|
52
|
-
"""Adjoint sqrt_y gate applied to qubit."""
|
|
53
|
-
op = _op.sqrt_y()
|
|
54
|
-
_qubit.apply(_op.adjoint(op), qubit)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@kernel
|
|
58
|
-
def sqrt_z(qubit: Qubit) -> None:
|
|
59
|
-
"""Square root z gate applied to qubit."""
|
|
60
|
-
op = _op.s()
|
|
61
|
-
_qubit.apply(op, qubit)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@kernel
|
|
65
|
-
def sqrt_z_adj(qubit: Qubit) -> None:
|
|
66
|
-
"""Adjoint square root z gate applied to qubit."""
|
|
67
|
-
op = _op.s()
|
|
68
|
-
_qubit.apply(_op.adjoint(op), qubit)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@kernel
|
|
72
|
-
def h(qubit: Qubit) -> None:
|
|
73
|
-
"""Hadamard gate applied to qubit."""
|
|
74
|
-
op = _op.h()
|
|
75
|
-
_qubit.apply(op, qubit)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@kernel
|
|
79
|
-
def s(qubit: Qubit) -> None:
|
|
80
|
-
"""s gate applied to qubit."""
|
|
81
|
-
op = _op.s()
|
|
82
|
-
_qubit.apply(op, qubit)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@kernel
|
|
86
|
-
def s_adj(qubit: Qubit) -> None:
|
|
87
|
-
"""Adjoint s gate applied to qubit."""
|
|
88
|
-
op = _op.s()
|
|
89
|
-
_qubit.apply(_op.adjoint(op), qubit)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
@kernel
|
|
93
|
-
def t(qubit: Qubit) -> None:
|
|
94
|
-
"""t gate applied to qubit."""
|
|
95
|
-
op = _op.t()
|
|
96
|
-
_qubit.apply(op, qubit)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
@kernel
|
|
100
|
-
def t_adj(qubit: Qubit) -> None:
|
|
101
|
-
"""Adjoint t gate applied to qubit."""
|
|
102
|
-
op = _op.t()
|
|
103
|
-
_qubit.apply(_op.adjoint(op), qubit)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@kernel
|
|
107
|
-
def p0(qubit: Qubit) -> None:
|
|
108
|
-
"""Projector on 0 applied to qubit."""
|
|
109
|
-
op = _op.p0()
|
|
110
|
-
_qubit.apply(op, qubit)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@kernel
|
|
114
|
-
def p1(qubit: Qubit) -> None:
|
|
115
|
-
"""Projector on 1 applied to qubit."""
|
|
116
|
-
op = _op.p1()
|
|
117
|
-
_qubit.apply(op, qubit)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@kernel
|
|
121
|
-
def spin_n(qubit: Qubit) -> None:
|
|
122
|
-
"""Spin lowering gate applied to qubit."""
|
|
123
|
-
op = _op.spin_n()
|
|
124
|
-
_qubit.apply(op, qubit)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@kernel
|
|
128
|
-
def spin_p(qubit: Qubit) -> None:
|
|
129
|
-
"""Spin raising gate applied to qubit."""
|
|
130
|
-
op = _op.spin_p()
|
|
131
|
-
_qubit.apply(op, qubit)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
@kernel
|
|
135
|
-
def reset(qubit: Qubit) -> None:
|
|
136
|
-
"""Reset qubit to 0."""
|
|
137
|
-
op = _op.reset()
|
|
138
|
-
_qubit.apply(op, qubit)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
@kernel
|
|
142
|
-
def reset_to_one(qubit: Qubit) -> None:
|
|
143
|
-
"""Reset qubit to 1."""
|
|
144
|
-
op = _op.reset_to_one()
|
|
145
|
-
_qubit.apply(op, qubit)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@kernel
|
|
149
|
-
def cx(control: Qubit, target: Qubit) -> None:
|
|
150
|
-
"""Controlled x gate applied to control and target"""
|
|
151
|
-
op = _op.cx()
|
|
152
|
-
_qubit.apply(op, control, target)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
@kernel
|
|
156
|
-
def cy(control: Qubit, target: Qubit) -> None:
|
|
157
|
-
"""Controlled y gate applied to control and target"""
|
|
158
|
-
op = _op.cy()
|
|
159
|
-
_qubit.apply(op, control, target)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@kernel
|
|
163
|
-
def cz(control: Qubit, target: Qubit) -> None:
|
|
164
|
-
"""Controlled z gate applied to control and target"""
|
|
165
|
-
op = _op.cz()
|
|
166
|
-
_qubit.apply(op, control, target)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@kernel
|
|
170
|
-
def ch(control: Qubit, target: Qubit) -> None:
|
|
171
|
-
"""Controlled Hadamard gate applied to control and target"""
|
|
172
|
-
op = _op.ch()
|
|
173
|
-
_qubit.apply(op, control, target)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
@kernel
|
|
177
|
-
def u(theta: float, phi: float, lam: float, qubit: Qubit) -> None:
|
|
178
|
-
"""3D rotation gate applied to control and target"""
|
|
179
|
-
op = _op.u(theta, phi, lam)
|
|
180
|
-
_qubit.apply(op, qubit)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@kernel
|
|
184
|
-
def rx(theta: float, qubit: Qubit) -> None:
|
|
185
|
-
"""Rotation X gate applied to qubit."""
|
|
186
|
-
op = _op.rot(_op.x(), theta)
|
|
187
|
-
_qubit.apply(op, qubit)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
@kernel
|
|
191
|
-
def ry(theta: float, qubit: Qubit) -> None:
|
|
192
|
-
"""Rotation Y gate applied to qubit."""
|
|
193
|
-
op = _op.rot(_op.y(), theta)
|
|
194
|
-
_qubit.apply(op, qubit)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
@kernel
|
|
198
|
-
def rz(theta: float, qubit: Qubit) -> None:
|
|
199
|
-
"""Rotation Z gate applied to qubit."""
|
|
200
|
-
op = _op.rot(_op.z(), theta)
|
|
201
|
-
_qubit.apply(op, qubit)
|
bloqade/squin/types.py
DELETED
bloqade/squin/wire.py
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
"""A NVIDIA QUAKE-like wire dialect.
|
|
2
|
-
|
|
3
|
-
This dialect is expected to be used in combination with the operator dialect
|
|
4
|
-
as an intermediate representation for analysis and optimization of quantum
|
|
5
|
-
circuits. Thus we do not define wrapping functions for the statements in this
|
|
6
|
-
dialect.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from kirin import ir, types, lowering, exception
|
|
10
|
-
from kirin.decl import info, statement
|
|
11
|
-
from kirin.dialects import func
|
|
12
|
-
from kirin.lowering import wraps
|
|
13
|
-
from kirin.ir.attrs.types import TypeAttribute
|
|
14
|
-
|
|
15
|
-
from bloqade.types import Qubit, QubitType
|
|
16
|
-
|
|
17
|
-
from .types import MeasurementResultType
|
|
18
|
-
from .op.types import Op, OpType
|
|
19
|
-
|
|
20
|
-
# from kirin.lowering import wraps
|
|
21
|
-
|
|
22
|
-
# from .op.types import Op, OpType
|
|
23
|
-
|
|
24
|
-
dialect = ir.Dialect("squin.wire")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class WireTerminator(ir.StmtTrait):
|
|
28
|
-
pass
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class Wire:
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
WireType = types.PyClass(Wire)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# no return value for `wrap`
|
|
39
|
-
@statement(dialect=dialect)
|
|
40
|
-
class Wrap(ir.Statement):
|
|
41
|
-
traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
|
|
42
|
-
wire: ir.SSAValue = info.argument(WireType)
|
|
43
|
-
qubit: ir.SSAValue = info.argument(QubitType)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# "Unwrap the quantum references to expose wires" -> From Quake Dialect documentation
|
|
47
|
-
# Unwrap(Qubit) -> Wire
|
|
48
|
-
@statement(dialect=dialect)
|
|
49
|
-
class Unwrap(ir.Statement):
|
|
50
|
-
traits = frozenset({lowering.FromPythonCall(), ir.Pure()})
|
|
51
|
-
qubit: ir.SSAValue = info.argument(QubitType)
|
|
52
|
-
result: ir.ResultValue = info.result(WireType)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@statement(dialect=dialect)
|
|
56
|
-
class Wired(ir.Statement):
|
|
57
|
-
traits = frozenset()
|
|
58
|
-
|
|
59
|
-
qubits: tuple[ir.SSAValue, ...] = info.argument(QubitType)
|
|
60
|
-
memory_zone: str = info.attribute()
|
|
61
|
-
body: ir.Region = info.region(multi=True)
|
|
62
|
-
|
|
63
|
-
def __init__(
|
|
64
|
-
self,
|
|
65
|
-
body: ir.Region,
|
|
66
|
-
*qubits: ir.SSAValue,
|
|
67
|
-
memory_zone: str,
|
|
68
|
-
result_types: tuple[TypeAttribute, ...] | None = None,
|
|
69
|
-
):
|
|
70
|
-
if result_types is None:
|
|
71
|
-
for block in body.blocks:
|
|
72
|
-
if isinstance(block.last_stmt, Yield):
|
|
73
|
-
result_types = tuple(arg.type for arg in block.last_stmt.values)
|
|
74
|
-
break
|
|
75
|
-
|
|
76
|
-
if result_types is None:
|
|
77
|
-
result_types = ()
|
|
78
|
-
|
|
79
|
-
super().__init__(
|
|
80
|
-
args=qubits,
|
|
81
|
-
args_slice={
|
|
82
|
-
"qubits": slice(0, None),
|
|
83
|
-
},
|
|
84
|
-
regions=[body],
|
|
85
|
-
attributes={
|
|
86
|
-
"memory_zone": ir.PyAttr(memory_zone)
|
|
87
|
-
}, # body of the wired statement
|
|
88
|
-
result_types=result_types,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
def check(self):
|
|
92
|
-
entry_block = self.body.blocks[0]
|
|
93
|
-
|
|
94
|
-
if len(entry_block.args) != len(self.qubits):
|
|
95
|
-
raise exception.StaticCheckError(
|
|
96
|
-
f"Expected {len(self.qubits)} arguments, got {len(entry_block.args)}."
|
|
97
|
-
)
|
|
98
|
-
for arg in entry_block.args:
|
|
99
|
-
if not arg.type.is_subseteq(WireType):
|
|
100
|
-
raise exception.StaticCheckError(
|
|
101
|
-
f"Expected argument of type {WireType}, got {arg.type}."
|
|
102
|
-
)
|
|
103
|
-
for block in self.body.blocks:
|
|
104
|
-
last_stmt = block.last_stmt
|
|
105
|
-
if isinstance(last_stmt, func.Return):
|
|
106
|
-
raise exception.StaticCheckError(
|
|
107
|
-
"Return statements are not allowed in the body of a Wired statement."
|
|
108
|
-
)
|
|
109
|
-
elif isinstance(last_stmt, Yield) and len(last_stmt.values) != len(
|
|
110
|
-
self.results
|
|
111
|
-
):
|
|
112
|
-
raise exception.StaticCheckError(
|
|
113
|
-
f"Expected {len(self.results)} return values, got {len(last_stmt.values)}."
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
@statement(dialect=dialect)
|
|
118
|
-
class Yield(ir.Statement):
|
|
119
|
-
traits = frozenset({})
|
|
120
|
-
values: tuple[ir.SSAValue, ...] = info.argument(WireType)
|
|
121
|
-
|
|
122
|
-
def __init__(self, *args: ir.SSAValue):
|
|
123
|
-
super().__init__(
|
|
124
|
-
args=args,
|
|
125
|
-
args_slice={
|
|
126
|
-
"values": slice(0, None),
|
|
127
|
-
},
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# In Quake, you put a wire in and get a wire out when you "apply" an operator
|
|
132
|
-
# In this case though we just need to indicate that an operator is applied to list[wires]
|
|
133
|
-
@statement(dialect=dialect)
|
|
134
|
-
class Apply(ir.Statement): # apply(op, w1, w2, ...)
|
|
135
|
-
traits = frozenset({lowering.FromPythonCall()})
|
|
136
|
-
operator: ir.SSAValue = info.argument(OpType)
|
|
137
|
-
inputs: tuple[ir.SSAValue, ...] = info.argument(WireType)
|
|
138
|
-
|
|
139
|
-
def __init__(self, operator: ir.SSAValue, *args: ir.SSAValue):
|
|
140
|
-
result_types = tuple(WireType for _ in args)
|
|
141
|
-
super().__init__(
|
|
142
|
-
args=(operator,) + args,
|
|
143
|
-
result_types=result_types, # result types of the Apply statement, should all be WireTypes
|
|
144
|
-
args_slice={
|
|
145
|
-
"operator": 0,
|
|
146
|
-
"inputs": slice(1, None),
|
|
147
|
-
}, # pretty printing + syntax sugar
|
|
148
|
-
) # custom lowering required for wrapper to work here
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
# Carry over from Qubit dialect
|
|
152
|
-
@statement(dialect=dialect)
|
|
153
|
-
class Broadcast(ir.Statement):
|
|
154
|
-
traits = frozenset({lowering.FromPythonCall(), ir.Pure()})
|
|
155
|
-
operator: ir.SSAValue = info.argument(OpType)
|
|
156
|
-
inputs: tuple[ir.SSAValue, ...] = info.argument(WireType)
|
|
157
|
-
|
|
158
|
-
def __init__(self, operator: ir.SSAValue, *args: ir.SSAValue):
|
|
159
|
-
result_types = tuple(WireType for _ in args)
|
|
160
|
-
super().__init__(
|
|
161
|
-
args=(operator,) + args,
|
|
162
|
-
result_types=result_types,
|
|
163
|
-
args_slice={
|
|
164
|
-
"operator": 0,
|
|
165
|
-
"inputs": slice(1, None),
|
|
166
|
-
}, # pretty printing + syntax sugar
|
|
167
|
-
) # custom lowering required for wrapper to work here
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
@statement(dialect=dialect)
|
|
171
|
-
class RegionMeasure(ir.Statement):
|
|
172
|
-
traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
|
|
173
|
-
wire: ir.SSAValue = info.argument(WireType)
|
|
174
|
-
result: ir.ResultValue = info.result(MeasurementResultType)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
# NOTE: measurement cannot be pure because they will collapse the state
|
|
178
|
-
# of the qubit. The state is a hidden state that is not visible to
|
|
179
|
-
# the user in the wire dialect.
|
|
180
|
-
@statement(dialect=dialect)
|
|
181
|
-
class Measure(ir.Statement):
|
|
182
|
-
traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
|
|
183
|
-
wire: ir.SSAValue = info.argument(WireType)
|
|
184
|
-
qubit: ir.SSAValue = info.argument(QubitType)
|
|
185
|
-
result: ir.ResultValue = info.result(MeasurementResultType)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
@statement(dialect=dialect)
|
|
189
|
-
class LossResolvingMeasure(ir.Statement):
|
|
190
|
-
traits = frozenset({lowering.FromPythonCall()})
|
|
191
|
-
input_wire: ir.SSAValue = info.argument(WireType)
|
|
192
|
-
result: ir.ResultValue = info.result(MeasurementResultType)
|
|
193
|
-
out_wire: ir.ResultValue = info.result(WireType)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@wraps(Unwrap)
|
|
197
|
-
def unwrap(qubit: Qubit) -> Wire: ...
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
@wraps(Apply)
|
|
201
|
-
def apply(op: Op, w: Wire) -> Wire: ...
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from kirin import ir
|
|
2
|
-
from kirin.rewrite.abc import RewriteRule, RewriteResult
|
|
3
|
-
|
|
4
|
-
from bloqade.squin import wire
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class SquinWireIdentityElimination(RewriteRule):
|
|
8
|
-
|
|
9
|
-
def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
|
|
10
|
-
"""
|
|
11
|
-
Handle the case where an unwrap feeds a wire directly into a wrap,
|
|
12
|
-
equivalent to nothing happening/identity operation
|
|
13
|
-
|
|
14
|
-
w = unwrap(qubit)
|
|
15
|
-
wrap(qubit, w)
|
|
16
|
-
"""
|
|
17
|
-
if isinstance(node, wire.Wrap):
|
|
18
|
-
wire_origin_stmt = node.wire.owner
|
|
19
|
-
if isinstance(wire_origin_stmt, wire.Unwrap):
|
|
20
|
-
node.delete() # get rid of wrap
|
|
21
|
-
wire_origin_stmt.delete() # get rid of the unwrap
|
|
22
|
-
return RewriteResult(has_done_something=True)
|
|
23
|
-
|
|
24
|
-
return RewriteResult()
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
from kirin import ir
|
|
2
|
-
from kirin.rewrite.abc import RewriteRule, RewriteResult
|
|
3
|
-
|
|
4
|
-
from bloqade.squin import op, wire, noise
|
|
5
|
-
from bloqade.stim.rewrite.util import (
|
|
6
|
-
SQUIN_STIM_OP_MAPPING,
|
|
7
|
-
rewrite_Control,
|
|
8
|
-
rewrite_QubitLoss,
|
|
9
|
-
insert_qubit_idx_from_wire_ssa,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class SquinWireToStim(RewriteRule):
|
|
14
|
-
|
|
15
|
-
def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
|
|
16
|
-
match node:
|
|
17
|
-
case wire.Apply() | wire.Broadcast():
|
|
18
|
-
return self.rewrite_Apply_and_Broadcast(node)
|
|
19
|
-
case _:
|
|
20
|
-
return RewriteResult()
|
|
21
|
-
|
|
22
|
-
def rewrite_Apply_and_Broadcast(
|
|
23
|
-
self, stmt: wire.Apply | wire.Broadcast
|
|
24
|
-
) -> RewriteResult:
|
|
25
|
-
|
|
26
|
-
# this is an SSAValue, need it to be the actual operator
|
|
27
|
-
applied_op = stmt.operator.owner
|
|
28
|
-
|
|
29
|
-
if isinstance(applied_op, noise.stmts.QubitLoss):
|
|
30
|
-
return rewrite_QubitLoss(stmt)
|
|
31
|
-
|
|
32
|
-
assert isinstance(applied_op, op.stmts.Operator)
|
|
33
|
-
|
|
34
|
-
if isinstance(applied_op, op.stmts.Control):
|
|
35
|
-
return rewrite_Control(stmt)
|
|
36
|
-
|
|
37
|
-
stim_1q_op = SQUIN_STIM_OP_MAPPING.get(type(applied_op))
|
|
38
|
-
if stim_1q_op is None:
|
|
39
|
-
return RewriteResult()
|
|
40
|
-
|
|
41
|
-
qubit_idx_ssas = insert_qubit_idx_from_wire_ssa(
|
|
42
|
-
wire_ssas=stmt.inputs, stmt_to_insert_before=stmt
|
|
43
|
-
)
|
|
44
|
-
if qubit_idx_ssas is None:
|
|
45
|
-
return RewriteResult()
|
|
46
|
-
|
|
47
|
-
stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas))
|
|
48
|
-
|
|
49
|
-
# Get the wires from the inputs of Apply or Broadcast,
|
|
50
|
-
# then put those as the result of the current stmt
|
|
51
|
-
# before replacing it entirely
|
|
52
|
-
for input_wire, output_wire in zip(stmt.inputs, stmt.results):
|
|
53
|
-
output_wire.replace_by(input_wire)
|
|
54
|
-
|
|
55
|
-
stmt.replace_by(stim_1q_stmt)
|
|
56
|
-
|
|
57
|
-
return RewriteResult(has_done_something=True)
|
|
File without changes
|
|
File without changes
|