codedistance 0.0.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.
@@ -0,0 +1,255 @@
1
+ import stim
2
+ from collections.abc import Callable
3
+
4
+
5
+ def _filter_flattened_dem(
6
+ dem: stim.DetectorErrorModel,
7
+ keep_error_func: Callable[[stim.DemInstruction], bool],
8
+ filter_detector_instructions: bool = False,
9
+ ) -> stim.DetectorErrorModel:
10
+ """Filters a flattened detector error model."""
11
+ if not filter_detector_instructions:
12
+ filtered_dem = stim.DetectorErrorModel()
13
+ for instruction in dem:
14
+ if instruction.type == "error":
15
+ if keep_error_func(instruction):
16
+ filtered_dem.append(instruction)
17
+ else:
18
+ filtered_dem.append(instruction)
19
+ return filtered_dem
20
+
21
+ # First, filter the error instructions and identify kept detectors
22
+ filtered_errors = []
23
+ kept_detectors = set()
24
+ for instruction in dem:
25
+ if instruction.type == "error":
26
+ if keep_error_func(instruction):
27
+ filtered_errors.append(instruction)
28
+ for target in instruction.targets_copy():
29
+ if target.is_relative_detector_id():
30
+ kept_detectors.add(target.val)
31
+
32
+ # Re-build the DEM, keeping only the relevant detector instructions
33
+ final_filtered_dem = stim.DetectorErrorModel()
34
+ for instruction in dem:
35
+ if instruction.type == "detector":
36
+ # Keep detector instruction if any of its targets are in kept_detectors
37
+ if any(
38
+ t.val in kept_detectors
39
+ for t in instruction.targets_copy()
40
+ if t.is_relative_detector_id()
41
+ ):
42
+ final_filtered_dem.append(instruction)
43
+ elif instruction.type != "error":
44
+ final_filtered_dem.append(instruction)
45
+
46
+ for error in filtered_errors:
47
+ final_filtered_dem.append(error)
48
+
49
+ return final_filtered_dem
50
+
51
+
52
+ def filter_dem_errors_by_detector_basis(
53
+ dem: stim.DetectorErrorModel,
54
+ detector_basis: Callable[[int], str],
55
+ desired_basis: str,
56
+ filter_detector_instructions: bool = False,
57
+ ) -> stim.DetectorErrorModel:
58
+ """Filters a DEM, keeping only errors where all detectors match a desired basis.
59
+
60
+ This function first flattens the DEM, which removes all REPEAT blocks and
61
+ resolves all relative detector indices to absolute ones.
62
+
63
+ Parameters
64
+ ----------
65
+ dem : stim.DetectorErrorModel
66
+ The detector error model to filter.
67
+ detector_basis : Callable[[int], str]
68
+ A function that returns the basis ('X' or 'Z') of a given absolute
69
+ detector index.
70
+ desired_basis : str
71
+ The basis to keep ('X' or 'Z').
72
+ filter_detector_instructions : bool, optional
73
+ If True, also filters out `detector` instructions for detectors that
74
+ are no longer present in any error. Defaults to False.
75
+
76
+ Returns
77
+ -------
78
+ stim.DetectorErrorModel
79
+ A new DEM containing only the errors whose detectors are all of the
80
+ `desired_basis`.
81
+ """
82
+ flattened_dem = dem.flattened()
83
+
84
+ def keep_error_func(instruction: stim.DemInstruction) -> bool:
85
+ detector_targets = [
86
+ t.val for t in instruction.targets_copy() if t.is_relative_detector_id()
87
+ ]
88
+ if not detector_targets:
89
+ # Keep errors with no detectors (e.g. pure logical errors)
90
+ return True
91
+ return all(detector_basis(t) == desired_basis for t in detector_targets)
92
+
93
+ return _filter_flattened_dem(
94
+ flattened_dem, keep_error_func, filter_detector_instructions
95
+ )
96
+
97
+
98
+ def _dem_has_observable_flip(dem: stim.DetectorErrorModel) -> bool:
99
+ """Checks if any error in the DEM flips a logical observable."""
100
+ for instruction in dem.flattened():
101
+ if instruction.type == "error":
102
+ if any(t.is_logical_observable_id() for t in instruction.targets_copy()):
103
+ return True
104
+ return False
105
+
106
+
107
+ def filter_dem_to_one_basis(
108
+ dem: stim.DetectorErrorModel, detector_basis: Callable[[int], str]
109
+ ) -> stim.DetectorErrorModel:
110
+ """Filters a DEM to a single basis, ensuring it's the one with a logical error.
111
+
112
+ This function separates a DEM into its X-basis and Z-basis components. It
113
+ succeeds only if exactly one of these components contains an error that
114
+ flips a logical observable.
115
+
116
+ Parameters
117
+ ----------
118
+ dem : stim.DetectorErrorModel
119
+ The detector error model to filter.
120
+ detector_basis : Callable[[int], str]
121
+ A function that returns the basis ('X' or 'Z') of a given absolute
122
+ detector index.
123
+
124
+ Returns
125
+ -------
126
+ stim.DetectorErrorModel
127
+ The filtered DEM for the single basis that has a logical error.
128
+
129
+ Raises
130
+ ------
131
+ ValueError
132
+ If both the X and Z filtered DEMs have logical errors, or if neither of
133
+ them do.
134
+ """
135
+ dem_x = filter_dem_errors_by_detector_basis(dem, detector_basis, "X")
136
+ dem_z = filter_dem_errors_by_detector_basis(dem, detector_basis, "Z")
137
+
138
+ x_has_flip = _dem_has_observable_flip(dem_x)
139
+ z_has_flip = _dem_has_observable_flip(dem_z)
140
+
141
+ if x_has_flip and not z_has_flip:
142
+ return dem_x
143
+ if z_has_flip and not x_has_flip:
144
+ return dem_z
145
+ if x_has_flip and z_has_flip:
146
+ raise ValueError(
147
+ "Both X and Z filtered DEMs have logical errors. The basis is ambiguous."
148
+ )
149
+ raise ValueError(
150
+ "Neither X nor Z filtered DEMs have a logical error. Cannot determine basis."
151
+ )
152
+
153
+
154
+ def filter_by_det_basis_using_chromobius_coords(
155
+ dem: stim.DetectorErrorModel,
156
+ ) -> stim.DetectorErrorModel:
157
+ """Filters a DEM with Chromobius-style coordinates to a single logical basis.
158
+
159
+ This function uses the 4th coordinate of each detector to determine its
160
+ basis according to the Chromobius convention:
161
+ - 0, 1, 2: X-basis
162
+ - 3, 4, 5: Z-basis
163
+ - -1 or other: Ignored (neither X nor Z)
164
+
165
+ It then filters the DEM to keep only the errors that trigger detectors only
166
+ from the basis ('X' or 'Z') of the logical observable.
167
+
168
+ More concretely, for each of basis=X and basis=Z it filters to a DEM that
169
+ only keeps an error instruction such as `error(p) Di Dj Dk ...` if
170
+ *all* the detectors that the error triggers are of the desired basis.
171
+ A filtered DEM is created for both basis=X and basis=Z. For a CSS circuit
172
+ that has a logical observable declared in one basis, only one of these
173
+ filtered DEMs will contain any errors that trigger an observable. It
174
+ is this DEM that is returned.
175
+
176
+ For a CSS circuit containing only CNOT gates, the filtered DEM will only
177
+ contain errors that are of the opposite basis to the observable (X errors
178
+ if the observable is Z, for example). However error bases are classified by
179
+ the basis of detector they trigger, so the code correctly handles circuits
180
+ transpiled to CZ gates and Hadamards (such as SI1000 noise models).
181
+
182
+
183
+ Parameters
184
+ ----------
185
+ dem : stim.DetectorErrorModel
186
+ The detector error model, with detectors annotated with 4D coordinates.
187
+
188
+ Returns
189
+ -------
190
+ stim.DetectorErrorModel
191
+ The filtered DEM for the single basis that has a logical error.
192
+ """
193
+ coords = dem.get_detector_coordinates()
194
+
195
+ def detector_basis(detector_index: int) -> str:
196
+ """Determines detector basis from 4th coordinate."""
197
+ if detector_index not in coords:
198
+ return "Unknown"
199
+ det_coords = coords[detector_index]
200
+ if len(det_coords) < 4:
201
+ return "Unknown"
202
+ basis_val = det_coords[3]
203
+ if 0 <= basis_val <= 2:
204
+ return "X"
205
+ if 3 <= basis_val <= 5:
206
+ return "Z"
207
+ return "Unknown"
208
+
209
+ return filter_dem_to_one_basis(dem, detector_basis)
210
+
211
+
212
+ if __name__ == "__main__":
213
+ import sys
214
+ from pathlib import Path
215
+
216
+ if len(sys.argv) != 2:
217
+ print("Usage: python dem_detector_filtering.py <circuits_dir>")
218
+ sys.exit(1)
219
+
220
+ circuits_dir = Path(sys.argv[1])
221
+
222
+ header = f"{'Circuit File':<80} {'Property':<20} {'Original':>10} {'Filtered':>10}"
223
+ print(header)
224
+ print("-" * len(header))
225
+
226
+ for circuit_path in sorted(circuits_dir.glob("*.stim")):
227
+ circuit = stim.Circuit.from_file(circuit_path)
228
+ original_dem = circuit.detector_error_model()
229
+
230
+ try:
231
+ filtered_dem = filter_by_det_basis_using_chromobius_coords(original_dem)
232
+ except ValueError as e:
233
+ print(f"Skipping {circuit_path.name}: {e}")
234
+ continue
235
+
236
+ try:
237
+ original_dist = len(original_dem.shortest_graphlike_error())
238
+ except (ValueError, IndexError):
239
+ original_dist = "Error"
240
+
241
+ try:
242
+ filtered_dist = len(filtered_dem.shortest_graphlike_error())
243
+ except (ValueError, IndexError):
244
+ filtered_dist = "Error"
245
+
246
+ print(
247
+ f"{circuit_path.name:<80} {'num_detectors':<20} {original_dem.num_detectors:>10} {filtered_dem.num_detectors:>10}"
248
+ )
249
+ print(
250
+ f"{'':<80} {'num_errors':<20} {original_dem.num_errors:>10} {filtered_dem.num_errors:>10}"
251
+ )
252
+ print(
253
+ f"{'':<80} {'distance':<20} {str(original_dist):>10} {str(filtered_dist):>10}"
254
+ )
255
+ print("-" * len(header))