|
| 1 | +import pytest |
1 | 2 | import stim |
2 | 3 | from numpy.testing import assert_allclose |
3 | 4 |
|
@@ -150,6 +151,158 @@ def test_heralded_erase_multiple_targets(self): |
150 | 151 | assert len(b.channel_probs) == 3 |
151 | 152 |
|
152 | 153 |
|
| 154 | +class TestProbabilityBearingInstructions: |
| 155 | + """Tests for instructions that create probabilistic error channels.""" |
| 156 | + |
| 157 | + @pytest.mark.parametrize( |
| 158 | + "program", |
| 159 | + [ |
| 160 | + "X_ERROR(0.07) 0", |
| 161 | + "Y_ERROR(0.07) 0", |
| 162 | + "Z_ERROR(0.07) 0", |
| 163 | + "M(0.07) 0", |
| 164 | + "MX(0.07) 0", |
| 165 | + "MY(0.07) 0", |
| 166 | + "MZ(0.07) 0", |
| 167 | + "MR(0.07) 0", |
| 168 | + "MRX(0.07) 0", |
| 169 | + "MRY(0.07) 0", |
| 170 | + "MRZ(0.07) 0", |
| 171 | + "MXX(0.07) 0 1", |
| 172 | + "MYY(0.07) 0 1", |
| 173 | + "MZZ(0.07) 0 1", |
| 174 | + "MPP(0.07) X0*Z1", |
| 175 | + "MPAD(0.07) 0", |
| 176 | + "CORRELATED_ERROR(0.07) X0", |
| 177 | + ], |
| 178 | + ) |
| 179 | + def test_single_bit_probability_channels(self, program): |
| 180 | + """Single-bit probability-bearing instructions should create one channel.""" |
| 181 | + b = parse_stim_circuit(stim.Circuit(program)) |
| 182 | + |
| 183 | + assert b.num_error_bits == 1 |
| 184 | + assert len(b.channel_probs) == 1 |
| 185 | + assert_allclose(b.channel_probs[0], [0.93, 0.07]) |
| 186 | + |
| 187 | + @pytest.mark.parametrize("program", ["MR(0.07) 0", "MRX(0.07) 0", "MRY(0.07) 0"]) |
| 188 | + def test_mr_family_does_not_double_count_measurement_noise(self, program): |
| 189 | + """MR-family measurement noise is a result flip, not an extra Pauli error.""" |
| 190 | + b = parse_stim_circuit(stim.Circuit(program)) |
| 191 | + |
| 192 | + assert len(b.rec) == 1 |
| 193 | + assert b.num_error_bits == 1 |
| 194 | + assert len(b.channel_probs) == 1 |
| 195 | + assert_allclose(b.channel_probs[0], [0.93, 0.07]) |
| 196 | + |
| 197 | + @pytest.mark.parametrize( |
| 198 | + ("program", "expected"), |
| 199 | + [ |
| 200 | + ("DEPOLARIZE1(0.12) 0", [0.88, 0.04, 0.04, 0.04]), |
| 201 | + ("PAULI_CHANNEL_1(0.01, 0.02, 0.03) 0", [0.94, 0.03, 0.01, 0.02]), |
| 202 | + ( |
| 203 | + "HERALDED_ERASE(0.2) 0", |
| 204 | + [0.8, 0.05, 0.0, 0.05, 0.0, 0.05, 0.0, 0.05], |
| 205 | + ), |
| 206 | + ( |
| 207 | + "HERALDED_PAULI_CHANNEL_1(0.01, 0.02, 0.03, 0.04) 0", |
| 208 | + [0.9, 0.01, 0.0, 0.04, 0.0, 0.02, 0.0, 0.03], |
| 209 | + ), |
| 210 | + ], |
| 211 | + ) |
| 212 | + def test_multi_bit_probability_channels(self, program, expected): |
| 213 | + """Multi-bit probability channels should use the documented outcome ordering.""" |
| 214 | + b = parse_stim_circuit(stim.Circuit(program)) |
| 215 | + |
| 216 | + assert len(b.channel_probs) == 1 |
| 217 | + assert_allclose(b.channel_probs[0], expected) |
| 218 | + |
| 219 | + def test_depolarize2_probability_channel(self): |
| 220 | + """DEPOLARIZE2(p) should distribute p uniformly over non-identity outcomes.""" |
| 221 | + b = parse_stim_circuit(stim.Circuit("DEPOLARIZE2(0.15) 0 1")) |
| 222 | + |
| 223 | + assert b.num_error_bits == 4 |
| 224 | + assert len(b.channel_probs) == 1 |
| 225 | + assert_allclose(b.channel_probs[0], [0.85] + [0.01] * 15) |
| 226 | + |
| 227 | + def test_pauli_channel_2_probability_channel(self): |
| 228 | + """PAULI_CHANNEL_2 should preserve the expected packed Pauli outcome order.""" |
| 229 | + args = [ |
| 230 | + 0.001, |
| 231 | + 0.002, |
| 232 | + 0.003, |
| 233 | + 0.004, |
| 234 | + 0.005, |
| 235 | + 0.006, |
| 236 | + 0.007, |
| 237 | + 0.008, |
| 238 | + 0.009, |
| 239 | + 0.010, |
| 240 | + 0.011, |
| 241 | + 0.012, |
| 242 | + 0.013, |
| 243 | + 0.014, |
| 244 | + 0.015, |
| 245 | + ] |
| 246 | + program = f"PAULI_CHANNEL_2({', '.join(map(str, args))}) 0 1" |
| 247 | + b = parse_stim_circuit(stim.Circuit(program)) |
| 248 | + |
| 249 | + assert b.num_error_bits == 4 |
| 250 | + assert len(b.channel_probs) == 1 |
| 251 | + assert_allclose( |
| 252 | + b.channel_probs[0], |
| 253 | + [ |
| 254 | + 0.88, |
| 255 | + 0.012, |
| 256 | + 0.004, |
| 257 | + 0.008, |
| 258 | + 0.003, |
| 259 | + 0.015, |
| 260 | + 0.007, |
| 261 | + 0.011, |
| 262 | + 0.001, |
| 263 | + 0.013, |
| 264 | + 0.005, |
| 265 | + 0.009, |
| 266 | + 0.002, |
| 267 | + 0.014, |
| 268 | + 0.006, |
| 269 | + 0.010, |
| 270 | + ], |
| 271 | + ) |
| 272 | + |
| 273 | + @pytest.mark.parametrize( |
| 274 | + "program", |
| 275 | + [ |
| 276 | + "X_ERROR(0.07) 0 1", |
| 277 | + "M(0.07) 0 1", |
| 278 | + "MR(0.07) 0 1", |
| 279 | + "MRX(0.07) 0 1", |
| 280 | + "MRY(0.07) 0 1", |
| 281 | + "MXX(0.07) 0 1 2 3", |
| 282 | + "MPP(0.07) X0 X1", |
| 283 | + "MPAD(0.07) 0 1", |
| 284 | + ], |
| 285 | + ) |
| 286 | + def test_repeated_probability_instructions_create_independent_channels( |
| 287 | + self, program |
| 288 | + ): |
| 289 | + """Repeated targets/products should each get their own probability channel.""" |
| 290 | + b = parse_stim_circuit(stim.Circuit(program)) |
| 291 | + |
| 292 | + assert b.num_error_bits == 2 |
| 293 | + assert len(b.channel_probs) == 2 |
| 294 | + for probs in b.channel_probs: |
| 295 | + assert_allclose(probs, [0.93, 0.07]) |
| 296 | + |
| 297 | + @pytest.mark.parametrize("program", ["I_ERROR(0.07) 0", "II_ERROR(0.07) 0 1"]) |
| 298 | + def test_identity_error_instructions_do_not_create_channels(self, program): |
| 299 | + """Identity error instructions allocate lanes but do not affect noise state.""" |
| 300 | + b = parse_stim_circuit(stim.Circuit(program)) |
| 301 | + |
| 302 | + assert b.num_error_bits == 0 |
| 303 | + assert len(b.channel_probs) == 0 |
| 304 | + |
| 305 | + |
153 | 306 | class TestParseIIError: |
154 | 307 | """Tests for parsing II_ERROR instructions with parenthesized arguments.""" |
155 | 308 |
|
|
0 commit comments