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.
- codedistance/.DS_Store +0 -0
- codedistance/NHow.py +804 -0
- codedistance/__init__.py +5 -0
- codedistance/code_library.py +556 -0
- codedistance/common.py +566 -0
- codedistance/complex_utils.py +117 -0
- codedistance/dem_detector_filtering.py +255 -0
- codedistance/distance.py +2473 -0
- codedistance-0.0.1.dist-info/METADATA +196 -0
- codedistance-0.0.1.dist-info/RECORD +12 -0
- codedistance-0.0.1.dist-info/WHEEL +4 -0
- codedistance-0.0.1.dist-info/licenses/LICENSE +674 -0
|
@@ -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))
|