microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
samples/notebooks/carbon_error_correction/decoder.py
352lines · modecode
| 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright (c) Microsoft Corporation. |
| 4 | # Licensed under the MIT License. |
| 5 | |
| 6 | import qsharp |
| 7 | |
| 8 | table = { |
| 9 | frozenset(): "IIIIIIIIIIII", |
| 10 | frozenset({3, 8, 9}): "XIIIIIIIIIII", |
| 11 | frozenset({0, 3, 6, 7, 8, 9}): "YIIIIIIIIIII", |
| 12 | frozenset({0, 6, 7}): "ZIIIIIIIIIII", |
| 13 | frozenset({3}): "IXIIIIIIIIII", |
| 14 | frozenset({0, 3, 6}): "IYIIIIIIIIII", |
| 15 | frozenset({0, 6}): "IZIIIIIIIIII", |
| 16 | frozenset({3, 8}): "IIXIIIIIIIII", |
| 17 | frozenset({0, 3, 8}): "IIYIIIIIIIII", |
| 18 | frozenset({0}): "IIZIIIIIIIII", |
| 19 | frozenset({3, 9}): "IIIXIIIIIIII", |
| 20 | frozenset({0, 3, 7, 9}): "IIIYIIIIIIII", |
| 21 | frozenset({0, 7}): "IIIZIIIIIIII", |
| 22 | frozenset({4, 9}): "IIIIXIIIIIII", |
| 23 | frozenset({1, 4, 7, 9}): "IIIIYIIIIIII", |
| 24 | frozenset({1, 7}): "IIIIZIIIIIII", |
| 25 | frozenset({4}): "IIIIIXIIIIII", |
| 26 | frozenset({1, 4, 6, 7}): "IIIIIYIIIIII", |
| 27 | frozenset({1, 6, 7}): "IIIIIZIIIIII", |
| 28 | frozenset({4, 8, 9}): "IIIIIIXIIIII", |
| 29 | frozenset({1, 4, 8, 9}): "IIIIIIYIIIII", |
| 30 | frozenset({1}): "IIIIIIZIIIII", |
| 31 | frozenset({4, 8}): "IIIIIIIXIIII", |
| 32 | frozenset({1, 4, 6, 8}): "IIIIIIIYIIII", |
| 33 | frozenset({1, 6}): "IIIIIIIZIIII", |
| 34 | frozenset({5, 8}): "IIIIIIIIXIII", |
| 35 | frozenset({2, 5, 6, 8}): "IIIIIIIIYIII", |
| 36 | frozenset({2, 6}): "IIIIIIIIZIII", |
| 37 | frozenset({5}): "IIIIIIIIIXII", |
| 38 | frozenset({2, 5, 7}): "IIIIIIIIIYII", |
| 39 | frozenset({2, 7}): "IIIIIIIIIZII", |
| 40 | frozenset({5, 9}): "IIIIIIIIIIXI", |
| 41 | frozenset({2, 5, 9}): "IIIIIIIIIIYI", |
| 42 | frozenset({2}): "IIIIIIIIIIZI", |
| 43 | frozenset({5, 8, 9}): "IIIIIIIIIIIX", |
| 44 | frozenset({2, 5, 6, 7, 8, 9}): "IIIIIIIIIIIY", |
| 45 | frozenset({2, 6, 7}): "IIIIIIIIIIIZ", |
| 46 | frozenset({0, 3, 6, 8, 9}): "XZIIIIIIIIII", |
| 47 | frozenset({0, 3, 6, 7}): "ZXIIIIIIIIII", |
| 48 | frozenset({0, 3, 8, 9}): "XIZIIIIIIIII", |
| 49 | frozenset({0, 3, 6, 7, 8}): "ZIXIIIIIIIII", |
| 50 | frozenset({0, 3, 7, 8, 9}): "XIIZIIIIIIII", |
| 51 | frozenset({0, 3, 6, 7, 9}): "ZIIXIIIIIIII", |
| 52 | frozenset({1, 3, 7, 8, 9}): "XIIIZIIIIIII", |
| 53 | frozenset({0, 4, 6, 7, 9}): "ZIIIXIIIIIII", |
| 54 | frozenset({1, 3, 6, 7, 8, 9}): "XIIIIZIIIIII", |
| 55 | frozenset({0, 4, 6, 7}): "ZIIIIXIIIIII", |
| 56 | frozenset({1, 3, 8, 9}): "XIIIIIZIIIII", |
| 57 | frozenset({0, 4, 6, 7, 8, 9}): "ZIIIIIXIIIII", |
| 58 | frozenset({1, 3, 6, 8, 9}): "XIIIIIIZIIII", |
| 59 | frozenset({0, 4, 6, 7, 8}): "ZIIIIIIXIIII", |
| 60 | frozenset({2, 3, 6, 8, 9}): "XIIIIIIIZIII", |
| 61 | frozenset({0, 5, 6, 7, 8}): "ZIIIIIIIXIII", |
| 62 | frozenset({2, 3, 7, 8, 9}): "XIIIIIIIIZII", |
| 63 | frozenset({0, 5, 6, 7}): "ZIIIIIIIIXII", |
| 64 | frozenset({2, 3, 8, 9}): "XIIIIIIIIIZI", |
| 65 | frozenset({0, 5, 6, 7, 9}): "ZIIIIIIIIIXI", |
| 66 | frozenset({2, 3, 6, 7, 8, 9}): "XIIIIIIIIIIZ", |
| 67 | frozenset({0, 5, 6, 7, 8, 9}): "ZIIIIIIIIIIX", |
| 68 | frozenset({0, 3}): "IXZIIIIIIIII", |
| 69 | frozenset({0, 3, 6, 8}): "IZXIIIIIIIII", |
| 70 | frozenset({0, 3, 7}): "IXIZIIIIIIII", |
| 71 | frozenset({0, 3, 6, 9}): "IZIXIIIIIIII", |
| 72 | frozenset({1, 3, 7}): "IXIIZIIIIIII", |
| 73 | frozenset({0, 4, 6, 9}): "IZIIXIIIIIII", |
| 74 | frozenset({1, 3, 6, 7}): "IXIIIZIIIIII", |
| 75 | frozenset({0, 4, 6}): "IZIIIXIIIIII", |
| 76 | frozenset({1, 3}): "IXIIIIZIIIII", |
| 77 | frozenset({0, 4, 6, 8, 9}): "IZIIIIXIIIII", |
| 78 | frozenset({1, 3, 6}): "IXIIIIIZIIII", |
| 79 | frozenset({0, 4, 6, 8}): "IZIIIIIXIIII", |
| 80 | frozenset({2, 3, 6}): "IXIIIIIIZIII", |
| 81 | frozenset({0, 5, 6, 8}): "IZIIIIIIXIII", |
| 82 | frozenset({2, 3, 7}): "IXIIIIIIIZII", |
| 83 | frozenset({0, 5, 6}): "IZIIIIIIIXII", |
| 84 | frozenset({2, 3}): "IXIIIIIIIIZI", |
| 85 | frozenset({0, 5, 6, 9}): "IZIIIIIIIIXI", |
| 86 | frozenset({2, 3, 6, 7}): "IXIIIIIIIIIZ", |
| 87 | frozenset({0, 5, 6, 8, 9}): "IZIIIIIIIIIX", |
| 88 | frozenset({0, 3, 7, 8}): "IIXZIIIIIIII", |
| 89 | frozenset({0, 3, 9}): "IIZXIIIIIIII", |
| 90 | frozenset({1, 3, 7, 8}): "IIXIZIIIIIII", |
| 91 | frozenset({0, 4, 9}): "IIZIXIIIIIII", |
| 92 | frozenset({1, 3, 6, 7, 8}): "IIXIIZIIIIII", |
| 93 | frozenset({0, 4}): "IIZIIXIIIIII", |
| 94 | frozenset({1, 3, 8}): "IIXIIIZIIIII", |
| 95 | frozenset({0, 4, 8, 9}): "IIZIIIXIIIII", |
| 96 | frozenset({1, 3, 6, 8}): "IIXIIIIZIIII", |
| 97 | frozenset({0, 4, 8}): "IIZIIIIXIIII", |
| 98 | frozenset({2, 3, 6, 8}): "IIXIIIIIZIII", |
| 99 | frozenset({0, 5, 8}): "IIZIIIIIXIII", |
| 100 | frozenset({2, 3, 7, 8}): "IIXIIIIIIZII", |
| 101 | frozenset({0, 5}): "IIZIIIIIIXII", |
| 102 | frozenset({2, 3, 8}): "IIXIIIIIIIZI", |
| 103 | frozenset({0, 5, 9}): "IIZIIIIIIIXI", |
| 104 | frozenset({2, 3, 6, 7, 8}): "IIXIIIIIIIIZ", |
| 105 | frozenset({0, 5, 8, 9}): "IIZIIIIIIIIX", |
| 106 | frozenset({1, 3, 7, 9}): "IIIXZIIIIIII", |
| 107 | frozenset({0, 4, 7, 9}): "IIIZXIIIIIII", |
| 108 | frozenset({1, 3, 6, 7, 9}): "IIIXIZIIIIII", |
| 109 | frozenset({0, 4, 7}): "IIIZIXIIIIII", |
| 110 | frozenset({1, 3, 9}): "IIIXIIZIIIII", |
| 111 | frozenset({0, 4, 7, 8, 9}): "IIIZIIXIIIII", |
| 112 | frozenset({1, 3, 6, 9}): "IIIXIIIZIIII", |
| 113 | frozenset({0, 4, 7, 8}): "IIIZIIIXIIII", |
| 114 | frozenset({2, 3, 6, 9}): "IIIXIIIIZIII", |
| 115 | frozenset({0, 5, 7, 8}): "IIIZIIIIXIII", |
| 116 | frozenset({2, 3, 7, 9}): "IIIXIIIIIZII", |
| 117 | frozenset({0, 5, 7}): "IIIZIIIIIXII", |
| 118 | frozenset({2, 3, 9}): "IIIXIIIIIIZI", |
| 119 | frozenset({0, 5, 7, 9}): "IIIZIIIIIIXI", |
| 120 | frozenset({2, 3, 6, 7, 9}): "IIIXIIIIIIIZ", |
| 121 | frozenset({0, 5, 7, 8, 9}): "IIIZIIIIIIIX", |
| 122 | frozenset({1, 4, 6, 7, 9}): "IIIIXZIIIIII", |
| 123 | frozenset({1, 4, 7}): "IIIIZXIIIIII", |
| 124 | frozenset({1, 4, 9}): "IIIIXIZIIIII", |
| 125 | frozenset({1, 4, 7, 8, 9}): "IIIIZIXIIIII", |
| 126 | frozenset({1, 4, 6, 9}): "IIIIXIIZIIII", |
| 127 | frozenset({1, 4, 7, 8}): "IIIIZIIXIIII", |
| 128 | frozenset({2, 4, 6, 9}): "IIIIXIIIZIII", |
| 129 | frozenset({1, 5, 7, 8}): "IIIIZIIIXIII", |
| 130 | frozenset({2, 4, 7, 9}): "IIIIXIIIIZII", |
| 131 | frozenset({1, 5, 7}): "IIIIZIIIIXII", |
| 132 | frozenset({2, 4, 9}): "IIIIXIIIIIZI", |
| 133 | frozenset({1, 5, 7, 9}): "IIIIZIIIIIXI", |
| 134 | frozenset({2, 4, 6, 7, 9}): "IIIIXIIIIIIZ", |
| 135 | frozenset({1, 5, 7, 8, 9}): "IIIIZIIIIIIX", |
| 136 | frozenset({1, 4}): "IIIIIXZIIIII", |
| 137 | frozenset({1, 4, 6, 7, 8, 9}): "IIIIIZXIIIII", |
| 138 | frozenset({1, 4, 6}): "IIIIIXIZIIII", |
| 139 | frozenset({1, 4, 6, 7, 8}): "IIIIIZIXIIII", |
| 140 | frozenset({2, 4, 6}): "IIIIIXIIZIII", |
| 141 | frozenset({1, 5, 6, 7, 8}): "IIIIIZIIXIII", |
| 142 | frozenset({2, 4, 7}): "IIIIIXIIIZII", |
| 143 | frozenset({1, 5, 6, 7}): "IIIIIZIIIXII", |
| 144 | frozenset({2, 4}): "IIIIIXIIIIZI", |
| 145 | frozenset({1, 5, 6, 7, 9}): "IIIIIZIIIIXI", |
| 146 | frozenset({2, 4, 6, 7}): "IIIIIXIIIIIZ", |
| 147 | frozenset({1, 5, 6, 7, 8, 9}): "IIIIIZIIIIIX", |
| 148 | frozenset({1, 4, 6, 8, 9}): "IIIIIIXZIIII", |
| 149 | frozenset({1, 4, 8}): "IIIIIIZXIIII", |
| 150 | frozenset({2, 4, 6, 8, 9}): "IIIIIIXIZIII", |
| 151 | frozenset({1, 5, 8}): "IIIIIIZIXIII", |
| 152 | frozenset({2, 4, 7, 8, 9}): "IIIIIIXIIZII", |
| 153 | frozenset({1, 5}): "IIIIIIZIIXII", |
| 154 | frozenset({2, 4, 8, 9}): "IIIIIIXIIIZI", |
| 155 | frozenset({1, 5, 9}): "IIIIIIZIIIXI", |
| 156 | frozenset({2, 4, 6, 7, 8, 9}): "IIIIIIXIIIIZ", |
| 157 | frozenset({1, 5, 8, 9}): "IIIIIIZIIIIX", |
| 158 | frozenset({2, 4, 6, 8}): "IIIIIIIXZIII", |
| 159 | frozenset({1, 5, 6, 8}): "IIIIIIIZXIII", |
| 160 | frozenset({2, 4, 7, 8}): "IIIIIIIXIZII", |
| 161 | frozenset({1, 5, 6}): "IIIIIIIZIXII", |
| 162 | frozenset({2, 4, 8}): "IIIIIIIXIIZI", |
| 163 | frozenset({1, 5, 6, 9}): "IIIIIIIZIIXI", |
| 164 | frozenset({2, 4, 6, 7, 8}): "IIIIIIIXIIIZ", |
| 165 | frozenset({1, 5, 6, 8, 9}): "IIIIIIIZIIIX", |
| 166 | frozenset({2, 5, 7, 8}): "IIIIIIIIXZII", |
| 167 | frozenset({2, 5, 6}): "IIIIIIIIZXII", |
| 168 | frozenset({2, 5, 8}): "IIIIIIIIXIZI", |
| 169 | frozenset({2, 5, 6, 9}): "IIIIIIIIZIXI", |
| 170 | frozenset({2, 5, 6, 7, 8}): "IIIIIIIIXIIZ", |
| 171 | frozenset({2, 5, 6, 8, 9}): "IIIIIIIIZIIX", |
| 172 | frozenset({2, 5}): "IIIIIIIIIXZI", |
| 173 | frozenset({2, 5, 7, 9}): "IIIIIIIIIZXI", |
| 174 | frozenset({2, 5, 6, 7}): "IIIIIIIIIXIZ", |
| 175 | frozenset({2, 5, 7, 8, 9}): "IIIIIIIIIZIX", |
| 176 | frozenset({2, 5, 6, 7, 9}): "IIIIIIIIIIXZ", |
| 177 | frozenset({2, 5, 8, 9}): "IIIIIIIIIIZX", |
| 178 | } |
| 179 | |
| 180 | generators = [ |
| 181 | "XXXXIIIIIIII", |
| 182 | "IIIIXXXXIIII", |
| 183 | "IIIIIIIIXXXX", |
| 184 | "ZZZZIIIIIIII", |
| 185 | "IIIIZZZZIIII", |
| 186 | "IIIIIIIIZZZZ", |
| 187 | "XXIIIXIXXIIX", |
| 188 | "XIIXXXIIIXIX", |
| 189 | "ZIZIIIZZZIIZ", |
| 190 | "ZIIZZIZIIIZZ", |
| 191 | ] |
| 192 | |
| 193 | logical_basis = ["XIIXIIIIIIXX", "ZIIZIIIIIZIZ", "IXIXIIIIIXXI", "ZZIIIIIIIZZI"] |
| 194 | |
| 195 | expanded_logical_basis = [ |
| 196 | "XIIXIIIIIIXX", |
| 197 | "ZIIZIIIIIZIZ", |
| 198 | "YIIYIIIIIZXY", |
| 199 | "IXIXIIIIIXXI", |
| 200 | "ZZIIIIIIIZZI", |
| 201 | "ZYIXIIIIIYYI", |
| 202 | ] |
| 203 | |
| 204 | |
| 205 | def results_as_pauli(results: list[qsharp.Result], pauli: str = "Z") -> str: |
| 206 | p = "" |
| 207 | for r in results: |
| 208 | if r == qsharp.Result.One: |
| 209 | p += pauli |
| 210 | else: |
| 211 | p += "I" |
| 212 | return p |
| 213 | |
| 214 | |
| 215 | def pauli_as_results(p: str, basis: str = "Z", count: int = 2): |
| 216 | results = [] |
| 217 | chars = "XYZ".replace(basis, "") |
| 218 | for i in range(count): |
| 219 | if p[i] in chars: |
| 220 | results.append(qsharp.Result.One) |
| 221 | else: |
| 222 | results.append(qsharp.Result.Zero) |
| 223 | return results |
| 224 | |
| 225 | |
| 226 | def pauli_support(p: str) -> list[int]: |
| 227 | return [i for i, char in enumerate(p) if char != "I"] |
| 228 | |
| 229 | |
| 230 | def logical_indexes_of(pauli: str): |
| 231 | for qubit in pauli_support(pauli): |
| 232 | character = pauli[qubit] |
| 233 | if character == "X": |
| 234 | yield 3 * qubit |
| 235 | if character == "Z": |
| 236 | yield 3 * qubit + 1 |
| 237 | if character == "Y": |
| 238 | yield 3 * qubit + 2 |
| 239 | |
| 240 | |
| 241 | def commutes_with(pauli1: str, pauli2: str) -> bool: |
| 242 | """Check if two Pauli strings commute.""" |
| 243 | assert len(pauli1) == len(pauli2) |
| 244 | anti_commute_count = 0 |
| 245 | for p1, p2 in zip(pauli1, pauli2): |
| 246 | if p1 == "I" or p2 == "I": |
| 247 | continue |
| 248 | if p1 != p2: |
| 249 | anti_commute_count += 1 |
| 250 | return anti_commute_count % 2 == 0 |
| 251 | |
| 252 | |
| 253 | def syndrome_of(error: str) -> list[int]: |
| 254 | syndrome = [] |
| 255 | for label, generator in enumerate(generators): |
| 256 | if not commutes_with(error, generator): |
| 257 | syndrome.append(label) |
| 258 | return syndrome |
| 259 | |
| 260 | |
| 261 | def mult_paulis(p1: str, p2: str) -> str: |
| 262 | if len(p1) < len(p2): |
| 263 | p1 = p1 + "I" * (len(p2) - len(p1)) |
| 264 | elif len(p2) < len(p1): |
| 265 | p2 = p2 + "I" * (len(p1) - len(p2)) |
| 266 | result = "" |
| 267 | for a, b in zip(p1, p2): |
| 268 | if a == "I": |
| 269 | result += b |
| 270 | elif b == "I": |
| 271 | result += a |
| 272 | elif a == b: |
| 273 | result += "I" |
| 274 | elif (a, b) in [("X", "Y"), ("Y", "X")]: |
| 275 | result += "Z" |
| 276 | elif (a, b) in [("Y", "Z"), ("Z", "Y")]: |
| 277 | result += "X" |
| 278 | elif (a, b) in [("Z", "X"), ("X", "Z")]: |
| 279 | result += "Y" |
| 280 | else: |
| 281 | raise ValueError(f"Unexpected Pauli characters: {a}, {b}") |
| 282 | return result |
| 283 | |
| 284 | |
| 285 | def unsigned_logical_action_of(error: str) -> str: |
| 286 | character_of = ("Y", "Z", "X", "I") |
| 287 | commutations = list(map(lambda lb: commutes_with(error, lb), logical_basis)) |
| 288 | indexes = [2 * x + z for x, z in [commutations[0:2], commutations[2:4]]] |
| 289 | characters = "".join(character_of[index] for index in indexes) |
| 290 | return characters |
| 291 | |
| 292 | |
| 293 | def representative_of(pauli: str) -> str: |
| 294 | generators = (expanded_logical_basis[index] for index in logical_indexes_of(pauli)) |
| 295 | res = "I" * 12 |
| 296 | for gen in generators: |
| 297 | res = mult_paulis(res, gen) |
| 298 | return res |
| 299 | |
| 300 | |
| 301 | def logical_action_of(error: str) -> str: |
| 302 | logical = unsigned_logical_action_of(error) |
| 303 | return logical |
| 304 | |
| 305 | |
| 306 | def recovery_from_syndrome_measurements( |
| 307 | x_meas: list[qsharp.Result], z_meas: list[qsharp.Result] |
| 308 | ) -> str: |
| 309 | error_z = results_as_pauli(x_meas, pauli="Z") |
| 310 | error_x = results_as_pauli(z_meas, pauli="X") |
| 311 | error = mult_paulis(error_z, error_x) |
| 312 | syndrome = frozenset(syndrome_of(error)) |
| 313 | recovery = table.get(syndrome, "IIIIIIIIIIII") |
| 314 | return logical_action_of(mult_paulis(recovery, error)) |
| 315 | |
| 316 | |
| 317 | # For each tuple of shot results, mark the shot as preselect if any preselect |
| 318 | # measurement is One. Then use the rounds of error correction syndrome measurements |
| 319 | # to generate Pauli corrections, collected in a frame. Use the final Pauli frame |
| 320 | # to calculate the corrected logical results. |
| 321 | def decode_results(results, basis: str = "Z"): |
| 322 | corrected_logical_results = [] |
| 323 | for res in results: |
| 324 | corrected_logical_results.append([]) |
| 325 | for shot in res: |
| 326 | if any([preselect == qsharp.Result.One for preselect in shot[0]]): |
| 327 | corrected_logical_results[-1] += ["PREselect"] |
| 328 | continue |
| 329 | recovery = "IIIIIIIIIIII" |
| 330 | r = None |
| 331 | for ec_output in shot[1]: |
| 332 | r = recovery_from_syndrome_measurements(ec_output[0], ec_output[1]) |
| 333 | if r is None: |
| 334 | corrected_logical_results[-1] += ["POSTselect"] |
| 335 | break |
| 336 | recovery = mult_paulis(recovery, r) |
| 337 | if r is None: |
| 338 | corrected_logical_results[-1] += [ |
| 339 | pauli_as_results(recovery, basis=basis) |
| 340 | ] |
| 341 | continue |
| 342 | if basis == "Z": |
| 343 | r = recovery_from_syndrome_measurements([], shot[2]) |
| 344 | else: |
| 345 | assert basis == "X" |
| 346 | r = recovery_from_syndrome_measurements(shot[2], []) |
| 347 | if r is None: |
| 348 | corrected_logical_results[-1] += ["POSTselect"] |
| 349 | continue |
| 350 | recovery = mult_paulis(recovery, r) |
| 351 | corrected_logical_results[-1] += [pauli_as_results(recovery, basis=basis)] |
| 352 | return corrected_logical_results |
| 353 | |