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.

Files changed (136) hide show
  1. bloqade/analysis/address/__init__.py +8 -4
  2. bloqade/analysis/address/analysis.py +119 -29
  3. bloqade/analysis/address/impls.py +290 -87
  4. bloqade/analysis/address/lattice.py +209 -24
  5. bloqade/analysis/fidelity/analysis.py +2 -2
  6. bloqade/analysis/measure_id/impls.py +3 -27
  7. bloqade/cirq_utils/__init__.py +3 -1
  8. bloqade/cirq_utils/emit/__init__.py +3 -0
  9. bloqade/cirq_utils/emit/base.py +243 -0
  10. bloqade/cirq_utils/emit/gate.py +104 -0
  11. bloqade/cirq_utils/emit/noise.py +90 -0
  12. bloqade/cirq_utils/emit/qubit.py +35 -0
  13. bloqade/cirq_utils/lowering.py +664 -0
  14. bloqade/native/__init__.py +0 -1
  15. bloqade/native/_prelude.py +3 -3
  16. bloqade/native/dialects/gate/__init__.py +2 -0
  17. bloqade/native/dialects/gate/_dialect.py +3 -0
  18. bloqade/native/dialects/{gates → gate}/_interface.py +5 -5
  19. bloqade/native/dialects/{gates → gate}/stmts.py +5 -5
  20. bloqade/native/stdlib/broadcast.py +19 -19
  21. bloqade/native/stdlib/simple.py +14 -13
  22. bloqade/native/upstream/__init__.py +5 -0
  23. bloqade/native/upstream/squin2native.py +136 -0
  24. bloqade/pyqrack/__init__.py +1 -2
  25. bloqade/pyqrack/device.py +6 -17
  26. bloqade/pyqrack/native.py +17 -17
  27. bloqade/pyqrack/reg.py +1 -6
  28. bloqade/pyqrack/squin/gate/__init__.py +1 -0
  29. bloqade/pyqrack/squin/gate/gate.py +136 -0
  30. bloqade/pyqrack/squin/noise/native.py +120 -54
  31. bloqade/pyqrack/squin/qubit.py +25 -41
  32. bloqade/pyqrack/target.py +2 -2
  33. bloqade/qasm2/dialects/core/address.py +21 -12
  34. bloqade/qasm2/dialects/noise/fidelity.py +2 -6
  35. bloqade/qasm2/dialects/noise/model.py +2 -1
  36. bloqade/qasm2/passes/parallel.py +3 -1
  37. bloqade/qasm2/rewrite/__init__.py +0 -1
  38. bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
  39. bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
  40. bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
  41. bloqade/qubit/__init__.py +12 -0
  42. bloqade/qubit/_dialect.py +3 -0
  43. bloqade/qubit/_interface.py +49 -0
  44. bloqade/qubit/_prelude.py +45 -0
  45. bloqade/qubit/analysis/__init__.py +1 -0
  46. bloqade/qubit/analysis/address_impl.py +40 -0
  47. bloqade/qubit/stdlib/__init__.py +2 -0
  48. bloqade/qubit/stdlib/_new.py +34 -0
  49. bloqade/qubit/stdlib/broadcast.py +62 -0
  50. bloqade/qubit/stdlib/simple.py +59 -0
  51. bloqade/qubit/stmts.py +60 -0
  52. bloqade/rewrite/passes/aggressive_unroll.py +2 -1
  53. bloqade/squin/__init__.py +44 -17
  54. bloqade/squin/analysis/__init__.py +0 -1
  55. bloqade/squin/analysis/schedule.py +2 -2
  56. bloqade/squin/gate/__init__.py +2 -0
  57. bloqade/squin/gate/_dialect.py +3 -0
  58. bloqade/squin/gate/_interface.py +98 -0
  59. bloqade/squin/gate/stmts.py +119 -0
  60. bloqade/squin/groups.py +4 -21
  61. bloqade/squin/noise/__init__.py +1 -9
  62. bloqade/squin/noise/_dialect.py +1 -1
  63. bloqade/squin/noise/_interface.py +45 -0
  64. bloqade/squin/noise/stmts.py +65 -29
  65. bloqade/squin/rewrite/U3_to_clifford.py +70 -51
  66. bloqade/squin/rewrite/__init__.py +0 -2
  67. bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
  68. bloqade/squin/rewrite/wrap_analysis.py +4 -35
  69. bloqade/squin/stdlib/broadcast/__init__.py +34 -0
  70. bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
  71. bloqade/squin/stdlib/broadcast/gate.py +260 -0
  72. bloqade/squin/stdlib/broadcast/noise.py +144 -0
  73. bloqade/squin/stdlib/simple/__init__.py +33 -0
  74. bloqade/squin/stdlib/simple/gate.py +242 -0
  75. bloqade/squin/stdlib/simple/noise.py +126 -0
  76. bloqade/stim/__init__.py +1 -0
  77. bloqade/stim/_wrappers.py +6 -0
  78. bloqade/stim/dialects/noise/emit.py +6 -1
  79. bloqade/stim/dialects/noise/stmts.py +5 -3
  80. bloqade/stim/emit/stim_str.py +2 -0
  81. bloqade/stim/parse/lowering.py +12 -17
  82. bloqade/stim/passes/__init__.py +0 -1
  83. bloqade/stim/passes/flatten.py +26 -0
  84. bloqade/stim/passes/simplify_ifs.py +6 -1
  85. bloqade/stim/passes/squin_to_stim.py +4 -70
  86. bloqade/stim/rewrite/__init__.py +0 -4
  87. bloqade/stim/rewrite/ifs_to_stim.py +23 -29
  88. bloqade/stim/rewrite/qubit_to_stim.py +90 -41
  89. bloqade/stim/rewrite/squin_measure.py +9 -18
  90. bloqade/stim/rewrite/squin_noise.py +132 -108
  91. bloqade/stim/rewrite/util.py +5 -204
  92. bloqade/types.py +10 -0
  93. {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/METADATA +2 -2
  94. {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/RECORD +96 -100
  95. bloqade/native/dialects/gates/__init__.py +0 -3
  96. bloqade/native/dialects/gates/_dialect.py +0 -3
  97. bloqade/pyqrack/squin/op.py +0 -180
  98. bloqade/pyqrack/squin/runtime.py +0 -543
  99. bloqade/pyqrack/squin/wire.py +0 -51
  100. bloqade/squin/_typeinfer.py +0 -20
  101. bloqade/squin/analysis/address_impl.py +0 -71
  102. bloqade/squin/analysis/nsites/__init__.py +0 -9
  103. bloqade/squin/analysis/nsites/analysis.py +0 -50
  104. bloqade/squin/analysis/nsites/impls.py +0 -99
  105. bloqade/squin/analysis/nsites/lattice.py +0 -49
  106. bloqade/squin/cirq/__init__.py +0 -306
  107. bloqade/squin/cirq/emit/emit_circuit.py +0 -129
  108. bloqade/squin/cirq/emit/noise.py +0 -49
  109. bloqade/squin/cirq/emit/op.py +0 -176
  110. bloqade/squin/cirq/emit/qubit.py +0 -58
  111. bloqade/squin/cirq/emit/runtime.py +0 -242
  112. bloqade/squin/cirq/lowering.py +0 -439
  113. bloqade/squin/lowering.py +0 -80
  114. bloqade/squin/noise/_wrapper.py +0 -36
  115. bloqade/squin/noise/rewrite.py +0 -129
  116. bloqade/squin/op/__init__.py +0 -41
  117. bloqade/squin/op/_dialect.py +0 -3
  118. bloqade/squin/op/_wrapper.py +0 -121
  119. bloqade/squin/op/number.py +0 -5
  120. bloqade/squin/op/rewrite.py +0 -46
  121. bloqade/squin/op/stdlib.py +0 -62
  122. bloqade/squin/op/stmts.py +0 -300
  123. bloqade/squin/op/traits.py +0 -43
  124. bloqade/squin/op/types.py +0 -128
  125. bloqade/squin/parallel.py +0 -200
  126. bloqade/squin/qubit.py +0 -194
  127. bloqade/squin/rewrite/canonicalize.py +0 -60
  128. bloqade/squin/rewrite/desugar.py +0 -102
  129. bloqade/squin/stdlib/channel.py +0 -86
  130. bloqade/squin/stdlib/gate.py +0 -201
  131. bloqade/squin/types.py +0 -8
  132. bloqade/squin/wire.py +0 -201
  133. bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
  134. bloqade/stim/rewrite/wire_to_stim.py +0 -57
  135. {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/WHEEL +0 -0
  136. {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -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)
@@ -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
@@ -1,8 +0,0 @@
1
- from kirin import types
2
-
3
-
4
- class MeasurementResult:
5
- pass
6
-
7
-
8
- MeasurementResultType = types.PyClass(MeasurementResult)
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)