microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/pip/tests/qre/test_models.py
1069lines · modecode
| 1 | # Copyright (c) Microsoft Corporation. |
| 2 | # Licensed under the MIT License. |
| 3 | |
| 4 | from qsharp.qre import LOGICAL, PHYSICAL |
| 5 | from qsharp.qre.instruction_ids import ( |
| 6 | T, |
| 7 | CCZ, |
| 8 | CCX, |
| 9 | CCY, |
| 10 | CNOT, |
| 11 | CZ, |
| 12 | H, |
| 13 | MEAS_Z, |
| 14 | MEAS_X, |
| 15 | MEAS_XX, |
| 16 | MEAS_ZZ, |
| 17 | PAULI_I, |
| 18 | PREP_X, |
| 19 | PREP_Z, |
| 20 | LATTICE_SURGERY, |
| 21 | MEMORY, |
| 22 | SQRT_SQRT_X, |
| 23 | SQRT_SQRT_X_DAG, |
| 24 | SQRT_SQRT_Y, |
| 25 | SQRT_SQRT_Y_DAG, |
| 26 | SQRT_SQRT_Z, |
| 27 | SQRT_SQRT_Z_DAG, |
| 28 | ) |
| 29 | from qsharp.qre.models import ( |
| 30 | GateBased, |
| 31 | Majorana, |
| 32 | RoundBasedFactory, |
| 33 | MagicUpToClifford, |
| 34 | Litinski19Factory, |
| 35 | SurfaceCode, |
| 36 | ThreeAux, |
| 37 | TwoDimensionalYokedSurfaceCode, |
| 38 | ) |
| 39 | from qsharp.qre.property_keys import DISTANCE |
| 40 | |
| 41 | |
| 42 | # --------------------------------------------------------------------------- |
| 43 | # GateBased architecture tests |
| 44 | # --------------------------------------------------------------------------- |
| 45 | |
| 46 | |
| 47 | class TestGateBased: |
| 48 | def test_default_error_rate(self): |
| 49 | """Test that default error rate is 1e-4.""" |
| 50 | arch = GateBased(gate_time=50, measurement_time=100) |
| 51 | assert arch.error_rate == 1e-4 |
| 52 | |
| 53 | def test_custom_error_rate(self): |
| 54 | """Test that a custom error rate is accepted.""" |
| 55 | arch = GateBased(error_rate=1e-3, gate_time=50, measurement_time=100) |
| 56 | assert arch.error_rate == 1e-3 |
| 57 | |
| 58 | def test_provided_isa_contains_expected_instructions(self): |
| 59 | """Test that GateBased ISA contains all expected physical instructions.""" |
| 60 | arch = GateBased(gate_time=50, measurement_time=100) |
| 61 | ctx = arch.context() |
| 62 | isa = ctx.isa |
| 63 | |
| 64 | for instr_id in [PAULI_I, CNOT, CZ, H, MEAS_Z, T]: |
| 65 | assert instr_id in isa |
| 66 | |
| 67 | def test_instruction_encodings_are_physical(self): |
| 68 | """Test that all GateBased ISA instructions have PHYSICAL encoding.""" |
| 69 | arch = GateBased(gate_time=50, measurement_time=100) |
| 70 | ctx = arch.context() |
| 71 | isa = ctx.isa |
| 72 | |
| 73 | for instr_id in [PAULI_I, CNOT, CZ, H, MEAS_Z, T]: |
| 74 | assert isa[instr_id].encoding == PHYSICAL |
| 75 | |
| 76 | def test_instruction_error_rates_match(self): |
| 77 | """Test that all instruction error rates match the architecture error rate.""" |
| 78 | rate = 1e-3 |
| 79 | arch = GateBased(error_rate=rate, gate_time=50, measurement_time=100) |
| 80 | ctx = arch.context() |
| 81 | isa = ctx.isa |
| 82 | |
| 83 | for instr_id in [PAULI_I, CNOT, CZ, H, MEAS_Z, T]: |
| 84 | assert isa[instr_id].expect_error_rate() == rate |
| 85 | |
| 86 | def test_gate_times(self): |
| 87 | """Test that gate times match the configured gate and measurement times.""" |
| 88 | arch = GateBased(gate_time=50, measurement_time=100) |
| 89 | ctx = arch.context() |
| 90 | isa = ctx.isa |
| 91 | |
| 92 | # Single-qubit gates: 50ns |
| 93 | for instr_id in [PAULI_I, H, T]: |
| 94 | assert isa[instr_id].expect_time() == 50 |
| 95 | |
| 96 | # Two-qubit gates: 50ns |
| 97 | for instr_id in [CNOT, CZ]: |
| 98 | assert isa[instr_id].expect_time() == 50 |
| 99 | |
| 100 | # Measurement: 100ns |
| 101 | assert isa[MEAS_Z].expect_time() == 100 |
| 102 | |
| 103 | def test_arities(self): |
| 104 | """Test that instruction arities match expected values.""" |
| 105 | arch = GateBased(gate_time=50, measurement_time=100) |
| 106 | ctx = arch.context() |
| 107 | isa = ctx.isa |
| 108 | |
| 109 | assert isa[PAULI_I].arity == 1 |
| 110 | assert isa[CNOT].arity == 2 |
| 111 | assert isa[CZ].arity == 2 |
| 112 | assert isa[H].arity == 1 |
| 113 | assert isa[MEAS_Z].arity == 1 |
| 114 | |
| 115 | def test_context_creation(self): |
| 116 | """Test that context creation succeeds.""" |
| 117 | arch = GateBased(gate_time=50, measurement_time=100) |
| 118 | ctx = arch.context() |
| 119 | assert ctx is not None |
| 120 | |
| 121 | |
| 122 | # --------------------------------------------------------------------------- |
| 123 | # Majorana architecture tests |
| 124 | # --------------------------------------------------------------------------- |
| 125 | |
| 126 | |
| 127 | class TestMajorana: |
| 128 | def test_default_error_rate(self): |
| 129 | """Test that default Majorana error rate is 1e-5.""" |
| 130 | arch = Majorana() |
| 131 | assert arch.error_rate == 1e-5 |
| 132 | |
| 133 | def test_provided_isa_contains_expected_instructions(self): |
| 134 | """Test that Majorana ISA contains all expected instructions.""" |
| 135 | arch = Majorana() |
| 136 | ctx = arch.context() |
| 137 | isa = ctx.isa |
| 138 | |
| 139 | for instr_id in [PREP_X, PREP_Z, MEAS_XX, MEAS_ZZ, MEAS_X, MEAS_Z, T]: |
| 140 | assert instr_id in isa |
| 141 | |
| 142 | def test_all_times_are_1us(self): |
| 143 | """Test that all Majorana instruction times are 1000 ns.""" |
| 144 | arch = Majorana() |
| 145 | ctx = arch.context() |
| 146 | isa = ctx.isa |
| 147 | |
| 148 | for instr_id in [PREP_X, PREP_Z, MEAS_XX, MEAS_ZZ, MEAS_X, MEAS_Z, T]: |
| 149 | assert isa[instr_id].expect_time() == 1000 |
| 150 | |
| 151 | def test_clifford_error_rates_match_qubit_error(self): |
| 152 | """Test that Clifford error rates match the qubit error rate.""" |
| 153 | for rate in [1e-4, 1e-5, 1e-6]: |
| 154 | arch = Majorana(error_rate=rate) |
| 155 | ctx = arch.context() |
| 156 | isa = ctx.isa |
| 157 | |
| 158 | for instr_id in [PREP_X, PREP_Z, MEAS_XX, MEAS_ZZ, MEAS_X, MEAS_Z]: |
| 159 | assert isa[instr_id].expect_error_rate() == rate |
| 160 | |
| 161 | def test_t_error_rate_mapping(self): |
| 162 | """T error rate maps: 1e-4 -> 5%, 1e-5 -> 1.5%, 1e-6 -> 1%.""" |
| 163 | expected = {1e-4: 0.05, 1e-5: 0.015, 1e-6: 0.01} |
| 164 | |
| 165 | for qubit_rate, t_rate in expected.items(): |
| 166 | arch = Majorana(error_rate=qubit_rate) |
| 167 | ctx = arch.context() |
| 168 | isa = ctx.isa |
| 169 | assert isa[T].expect_error_rate() == t_rate |
| 170 | |
| 171 | def test_two_qubit_measurement_arities(self): |
| 172 | """Test that two-qubit measurement instructions have arity 2.""" |
| 173 | arch = Majorana() |
| 174 | ctx = arch.context() |
| 175 | isa = ctx.isa |
| 176 | |
| 177 | assert isa[MEAS_XX].arity == 2 |
| 178 | assert isa[MEAS_ZZ].arity == 2 |
| 179 | |
| 180 | |
| 181 | # --------------------------------------------------------------------------- |
| 182 | # SurfaceCode QEC tests |
| 183 | # --------------------------------------------------------------------------- |
| 184 | |
| 185 | |
| 186 | class TestSurfaceCode: |
| 187 | def test_required_isa(self): |
| 188 | """Test that SurfaceCode has non-None required ISA.""" |
| 189 | reqs = SurfaceCode.required_isa() |
| 190 | assert reqs is not None |
| 191 | |
| 192 | def test_default_distance(self): |
| 193 | """Test SurfaceCode with explicit distance parameter.""" |
| 194 | sc = SurfaceCode(distance=3) |
| 195 | assert sc.distance == 3 |
| 196 | |
| 197 | def test_provides_lattice_surgery(self): |
| 198 | """Test that SurfaceCode provides a logical LATTICE_SURGERY instruction.""" |
| 199 | arch = GateBased(gate_time=50, measurement_time=100) |
| 200 | ctx = arch.context() |
| 201 | sc = SurfaceCode(distance=3) |
| 202 | |
| 203 | isas = list(sc.provided_isa(ctx.isa, ctx)) |
| 204 | assert len(isas) == 1 |
| 205 | |
| 206 | isa = isas[0] |
| 207 | assert LATTICE_SURGERY in isa |
| 208 | |
| 209 | ls = isa[LATTICE_SURGERY] |
| 210 | assert ls.encoding == LOGICAL |
| 211 | |
| 212 | def test_space_scales_with_distance(self): |
| 213 | """Space = 2*d^2 - 1 physical qubits per logical qubit.""" |
| 214 | arch = GateBased(gate_time=50, measurement_time=100) |
| 215 | |
| 216 | for d in [3, 5, 7, 9]: |
| 217 | ctx = arch.context() |
| 218 | sc = SurfaceCode(distance=d) |
| 219 | isas = list(sc.provided_isa(ctx.isa, ctx)) |
| 220 | ls = isas[0][LATTICE_SURGERY] |
| 221 | expected_space = 2 * d**2 - 1 |
| 222 | assert ls.expect_space(1) == expected_space |
| 223 | |
| 224 | def test_time_scales_with_distance(self): |
| 225 | """Time = (h_time + 4*cnot_time + meas_time) * d.""" |
| 226 | arch = GateBased(gate_time=50, measurement_time=100) |
| 227 | # h=50, cnot=50, meas=100 for GateBased |
| 228 | syndrome_time = 50 + 4 * 50 + 100 # = 350 |
| 229 | |
| 230 | for d in [3, 5, 7]: |
| 231 | ctx = arch.context() |
| 232 | sc = SurfaceCode(distance=d) |
| 233 | isas = list(sc.provided_isa(ctx.isa, ctx)) |
| 234 | ls = isas[0][LATTICE_SURGERY] |
| 235 | assert ls.expect_time(1) == syndrome_time * d |
| 236 | |
| 237 | def test_error_rate_decreases_with_distance(self): |
| 238 | """Test that logical error rate decreases as code distance increases.""" |
| 239 | arch = GateBased(gate_time=50, measurement_time=100) |
| 240 | |
| 241 | errors = [] |
| 242 | for d in [3, 5, 7, 9, 11]: |
| 243 | ctx = arch.context() |
| 244 | sc = SurfaceCode(distance=d) |
| 245 | isas = list(sc.provided_isa(ctx.isa, ctx)) |
| 246 | errors.append(isas[0][LATTICE_SURGERY].expect_error_rate(1)) |
| 247 | |
| 248 | # Each successive distance should have a lower error rate |
| 249 | for i in range(len(errors) - 1): |
| 250 | assert errors[i] > errors[i + 1] |
| 251 | |
| 252 | def test_enumeration_via_query(self): |
| 253 | """Enumerating SurfaceCode.q() should yield multiple distances.""" |
| 254 | arch = GateBased(gate_time=50, measurement_time=100) |
| 255 | ctx = arch.context() |
| 256 | |
| 257 | count = 0 |
| 258 | for isa in SurfaceCode.q().enumerate(ctx): |
| 259 | assert LATTICE_SURGERY in isa |
| 260 | count += 1 |
| 261 | |
| 262 | # domain is range(3, 26, 2) = 12 distances |
| 263 | assert count == 12 |
| 264 | |
| 265 | def test_custom_crossing_prefactor(self): |
| 266 | """Test that doubling the crossing prefactor doubles the error rate.""" |
| 267 | arch = GateBased(gate_time=50, measurement_time=100) |
| 268 | ctx = arch.context() |
| 269 | |
| 270 | sc_default = SurfaceCode(distance=5) |
| 271 | sc_custom = SurfaceCode(crossing_prefactor=0.06, distance=5) |
| 272 | |
| 273 | default_error = list(sc_default.provided_isa(ctx.isa, ctx))[0][ |
| 274 | LATTICE_SURGERY |
| 275 | ].expect_error_rate(1) |
| 276 | |
| 277 | ctx2 = arch.context() |
| 278 | custom_error = list(sc_custom.provided_isa(ctx2.isa, ctx2))[0][ |
| 279 | LATTICE_SURGERY |
| 280 | ].expect_error_rate(1) |
| 281 | |
| 282 | # Doubling prefactor should double the error rate |
| 283 | assert abs(custom_error - 2 * default_error) < 1e-20 |
| 284 | |
| 285 | def test_custom_error_correction_threshold(self): |
| 286 | """Test that a lower error correction threshold yields a higher logical error.""" |
| 287 | arch = GateBased(gate_time=50, measurement_time=100) |
| 288 | |
| 289 | ctx1 = arch.context() |
| 290 | sc_low_threshold = SurfaceCode(error_correction_threshold=0.005, distance=5) |
| 291 | error_low = list(sc_low_threshold.provided_isa(ctx1.isa, ctx1))[0][ |
| 292 | LATTICE_SURGERY |
| 293 | ].expect_error_rate(1) |
| 294 | |
| 295 | ctx2 = arch.context() |
| 296 | sc_high_threshold = SurfaceCode(error_correction_threshold=0.02, distance=5) |
| 297 | error_high = list(sc_high_threshold.provided_isa(ctx2.isa, ctx2))[0][ |
| 298 | LATTICE_SURGERY |
| 299 | ].expect_error_rate(1) |
| 300 | |
| 301 | # Lower threshold means worse ratio => higher logical error |
| 302 | assert error_low > error_high |
| 303 | |
| 304 | |
| 305 | # --------------------------------------------------------------------------- |
| 306 | # ThreeAux QEC tests |
| 307 | # --------------------------------------------------------------------------- |
| 308 | |
| 309 | |
| 310 | class TestThreeAux: |
| 311 | def test_required_isa(self): |
| 312 | """Test that ThreeAux has non-None required ISA.""" |
| 313 | reqs = ThreeAux.required_isa() |
| 314 | assert reqs is not None |
| 315 | |
| 316 | def test_provides_lattice_surgery(self): |
| 317 | """Test that ThreeAux provides a LATTICE_SURGERY instruction.""" |
| 318 | arch = Majorana() |
| 319 | ctx = arch.context() |
| 320 | ta = ThreeAux(distance=3) |
| 321 | |
| 322 | isas = list(ta.provided_isa(ctx.isa, ctx)) |
| 323 | assert len(isas) == 1 |
| 324 | assert LATTICE_SURGERY in isas[0] |
| 325 | |
| 326 | def test_space_formula(self): |
| 327 | """Space = 4*d^2 - 3 per logical qubit.""" |
| 328 | arch = Majorana() |
| 329 | |
| 330 | for d in [3, 5, 7]: |
| 331 | ctx = arch.context() |
| 332 | ta = ThreeAux(distance=d) |
| 333 | isas = list(ta.provided_isa(ctx.isa, ctx)) |
| 334 | ls = isas[0][LATTICE_SURGERY] |
| 335 | expected = 4 * d**2 - 3 |
| 336 | assert ls.expect_space(1) == expected |
| 337 | |
| 338 | def test_time_formula_double_rail(self): |
| 339 | """Time = gate_time * (4*d + 4) for double-rail (default).""" |
| 340 | arch = Majorana() |
| 341 | |
| 342 | for d in [3, 5, 7]: |
| 343 | ctx = arch.context() |
| 344 | ta = ThreeAux(distance=d, single_rail=False) |
| 345 | isas = list(ta.provided_isa(ctx.isa, ctx)) |
| 346 | ls = isas[0][LATTICE_SURGERY] |
| 347 | # MEAS_XX and MEAS_ZZ have time=1000 each; max is 1000 |
| 348 | expected_time = 1000 * (4 * d + 4) |
| 349 | assert ls.expect_time(1) == expected_time |
| 350 | |
| 351 | def test_time_formula_single_rail(self): |
| 352 | """Time = gate_time * (5*d + 4) for single-rail.""" |
| 353 | arch = Majorana() |
| 354 | |
| 355 | for d in [3, 5, 7]: |
| 356 | ctx = arch.context() |
| 357 | ta = ThreeAux(distance=d, single_rail=True) |
| 358 | isas = list(ta.provided_isa(ctx.isa, ctx)) |
| 359 | ls = isas[0][LATTICE_SURGERY] |
| 360 | expected_time = 1000 * (5 * d + 4) |
| 361 | assert ls.expect_time(1) == expected_time |
| 362 | |
| 363 | def test_error_rate_decreases_with_distance(self): |
| 364 | """Test that ThreeAux error rate decreases with increasing distance.""" |
| 365 | arch = Majorana() |
| 366 | |
| 367 | errors = [] |
| 368 | for d in [3, 5, 7, 9]: |
| 369 | ctx = arch.context() |
| 370 | ta = ThreeAux(distance=d) |
| 371 | isas = list(ta.provided_isa(ctx.isa, ctx)) |
| 372 | errors.append(isas[0][LATTICE_SURGERY].expect_error_rate(1)) |
| 373 | |
| 374 | for i in range(len(errors) - 1): |
| 375 | assert errors[i] > errors[i + 1] |
| 376 | |
| 377 | def test_single_rail_has_different_error_threshold(self): |
| 378 | """Single-rail has threshold 0.0051, double-rail 0.0066.""" |
| 379 | arch = Majorana() |
| 380 | |
| 381 | ctx1 = arch.context() |
| 382 | double = ThreeAux(distance=5, single_rail=False) |
| 383 | error_double = list(double.provided_isa(ctx1.isa, ctx1))[0][ |
| 384 | LATTICE_SURGERY |
| 385 | ].expect_error_rate(1) |
| 386 | |
| 387 | ctx2 = arch.context() |
| 388 | single = ThreeAux(distance=5, single_rail=True) |
| 389 | error_single = list(single.provided_isa(ctx2.isa, ctx2))[0][ |
| 390 | LATTICE_SURGERY |
| 391 | ].expect_error_rate(1) |
| 392 | |
| 393 | # Both should be positive but differ |
| 394 | assert error_double > 0 |
| 395 | assert error_single > 0 |
| 396 | assert error_double != error_single |
| 397 | |
| 398 | def test_enumeration_via_query(self): |
| 399 | """Test that ThreeAux.q() enumerates all distance and rail combinations.""" |
| 400 | arch = Majorana() |
| 401 | ctx = arch.context() |
| 402 | |
| 403 | count = 0 |
| 404 | for isa in ThreeAux.q().enumerate(ctx): |
| 405 | assert LATTICE_SURGERY in isa |
| 406 | count += 1 |
| 407 | |
| 408 | # domain: range(3, 26, 2) × {True, False} for single_rail |
| 409 | # = 12 distances × 2 = 24 |
| 410 | assert count == 24 |
| 411 | |
| 412 | |
| 413 | # --------------------------------------------------------------------------- |
| 414 | # YokedSurfaceCode tests |
| 415 | # --------------------------------------------------------------------------- |
| 416 | |
| 417 | |
| 418 | class TestYokedSurfaceCode: |
| 419 | def _get_lattice_surgery_isa(self, distance=5): |
| 420 | """Helper to get a lattice surgery ISA from SurfaceCode.""" |
| 421 | arch = GateBased(gate_time=50, measurement_time=100) |
| 422 | ctx = arch.context() |
| 423 | sc = SurfaceCode(distance=distance) |
| 424 | isas = list(sc.provided_isa(ctx.isa, ctx)) |
| 425 | return isas[0], ctx |
| 426 | |
| 427 | def test_provides_memory_instruction(self): |
| 428 | """Test that YokedSurfaceCode provides a MEMORY instruction.""" |
| 429 | ls_isa, ctx = self._get_lattice_surgery_isa() |
| 430 | ysc = TwoDimensionalYokedSurfaceCode() |
| 431 | |
| 432 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 433 | assert len(isas) == 1 |
| 434 | assert MEMORY in isas[0] |
| 435 | |
| 436 | def test_memory_is_logical(self): |
| 437 | """Test that the MEMORY instruction has LOGICAL encoding.""" |
| 438 | ls_isa, ctx = self._get_lattice_surgery_isa() |
| 439 | ysc = TwoDimensionalYokedSurfaceCode() |
| 440 | |
| 441 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 442 | mem = isas[0][MEMORY] |
| 443 | assert mem.encoding == LOGICAL |
| 444 | |
| 445 | def test_memory_arity_is_variable(self): |
| 446 | """Test that MEMORY instruction has variable arity (None).""" |
| 447 | ls_isa, ctx = self._get_lattice_surgery_isa() |
| 448 | ysc = TwoDimensionalYokedSurfaceCode() |
| 449 | |
| 450 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 451 | mem = isas[0][MEMORY] |
| 452 | # arity=None means variable arity |
| 453 | assert mem.arity is None |
| 454 | |
| 455 | def test_space_increases_with_arity(self): |
| 456 | """Test that MEMORY space increases with the number of qubits.""" |
| 457 | ls_isa, ctx = self._get_lattice_surgery_isa() |
| 458 | ysc = TwoDimensionalYokedSurfaceCode() |
| 459 | |
| 460 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 461 | mem = isas[0][MEMORY] |
| 462 | |
| 463 | spaces = [mem.expect_space(n) for n in [4, 16, 64]] |
| 464 | for i in range(len(spaces) - 1): |
| 465 | assert spaces[i] < spaces[i + 1] |
| 466 | |
| 467 | def test_time_increases_with_arity(self): |
| 468 | """Test that MEMORY time increases with the number of qubits.""" |
| 469 | ls_isa, ctx = self._get_lattice_surgery_isa() |
| 470 | ysc = TwoDimensionalYokedSurfaceCode() |
| 471 | |
| 472 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 473 | mem = isas[0][MEMORY] |
| 474 | |
| 475 | times = [mem.expect_time(n) for n in [4, 16, 64]] |
| 476 | for i in range(len(times) - 1): |
| 477 | assert times[i] < times[i + 1] |
| 478 | |
| 479 | def test_error_rate_increases_with_arity(self): |
| 480 | """Test that MEMORY error rate increases with the number of qubits.""" |
| 481 | ls_isa, ctx = self._get_lattice_surgery_isa() |
| 482 | ysc = TwoDimensionalYokedSurfaceCode() |
| 483 | |
| 484 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 485 | mem = isas[0][MEMORY] |
| 486 | |
| 487 | errors = [mem.expect_error_rate(n) for n in [4, 16, 64]] |
| 488 | for i in range(len(errors) - 1): |
| 489 | assert errors[i] < errors[i + 1] |
| 490 | |
| 491 | def test_distance_property_propagated(self): |
| 492 | """Test that the distance property is propagated to the MEMORY instruction.""" |
| 493 | d = 7 |
| 494 | ls_isa, ctx = self._get_lattice_surgery_isa(distance=d) |
| 495 | ysc = TwoDimensionalYokedSurfaceCode() |
| 496 | |
| 497 | isas = list(ysc.provided_isa(ls_isa, ctx)) |
| 498 | mem = isas[0][MEMORY] |
| 499 | assert mem.get_property(DISTANCE) == d |
| 500 | |
| 501 | |
| 502 | # --------------------------------------------------------------------------- |
| 503 | # Litinski19Factory tests |
| 504 | # --------------------------------------------------------------------------- |
| 505 | |
| 506 | |
| 507 | class TestLitinski19Factory: |
| 508 | def test_required_isa(self): |
| 509 | """Test that Litinski19Factory has non-None required ISA.""" |
| 510 | reqs = Litinski19Factory.required_isa() |
| 511 | assert reqs is not None |
| 512 | |
| 513 | def test_table1_yields_t_and_ccz(self): |
| 514 | """GateBased (error 1e-4) matches Table 1 scenario: T & CCZ.""" |
| 515 | arch = GateBased(gate_time=50, measurement_time=100) |
| 516 | ctx = arch.context() |
| 517 | factory = Litinski19Factory() |
| 518 | |
| 519 | isas = list(factory.provided_isa(ctx.isa, ctx)) |
| 520 | |
| 521 | # 6 T entries × 1 CCZ entry = 6 combinations |
| 522 | assert len(isas) == 6 |
| 523 | |
| 524 | for isa in isas: |
| 525 | assert T in isa |
| 526 | assert CCZ in isa |
| 527 | assert len(isa) == 2 |
| 528 | |
| 529 | def test_table1_instruction_properties(self): |
| 530 | """Test that Table 1 T and CCZ instructions have valid properties.""" |
| 531 | arch = GateBased(gate_time=50, measurement_time=100) |
| 532 | ctx = arch.context() |
| 533 | factory = Litinski19Factory() |
| 534 | |
| 535 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 536 | t_instr = isa[T] |
| 537 | ccz_instr = isa[CCZ] |
| 538 | |
| 539 | assert t_instr.arity == 1 |
| 540 | assert t_instr.encoding == LOGICAL |
| 541 | assert t_instr.expect_error_rate() > 0 |
| 542 | assert t_instr.expect_time() > 0 |
| 543 | assert t_instr.expect_space() > 0 |
| 544 | |
| 545 | assert ccz_instr.arity == 3 |
| 546 | assert ccz_instr.encoding == LOGICAL |
| 547 | assert ccz_instr.expect_error_rate() > 0 |
| 548 | |
| 549 | def test_table1_t_error_rates_are_diverse(self): |
| 550 | """T entries in Table 1 should span a range of error rates.""" |
| 551 | arch = GateBased(gate_time=50, measurement_time=100) |
| 552 | ctx = arch.context() |
| 553 | factory = Litinski19Factory() |
| 554 | |
| 555 | isas = list(factory.provided_isa(ctx.isa, ctx)) |
| 556 | t_errors = [isa[T].expect_error_rate() for isa in isas] |
| 557 | |
| 558 | # Should have multiple distinct T error rates |
| 559 | unique_errors = set(t_errors) |
| 560 | assert len(unique_errors) > 1 |
| 561 | |
| 562 | # All error rates should be positive and very small |
| 563 | for err in t_errors: |
| 564 | assert 0 < err < 1e-5 |
| 565 | |
| 566 | def test_table1_1e3_clifford_yields_6_isas(self): |
| 567 | """GateBased with 1e-3 error matches Table 1 at 1e-3 Clifford.""" |
| 568 | arch = GateBased(error_rate=1e-3, gate_time=50, measurement_time=100) |
| 569 | ctx = arch.context() |
| 570 | factory = Litinski19Factory() |
| 571 | |
| 572 | isas = list(factory.provided_isa(ctx.isa, ctx)) |
| 573 | |
| 574 | # 6 T entries × 1 CCZ entry = 6 combinations |
| 575 | assert len(isas) == 6 |
| 576 | |
| 577 | for isa in isas: |
| 578 | assert T in isa |
| 579 | assert CCZ in isa |
| 580 | |
| 581 | def test_table2_scenario_no_ccz(self): |
| 582 | """Table 2 scenario: T error ~10x higher than Clifford, no CCZ.""" |
| 583 | from qsharp.qre._qre import _ProvenanceGraph |
| 584 | |
| 585 | arch = GateBased(gate_time=50, measurement_time=100) |
| 586 | ctx = arch.context() |
| 587 | |
| 588 | # Manually create ISA with T error rate 10x Clifford |
| 589 | graph = _ProvenanceGraph() |
| 590 | isa_input = graph.make_isa( |
| 591 | [ |
| 592 | graph.add_instruction(CNOT, arity=2, time=50, error_rate=1e-4), |
| 593 | graph.add_instruction(H, time=50, error_rate=1e-4), |
| 594 | graph.add_instruction(MEAS_Z, time=100, error_rate=1e-4), |
| 595 | graph.add_instruction(T, time=50, error_rate=1e-3), |
| 596 | ] |
| 597 | ) |
| 598 | |
| 599 | factory = Litinski19Factory() |
| 600 | isas = list(factory.provided_isa(isa_input, ctx)) |
| 601 | |
| 602 | # Table 2 at 1e-4 Clifford: 4 T entries, no CCZ |
| 603 | assert len(isas) == 4 |
| 604 | |
| 605 | for isa in isas: |
| 606 | assert T in isa |
| 607 | assert CCZ not in isa |
| 608 | |
| 609 | def test_no_yield_when_error_too_high(self): |
| 610 | """If T error > 10x Clifford, no entries match.""" |
| 611 | from qsharp.qre._qre import _ProvenanceGraph |
| 612 | |
| 613 | arch = GateBased(gate_time=50, measurement_time=100) |
| 614 | ctx = arch.context() |
| 615 | |
| 616 | graph = _ProvenanceGraph() |
| 617 | isa_input = graph.make_isa( |
| 618 | [ |
| 619 | graph.add_instruction(CNOT, arity=2, time=50, error_rate=1e-4), |
| 620 | graph.add_instruction(H, time=50, error_rate=1e-4), |
| 621 | graph.add_instruction(MEAS_Z, time=100, error_rate=1e-4), |
| 622 | graph.add_instruction(T, time=50, error_rate=0.05), |
| 623 | ] |
| 624 | ) |
| 625 | |
| 626 | factory = Litinski19Factory() |
| 627 | isas = list(factory.provided_isa(isa_input, ctx)) |
| 628 | assert len(isas) == 0 |
| 629 | |
| 630 | def test_time_based_on_syndrome_extraction(self): |
| 631 | """Time should be based on syndrome extraction time × cycles.""" |
| 632 | arch = GateBased(gate_time=50, measurement_time=100) |
| 633 | ctx = arch.context() |
| 634 | factory = Litinski19Factory() |
| 635 | |
| 636 | # For GateBased: syndrome_extraction_time = 4*50 + 50 + 100 = 350 |
| 637 | syndrome_time = 4 * 50 + 50 + 100 # 350 ns |
| 638 | |
| 639 | isas = list(factory.provided_isa(ctx.isa, ctx)) |
| 640 | for isa in isas: |
| 641 | t_time = isa[T].expect_time() |
| 642 | assert t_time > 0 |
| 643 | # Time should be ceil(syndrome_time * cycles), so it must be at |
| 644 | # least syndrome_time (cycles >= 1) |
| 645 | assert t_time >= syndrome_time |
| 646 | |
| 647 | |
| 648 | # --------------------------------------------------------------------------- |
| 649 | # MagicUpToClifford tests |
| 650 | # --------------------------------------------------------------------------- |
| 651 | |
| 652 | |
| 653 | class TestMagicUpToClifford: |
| 654 | def test_required_isa_is_empty(self): |
| 655 | """Test that MagicUpToClifford has non-None required ISA.""" |
| 656 | reqs = MagicUpToClifford.required_isa() |
| 657 | assert reqs is not None |
| 658 | |
| 659 | def test_adds_clifford_equivalent_t_gates(self): |
| 660 | """Given T gate, should add SQRT_SQRT_X/Y/Z and dagger variants.""" |
| 661 | arch = GateBased(gate_time=50, measurement_time=100) |
| 662 | ctx = arch.context() |
| 663 | factory = Litinski19Factory() |
| 664 | modifier = MagicUpToClifford() |
| 665 | |
| 666 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 667 | modified_isas = list(modifier.provided_isa(isa, ctx)) |
| 668 | assert len(modified_isas) == 1 |
| 669 | modified_isa = modified_isas[0] |
| 670 | |
| 671 | # T family equivalents |
| 672 | for equiv_id in [ |
| 673 | SQRT_SQRT_X, |
| 674 | SQRT_SQRT_X_DAG, |
| 675 | SQRT_SQRT_Y, |
| 676 | SQRT_SQRT_Y_DAG, |
| 677 | SQRT_SQRT_Z, |
| 678 | SQRT_SQRT_Z_DAG, |
| 679 | ]: |
| 680 | assert equiv_id in modified_isa |
| 681 | |
| 682 | break # Just test the first one |
| 683 | |
| 684 | def test_adds_clifford_equivalent_ccz(self): |
| 685 | """Given CCZ, should add CCX and CCY.""" |
| 686 | arch = GateBased(gate_time=50, measurement_time=100) |
| 687 | ctx = arch.context() |
| 688 | factory = Litinski19Factory() |
| 689 | modifier = MagicUpToClifford() |
| 690 | |
| 691 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 692 | modified_isas = list(modifier.provided_isa(isa, ctx)) |
| 693 | modified_isa = modified_isas[0] |
| 694 | |
| 695 | assert CCX in modified_isa |
| 696 | assert CCY in modified_isa |
| 697 | assert CCZ in modified_isa |
| 698 | break |
| 699 | |
| 700 | def test_full_count_of_instructions(self): |
| 701 | """T gate (1) + 5 equivalents (SQRT_SQRT_*) + CCZ (1) + 2 equivalents (CCX, CCY) = 9.""" |
| 702 | arch = GateBased(gate_time=50, measurement_time=100) |
| 703 | ctx = arch.context() |
| 704 | factory = Litinski19Factory() |
| 705 | modifier = MagicUpToClifford() |
| 706 | |
| 707 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 708 | modified_isas = list(modifier.provided_isa(isa, ctx)) |
| 709 | assert len(modified_isas[0]) == 9 |
| 710 | break |
| 711 | |
| 712 | def test_equivalent_instructions_share_properties(self): |
| 713 | """Clifford equivalents should have same time, space, error rate.""" |
| 714 | arch = GateBased(gate_time=50, measurement_time=100) |
| 715 | ctx = arch.context() |
| 716 | factory = Litinski19Factory() |
| 717 | modifier = MagicUpToClifford() |
| 718 | |
| 719 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 720 | modified_isas = list(modifier.provided_isa(isa, ctx)) |
| 721 | modified_isa = modified_isas[0] |
| 722 | |
| 723 | t_instr = modified_isa[T] |
| 724 | for equiv_id in [ |
| 725 | SQRT_SQRT_X, |
| 726 | SQRT_SQRT_X_DAG, |
| 727 | SQRT_SQRT_Y, |
| 728 | SQRT_SQRT_Y_DAG, |
| 729 | SQRT_SQRT_Z_DAG, |
| 730 | ]: |
| 731 | equiv = modified_isa[equiv_id] |
| 732 | assert equiv.expect_error_rate() == t_instr.expect_error_rate() |
| 733 | assert equiv.expect_time() == t_instr.expect_time() |
| 734 | assert equiv.expect_space() == t_instr.expect_space() |
| 735 | |
| 736 | ccz_instr = modified_isa[CCZ] |
| 737 | for equiv_id in [CCX, CCY]: |
| 738 | equiv = modified_isa[equiv_id] |
| 739 | assert equiv.expect_error_rate() == ccz_instr.expect_error_rate() |
| 740 | |
| 741 | break |
| 742 | |
| 743 | def test_modification_count_matches_factory_output(self): |
| 744 | """MagicUpToClifford should produce one modified ISA per input ISA.""" |
| 745 | arch = GateBased(gate_time=50, measurement_time=100) |
| 746 | ctx = arch.context() |
| 747 | factory = Litinski19Factory() |
| 748 | modifier = MagicUpToClifford() |
| 749 | |
| 750 | modified_count = 0 |
| 751 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 752 | for _ in modifier.provided_isa(isa, ctx): |
| 753 | modified_count += 1 |
| 754 | |
| 755 | assert modified_count == 6 |
| 756 | |
| 757 | def test_no_family_present_passes_through(self): |
| 758 | """If no family member is present, ISA passes through unchanged.""" |
| 759 | from qsharp.qre._qre import _ProvenanceGraph |
| 760 | |
| 761 | arch = GateBased(gate_time=50, measurement_time=100) |
| 762 | ctx = arch.context() |
| 763 | modifier = MagicUpToClifford() |
| 764 | |
| 765 | # ISA with only a LATTICE_SURGERY instruction (no T or CCZ family) |
| 766 | from qsharp.qre import linear_function |
| 767 | |
| 768 | graph = _ProvenanceGraph() |
| 769 | isa_input = graph.make_isa( |
| 770 | [ |
| 771 | graph.add_instruction( |
| 772 | LATTICE_SURGERY, |
| 773 | encoding=LOGICAL, |
| 774 | arity=None, |
| 775 | time=1000, |
| 776 | space=linear_function(17), |
| 777 | error_rate=linear_function(1e-10), |
| 778 | ) |
| 779 | ] |
| 780 | ) |
| 781 | |
| 782 | results = list(modifier.provided_isa(isa_input, ctx)) |
| 783 | assert len(results) == 1 |
| 784 | # Should only contain the original instruction |
| 785 | assert len(results[0]) == 1 |
| 786 | |
| 787 | |
| 788 | # --------------------------------------------------------------------------- |
| 789 | # Litinski19Factory + MagicUpToClifford integration (from original test) |
| 790 | # --------------------------------------------------------------------------- |
| 791 | |
| 792 | |
| 793 | def test_isa_manipulation(): |
| 794 | """Test Litinski19Factory and MagicUpToClifford ISA integration.""" |
| 795 | arch = GateBased(gate_time=50, measurement_time=100) |
| 796 | factory = Litinski19Factory() |
| 797 | modifier = MagicUpToClifford() |
| 798 | |
| 799 | ctx = arch.context() |
| 800 | |
| 801 | # Table 1 scenario: should yield ISAs with both T and CCZ instructions |
| 802 | isas = list(factory.provided_isa(ctx.isa, ctx)) |
| 803 | |
| 804 | # 6 T entries × 1 CCZ entry = 6 combinations |
| 805 | assert len(isas) == 6 |
| 806 | |
| 807 | for isa in isas: |
| 808 | # Each ISA should contain both T and CCZ instructions |
| 809 | assert T in isa |
| 810 | assert CCZ in isa |
| 811 | assert len(isa) == 2 |
| 812 | |
| 813 | t_instr = isa[T] |
| 814 | ccz_instr = isa[CCZ] |
| 815 | |
| 816 | # Verify instruction properties |
| 817 | assert t_instr.arity == 1 |
| 818 | assert t_instr.encoding == LOGICAL |
| 819 | assert t_instr.expect_error_rate() > 0 |
| 820 | |
| 821 | assert ccz_instr.arity == 3 |
| 822 | assert ccz_instr.encoding == LOGICAL |
| 823 | assert ccz_instr.expect_error_rate() > 0 |
| 824 | |
| 825 | # After MagicUpToClifford modifier |
| 826 | modified_count = 0 |
| 827 | for isa in factory.provided_isa(ctx.isa, ctx): |
| 828 | for modified_isa in modifier.provided_isa(isa, ctx): |
| 829 | modified_count += 1 |
| 830 | # MagicUpToClifford should add derived instructions |
| 831 | assert T in modified_isa |
| 832 | assert CCZ in modified_isa |
| 833 | assert CCX in modified_isa |
| 834 | assert len(modified_isa) == 9 |
| 835 | |
| 836 | assert modified_count == 6 |
| 837 | |
| 838 | |
| 839 | # --------------------------------------------------------------------------- |
| 840 | # RoundBasedFactory tests |
| 841 | # --------------------------------------------------------------------------- |
| 842 | |
| 843 | |
| 844 | class TestRoundBasedFactory: |
| 845 | def test_required_isa(self): |
| 846 | """Test that RoundBasedFactory has non-None required ISA.""" |
| 847 | reqs = RoundBasedFactory.required_isa() |
| 848 | assert reqs is not None |
| 849 | |
| 850 | def test_produces_logical_t_gates(self): |
| 851 | """Test that RoundBasedFactory produces logical T gates with valid properties.""" |
| 852 | arch = GateBased(gate_time=50, measurement_time=100) |
| 853 | |
| 854 | for isa in RoundBasedFactory.q(use_cache=False).enumerate(arch.context()): |
| 855 | t = isa[T] |
| 856 | assert t.encoding == LOGICAL |
| 857 | assert t.arity == 1 |
| 858 | assert t.expect_error_rate() > 0 |
| 859 | assert t.expect_time() > 0 |
| 860 | assert t.expect_space() > 0 |
| 861 | break # Just check the first |
| 862 | |
| 863 | def test_error_rates_are_bounded(self): |
| 864 | """Distilled T error rates should be bounded and mostly small.""" |
| 865 | arch = GateBased(gate_time=50, measurement_time=100) # T error rate is 1e-4 |
| 866 | |
| 867 | errors = [] |
| 868 | for isa in RoundBasedFactory.q(use_cache=False).enumerate(arch.context()): |
| 869 | errors.append(isa[T].expect_error_rate()) |
| 870 | |
| 871 | # All should be positive |
| 872 | assert all(e > 0 for e in errors) |
| 873 | # Most distilled error rates should be much lower than 1 |
| 874 | assert min(errors) < 1e-4 |
| 875 | # Median should be well below raw physical error |
| 876 | sorted_errors = sorted(errors) |
| 877 | median = sorted_errors[len(sorted_errors) // 2] |
| 878 | assert median < 1e-3 |
| 879 | |
| 880 | def test_max_produces_fewer_or_equal_results_than_sum(self): |
| 881 | """Using max for physical_qubit_calculation may filter differently.""" |
| 882 | arch = GateBased(gate_time=50, measurement_time=100) |
| 883 | |
| 884 | sum_count = sum( |
| 885 | 1 for _ in RoundBasedFactory.q(use_cache=False).enumerate(arch.context()) |
| 886 | ) |
| 887 | max_count = sum( |
| 888 | 1 |
| 889 | for _ in RoundBasedFactory.q( |
| 890 | use_cache=False, physical_qubit_calculation=max |
| 891 | ).enumerate(arch.context()) |
| 892 | ) |
| 893 | |
| 894 | assert max_count <= sum_count |
| 895 | |
| 896 | def test_max_space_less_than_or_equal_sum_space(self): |
| 897 | """max-aggregated space should be <= sum-aggregated space for each.""" |
| 898 | arch = GateBased(gate_time=50, measurement_time=100) |
| 899 | |
| 900 | sum_spaces = sorted( |
| 901 | isa[T].expect_space() |
| 902 | for isa in RoundBasedFactory.q(use_cache=False).enumerate(arch.context()) |
| 903 | ) |
| 904 | |
| 905 | max_spaces = sorted( |
| 906 | isa[T].expect_space() |
| 907 | for isa in RoundBasedFactory.q( |
| 908 | use_cache=False, physical_qubit_calculation=max |
| 909 | ).enumerate(arch.context()) |
| 910 | ) |
| 911 | |
| 912 | # The minimum space with max should be <= minimum space with sum |
| 913 | assert max_spaces[0] <= sum_spaces[0] |
| 914 | |
| 915 | def test_with_three_aux_code_query(self): |
| 916 | """RoundBasedFactory with ThreeAux code query should produce results.""" |
| 917 | arch = Majorana() |
| 918 | |
| 919 | count = 0 |
| 920 | for isa in RoundBasedFactory.q( |
| 921 | use_cache=False, code_query=ThreeAux.q() |
| 922 | ).enumerate(arch.context()): |
| 923 | assert T in isa |
| 924 | assert isa[T].encoding == LOGICAL |
| 925 | count += 1 |
| 926 | |
| 927 | assert count > 0 |
| 928 | |
| 929 | def test_round_based_gate_based_sum(self): |
| 930 | """Test RoundBasedFactory aggregated totals with GateBased sum mode.""" |
| 931 | arch = GateBased(gate_time=50, measurement_time=100) |
| 932 | |
| 933 | total_space = 0 |
| 934 | total_time = 0 |
| 935 | total_error = 0.0 |
| 936 | count = 0 |
| 937 | |
| 938 | for isa in RoundBasedFactory.q(use_cache=False).enumerate(arch.context()): |
| 939 | count += 1 |
| 940 | total_space += isa[T].expect_space() |
| 941 | total_time += isa[T].expect_time() |
| 942 | total_error += isa[T].expect_error_rate() |
| 943 | |
| 944 | assert total_space == 12_946_488 |
| 945 | assert total_time == 12_032_250 |
| 946 | assert abs(total_error - 0.001_463_030_863_973_197_8) < 1e-8 |
| 947 | assert count == 107 |
| 948 | |
| 949 | def test_round_based_gate_based_max(self): |
| 950 | """Test RoundBasedFactory aggregated totals with GateBased max mode.""" |
| 951 | arch = GateBased(gate_time=50, measurement_time=100) |
| 952 | |
| 953 | total_space = 0 |
| 954 | total_time = 0 |
| 955 | total_error = 0.0 |
| 956 | count = 0 |
| 957 | |
| 958 | for isa in RoundBasedFactory.q( |
| 959 | use_cache=False, physical_qubit_calculation=max |
| 960 | ).enumerate(arch.context()): |
| 961 | count += 1 |
| 962 | total_space += isa[T].expect_space() |
| 963 | total_time += isa[T].expect_time() |
| 964 | total_error += isa[T].expect_error_rate() |
| 965 | |
| 966 | assert total_space == 4_651_617 |
| 967 | assert total_time == 7_785_000 |
| 968 | assert abs(total_error - 0.001_463_030_863_973_197_8) < 1e-8 |
| 969 | assert count == 77 |
| 970 | |
| 971 | def test_round_based_msft_sum(self): |
| 972 | """Test RoundBasedFactory aggregated totals with Majorana sum mode.""" |
| 973 | arch = Majorana() |
| 974 | |
| 975 | total_space = 0 |
| 976 | total_time = 0 |
| 977 | total_error = 0.0 |
| 978 | count = 0 |
| 979 | |
| 980 | for isa in RoundBasedFactory.q( |
| 981 | use_cache=False, code_query=ThreeAux.q() |
| 982 | ).enumerate(arch.context()): |
| 983 | count += 1 |
| 984 | total_space += isa[T].expect_space() |
| 985 | total_time += isa[T].expect_time() |
| 986 | total_error += isa[T].expect_error_rate() |
| 987 | |
| 988 | assert total_space == 255_952_723 |
| 989 | assert total_time == 478_235_000 |
| 990 | assert abs(total_error - 0.000_880_967_766_732_897_4) < 1e-8 |
| 991 | assert count == 301 |
| 992 | |
| 993 | |
| 994 | # --------------------------------------------------------------------------- |
| 995 | # Cross-model integration tests |
| 996 | # --------------------------------------------------------------------------- |
| 997 | |
| 998 | |
| 999 | class TestCrossModelIntegration: |
| 1000 | def test_surface_code_feeds_into_litinski(self): |
| 1001 | """SurfaceCode -> Litinski19Factory pipeline works end to end.""" |
| 1002 | arch = GateBased(gate_time=50, measurement_time=100) |
| 1003 | ctx = arch.context() |
| 1004 | |
| 1005 | # SurfaceCode takes gate-based physical ISA -> LATTICE_SURGERY |
| 1006 | sc = SurfaceCode(distance=5) |
| 1007 | sc_isas = list(sc.provided_isa(ctx.isa, ctx)) |
| 1008 | assert len(sc_isas) == 1 |
| 1009 | |
| 1010 | # Litinski takes H, CNOT, MEAS_Z, T from the physical ISA |
| 1011 | factory = Litinski19Factory() |
| 1012 | factory_isas = list(factory.provided_isa(ctx.isa, ctx)) |
| 1013 | assert len(factory_isas) > 0 |
| 1014 | |
| 1015 | def test_three_aux_feeds_into_round_based(self): |
| 1016 | """ThreeAux -> RoundBasedFactory pipeline works.""" |
| 1017 | arch = Majorana() |
| 1018 | ctx = arch.context() |
| 1019 | |
| 1020 | count = 0 |
| 1021 | for isa in RoundBasedFactory.q( |
| 1022 | use_cache=False, code_query=ThreeAux.q() |
| 1023 | ).enumerate(ctx): |
| 1024 | assert T in isa |
| 1025 | count += 1 |
| 1026 | |
| 1027 | assert count > 0 |
| 1028 | |
| 1029 | def test_litinski_with_magic_up_to_clifford_query(self): |
| 1030 | """Full query chain: Litinski19Factory -> MagicUpToClifford.""" |
| 1031 | arch = GateBased(gate_time=50, measurement_time=100) |
| 1032 | ctx = arch.context() |
| 1033 | |
| 1034 | count = 0 |
| 1035 | for isa in MagicUpToClifford.q(source=Litinski19Factory.q()).enumerate(ctx): |
| 1036 | assert T in isa |
| 1037 | assert CCX in isa |
| 1038 | assert CCY in isa |
| 1039 | assert CCZ in isa |
| 1040 | count += 1 |
| 1041 | |
| 1042 | assert count == 6 |
| 1043 | |
| 1044 | def test_surface_code_with_yoked_surface_code(self): |
| 1045 | """SurfaceCode -> YokedSurfaceCode pipeline provides MEMORY.""" |
| 1046 | arch = GateBased(gate_time=50, measurement_time=100) |
| 1047 | ctx = arch.context() |
| 1048 | |
| 1049 | count = 0 |
| 1050 | for isa in TwoDimensionalYokedSurfaceCode.q(source=SurfaceCode.q()).enumerate( |
| 1051 | ctx |
| 1052 | ): |
| 1053 | assert MEMORY in isa |
| 1054 | count += 1 |
| 1055 | |
| 1056 | # 12 distances × 1 shape heuristic = 12 |
| 1057 | assert count == 12 |
| 1058 | |
| 1059 | def test_majorana_three_aux_yoked(self): |
| 1060 | """Majorana -> ThreeAux -> YokedSurfaceCode pipeline.""" |
| 1061 | arch = Majorana() |
| 1062 | ctx = arch.context() |
| 1063 | |
| 1064 | count = 0 |
| 1065 | for isa in TwoDimensionalYokedSurfaceCode.q(source=ThreeAux.q()).enumerate(ctx): |
| 1066 | assert MEMORY in isa |
| 1067 | count += 1 |
| 1068 | |
| 1069 | assert count > 0 |
| 1070 | |