Files
quantum-computing/task11/shor9.py
2025-12-28 15:16:22 +03:00

281 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()