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.

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 +96 -51
  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.13.dist-info → bloqade_circuit-0.8.0.dist-info}/METADATA +2 -2
  94. {bloqade_circuit-0.7.13.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.13.dist-info → bloqade_circuit-0.8.0.dist-info}/WHEEL +0 -0
  136. {bloqade_circuit-0.7.13.dist-info → bloqade_circuit-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,11 @@
1
1
  from kirin import ir
2
2
  from kirin.rewrite.abc import RewriteRule, RewriteResult
3
3
 
4
- from bloqade.squin import op, noise, qubit
4
+ from bloqade import qubit
5
+ from bloqade.squin import gate
5
6
  from bloqade.squin.rewrite import AddressAttribute
7
+ from bloqade.stim.dialects import gate as stim_gate, collapse as stim_collapse
6
8
  from bloqade.stim.rewrite.util import (
7
- SQUIN_STIM_OP_MAPPING,
8
- rewrite_Control,
9
- rewrite_QubitLoss,
10
9
  insert_qubit_idx_from_address,
11
10
  )
12
11
 
@@ -19,69 +18,115 @@ class SquinQubitToStim(RewriteRule):
19
18
  def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
20
19
 
21
20
  match node:
22
- case qubit.Apply() | qubit.Broadcast():
23
- return self.rewrite_Apply_and_Broadcast(node)
21
+ # not supported by Stim
22
+ case gate.stmts.T() | gate.stmts.RotationGate():
23
+ return RewriteResult()
24
+ # If you've reached this point all gates have stim equivalents
25
+ case qubit.stmts.Reset():
26
+ return self.rewrite_Reset(node)
27
+ case gate.stmts.SingleQubitGate():
28
+ return self.rewrite_SingleQubitGate(node)
29
+ case gate.stmts.ControlledGate():
30
+ return self.rewrite_ControlledGate(node)
24
31
  case _:
25
32
  return RewriteResult()
26
33
 
27
- def rewrite_Apply_and_Broadcast(
28
- self, stmt: qubit.Apply | qubit.Broadcast
29
- ) -> RewriteResult:
30
- """
31
- Rewrite Apply and Broadcast nodes to their stim equivalent statements.
32
- """
34
+ def rewrite_Reset(self, stmt: qubit.stmts.Reset) -> RewriteResult:
33
35
 
34
- # this is an SSAValue, need it to be the actual operator
35
- applied_op = stmt.operator.owner
36
-
37
- if isinstance(applied_op, noise.stmts.QubitLoss):
38
- return rewrite_QubitLoss(stmt)
39
-
40
- assert isinstance(applied_op, op.stmts.Operator)
41
-
42
- # Handle controlled gates with a separate procedure
43
- if isinstance(applied_op, op.stmts.Control):
44
- return rewrite_Control(stmt)
45
-
46
- # check if its adjoint, assume its canonicalized so no nested adjoints.
47
- is_conj = False
48
- if isinstance(applied_op, op.stmts.Adjoint):
49
- # By default the Adjoint has is_unitary = False, so we need to check
50
- # the inner applied operator to make sure its not just unitary,
51
- # but something that has an equivalent stim representation with *_DAG format.
52
- if isinstance(
53
- applied_op.op.owner, (op.stmts.SqrtX, op.stmts.SqrtY, op.stmts.S)
54
- ):
55
- is_conj = True
56
- applied_op = applied_op.op.owner
57
- else:
58
- return RewriteResult()
36
+ qubit_addr_attr = stmt.qubits.hints.get("address", None)
37
+
38
+ if qubit_addr_attr is None:
39
+ return RewriteResult()
40
+
41
+ assert isinstance(qubit_addr_attr, AddressAttribute)
42
+
43
+ qubit_idx_ssas = insert_qubit_idx_from_address(
44
+ address=qubit_addr_attr, stmt_to_insert_before=stmt
45
+ )
59
46
 
60
- stim_1q_op = SQUIN_STIM_OP_MAPPING.get(type(applied_op))
61
- if stim_1q_op is None:
47
+ if qubit_idx_ssas is None:
62
48
  return RewriteResult()
63
49
 
64
- address_attr = stmt.qubits[0].hints.get("address")
50
+ stim_stmt = stim_collapse.RZ(targets=tuple(qubit_idx_ssas))
51
+ stmt.replace_by(stim_stmt)
52
+
53
+ return RewriteResult(has_done_something=True)
54
+
55
+ def rewrite_SingleQubitGate(
56
+ self, stmt: gate.stmts.SingleQubitGate
57
+ ) -> RewriteResult:
58
+ """
59
+ Rewrite single qubit gate nodes to their stim equivalent statements.
60
+ Address Analysis should have been run along with Wrap Analysis before this rewrite is applied.
61
+ """
65
62
 
66
- if address_attr is None:
63
+ qubit_addr_attr = stmt.qubits.hints.get("address", None)
64
+ if qubit_addr_attr is None:
67
65
  return RewriteResult()
68
66
 
69
- assert isinstance(address_attr, AddressAttribute)
67
+ assert isinstance(qubit_addr_attr, AddressAttribute)
68
+
70
69
  qubit_idx_ssas = insert_qubit_idx_from_address(
71
- address=address_attr, stmt_to_insert_before=stmt
70
+ address=qubit_addr_attr, stmt_to_insert_before=stmt
72
71
  )
73
72
 
74
73
  if qubit_idx_ssas is None:
75
74
  return RewriteResult()
76
75
 
77
- # At this point, we know for certain stim_1q_op must be SQRT_X, SQRT_Y, or S
78
- # and has the option to set the dagger attribute. If is_conj is false,
79
- # the rewrite would have terminated early so we know anything else has to be
80
- # a non 1Q gate operation.
81
- if is_conj:
82
- stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas), dagger=is_conj)
76
+ # Get the name of the inputted stmt and see if there is an
77
+ # equivalently named statement in stim,
78
+ # then create an instance of that stim statement
79
+ stmt_name = type(stmt).__name__
80
+ stim_stmt_cls = getattr(stim_gate.stmts, stmt_name, None)
81
+ if stim_stmt_cls is None:
82
+ return RewriteResult()
83
+
84
+ if isinstance(stmt, gate.stmts.SingleQubitNonHermitianGate):
85
+ stim_stmt = stim_stmt_cls(
86
+ targets=tuple(qubit_idx_ssas), dagger=stmt.adjoint
87
+ )
83
88
  else:
84
- stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas))
85
- stmt.replace_by(stim_1q_stmt)
89
+ stim_stmt = stim_stmt_cls(targets=tuple(qubit_idx_ssas))
90
+ stmt.replace_by(stim_stmt)
91
+
92
+ return RewriteResult(has_done_something=True)
93
+
94
+ def rewrite_ControlledGate(self, stmt: gate.stmts.ControlledGate) -> RewriteResult:
95
+ """
96
+ Rewrite controlled gate nodes to their stim equivalent statements.
97
+ Address Analysis should have been run along with Wrap Analysis before this rewrite is applied.
98
+ """
99
+
100
+ controls_addr_attr = stmt.controls.hints.get("address", None)
101
+ targets_addr_attr = stmt.targets.hints.get("address", None)
102
+
103
+ if controls_addr_attr is None or targets_addr_attr is None:
104
+ return RewriteResult()
105
+
106
+ assert isinstance(controls_addr_attr, AddressAttribute)
107
+ assert isinstance(targets_addr_attr, AddressAttribute)
108
+
109
+ controls_idx_ssas = insert_qubit_idx_from_address(
110
+ address=controls_addr_attr, stmt_to_insert_before=stmt
111
+ )
112
+ targets_idx_ssas = insert_qubit_idx_from_address(
113
+ address=targets_addr_attr, stmt_to_insert_before=stmt
114
+ )
115
+
116
+ if controls_idx_ssas is None or targets_idx_ssas is None:
117
+ return RewriteResult()
118
+
119
+ # Get the name of the inputted stmt and see if there is an
120
+ # equivalently named statement in stim,
121
+ # then create an instance of that stim statement
122
+ stmt_name = type(stmt).__name__
123
+ stim_stmt_cls = getattr(stim_gate.stmts, stmt_name, None)
124
+ if stim_stmt_cls is None:
125
+ return RewriteResult()
126
+
127
+ stim_stmt = stim_stmt_cls(
128
+ targets=tuple(targets_idx_ssas), controls=tuple(controls_idx_ssas)
129
+ )
130
+ stmt.replace_by(stim_stmt)
86
131
 
87
132
  return RewriteResult(has_done_something=True)
@@ -5,11 +5,10 @@ from kirin import ir
5
5
  from kirin.dialects import py
6
6
  from kirin.rewrite.abc import RewriteRule, RewriteResult
7
7
 
8
- from bloqade.squin import wire, qubit
8
+ from bloqade import qubit
9
9
  from bloqade.squin.rewrite import AddressAttribute
10
10
  from bloqade.stim.dialects import collapse
11
11
  from bloqade.stim.rewrite.util import (
12
- is_measure_result_used,
13
12
  insert_qubit_idx_from_address,
14
13
  )
15
14
 
@@ -23,14 +22,12 @@ class SquinMeasureToStim(RewriteRule):
23
22
  def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
24
23
 
25
24
  match node:
26
- case qubit.MeasureQubit() | qubit.MeasureQubitList() | wire.Measure():
25
+ case qubit.stmts.Measure():
27
26
  return self.rewrite_Measure(node)
28
27
  case _:
29
28
  return RewriteResult()
30
29
 
31
- def rewrite_Measure(
32
- self, measure_stmt: qubit.MeasureQubit | qubit.MeasureQubitList | wire.Measure
33
- ) -> RewriteResult:
30
+ def rewrite_Measure(self, measure_stmt: qubit.stmts.Measure) -> RewriteResult:
34
31
 
35
32
  qubit_idx_ssas = self.get_qubit_idx_ssas(measure_stmt)
36
33
  if qubit_idx_ssas is None:
@@ -44,27 +41,21 @@ class SquinMeasureToStim(RewriteRule):
44
41
  prob_noise_stmt.insert_before(measure_stmt)
45
42
  stim_measure_stmt.insert_before(measure_stmt)
46
43
 
47
- if not is_measure_result_used(measure_stmt):
44
+ # if the measurement is not being used anywhere
45
+ # we can safely get rid of it. Measure cannot be DCE'd because
46
+ # it is not pure.
47
+ if not bool(measure_stmt.result.uses):
48
48
  measure_stmt.delete()
49
49
 
50
50
  return RewriteResult(has_done_something=True)
51
51
 
52
52
  def get_qubit_idx_ssas(
53
- self, measure_stmt: qubit.MeasureQubit | qubit.MeasureQubitList | wire.Measure
53
+ self, measure_stmt: qubit.stmts.Measure
54
54
  ) -> tuple[ir.SSAValue, ...] | None:
55
55
  """
56
56
  Extract the address attribute and insert qubit indices for the given measure statement.
57
57
  """
58
- match measure_stmt:
59
- case qubit.MeasureQubit():
60
- address_attr = measure_stmt.qubit.hints.get("address")
61
- case qubit.MeasureQubitList():
62
- address_attr = measure_stmt.qubits.hints.get("address")
63
- case wire.Measure():
64
- address_attr = measure_stmt.wire.hints.get("address")
65
- case _:
66
- return None
67
-
58
+ address_attr = measure_stmt.qubits.hints.get("address")
68
59
  if address_attr is None:
69
60
  return None
70
61
 
@@ -1,17 +1,17 @@
1
+ import itertools
1
2
  from typing import Tuple
2
3
  from dataclasses import dataclass
3
4
 
5
+ from kirin import types
4
6
  from kirin.ir import SSAValue, Statement
5
- from kirin.dialects import py, ilist
7
+ from kirin.dialects import py
6
8
  from kirin.rewrite.abc import RewriteRule, RewriteResult
7
9
 
8
- from bloqade.squin import op, wire, noise as squin_noise, qubit
10
+ from bloqade.squin import noise as squin_noise
9
11
  from bloqade.stim.dialects import noise as stim_noise
10
- from bloqade.stim.rewrite.util import (
11
- get_const_value,
12
- create_wire_passthrough,
13
- insert_qubit_idx_after_apply,
14
- )
12
+ from bloqade.stim.rewrite.util import insert_qubit_idx_from_address
13
+ from bloqade.analysis.address.lattice import AddressReg, PartialIList
14
+ from bloqade.squin.rewrite.wrap_analysis import AddressAttribute
15
15
 
16
16
 
17
17
  @dataclass
@@ -19,159 +19,183 @@ class SquinNoiseToStim(RewriteRule):
19
19
 
20
20
  def rewrite_Statement(self, node: Statement) -> RewriteResult:
21
21
  match node:
22
- case qubit.Apply() | qubit.Broadcast() | wire.Apply() | wire.Broadcast():
23
- return self.rewrite_Apply_and_Broadcast(node)
22
+ case squin_noise.stmts.NoiseChannel():
23
+ return self.rewrite_NoiseChannel(node)
24
24
  case _:
25
25
  return RewriteResult()
26
26
 
27
- def rewrite_Apply_and_Broadcast(
28
- self, stmt: qubit.Apply | qubit.Broadcast | wire.Apply | wire.Broadcast
27
+ def rewrite_NoiseChannel(
28
+ self, stmt: squin_noise.stmts.NoiseChannel
29
29
  ) -> RewriteResult:
30
- """Rewrite Apply and Broadcast to their stim statements."""
30
+ """Rewrite NoiseChannel statements to their stim equivalents."""
31
31
 
32
- # this is an SSAValue, need it to be the actual operator
33
- applied_op = stmt.operator.owner
32
+ rewrite_method = getattr(self, f"rewrite_{type(stmt).__name__}", None)
34
33
 
35
- if isinstance(applied_op, squin_noise.stmts.QubitLoss):
34
+ # No rewrite method exists and the rewrite should stop
35
+ if rewrite_method is None:
36
36
  return RewriteResult()
37
+ if isinstance(stmt, squin_noise.stmts.CorrelatedQubitLoss):
38
+ # CorrelatedQubitLoss represents a broadcast operation, but Stim does not
39
+ # support broadcasting for multi-qubit noise channels.
40
+ # Therefore, we must expand the broadcast into individual stim statements.
41
+ qubit_address_attr = stmt.qubits.hints.get("address", None)
37
42
 
38
- if isinstance(applied_op, squin_noise.stmts.NoiseChannel):
43
+ if not isinstance(qubit_address_attr, AddressAttribute):
44
+ return RewriteResult()
39
45
 
40
- rewrite_method = getattr(self, f"rewrite_{type(applied_op).__name__}", None)
41
- # No rewrite method exists and the rewrite should stop
42
- if rewrite_method is None:
46
+ if not isinstance(address := qubit_address_attr.address, PartialIList):
43
47
  return RewriteResult()
44
48
 
45
- qubit_idx_ssas = insert_qubit_idx_after_apply(stmt=stmt)
46
- if qubit_idx_ssas is None:
49
+ if not types.is_tuple_of(data := address.data, AddressReg):
47
50
  return RewriteResult()
48
51
 
49
- stim_stmt = rewrite_method(stmt, qubit_idx_ssas)
52
+ for address_reg in data:
53
+
54
+ qubit_idx_ssas = insert_qubit_idx_from_address(
55
+ AddressAttribute(address_reg), stmt
56
+ )
50
57
 
51
- if isinstance(stmt, (wire.Apply, wire.Broadcast)):
52
- create_wire_passthrough(stmt)
58
+ stim_stmt = rewrite_method(stmt, qubit_idx_ssas)
59
+ stim_stmt.insert_before(stmt)
53
60
 
54
- # guaranteed that you have a valid stim_stmt to plug in
55
- stmt.replace_by(stim_stmt)
61
+ stmt.delete()
56
62
 
57
63
  return RewriteResult(has_done_something=True)
58
- return RewriteResult()
59
64
 
60
- def rewrite_PauliError(
61
- self,
62
- stmt: qubit.Apply | qubit.Broadcast | wire.Broadcast | wire.Apply,
63
- qubit_idx_ssas: Tuple[SSAValue],
64
- ) -> Statement:
65
- """Rewrite squin.noise.PauliError to XError, YError, ZError."""
66
- squin_channel = stmt.operator.owner
67
- assert isinstance(squin_channel, squin_noise.stmts.PauliError)
68
- basis = squin_channel.basis.owner
69
- assert isinstance(basis, op.stmts.PauliOp)
70
- p = get_const_value(float, squin_channel.p)
71
-
72
- p_stmt = py.Constant(p)
73
- p_stmt.insert_before(stmt)
74
-
75
- if isinstance(basis, op.stmts.X):
76
- stim_stmt = stim_noise.XError(targets=qubit_idx_ssas, p=p_stmt.result)
77
- elif isinstance(basis, op.stmts.Y):
78
- stim_stmt = stim_noise.YError(targets=qubit_idx_ssas, p=p_stmt.result)
65
+ if isinstance(stmt, squin_noise.stmts.SingleQubitNoiseChannel):
66
+ qubit_address_attr = stmt.qubits.hints.get("address", None)
67
+ if qubit_address_attr is None:
68
+ return RewriteResult()
69
+ qubit_idx_ssas = insert_qubit_idx_from_address(qubit_address_attr, stmt)
70
+
71
+ elif isinstance(stmt, squin_noise.stmts.TwoQubitNoiseChannel):
72
+ control_address_attr = stmt.controls.hints.get("address", None)
73
+ target_address_attr = stmt.targets.hints.get("address", None)
74
+ if control_address_attr is None or target_address_attr is None:
75
+ return RewriteResult()
76
+ control_qubit_idx_ssas = insert_qubit_idx_from_address(
77
+ control_address_attr, stmt
78
+ )
79
+ target_qubit_idx_ssas = insert_qubit_idx_from_address(
80
+ target_address_attr, stmt
81
+ )
82
+ if control_qubit_idx_ssas is None or target_qubit_idx_ssas is None:
83
+ return RewriteResult()
84
+
85
+ # For stim statements you want to interleave the control and target qubit indices:
86
+ # ex: CX controls = (0,1) targets = (2,3) in stim is: CX 0 2 1 3
87
+ qubit_idx_ssas = list(
88
+ itertools.chain.from_iterable(
89
+ zip(control_qubit_idx_ssas, target_qubit_idx_ssas)
90
+ )
91
+ )
79
92
  else:
80
- stim_stmt = stim_noise.ZError(targets=qubit_idx_ssas, p=p_stmt.result)
81
- return stim_stmt
93
+ return RewriteResult()
94
+
95
+ # guaranteed that you have a valid stim_stmt to plug in
96
+ stim_stmt = rewrite_method(stmt, tuple(qubit_idx_ssas))
97
+ stmt.replace_by(stim_stmt)
98
+
99
+ return RewriteResult(has_done_something=True)
82
100
 
83
101
  def rewrite_SingleQubitPauliChannel(
84
102
  self,
85
- stmt: qubit.Apply | qubit.Broadcast | wire.Broadcast | wire.Apply,
103
+ stmt: squin_noise.stmts.SingleQubitPauliChannel,
86
104
  qubit_idx_ssas: Tuple[SSAValue],
87
105
  ) -> Statement:
88
106
  """Rewrite squin.noise.SingleQubitPauliChannel to stim.PauliChannel1."""
89
107
 
90
- squin_channel = stmt.operator.owner
91
- assert isinstance(squin_channel, squin_noise.stmts.SingleQubitPauliChannel)
92
-
93
- params = get_const_value(ilist.IList, squin_channel.params)
94
- new_stmts = [
95
- p_x := py.Constant(params[0]),
96
- p_y := py.Constant(params[1]),
97
- p_z := py.Constant(params[2]),
98
- ]
99
- for new_stmt in new_stmts:
100
- new_stmt.insert_before(stmt)
101
-
102
108
  stim_stmt = stim_noise.PauliChannel1(
103
109
  targets=qubit_idx_ssas,
104
- px=p_x.result,
105
- py=p_y.result,
106
- pz=p_z.result,
110
+ px=stmt.px,
111
+ py=stmt.py,
112
+ pz=stmt.pz,
107
113
  )
108
114
  return stim_stmt
109
115
 
110
- def rewrite_TwoQubitPauliChannel(
116
+ def rewrite_QubitLoss(
111
117
  self,
112
- stmt: qubit.Apply | qubit.Broadcast | wire.Broadcast | wire.Apply,
118
+ stmt: squin_noise.stmts.QubitLoss,
113
119
  qubit_idx_ssas: Tuple[SSAValue],
114
120
  ) -> Statement:
115
- """Rewrite squin.noise.SingleQubitPauliChannel to stim.PauliChannel1."""
121
+ """Rewrite squin.noise.QubitLoss to stim.TrivialError."""
116
122
 
117
- squin_channel = stmt.operator.owner
118
- assert isinstance(squin_channel, squin_noise.stmts.TwoQubitPauliChannel)
123
+ stim_stmt = stim_noise.QubitLoss(
124
+ targets=qubit_idx_ssas,
125
+ probs=(stmt.p,),
126
+ )
119
127
 
120
- params = get_const_value(ilist.IList, squin_channel.params)
121
- param_stmts = [py.Constant(p) for p in params]
122
- for param_stmt in param_stmts:
123
- param_stmt.insert_before(stmt)
128
+ return stim_stmt
124
129
 
125
- stim_stmt = stim_noise.PauliChannel2(
130
+ def rewrite_CorrelatedQubitLoss(
131
+ self,
132
+ stmt: squin_noise.stmts.CorrelatedQubitLoss,
133
+ qubit_idx_ssas: Tuple[SSAValue],
134
+ ) -> Statement:
135
+ """Rewrite squin.noise.CorrelatedQubitLoss to stim.CorrelatedQubitLoss."""
136
+ stim_stmt = stim_noise.CorrelatedQubitLoss(
126
137
  targets=qubit_idx_ssas,
127
- pix=param_stmts[0].result,
128
- piy=param_stmts[1].result,
129
- piz=param_stmts[2].result,
130
- pxi=param_stmts[3].result,
131
- pxx=param_stmts[4].result,
132
- pxy=param_stmts[5].result,
133
- pxz=param_stmts[6].result,
134
- pyi=param_stmts[7].result,
135
- pyx=param_stmts[8].result,
136
- pyy=param_stmts[9].result,
137
- pyz=param_stmts[10].result,
138
- pzi=param_stmts[11].result,
139
- pzx=param_stmts[12].result,
140
- pzy=param_stmts[13].result,
141
- pzz=param_stmts[14].result,
138
+ probs=(stmt.p,),
142
139
  )
140
+
143
141
  return stim_stmt
144
142
 
145
- def rewrite_Depolarize2(
143
+ def rewrite_Depolarize(
146
144
  self,
147
- stmt: qubit.Apply | qubit.Broadcast | wire.Broadcast | wire.Apply,
145
+ stmt: squin_noise.stmts.Depolarize,
148
146
  qubit_idx_ssas: Tuple[SSAValue],
149
147
  ) -> Statement:
150
- """Rewrite squin.noise.Depolarize2 to stim.Depolarize2."""
151
-
152
- squin_channel = stmt.operator.owner
153
- assert isinstance(squin_channel, squin_noise.stmts.Depolarize2)
148
+ """Rewrite squin.noise.Depolarize to stim.Depolarize1."""
154
149
 
155
- p = get_const_value(float, squin_channel.p)
156
- p_stmt = py.Constant(p)
157
- p_stmt.insert_before(stmt)
150
+ stim_stmt = stim_noise.Depolarize1(
151
+ targets=qubit_idx_ssas,
152
+ p=stmt.p,
153
+ )
158
154
 
159
- stim_stmt = stim_noise.Depolarize2(targets=qubit_idx_ssas, p=p_stmt.result)
160
155
  return stim_stmt
161
156
 
162
- def rewrite_Depolarize(
157
+ def rewrite_TwoQubitPauliChannel(
163
158
  self,
164
- stmt: qubit.Apply | qubit.Broadcast | wire.Broadcast | wire.Apply,
159
+ stmt: squin_noise.stmts.TwoQubitPauliChannel,
165
160
  qubit_idx_ssas: Tuple[SSAValue],
166
161
  ) -> Statement:
167
- """Rewrite squin.noise.Depolarize to stim.Depolarize1."""
162
+ """Rewrite squin.noise.TwoQubitPauliChannel to stim.PauliChannel2."""
163
+
164
+ params = stmt.probabilities
165
+ prob_ssas = []
166
+ for idx in range(15):
167
+ idx_stmt = py.Constant(value=idx)
168
+ idx_stmt.insert_before(stmt)
169
+ getitem_stmt = py.GetItem(obj=params, index=idx_stmt.result)
170
+ getitem_stmt.insert_before(stmt)
171
+ prob_ssas.append(getitem_stmt.result)
168
172
 
169
- squin_channel = stmt.operator.owner
170
- assert isinstance(squin_channel, squin_noise.stmts.Depolarize)
173
+ stim_stmt = stim_noise.PauliChannel2(
174
+ targets=qubit_idx_ssas,
175
+ pix=prob_ssas[0],
176
+ piy=prob_ssas[1],
177
+ piz=prob_ssas[2],
178
+ pxi=prob_ssas[3],
179
+ pxx=prob_ssas[4],
180
+ pxy=prob_ssas[5],
181
+ pxz=prob_ssas[6],
182
+ pyi=prob_ssas[7],
183
+ pyx=prob_ssas[8],
184
+ pyy=prob_ssas[9],
185
+ pyz=prob_ssas[10],
186
+ pzi=prob_ssas[11],
187
+ pzx=prob_ssas[12],
188
+ pzy=prob_ssas[13],
189
+ pzz=prob_ssas[14],
190
+ )
191
+ return stim_stmt
171
192
 
172
- p = get_const_value(float, squin_channel.p)
173
- p_stmt = py.Constant(p)
174
- p_stmt.insert_before(stmt)
193
+ def rewrite_Depolarize2(
194
+ self,
195
+ stmt: squin_noise.stmts.Depolarize2,
196
+ qubit_idx_ssas: Tuple[SSAValue],
197
+ ) -> Statement:
198
+ """Rewrite squin.noise.Depolarize2 to stim.Depolarize2."""
175
199
 
176
- stim_stmt = stim_noise.Depolarize1(targets=qubit_idx_ssas, p=p_stmt.result)
200
+ stim_stmt = stim_noise.Depolarize2(targets=qubit_idx_ssas, p=stmt.p)
177
201
  return stim_stmt