| |
| |
| from __future__ import annotations |
|
|
| from typing import Iterable, List, Optional |
|
|
| from surface_code_in_stem.noise_models import NoiseModel, resolve_noise_model |
|
|
| def data_coords(distance): |
| |
| coords = [] |
| for row in range(1, distance+1): |
| for col in range(1, distance+1): |
| coords.append((col, row)) |
| return coords |
|
|
| def z_measure_coords(distance): |
| |
| |
| coords = [] |
| for row in range(1, distance): |
| for col in range(1, distance+1, 2): |
| if row%2: |
| coords.append((col-0.5, row+0.5)) |
| else: |
| coords.append((col+0.5, row+0.5)) |
| return coords |
|
|
| def x_measure_coords(distance): |
| |
| |
| coords = [] |
| for row in range(1, distance+2): |
| for col in range(2, distance, 2): |
| if row%2: |
| coords.append((col+0.5, row-0.5)) |
| else: |
| coords.append((col-0.5, row-0.5)) |
| return coords |
|
|
| def coords_to_index(coords): |
| |
| |
| return {tuple(c):i for i,c in dict(enumerate(coords)).items()} |
|
|
| def adjacent_coords(coord): |
| |
| |
| |
| col, row = coord |
| adjacents = [(col-0.5, row-0.5), (col+0.5, row-0.5), |
| (col-0.5, row+0.5), (col+0.5, row+0.5), |
| ] |
| return adjacents |
|
|
| def index_string(coord_list, c2i): |
| |
| return ' '.join(str(c2i[coord]) for coord in coord_list) |
|
|
| def prepare_coords(distance): |
| |
| |
| |
| datas = data_coords(distance) |
| x_measures = x_measure_coords(distance) |
| z_measures = z_measure_coords(distance) |
| c2i = coords_to_index(datas+x_measures+z_measures) |
| return datas, x_measures, z_measures, c2i |
|
|
| def coord_circuit(distance): |
| |
| |
| _, _, _, c2i = prepare_coords(distance) |
| stim_circuit = "" |
| for coord, index in c2i.items(): |
| stim_circuit += f"QUBIT_COORDS({','.join(map(str, coord))}) {index}\n" |
| return stim_circuit |
|
|
| def label_indices(distance): |
| |
| |
| |
| |
| |
| |
| |
| datas, x_measures, z_measures, c2i = prepare_coords(distance) |
| all_qubits = datas + x_measures + z_measures |
| i = 0 |
| stim_string = "" |
| for coord in datas: |
| stim_string += f"Y_ERROR(0.{i:>02}) {c2i[coord]}\n" |
| i += 1 |
| stim_string += "TICK\n" |
| for coord in x_measures: |
| stim_string += f"X_ERROR(0.{i:>02}) {c2i[coord]}\n" |
| i += 1 |
| stim_string += "TICK\n" |
| for coord in z_measures: |
| stim_string += f"Z_ERROR(0.{i:>02}) {c2i[coord]}\n" |
| i += 1 |
|
|
| return stim_string |
|
|
| |
| |
|
|
| def _extend_noise(lines: List[str], noise_lines: Iterable[str]) -> None: |
| for line in noise_lines: |
| if line: |
| lines.append(line) |
|
|
|
|
| def lattice_with_noise(distance, p, noise_model: Optional[NoiseModel] = None): |
| noise_model = resolve_noise_model(p, noise_model) |
| datas, x_measures, z_measures, c2i = prepare_coords(distance) |
| |
| |
| lines: List[str] = [] |
| for i in range(4): |
| cx_qubits = [] |
| for measure in z_measures: |
| z_controls = adjacent_coords(measure) |
| control = z_controls[i] |
| if control in c2i: |
| cx_qubits.extend([control, measure]) |
|
|
| for measure in x_measures: |
| x_targets = adjacent_coords(measure) |
| index_reorder = [0, 2, 1, 3] |
| target = x_targets[index_reorder[i]] |
| if target in c2i: |
| cx_qubits.extend([measure, target]) |
|
|
| idle_qubits = [coord for coord in c2i.keys() if coord not in cx_qubits] |
|
|
| pair_indices = [c2i[q] for q in cx_qubits] |
| idle_indices = [c2i[q] for q in idle_qubits] |
| lines.append(f"CX {' '.join(map(str, pair_indices))}") |
| _extend_noise( |
| lines, |
| noise_model.gate_noise( |
| gate="CX", |
| pair_targets=pair_indices, |
| idle_targets=idle_indices, |
| layer_id=f"lattice_orient_{i}", |
| ), |
| ) |
| lines.append("TICK") |
|
|
| return "\n".join(lines) + "\n" |
|
|
|
|
| def stabilizers_with_noise(distance, p, noise_model: Optional[NoiseModel] = None): |
| noise_model = resolve_noise_model(p, noise_model) |
| datas, x_measures, z_measures, c2i = prepare_coords(distance) |
| all_measures = x_measures + z_measures |
| all_qubits = datas + all_measures |
| |
| |
| lines = [f"R {index_string(all_measures, c2i)}"] |
| _extend_noise(lines, noise_model.reset_noise(qubits=[c2i[q] for q in all_measures], layer_id="stabilizer_reset")) |
| _extend_noise( |
| lines, |
| noise_model.gate_noise( |
| gate="IDLE", |
| pair_targets=[], |
| idle_targets=[c2i[q] for q in datas], |
| layer_id="stabilizer_post_reset_idle", |
| ), |
| ) |
| lines.append("TICK") |
| lines.append(f"H {index_string(x_measures, c2i)}") |
| _extend_noise( |
| lines, |
| noise_model.gate_noise( |
| gate="H", |
| pair_targets=[], |
| idle_targets=[c2i[q] for q in all_qubits], |
| layer_id="stabilizer_h_pre", |
| ), |
| ) |
| lines.append("TICK") |
|
|
| lines.append(lattice_with_noise(distance, p, noise_model=noise_model).strip()) |
|
|
| lines.append(f"H {index_string(x_measures, c2i)}") |
| _extend_noise( |
| lines, |
| noise_model.gate_noise( |
| gate="H", |
| pair_targets=[], |
| idle_targets=[c2i[q] for q in all_qubits], |
| layer_id="stabilizer_h_post", |
| ), |
| ) |
| lines.append("TICK") |
| _extend_noise(lines, noise_model.measurement_noise(qubits=[c2i[q] for q in all_measures], layer_id="stabilizer_meas")) |
| _extend_noise( |
| lines, |
| noise_model.gate_noise( |
| gate="IDLE", |
| pair_targets=[], |
| idle_targets=[c2i[q] for q in datas], |
| layer_id="stabilizer_pre_meas_idle", |
| ), |
| ) |
| lines.append(f"M {index_string(all_measures, c2i)}") |
| lines.append("TICK") |
|
|
| return "\n".join(lines) + "\n" |
|
|
| def initialization_step(distance, p, noise_model: Optional[NoiseModel] = None): |
| noise_model = resolve_noise_model(p, noise_model) |
| datas, x_measures, z_measures, c2i = prepare_coords(distance) |
| all_measures = x_measures + z_measures |
| all_qubits = datas + all_measures |
| |
| |
| |
| lines = [f"R {index_string(all_qubits, c2i)}"] |
| _extend_noise(lines, noise_model.reset_noise(qubits=[c2i[q] for q in all_qubits], layer_id="init_reset")) |
| lines.append("TICK") |
| lines.append(f"H {index_string(x_measures, c2i)}") |
| _extend_noise( |
| lines, |
| noise_model.gate_noise(gate="H", pair_targets=[], idle_targets=[c2i[q] for q in all_qubits], layer_id="init_h_pre"), |
| ) |
| lines.append("TICK") |
|
|
| lines.append(lattice_with_noise(distance, p, noise_model=noise_model).strip()) |
|
|
| lines.append(f"H {index_string(x_measures, c2i)}") |
| _extend_noise( |
| lines, |
| noise_model.gate_noise(gate="H", pair_targets=[], idle_targets=[c2i[q] for q in all_qubits], layer_id="init_h_post"), |
| ) |
| lines.append("TICK") |
| _extend_noise(lines, noise_model.measurement_noise(qubits=[c2i[q] for q in all_measures], layer_id="init_meas")) |
| lines.append(f"M {index_string(all_measures, c2i)}") |
| _extend_noise(lines, noise_model.gate_noise(gate="IDLE", pair_targets=[], idle_targets=[c2i[q] for q in datas], layer_id="init_meas_data_idle")) |
| lines.append("TICK") |
|
|
| for i in range(1, len(z_measures) + 1): |
| lines.append(f"DETECTOR({i}, 0) rec[{-i}]") |
| return "\n".join(lines) + "\n" |
|
|
| def rounds_step(distance, rounds, p, noise_model: Optional[NoiseModel] = None): |
| noise_model = resolve_noise_model(p, noise_model) |
| |
| |
| stim_string = f"" |
| if rounds <= 2: |
| return "\n" |
| datas, x_measures, z_measures, c2i = prepare_coords(distance) |
|
|
| stim_string = f"REPEAT {rounds-2} {{\n" |
| stim_string += stabilizers_with_noise(distance, p, noise_model=noise_model) |
|
|
| num_measures_per_type = len(z_measures) |
| for i in range(1, num_measures_per_type + 1): |
| stim_string += f"DETECTOR({i}, 0) rec[{-i}] rec[{-(i+2*num_measures_per_type)}]\n" |
| for i in range(1, num_measures_per_type + 1): |
| stim_string += f"DETECTOR({i}, 0) rec[{-(i+num_measures_per_type)}] rec[{-(i+3*num_measures_per_type)}]\n" |
|
|
| stim_string += """ |
| } |
| """ |
|
|
| return stim_string |
| |
| def final_step(distance, p, noise_model: Optional[NoiseModel] = None): |
| noise_model = resolve_noise_model(p, noise_model) |
| datas, x_measures, z_measures, c2i = prepare_coords(distance) |
| all_measures = x_measures + z_measures |
| all_qubits = datas + all_measures |
| |
| |
| |
| |
| lines = [f"R {index_string(all_measures, c2i)}"] |
| _extend_noise(lines, noise_model.reset_noise(qubits=[c2i[q] for q in all_measures], layer_id="final_reset")) |
| _extend_noise(lines, noise_model.gate_noise(gate="IDLE", pair_targets=[], idle_targets=[c2i[q] for q in datas], layer_id="final_reset_data_idle")) |
| lines.append("TICK") |
| lines.append(f"H {index_string(x_measures, c2i)}") |
| _extend_noise(lines, noise_model.gate_noise(gate="H", pair_targets=[], idle_targets=[c2i[q] for q in all_qubits], layer_id="final_h_pre")) |
| lines.append("TICK") |
|
|
| lines.append(lattice_with_noise(distance, p, noise_model=noise_model).strip()) |
|
|
| lines.append(f"H {index_string(x_measures, c2i)}") |
| _extend_noise(lines, noise_model.gate_noise(gate="H", pair_targets=[], idle_targets=[c2i[q] for q in all_qubits], layer_id="final_h_post")) |
| lines.append("TICK") |
| _extend_noise(lines, noise_model.measurement_noise(qubits=[c2i[q] for q in all_qubits], layer_id="final_measurement")) |
| lines.append(f"M {index_string(all_qubits, c2i)}") |
| |
| |
| num_measures_per_type = len(z_measures) |
| num_datas = len(datas) |
| for i in range(1, num_measures_per_type + 1): |
| lines.append(f"DETECTOR({i}, 0) rec[{-i}] rec[{-(i+2*num_measures_per_type+num_datas)}]") |
| for i in range(1, num_measures_per_type + 1): |
| lines.append(f"DETECTOR({i}, 0) rec[{-(i+num_measures_per_type)}] rec[{-(i+3*num_measures_per_type+num_datas)}]") |
|
|
| |
| |
| coord_to_record_index = {coord: i - len(all_qubits) for i, coord in enumerate(all_qubits)} |
| for i, measure in enumerate(z_measures): |
| record_indices = [] |
| record_indices.append(coord_to_record_index[measure]) |
| adjacent_datas = adjacent_coords(measure) |
|
|
| for data in adjacent_datas: |
| if data in all_qubits: |
| record_indices.append(coord_to_record_index[data]) |
| recs = [f"rec[{j}]" for j in record_indices] |
| lines.append(f"DETECTOR({i}, 0) {' '.join(recs)}") |
|
|
| obs_recs = [f"rec[{-(i+2*num_measures_per_type)}]" for i in range(1, distance + 1)] |
| lines.append(f"OBSERVABLE_INCLUDE(0) {' '.join(obs_recs)}") |
|
|
| return "\n".join(lines) + "\n" |
|
|
| def surface_code_circuit_string(distance, rounds, p, noise_model: Optional[NoiseModel] = None): |
| noise_model = resolve_noise_model(p, noise_model) |
| string = coord_circuit(distance) |
| string += initialization_step(distance, p, noise_model=noise_model) |
| string += rounds_step(distance, rounds, p, noise_model=noise_model) |
| string += final_step(distance, p, noise_model=noise_model) |
| return string |
|
|