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.
Files changed (191) hide show
  1. bloqade/analysis/address/__init__.py +8 -4
  2. bloqade/analysis/address/analysis.py +123 -33
  3. bloqade/analysis/address/impls.py +293 -90
  4. bloqade/analysis/address/lattice.py +209 -24
  5. bloqade/analysis/fidelity/analysis.py +11 -23
  6. bloqade/analysis/measure_id/analysis.py +18 -20
  7. bloqade/analysis/measure_id/impls.py +31 -29
  8. bloqade/annotate/__init__.py +6 -0
  9. bloqade/annotate/_dialect.py +3 -0
  10. bloqade/annotate/_interface.py +22 -0
  11. bloqade/annotate/stmts.py +29 -0
  12. bloqade/annotate/types.py +13 -0
  13. bloqade/cirq_utils/__init__.py +4 -2
  14. bloqade/cirq_utils/emit/__init__.py +3 -0
  15. bloqade/cirq_utils/emit/base.py +246 -0
  16. bloqade/cirq_utils/emit/gate.py +104 -0
  17. bloqade/cirq_utils/emit/noise.py +90 -0
  18. bloqade/cirq_utils/emit/qubit.py +35 -0
  19. bloqade/cirq_utils/lowering.py +660 -0
  20. bloqade/cirq_utils/noise/__init__.py +0 -2
  21. bloqade/cirq_utils/noise/_two_zone_utils.py +7 -15
  22. bloqade/cirq_utils/noise/model.py +151 -191
  23. bloqade/cirq_utils/noise/transform.py +2 -2
  24. bloqade/cirq_utils/parallelize.py +9 -6
  25. bloqade/gemini/__init__.py +1 -0
  26. bloqade/gemini/analysis/__init__.py +3 -0
  27. bloqade/gemini/analysis/logical_validation/__init__.py +1 -0
  28. bloqade/gemini/analysis/logical_validation/analysis.py +17 -0
  29. bloqade/gemini/analysis/logical_validation/impls.py +101 -0
  30. bloqade/gemini/groups.py +67 -0
  31. bloqade/native/__init__.py +23 -0
  32. bloqade/native/_prelude.py +45 -0
  33. bloqade/native/dialects/__init__.py +0 -0
  34. bloqade/native/dialects/gate/__init__.py +2 -0
  35. bloqade/native/dialects/gate/_dialect.py +3 -0
  36. bloqade/native/dialects/gate/_interface.py +32 -0
  37. bloqade/native/dialects/gate/stmts.py +31 -0
  38. bloqade/native/stdlib/__init__.py +0 -0
  39. bloqade/native/stdlib/broadcast.py +246 -0
  40. bloqade/native/stdlib/simple.py +220 -0
  41. bloqade/native/upstream/__init__.py +4 -0
  42. bloqade/native/upstream/squin2native.py +79 -0
  43. bloqade/pyqrack/__init__.py +2 -2
  44. bloqade/pyqrack/base.py +7 -1
  45. bloqade/pyqrack/device.py +192 -18
  46. bloqade/pyqrack/native.py +49 -0
  47. bloqade/pyqrack/reg.py +6 -6
  48. bloqade/pyqrack/squin/gate/__init__.py +1 -0
  49. bloqade/pyqrack/squin/gate/gate.py +136 -0
  50. bloqade/pyqrack/squin/noise/native.py +120 -54
  51. bloqade/pyqrack/squin/qubit.py +39 -36
  52. bloqade/pyqrack/target.py +5 -4
  53. bloqade/pyqrack/task.py +114 -7
  54. bloqade/qasm2/_qasm_loading.py +3 -3
  55. bloqade/qasm2/dialects/core/address.py +21 -12
  56. bloqade/qasm2/dialects/expr/_emit.py +19 -8
  57. bloqade/qasm2/dialects/expr/stmts.py +7 -7
  58. bloqade/qasm2/dialects/noise/fidelity.py +4 -8
  59. bloqade/qasm2/dialects/noise/model.py +2 -1
  60. bloqade/qasm2/emit/base.py +16 -11
  61. bloqade/qasm2/emit/gate.py +11 -8
  62. bloqade/qasm2/emit/main.py +103 -3
  63. bloqade/qasm2/emit/target.py +9 -5
  64. bloqade/qasm2/groups.py +3 -2
  65. bloqade/qasm2/parse/lowering.py +0 -1
  66. bloqade/qasm2/passes/fold.py +14 -73
  67. bloqade/qasm2/passes/glob.py +2 -2
  68. bloqade/qasm2/passes/noise.py +1 -1
  69. bloqade/qasm2/passes/parallel.py +7 -5
  70. bloqade/qasm2/rewrite/__init__.py +0 -1
  71. bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
  72. bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
  73. bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
  74. bloqade/qasm2/rewrite/register.py +2 -2
  75. bloqade/qasm2/rewrite/uop_to_parallel.py +4 -2
  76. bloqade/qbraid/lowering.py +1 -0
  77. bloqade/qbraid/schema.py +2 -2
  78. bloqade/qubit/__init__.py +12 -0
  79. bloqade/qubit/_dialect.py +3 -0
  80. bloqade/qubit/_interface.py +49 -0
  81. bloqade/qubit/_prelude.py +45 -0
  82. bloqade/qubit/analysis/__init__.py +1 -0
  83. bloqade/qubit/analysis/address_impl.py +40 -0
  84. bloqade/qubit/stdlib/__init__.py +2 -0
  85. bloqade/qubit/stdlib/_new.py +34 -0
  86. bloqade/qubit/stdlib/broadcast.py +62 -0
  87. bloqade/qubit/stdlib/simple.py +59 -0
  88. bloqade/qubit/stmts.py +60 -0
  89. bloqade/rewrite/passes/__init__.py +6 -0
  90. bloqade/rewrite/passes/aggressive_unroll.py +103 -0
  91. bloqade/rewrite/passes/callgraph.py +116 -0
  92. bloqade/rewrite/passes/canonicalize_ilist.py +20 -14
  93. bloqade/rewrite/rules/split_ifs.py +18 -1
  94. bloqade/squin/__init__.py +47 -14
  95. bloqade/squin/analysis/__init__.py +0 -1
  96. bloqade/squin/analysis/schedule.py +10 -11
  97. bloqade/squin/gate/__init__.py +2 -0
  98. bloqade/squin/gate/_dialect.py +3 -0
  99. bloqade/squin/gate/_interface.py +98 -0
  100. bloqade/squin/gate/stmts.py +125 -0
  101. bloqade/squin/groups.py +5 -22
  102. bloqade/squin/noise/__init__.py +1 -10
  103. bloqade/squin/noise/_dialect.py +1 -1
  104. bloqade/squin/noise/_interface.py +45 -0
  105. bloqade/squin/noise/stmts.py +66 -28
  106. bloqade/squin/rewrite/U3_to_clifford.py +70 -51
  107. bloqade/squin/rewrite/__init__.py +0 -2
  108. bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
  109. bloqade/squin/rewrite/wrap_analysis.py +4 -35
  110. bloqade/squin/stdlib/__init__.py +0 -0
  111. bloqade/squin/stdlib/broadcast/__init__.py +34 -0
  112. bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
  113. bloqade/squin/stdlib/broadcast/gate.py +260 -0
  114. bloqade/squin/stdlib/broadcast/noise.py +144 -0
  115. bloqade/squin/stdlib/simple/__init__.py +33 -0
  116. bloqade/squin/stdlib/simple/gate.py +242 -0
  117. bloqade/squin/stdlib/simple/noise.py +126 -0
  118. bloqade/stim/__init__.py +1 -0
  119. bloqade/stim/_wrappers.py +6 -0
  120. bloqade/stim/dialects/auxiliary/emit.py +19 -18
  121. bloqade/stim/dialects/collapse/emit_str.py +7 -8
  122. bloqade/stim/dialects/gate/emit.py +9 -10
  123. bloqade/stim/dialects/noise/emit.py +17 -13
  124. bloqade/stim/dialects/noise/stmts.py +5 -3
  125. bloqade/stim/emit/__init__.py +1 -0
  126. bloqade/stim/emit/impls.py +16 -0
  127. bloqade/stim/emit/stim_str.py +48 -31
  128. bloqade/stim/groups.py +12 -2
  129. bloqade/stim/parse/lowering.py +14 -17
  130. bloqade/stim/passes/__init__.py +0 -2
  131. bloqade/stim/passes/flatten.py +26 -0
  132. bloqade/stim/passes/simplify_ifs.py +6 -1
  133. bloqade/stim/passes/squin_to_stim.py +9 -84
  134. bloqade/stim/rewrite/__init__.py +2 -4
  135. bloqade/stim/rewrite/get_record_util.py +24 -0
  136. bloqade/stim/rewrite/ifs_to_stim.py +24 -25
  137. bloqade/stim/rewrite/qubit_to_stim.py +90 -41
  138. bloqade/stim/rewrite/set_detector_to_stim.py +68 -0
  139. bloqade/stim/rewrite/set_observable_to_stim.py +52 -0
  140. bloqade/stim/rewrite/squin_measure.py +9 -18
  141. bloqade/stim/rewrite/squin_noise.py +134 -108
  142. bloqade/stim/rewrite/util.py +5 -192
  143. bloqade/test_utils.py +1 -1
  144. bloqade/types.py +10 -0
  145. bloqade/validation/__init__.py +2 -0
  146. bloqade/validation/analysis/__init__.py +5 -0
  147. bloqade/validation/analysis/analysis.py +41 -0
  148. bloqade/validation/analysis/lattice.py +58 -0
  149. bloqade/validation/kernel_validation.py +77 -0
  150. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/METADATA +5 -6
  151. bloqade_circuit-0.9.1.dist-info/RECORD +265 -0
  152. bloqade/pyqrack/squin/op.py +0 -180
  153. bloqade/pyqrack/squin/runtime.py +0 -535
  154. bloqade/pyqrack/squin/wire.py +0 -51
  155. bloqade/rewrite/rules/flatten_ilist.py +0 -51
  156. bloqade/rewrite/rules/inline_getitem_ilist.py +0 -31
  157. bloqade/squin/_typeinfer.py +0 -20
  158. bloqade/squin/analysis/address_impl.py +0 -71
  159. bloqade/squin/analysis/nsites/__init__.py +0 -9
  160. bloqade/squin/analysis/nsites/analysis.py +0 -50
  161. bloqade/squin/analysis/nsites/impls.py +0 -92
  162. bloqade/squin/analysis/nsites/lattice.py +0 -49
  163. bloqade/squin/cirq/__init__.py +0 -280
  164. bloqade/squin/cirq/emit/emit_circuit.py +0 -109
  165. bloqade/squin/cirq/emit/noise.py +0 -49
  166. bloqade/squin/cirq/emit/op.py +0 -125
  167. bloqade/squin/cirq/emit/qubit.py +0 -60
  168. bloqade/squin/cirq/emit/runtime.py +0 -242
  169. bloqade/squin/cirq/lowering.py +0 -440
  170. bloqade/squin/lowering.py +0 -54
  171. bloqade/squin/noise/_wrapper.py +0 -40
  172. bloqade/squin/noise/rewrite.py +0 -111
  173. bloqade/squin/op/__init__.py +0 -41
  174. bloqade/squin/op/_dialect.py +0 -3
  175. bloqade/squin/op/_wrapper.py +0 -121
  176. bloqade/squin/op/number.py +0 -5
  177. bloqade/squin/op/rewrite.py +0 -46
  178. bloqade/squin/op/stdlib.py +0 -62
  179. bloqade/squin/op/stmts.py +0 -276
  180. bloqade/squin/op/traits.py +0 -43
  181. bloqade/squin/op/types.py +0 -26
  182. bloqade/squin/qubit.py +0 -184
  183. bloqade/squin/rewrite/canonicalize.py +0 -60
  184. bloqade/squin/rewrite/desugar.py +0 -124
  185. bloqade/squin/types.py +0 -8
  186. bloqade/squin/wire.py +0 -201
  187. bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
  188. bloqade/stim/rewrite/wire_to_stim.py +0 -57
  189. bloqade_circuit-0.6.4.dist-info/RECORD +0 -234
  190. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/WHEEL +0 -0
  191. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,246 @@
1
+ from typing import Sequence
2
+ from warnings import warn
3
+ from dataclasses import field, dataclass
4
+
5
+ import cirq
6
+ from kirin import ir, types, interp
7
+ from kirin.emit import EmitABC, EmitFrame
8
+ from kirin.interp import MethodTable, impl
9
+ from kirin.dialects import py, func, ilist
10
+ from typing_extensions import Self
11
+
12
+ from bloqade.squin import kernel
13
+ from bloqade.rewrite.passes import AggressiveUnroll
14
+
15
+
16
+ def emit_circuit(
17
+ mt: ir.Method,
18
+ qubits: Sequence[cirq.Qid] | None = None,
19
+ circuit_qubits: Sequence[cirq.Qid] | None = None,
20
+ args: tuple = (),
21
+ ignore_returns: bool = False,
22
+ ) -> cirq.Circuit:
23
+ """Converts a squin.kernel method to a cirq.Circuit object.
24
+
25
+ Args:
26
+ mt (ir.Method): The kernel method from which to construct the circuit.
27
+
28
+ Keyword Args:
29
+ circuit_qubits (Sequence[cirq.Qid] | None):
30
+ A list of qubits to use as the qubits in the circuit. Defaults to None.
31
+ If this is None, then `cirq.LineQubit`s are inserted for every `squin.qalloc`
32
+ statement in the order they appear inside the kernel.
33
+ **Note**: If a list of qubits is provided, make sure that there is a sufficient
34
+ number of qubits for the resulting circuit.
35
+ args (tuple):
36
+ The arguments of the kernel function from which to emit a circuit.
37
+ ignore_returns (bool):
38
+ If `False`, emitting a circuit from a kernel that returns a value will error.
39
+ Set it to `True` in order to ignore the return value(s). Defaults to `False`.
40
+
41
+ ## Examples:
42
+
43
+ Here's a very basic example:
44
+
45
+ ```python
46
+ from bloqade import squin
47
+ from bloqade.cirq_utils import emit_circuit
48
+
49
+ @squin.kernel
50
+ def main():
51
+ q = squin.qalloc(2)
52
+ squin.h(q[0])
53
+ squin.cx(q[0], q[1])
54
+
55
+ circuit = emit_circuit(main)
56
+
57
+ print(circuit)
58
+ ```
59
+
60
+ You can also compose multiple kernels. Those are emitted as subcircuits within the "main" circuit.
61
+ Subkernels can accept arguments and return a value.
62
+
63
+ ```python
64
+ from bloqade import squin
65
+ from bloqade.cirq_utils import emit_circuit
66
+ from kirin.dialects import ilist
67
+ from typing import Literal
68
+ import cirq
69
+
70
+ @squin.kernel
71
+ def entangle(q: ilist.IList[squin.qubit.Qubit, Literal[2]]):
72
+ squin.h(q[0])
73
+ squin.cx(q[0], q[1])
74
+
75
+ @squin.kernel
76
+ def main():
77
+ q = squin.qalloc(2)
78
+ q2 = squin.qalloc(3)
79
+ squin.cx(q[1], q2[2])
80
+
81
+
82
+ # custom list of qubits on grid
83
+ qubits = [cirq.GridQubit(i, i+1) for i in range(5)]
84
+
85
+ circuit = emit_circuit(main, circuit_qubits=qubits)
86
+ print(circuit)
87
+
88
+ ```
89
+
90
+ We also passed in a custom list of qubits above. This allows you to provide a custom geometry
91
+ and manipulate the qubits in other circuits directly written in cirq as well.
92
+ """
93
+
94
+ if circuit_qubits is None and qubits is not None:
95
+ circuit_qubits = qubits
96
+ warn(
97
+ "The keyword argument `qubits` is deprecated. Use `circuit_qubits` instead."
98
+ )
99
+
100
+ if (
101
+ not ignore_returns
102
+ and isinstance(mt.code, func.Function)
103
+ and not mt.code.signature.output.is_subseteq(types.NoneType)
104
+ ):
105
+ raise interp.exceptions.InterpreterError(
106
+ "The method you are trying to convert to a circuit has a return value, but returning from a circuit is not supported."
107
+ " Set `ignore_returns = True` in order to simply ignore the return values and emit a circuit."
108
+ )
109
+
110
+ if len(args) != len(mt.args):
111
+ raise ValueError(
112
+ f"The method from which you're trying to emit a circuit takes {len(mt.args)} as input, but you passed in {len(args)} via the `args` keyword!"
113
+ )
114
+
115
+ emitter = EmitCirq(qubits=circuit_qubits)
116
+
117
+ symbol_op_trait = mt.code.get_trait(ir.SymbolOpInterface)
118
+ if (symbol_op_trait := mt.code.get_trait(ir.SymbolOpInterface)) is None:
119
+ raise interp.exceptions.InterpreterError(
120
+ "The method is not a symbol, cannot emit circuit!"
121
+ )
122
+
123
+ sym_name = symbol_op_trait.get_sym_name(mt.code).unwrap()
124
+
125
+ if (signature_trait := mt.code.get_trait(ir.HasSignature)) is None:
126
+ raise interp.exceptions.InterpreterError(
127
+ f"The method {sym_name} does not have a signature, cannot emit circuit!"
128
+ )
129
+
130
+ signature = signature_trait.get_signature(mt.code)
131
+ new_signature = func.Signature(inputs=(), output=signature.output)
132
+
133
+ callable_region = mt.callable_region.clone()
134
+ entry_block = callable_region.blocks[0]
135
+ args_ssa = list(entry_block.args)
136
+ first_stmt = entry_block.first_stmt
137
+
138
+ assert first_stmt is not None, "Method has no statements!"
139
+ if len(args_ssa) - 1 != len(args):
140
+ raise interp.exceptions.InterpreterError(
141
+ f"The method {sym_name} takes {len(args_ssa) - 1} arguments, but you passed in {len(args)} via the `args` keyword!"
142
+ )
143
+
144
+ for arg, arg_ssa in zip(args, args_ssa[1:], strict=True):
145
+ (value := py.Constant(arg)).insert_before(first_stmt)
146
+ arg_ssa.replace_by(value.result)
147
+ entry_block.args.delete(arg_ssa)
148
+
149
+ new_func = func.Function(
150
+ sym_name=sym_name, body=callable_region, signature=new_signature
151
+ )
152
+ mt_ = ir.Method(
153
+ dialects=mt.dialects,
154
+ code=new_func,
155
+ sym_name=sym_name,
156
+ )
157
+
158
+ AggressiveUnroll(mt_.dialects).fixpoint(mt_)
159
+ emitter.initialize()
160
+ emitter.run(mt_)
161
+ return emitter.circuit
162
+
163
+
164
+ @dataclass
165
+ class EmitCirqFrame(EmitFrame):
166
+ qubit_index: int = 0
167
+ qubits: Sequence[cirq.Qid] | None = None
168
+
169
+
170
+ def _default_kernel():
171
+ return kernel
172
+
173
+
174
+ @dataclass
175
+ class EmitCirq(EmitABC[EmitCirqFrame, cirq.Circuit]):
176
+ keys = ("emit.cirq", "emit.main")
177
+ dialects: ir.DialectGroup = field(default_factory=_default_kernel)
178
+ void = cirq.Circuit()
179
+ qubits: Sequence[cirq.Qid] | None = None
180
+ circuit: cirq.Circuit = field(default_factory=cirq.Circuit)
181
+
182
+ def initialize(self) -> Self:
183
+ return super().initialize()
184
+
185
+ def initialize_frame(
186
+ self, node: ir.Statement, *, has_parent_access: bool = False
187
+ ) -> EmitCirqFrame:
188
+ return EmitCirqFrame(
189
+ node, has_parent_access=has_parent_access, qubits=self.qubits
190
+ )
191
+
192
+ def reset(self):
193
+ self.circuit = cirq.Circuit()
194
+
195
+ def eval_fallback(self, frame: EmitCirqFrame, node: ir.Statement) -> tuple:
196
+ return tuple(None for _ in range(len(node.results)))
197
+
198
+
199
+ @func.dialect.register(key="emit.cirq")
200
+ class __FuncEmit(MethodTable):
201
+
202
+ @impl(func.Function)
203
+ def emit_func(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: func.Function):
204
+ for block in stmt.body.blocks:
205
+ frame.current_block = block
206
+ for s in block.stmts:
207
+ frame.current_stmt = s
208
+ stmt_results = emit.frame_eval(frame, s)
209
+ if isinstance(stmt_results, tuple):
210
+ if len(stmt_results) != 0:
211
+ frame.set_values(s.results, stmt_results)
212
+ continue
213
+
214
+ return (emit.circuit,)
215
+
216
+ @impl(func.Invoke)
217
+ def emit_invoke(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: func.Invoke):
218
+ raise interp.exceptions.InterpreterError(
219
+ "Function invokes should need to be inlined! "
220
+ "If you called the emit_circuit method, that should have happened, please report this issue."
221
+ )
222
+
223
+
224
+ @py.indexing.dialect.register(key="emit.cirq")
225
+ class __Concrete(interp.MethodTable):
226
+
227
+ @interp.impl(py.indexing.GetItem)
228
+ def getindex(self, interp, frame: interp.Frame, stmt: py.indexing.GetItem):
229
+ # NOTE: no support for indexing into single statements in cirq
230
+ return ()
231
+
232
+ @interp.impl(py.Constant)
233
+ def emit_constant(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: py.Constant):
234
+ return (stmt.value.data,) # pyright: ignore[reportAttributeAccessIssue]
235
+
236
+
237
+ @ilist.dialect.register(key="emit.cirq")
238
+ class __IList(interp.MethodTable):
239
+ @interp.impl(ilist.New)
240
+ def new_ilist(
241
+ self,
242
+ emit: EmitCirq,
243
+ frame: interp.Frame,
244
+ stmt: ilist.New,
245
+ ):
246
+ return (ilist.IList(data=frame.get_values(stmt.values)),)
@@ -0,0 +1,104 @@
1
+ import math
2
+
3
+ import cirq
4
+ from kirin.interp import MethodTable, impl
5
+
6
+ from bloqade.squin import gate
7
+
8
+ from .base import EmitCirq, EmitCirqFrame
9
+
10
+
11
+ @gate.dialect.register(key="emit.cirq")
12
+ class __EmitCirqGateMethods(MethodTable):
13
+
14
+ @impl(gate.stmts.X)
15
+ @impl(gate.stmts.Y)
16
+ @impl(gate.stmts.Z)
17
+ @impl(gate.stmts.H)
18
+ def hermitian(
19
+ self, emit: EmitCirq, frame: EmitCirqFrame, stmt: gate.stmts.SingleQubitGate
20
+ ):
21
+ qubits = frame.get(stmt.qubits)
22
+ cirq_op = getattr(cirq, stmt.name.upper())
23
+ emit.circuit.append(cirq_op.on_each(qubits))
24
+ return ()
25
+
26
+ @impl(gate.stmts.S)
27
+ @impl(gate.stmts.T)
28
+ def unitary(
29
+ self,
30
+ emit: EmitCirq,
31
+ frame: EmitCirqFrame,
32
+ stmt: gate.stmts.SingleQubitNonHermitianGate,
33
+ ):
34
+ qubits = frame.get(stmt.qubits)
35
+ cirq_op = getattr(cirq, stmt.name.upper())
36
+ if stmt.adjoint:
37
+ cirq_op = cirq_op ** (-1)
38
+
39
+ emit.circuit.append(cirq_op.on_each(qubits))
40
+ return ()
41
+
42
+ @impl(gate.stmts.SqrtX)
43
+ @impl(gate.stmts.SqrtY)
44
+ def sqrt(
45
+ self,
46
+ emit: EmitCirq,
47
+ frame: EmitCirqFrame,
48
+ stmt: gate.stmts.SqrtX | gate.stmts.SqrtY,
49
+ ):
50
+ qubits = frame.get(stmt.qubits)
51
+
52
+ exponent = 0.5
53
+ if stmt.adjoint:
54
+ exponent *= -1
55
+
56
+ if isinstance(stmt, gate.stmts.SqrtX):
57
+ cirq_op = cirq.XPowGate(exponent=exponent)
58
+ else:
59
+ cirq_op = cirq.YPowGate(exponent=exponent)
60
+
61
+ emit.circuit.append(cirq_op.on_each(qubits))
62
+ return ()
63
+
64
+ @impl(gate.stmts.CX)
65
+ @impl(gate.stmts.CY)
66
+ @impl(gate.stmts.CZ)
67
+ def control(
68
+ self, emit: EmitCirq, frame: EmitCirqFrame, stmt: gate.stmts.ControlledGate
69
+ ):
70
+ controls = frame.get(stmt.controls)
71
+ targets = frame.get(stmt.targets)
72
+ cirq_op = getattr(cirq, stmt.name.upper())
73
+ cirq_qubits = [(ctrl, target) for ctrl, target in zip(controls, targets)]
74
+ emit.circuit.append(cirq_op.on_each(cirq_qubits))
75
+ return ()
76
+
77
+ @impl(gate.stmts.Rx)
78
+ @impl(gate.stmts.Ry)
79
+ @impl(gate.stmts.Rz)
80
+ def rot(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: gate.stmts.RotationGate):
81
+ qubits = frame.get(stmt.qubits)
82
+
83
+ turns = frame.get(stmt.angle)
84
+ angle = turns * 2 * math.pi
85
+ cirq_op = getattr(cirq, stmt.name.title())(rads=angle)
86
+
87
+ emit.circuit.append(cirq_op.on_each(qubits))
88
+ return ()
89
+
90
+ @impl(gate.stmts.U3)
91
+ def u3(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: gate.stmts.U3):
92
+ qubits = frame.get(stmt.qubits)
93
+
94
+ theta = frame.get(stmt.theta) * 2 * math.pi
95
+ phi = frame.get(stmt.phi) * 2 * math.pi
96
+ lam = frame.get(stmt.lam) * 2 * math.pi
97
+
98
+ emit.circuit.append(cirq.Rz(rads=lam).on_each(*qubits))
99
+
100
+ emit.circuit.append(cirq.Ry(rads=theta).on_each(*qubits))
101
+
102
+ emit.circuit.append(cirq.Rz(rads=phi).on_each(*qubits))
103
+
104
+ return ()
@@ -0,0 +1,90 @@
1
+ import cirq
2
+ from kirin.interp import MethodTable, impl
3
+
4
+ from bloqade.squin import noise
5
+
6
+ from .base import EmitCirq, EmitCirqFrame
7
+
8
+
9
+ @noise.dialect.register(key="emit.cirq")
10
+ class __EmitCirqNoiseMethods(MethodTable):
11
+
12
+ two_qubit_paulis = (
13
+ "IX",
14
+ "IY",
15
+ "IZ",
16
+ "XI",
17
+ "XX",
18
+ "XY",
19
+ "XZ",
20
+ "YI",
21
+ "YX",
22
+ "YY",
23
+ "YZ",
24
+ "ZI",
25
+ "ZX",
26
+ "ZY",
27
+ "ZZ",
28
+ )
29
+
30
+ @impl(noise.stmts.Depolarize)
31
+ def depolarize(
32
+ self, interp: EmitCirq, frame: EmitCirqFrame, stmt: noise.stmts.Depolarize
33
+ ):
34
+ p = frame.get(stmt.p)
35
+ qubits = frame.get(stmt.qubits)
36
+ cirfq_op = cirq.depolarize(p, n_qubits=1).on_each(qubits)
37
+ interp.circuit.append(cirfq_op)
38
+ return ()
39
+
40
+ @impl(noise.stmts.Depolarize2)
41
+ def depolarize2(
42
+ self, interp: EmitCirq, frame: EmitCirqFrame, stmt: noise.stmts.Depolarize2
43
+ ):
44
+ p = frame.get(stmt.p)
45
+ controls = frame.get(stmt.controls)
46
+ targets = frame.get(stmt.targets)
47
+ cirq_qubits = [(ctrl, target) for ctrl, target in zip(controls, targets)]
48
+ cirq_op = cirq.depolarize(p, n_qubits=2).on_each(cirq_qubits)
49
+ interp.circuit.append(cirq_op)
50
+ return ()
51
+
52
+ @impl(noise.stmts.SingleQubitPauliChannel)
53
+ def single_qubit_pauli_channel(
54
+ self,
55
+ interp: EmitCirq,
56
+ frame: EmitCirqFrame,
57
+ stmt: noise.stmts.SingleQubitPauliChannel,
58
+ ):
59
+ px = frame.get(stmt.px)
60
+ py = frame.get(stmt.py)
61
+ pz = frame.get(stmt.pz)
62
+ qubits = frame.get(stmt.qubits)
63
+
64
+ cirq_op = cirq.asymmetric_depolarize(px, py, pz).on_each(qubits)
65
+ interp.circuit.append(cirq_op)
66
+
67
+ return ()
68
+
69
+ @impl(noise.stmts.TwoQubitPauliChannel)
70
+ def two_qubit_pauli_channel(
71
+ self,
72
+ interp: EmitCirq,
73
+ frame: EmitCirqFrame,
74
+ stmt: noise.stmts.TwoQubitPauliChannel,
75
+ ):
76
+ ps = frame.get(stmt.probabilities)
77
+ error_probabilities = {
78
+ key: p for (key, p) in zip(self.two_qubit_paulis, ps) if p != 0
79
+ }
80
+
81
+ controls = frame.get(stmt.controls)
82
+ targets = frame.get(stmt.targets)
83
+ cirq_qubits = [(ctrl, target) for ctrl, target in zip(controls, targets)]
84
+
85
+ cirq_op = cirq.asymmetric_depolarize(
86
+ error_probabilities=error_probabilities
87
+ ).on_each(cirq_qubits)
88
+ interp.circuit.append(cirq_op)
89
+
90
+ return ()
@@ -0,0 +1,35 @@
1
+ import cirq
2
+ from kirin.interp import MethodTable, impl
3
+
4
+ from bloqade.qubit import stmts as qubit
5
+
6
+ from .base import EmitCirq, EmitCirqFrame
7
+
8
+
9
+ @qubit.dialect.register(key="emit.cirq")
10
+ class EmitCirqQubitMethods(MethodTable):
11
+ @impl(qubit.New)
12
+ def new(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: qubit.New):
13
+ if frame.qubits is not None:
14
+ cirq_qubit = frame.qubits[frame.qubit_index]
15
+ else:
16
+ cirq_qubit = cirq.LineQubit(frame.qubit_index)
17
+
18
+ frame.qubit_index += 1
19
+ return (cirq_qubit,)
20
+
21
+ @impl(qubit.Measure)
22
+ def measure_qubit_list(
23
+ self, emit: EmitCirq, frame: EmitCirqFrame, stmt: qubit.Measure
24
+ ):
25
+ qbits = frame.get(stmt.qubits)
26
+ emit.circuit.append(cirq.measure(qbits))
27
+ return (emit.void,)
28
+
29
+ @impl(qubit.Reset)
30
+ def reset(self, emit: EmitCirq, frame: EmitCirqFrame, stmt: qubit.Reset):
31
+ qubits = frame.get(stmt.qubits)
32
+ emit.circuit.append(
33
+ cirq.ResetChannel().on_each(*qubits),
34
+ )
35
+ return ()