This commit is contained in:
2025-10-07 19:09:08 +03:00
parent a512e5a534
commit 152db4bd80

335
task6/main.py Normal file
View File

@@ -0,0 +1,335 @@
import cirq
import numpy as np
def bitstring(bits):
"""Преобразует список битов в строку, состоящую из символов '0' и '1'.
Аргументы:
bits -- список или массив битов (0 или 1)
Возвращает:
строку, например, '0101101'
"""
return "".join(str(int(b)) for b in bits)
class BB84(object):
def __init__(self, repetitions=1, random_seed=None):
"""
Инициализация протокола BB84.
repetitions -- число повторений измерений (обычно 1, для статистики можно больше)
random_seed -- зерно генератора случайных чисел для воспроизводимости (если None, сгенерируется случайно)
"""
super().__init__()
if random_seed is None:
random_seed = np.random.randint(0, 2**31 - 1) # Генерируем случайное зерно
np.random.seed(random_seed) # Настраиваем генератор случайных чисел
self.repetitions = repetitions # Количество повторов симуляции схемы
self.circuits = {} # Словарь для хранения схем с разными названиями
def setup_demo(self, num_qubits):
"""
Генерация случайных параметров для одной сессии BB84.
Выбираются:
- Базисы Алисы (0 - вычислительный, 1 - базис Адамара)
- Состояния Алисы для каждого кубита (0 или 1)
- Базисы Боба (случайные 0 или 1)
- Базисы Евы (случайные 0 или 1)
Также вычисляется ожидаемый секретный ключ для совпадающих базисов Алисы и Боба
"""
self.alice_basis = [
np.random.randint(0, 2) for _ in range(num_qubits)
] # 0 или 1
self.alice_state = [
np.random.randint(0, 2) for _ in range(num_qubits)
] # 0 или 1
self.bob_basis = [np.random.randint(0, 2) for _ in range(num_qubits)]
self.eve_basis = [np.random.randint(0, 2) for _ in range(num_qubits)]
# Формируем ожидаемый ключ - отбираем биты Алисы там, где базисы совпали с Бобом
self.expected_key = bitstring(
[
self.alice_state[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.bob_basis[i]
]
)
def make_bb84_circuit(
self, num_qubits, sender_basis, receiver_basis, sender_state, name="1"
):
"""
Построение квантовой схемы для протокола BB84.
Параметры:
num_qubits -- число кубитов
sender_basis -- базисы отправителя (0 - вычислительный, 1 - Адамара)
receiver_basis -- базисы получателя (0 или 1)
sender_state -- биты состояния отправителя (0 или 1)
name -- название схемы (ключ в словаре circuits)
Алгоритм:
1) Отправитель подготавливает кубиты:
- Применяет X к кубитам с состоянием 1 (переключение |0> -> |1>)
- Применяет H к кубитам, если базис Адамара (1)
2) Получатель измеряет кубиты:
- Применяет H перед измерением, если базис Адамара (1)
3) Все кубиты измеряются
"""
self.qubits = cirq.LineQubit.range(num_qubits) # Создание линии кубитов
self.circuits[name] = cirq.Circuit() # Инициализация пустой схемы
sender_flips = [] # Операции X для изменения состояния с |0> на |1>
sender_rotations = [] # Операции H для переключения базиса
# Цикл по кубитам на стороне отправителя
for index in range(num_qubits):
if sender_state[index] == 1:
sender_flips.append(
cirq.X(self.qubits[index])
) # Применяем X, если бит 1
if sender_basis[index] == 1:
sender_rotations.append(
cirq.H(self.qubits[index])
) # Применяем H, если выбран базис Адамара
# Добавляем операции отправителя в схему как отдельные "слои" времени (Moments),
# что соответствует выполнению этих операций одновременно независимо для разных кубитов
self.circuits[name].append(cirq.Moment(sender_flips))
self.circuits[name].append(cirq.Moment(sender_rotations))
# Операции получателя - подготовка базиса измерения
recv_basis_choice = []
for index, rbasis in enumerate(receiver_basis):
if rbasis == 1:
recv_basis_choice.append(
cirq.H(self.qubits[index])
) # Применяем H для базиса Адамара перед измерением
self.circuits[name].append(
cirq.Moment(recv_basis_choice)
) # Добавляем перед измерением
self.circuits[name].append(
cirq.Moment(cirq.measure_each(*self.qubits))
) # Измеряем все кубиты
def simulate_base_protocol(self, num_qubits=8, print_legend=None):
"""
Симуляция базового протокола BB84 без присутствия перехватчика (Евы).
- Генерация параметров
- Создание схемы для Алисы и Боба
- Симуляция измерений
- Выделение ключа на основе совпадения базисов
- Печать схемы и результатов
"""
self.setup_demo(num_qubits)
# Строим схему взаимодействия Алисы и Боба
self.make_bb84_circuit(
num_qubits, self.alice_basis, self.bob_basis, self.alice_state, name="base"
)
# Запуск симуляции схемы c заданным числом повторений
result = cirq.Simulator().run(
program=self.circuits["base"], repetitions=self.repetitions
)
# Получение бит результата измерений: для каждого кубита берётся первый результат первого повторения (.item())
result_bitstring = bitstring(
[int(result.measurements[str(q)][0].item()) for q in self.qubits]
)
# Формируем ключ Боба только для тех позиций, где базисы совпали с Алисой
obtained_key = "".join(
[
result_bitstring[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.bob_basis[i]
]
)
# Выводим схему и результаты
print("########## Печать схемы ##########")
print(self.circuits["base"])
print("########## Печать результатов ##########")
self.print_results(
self.alice_basis,
self.bob_basis,
self.alice_state,
self.expected_key,
obtained_key,
)
print(
"Общий секретный ключ успешно распределен и может использоваться для шифрования."
)
def simulate_eavesdropped_protocol(self, num_qubits=8, print_legend=None):
"""
Симуляция протокола BB84 с перехватом.
- Генерация параметров
- Симуляция перехвата Евы между Алисой и Бобом
- Симуляция передачи от Евы к Бобу
- Формирование ключей по разным базисам
- Печать схемы и результатов
"""
self.setup_demo(num_qubits)
# Создаем схему передачи Алиса -> Ева (с их базисами)
self.make_bb84_circuit(
num_qubits,
self.alice_basis,
self.eve_basis,
self.alice_state,
name="alice_eve",
)
# Симуляция измерений Евы
result = cirq.Simulator().run(
program=self.circuits["alice_eve"], repetitions=self.repetitions
)
# Ева сохраняет измеренные биты
eve_state = [int(result.measurements[str(q)][0].item()) for q in self.qubits]
# Ева формирует новую схему для передачи Бобу, используя свои измерения и выбранный базис
self.make_bb84_circuit(
num_qubits, self.eve_basis, self.bob_basis, eve_state, name="eve_bob"
)
# Симуляция измерений Боба
result = cirq.Simulator().run(
program=self.circuits["eve_bob"], repetitions=self.repetitions
)
result_bitstring = bitstring(
[int(result.measurements[str(q)][0].item()) for q in self.qubits]
)
# Формируем ключи для анализа совпадения (Алиса-Ева и Алиса-Боб)
intercepted_key = "".join(
[
result_bitstring[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.eve_basis[i]
]
)
obtained_key = "".join(
[
result_bitstring[i]
for i in range(num_qubits)
if self.alice_basis[i] == self.bob_basis[i]
]
)
# Объединяем цепочки схем Алиса->Ева и Ева->Боб для вывода
circuit = self.circuits["alice_eve"] + self.circuits["eve_bob"]
print("########## Печать схемы ##########")
print(circuit)
print("########## Печать результатов ##########")
self.print_eavesdropped_results(
self.alice_basis,
self.bob_basis,
self.eve_basis,
self.alice_state,
eve_state,
self.expected_key,
obtained_key,
)
print("Ева попыталась подслушать и повторно отправить кубиты.")
print("Это приводит к ошибкам в ключе, которые обнаруживает Боб.")
def print_results(
self, alice_basis, bob_basis, alice_state, expected_key, obtained_key
):
"""
Вывод результатов базового протокола без перехвата.
Печатаются:
- исходное сообщение Алисы,
- базисы Алисы и Боба,
- совпадения базисов (в виде X),
- ключи Алисы и Боба.
"""
num_qubits = len(alice_basis)
basis_match = "".join(
["X" if alice_basis[i] == bob_basis[i] else "_" for i in range(num_qubits)]
)
alice_basis_str = "".join(
["C" if alice_basis[i] == 0 else "H" for i in range(num_qubits)]
)
bob_basis_str = "".join(
["C" if bob_basis[i] == 0 else "H" for i in range(num_qubits)]
)
print(f"Только Алиса знает сообщение:\t{bitstring(alice_state)}")
print("Базисы Алисы и Боба (C - вычислительный, H - Адамара):")
print(f"Алиса: {alice_basis_str}")
print(f"Боб: {bob_basis_str}")
print(f"Совпадение базисов: {basis_match}")
print(f"Ключ Алисы: {expected_key}")
print(f"Ключ Боба: {obtained_key}")
def print_eavesdropped_results(
self,
alice_basis,
bob_basis,
eve_basis,
alice_state,
eve_state,
expected_key,
obtained_key,
):
"""
Вывод результатов протокола с перехватом.
Печатаются:
- исходное сообщение Алисы,
- предположение Евы (ее измеренные биты),
- базисы Алисы, Евы и Боба,
- совпадения базисов,
- ключи Алисы, Евы (ставится равным ключу Алисы, тк она пытается угадать),
и ключ Боба.
"""
num_qubits = len(alice_basis)
eve_basis_match = "".join(
["X" if alice_basis[i] == eve_basis[i] else "_" for i in range(num_qubits)]
)
bob_basis_match = "".join(
["X" if alice_basis[i] == bob_basis[i] else "_" for i in range(num_qubits)]
)
alice_basis_str = "".join(
["C" if alice_basis[i] == 0 else "H" for i in range(num_qubits)]
)
eve_basis_str = "".join(
["C" if eve_basis[i] == 0 else "H" for i in range(num_qubits)]
)
bob_basis_str = "".join(
["C" if bob_basis[i] == 0 else "H" for i in range(num_qubits)]
)
print(f"Только Алиса знает сообщение:\t{bitstring(alice_state)}")
print(f"Предположение Евы о cообщении:\t{bitstring(eve_state)}")
print("Базисы (C - вычислительный, H - Адамара):")
print(f"Алиса: {alice_basis_str}")
print(f"Ева: {eve_basis_str}")
print(f"Боб: {bob_basis_str}")
print(f"Совпадение базисов Алисы и Евы: {eve_basis_match}")
print(f"Совпадение базисов Алисы и Боба: {bob_basis_match}")
print(f"Ключ Алисы: {expected_key}")
print(f"Ключ Евы: {expected_key}") # По идее, Ева пытается угадать ключ Алисы
print(f"Ключ Боба: {obtained_key}")
if __name__ == "__main__":
bb84 = BB84()
print("=== БАЗОВЫЙ ПРОТОКОЛ BB84 ===")
bb84.simulate_base_protocol(num_qubits=8, print_legend=True)
print("\n\n=== ПРОТОКОЛ BB84 С ПЕРЕХВАТОМ ===")
bb84.simulate_eavesdropped_protocol(num_qubits=8, print_legend=False)