diff --git a/task11/shor9.py b/task11/shor9.py new file mode 100644 index 0000000..d21d734 --- /dev/null +++ b/task11/shor9.py @@ -0,0 +1,280 @@ +import concurrent.futures +import random +from typing import List, Tuple + +import cirq +import numpy as np + + +def build_shor9_circuit( + p_error: float, with_correction: bool = True +) -> Tuple[cirq.Circuit, List[cirq.Qid]]: + """Построение схемы кода Шора на 9 кубитах.""" + qubits = [cirq.LineQubit(i) for i in range(9)] + circuit = cirq.Circuit() + + # Подготовка исходного состояния логического кубита + circuit.append(cirq.rx(np.pi / 5)(qubits[0])) + + # ----- Блок кодирования ----- + # S1: CNOT 0->3 + circuit.append(cirq.CNOT(qubits[0], qubits[3])) + # S2: CNOT 0->6 + circuit.append(cirq.CNOT(qubits[0], qubits[6])) + # S3: H на 0, 3, 6 + circuit.append(cirq.H.on_each(qubits[0], qubits[3], qubits[6])) + # S4: CNOT внутри блоков + circuit.append(cirq.CNOT(qubits[0], qubits[1])) + circuit.append(cirq.CNOT(qubits[3], qubits[4])) + circuit.append(cirq.CNOT(qubits[6], qubits[7])) + # S5: еще CNOT внутри блоков + circuit.append(cirq.CNOT(qubits[0], qubits[2])) + circuit.append(cirq.CNOT(qubits[3], qubits[5])) + circuit.append(cirq.CNOT(qubits[6], qubits[8])) + + # ----- Блок шума ----- + # ВАЖНО: как в оригинале - по одному кубиту проверяем вероятность ошибки + error_count = 0 + error_idx = None + error_type = None + + for idx in range(9): + if random.random() <= p_error: + error_count += 1 + if error_count > 1: + break # Более одной ошибки - сразу неудача + error_idx = idx + error_type = "X" if random.random() > 0.5 else "Z" + + if error_count == 1: + if error_type == "X": + circuit.append(cirq.X(qubits[error_idx])) + else: + circuit.append(cirq.Z(qubits[error_idx])) + + # ----- Блок декодирования и коррекции ----- + if with_correction and error_count <= 1: + # S6: обратное S4 + circuit.append(cirq.CNOT(qubits[0], qubits[1])) + circuit.append(cirq.CNOT(qubits[3], qubits[4])) + circuit.append(cirq.CNOT(qubits[6], qubits[7])) + # S7: обратное S5 + circuit.append(cirq.CNOT(qubits[0], qubits[2])) + circuit.append(cirq.CNOT(qubits[3], qubits[5])) + circuit.append(cirq.CNOT(qubits[6], qubits[8])) + # S8: Toffoli для коррекции + circuit.append(cirq.TOFFOLI(qubits[1], qubits[2], qubits[0])) + circuit.append(cirq.TOFFOLI(qubits[4], qubits[5], qubits[3])) + circuit.append(cirq.TOFFOLI(qubits[7], qubits[8], qubits[6])) + # S9: обратное S3 + circuit.append(cirq.H.on_each(qubits[0], qubits[3], qubits[6])) + # S10: обратное S1 + circuit.append(cirq.CNOT(qubits[0], qubits[3])) + # S11: обратное S2 + circuit.append(cirq.CNOT(qubits[0], qubits[6])) + # S12: Toffoli между блоками + circuit.append(cirq.TOFFOLI(qubits[3], qubits[6], qubits[0])) + + return circuit, qubits, error_count + + +def shor9(P: float = 0.05, debug: bool = False) -> Tuple[bool, int]: + """Один прогон кода Шора - точно как в оригинале.""" + simulator = cirq.Simulator() + + # Сохраняем начальное состояние кубита 0 + # Создаем схему только с подготовкой состояния + prep_circuit = cirq.Circuit(cirq.rx(np.pi / 5)(cirq.LineQubit(0))) + prep_result = simulator.simulate(prep_circuit) + + # Получаем начальные амплитуды + initial_state = prep_result.final_state_vector + initial_0 = np.abs(initial_state[0]) ** 2 + initial_1 = np.abs(initial_state[1]) ** 2 + + # Строим полную схему Шора + circuit, qubits, error_count = build_shor9_circuit(P, with_correction=True) + + # Если более одной ошибки - сразу неудача (как в оригинале) + if error_count > 1: + return False, error_count + + result = simulator.simulate(circuit) + final_state = result.final_state_vector + + # Получаем амплитуды для кубита 0 + # Вектор состояния имеет размер 2^9 = 512 + # Индексы где q0=0: 0-255, где q0=1: 256-511 + + prob_0 = 0.0 + prob_1 = 0.0 + + for i in range(512): + amp = final_state[i] + prob = np.abs(amp) ** 2 + if i < 256: # q0 = 0 + prob_0 += prob + else: # q0 = 1 + prob_1 += prob + + # Проверяем как в оригинале: np.isclose с tolerance 0.01 + success = np.isclose(prob_0, initial_0, atol=0.01) and np.isclose( + prob_1, initial_1, atol=0.01 + ) + + return success, error_count + + +def no_correction(P: float = 0.05, debug: bool = False) -> Tuple[bool, int]: + """Модель без коррекции ошибок - точно как в оригинале.""" + q = cirq.LineQubit(0) + + # Сохраняем начальное состояние + prep_circuit = cirq.Circuit(cirq.rx(np.pi / 5)(q)) + simulator = cirq.Simulator() + prep_result = simulator.simulate(prep_circuit) + initial_state = prep_result.final_state_vector + initial_0 = np.abs(initial_state[0]) ** 2 + initial_1 = np.abs(initial_state[1]) ** 2 + + # Создаем схему с возможной ошибкой + circuit = cirq.Circuit(cirq.rx(np.pi / 5)(q)) + + # Применяем ошибку с вероятностью P + error_count = 0 + if random.random() <= P: + error_count += 1 + circuit.append(cirq.X(q)) + + result = simulator.simulate(circuit) + final_state = result.final_state_vector + + final_0 = np.abs(final_state[0]) ** 2 + final_1 = np.abs(final_state[1]) ** 2 + + # Проверяем как в оригинале, но с tolerance 0.0001 + success = np.isclose(final_0, initial_0, atol=0.0001) and np.isclose( + final_1, initial_1, atol=0.0001 + ) + + return success, error_count + + +def compute_failure_probability( + P: float, total_rounds: int = 500 +) -> Tuple[float, float, float]: + """Оценка вероятности сбоя для заданного P - точно как в оригинале.""" + failure_count = 0 + + # Если P == 0, то вероятность ошибки = 0 (как в оригинале) + if P == 0: + return P, 0.0, 0.0 + + for _ in range(total_rounds): + correct, _ = shor9(P=P) + if not correct: + failure_count += 1 + + p_e = failure_count / total_rounds + p_e_nc = P # Как в оригинале: для схемы без коррекции вероятность ошибки = P + + return P, p_e, p_e_nc + + +def parallel_compute_failure_probability( + p_values: List[float], total_rounds: int = 500 +): + """Параллельный запуск оценки.""" + results = [] + with concurrent.futures.ProcessPoolExecutor() as executor: + results = list( + executor.map( + compute_failure_probability, p_values, [total_rounds] * len(p_values) + ) + ) + return results + + +def theory(p: float) -> float: + """Теоретическая оценка вероятности ошибки для кода Шора.""" + return 1 - (1 + 8 * p) * (1 - p) ** 8 + + +def shor9_withplot(): + """Построение графика.""" + import matplotlib.pyplot as plt + + p = [0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1] + p_e = [] + p_e_nc = [] + total_rounds = 100 + + print("Запуск моделирования...") + results = parallel_compute_failure_probability(p, total_rounds) + + for P, p_e_value, p_e_nc_value in results: + p_e.append(p_e_value) + p_e_nc.append(p_e_nc_value) + print(f"P={P:.2f}: P_e={p_e_value:.4f} || P_e_nc={p_e_nc_value:.4f}") + + # Построение графика + plt.figure(figsize=(10, 6)) + + # Экспериментальные данные + plt.plot( + p, + p_e, + marker="o", + label="С коррекцией (Шор)", + linestyle="-", + color="b", + alpha=0.7, + ) + plt.plot( + p, + p_e_nc, + marker="s", + label="Без коррекции", + linestyle="-", + color="r", + alpha=0.7, + ) + + # Теоретическая кривая + p_values = np.linspace(0, max(p), 100) + theory_values = [theory(x) for x in p_values] + plt.plot( + p_values, + theory_values, + label="Теория (Шор)", + color="green", + linestyle="--", + linewidth=2.0, + ) + + plt.xlabel("Вероятность ошибки P") + plt.ylabel("Вероятность логической ошибки P_e") + plt.title("Зависимость вероятности ошибки от P") + plt.grid(True, alpha=0.3) + plt.legend() + plt.tight_layout() + + # Сохраняем график + plt.savefig("shor_code_cirq.pdf", format="pdf", bbox_inches="tight") + plt.show() + + +if __name__ == "__main__": + # Быстрый тест + print("Тестирование кода Шора...") + for P_test in [0, 0.05, 0.1]: + success, errors = shor9(P=P_test) + print(f"P={P_test:.2f}: Успех={success}, Ошибок={errors}") + + print("\nТестирование без коррекции...") + for P_test in [0, 0.05, 0.1]: + success, errors = no_correction(P=P_test) + print(f"P={P_test:.2f}: Успех={success}, Ошибок={errors}") + + # Основное моделирование + shor9_withplot()