/
timshk
/
Shalaev_Stepik
Обзор
Документация
Войти
/
timshk
/
Shalaev_Stepik
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Task1_6/Task1_6.py
380 строк
18 KB
timshk
Stepik задания
01 июн 2026, 18:58
Верифицирован
01 июн 2026, 18:58
2e75834
Код
Авторство
О чём код?
""" СРАВНИТЕЛЬНЫЙ АНАЛИЗ АЛГОРИТМА ГРОВЕРА Три бэкенда: идеальный симулятор, шумовой симулятор (FakeBackend), реальное устройство IBM Quantum Алгоритм: поиск состояния "10" в базе из 4 элементов (2 кубита) Автор: Студент МАДИ Дисциплина: Квантовые технологии на транспорте """ import numpy as np from qiskit import QuantumCircuit, transpile from qiskit_aer import AerSimulator from qiskit_ibm_runtime import QiskitRuntimeService from qiskit.visualization import plot_histogram import matplotlib.pyplot as plt import os # ============================================================================ # 1. ПАРАМЕТРЫ АЛГОРИТМА # ============================================================================ TARGET_STATE = '01' # вместо '10' # Искомое состояние (помеченный маршрут) N_QUBITS = 2 # Два кубита = 4 состояния SHOTS = 1024 # Количество измерений print("="*70) print("🔍 АЛГОРИТМ ГРОВЕРА: СРАВНЕНИЕ ТРЁХ БЭКЕНДОВ") print("="*70) print(f"Искомое состояние: |{TARGET_STATE}⟩") print(f"Размер базы: {2**N_QUBITS} элементов") print(f"Измерений: {SHOTS}\n") # ============================================================================ # 2. СОЗДАНИЕ ОРАКУЛА ДЛЯ СОСТОЯНИЯ "10" # ============================================================================ def create_oracle(target_state): """ Создаёт оракул для заданного состояния. Учитывает little-endian порядок Qiskit: строка '10' означает q0=0, q1=1. """ oracle = QuantumCircuit(N_QUBITS, name=f"Oracle_{target_state}") n = N_QUBITS # Первая группа X-гейтов (перед CZ) for i in range(n): bit = target_state[n - 1 - i] # инвертируем порядок для little-endian if bit == '0': oracle.x(i) # Контролируемый Z oracle.cz(0, 1) # Вторая группа X-гейтов (после CZ) for i in range(n): bit = target_state[n - 1 - i] if bit == '0': oracle.x(i) return oracle # ============================================================================ # 3. СОЗДАНИЕ ОПЕРАТОРА ДИФФУЗИИ (ОТРАЖЕНИЕ ОТНОСИТЕЛЬНО СРЕДНЕГО) # ============================================================================ def create_diffusion_operator(n_qubits): """ Создаёт оператор диффузии Гровера. Математически: 2|s⟩⟨s| - I, где |s⟩ — равномерная суперпозиция. Физически: H⊗n - X⊗n - MCZ - X⊗n - H⊗n MCZ = много-контролируемый Z (через H-MCX-H) """ diffusion = QuantumCircuit(n_qubits, name="Diffusion") # Шаг 1: Переход в базис X diffusion.h(range(n_qubits)) diffusion.x(range(n_qubits)) # Шаг 2: Много-контролируемый Z (инвертирует фазу |11...1⟩) diffusion.h(n_qubits - 1) diffusion.mcx(list(range(n_qubits - 1)), n_qubits - 1) diffusion.h(n_qubits - 1) # Шаг 3: Возврат в исходный базис diffusion.x(range(n_qubits)) diffusion.h(range(n_qubits)) return diffusion # ============================================================================ # 4. ПОСТРОЕНИЕ ПОЛНОЙ СХЕМЫ ГРОВЕРА # ============================================================================ def build_grover_circuit(): """Собирает полную схему одной итерации алгоритма Гровера.""" circuit = QuantumCircuit(N_QUBITS, N_QUBITS, name="Grover_1_iteration") # Этап 1: Равномерная суперпозиция (|00⟩ → (|00⟩+|01⟩+|10⟩+|11⟩)/2) circuit.h(range(N_QUBITS)) # Этап 2: Оракул — инверсия фазы искомого состояния oracle = create_oracle(TARGET_STATE) circuit.append(oracle, range(N_QUBITS)) # Этап 3: Оператор диффузии — усиление амплитуды diffusion = create_diffusion_operator(N_QUBITS) circuit.append(diffusion, range(N_QUBITS)) # Этап 4: Измерение circuit.measure(range(N_QUBITS), range(N_QUBITS)) return circuit # ============================================================================ # 5. ЗАПУСК НА ИДЕАЛЬНОМ СИМУЛЯТОРЕ (БЕЗ ШУМА) # ============================================================================ def run_on_ideal_simulator(circuit): """Запуск на AerSimulator без шума — идеальный случай.""" print("🟢 ЗАПУСК 1: ИДЕАЛЬНЫЙ СИМУЛЯТОР (AerSimulator без шума)") print("-" * 50) simulator = AerSimulator() compiled = transpile(circuit, simulator) result = simulator.run(compiled, shots=SHOTS).result() counts = result.get_counts() success_count = counts.get(TARGET_STATE, 0) success_prob = (success_count / SHOTS) * 100 print(f" Результаты: {counts}") print(f" Успешных исходов (|{TARGET_STATE}⟩): {success_count} из {SHOTS}") print(f" Вероятность успеха: {success_prob:.1f}%\n") return counts, success_prob, success_count # ============================================================================ # 6. ЗАПУСК НА ШУМОВОМ СИМУЛЯТОРЕ (FAKEBACKEND) # ============================================================================ def run_on_noisy_simulator(circuit): """ Запуск на симуляторе с шумовой моделью реального устройства. Используется FakeSantiagoV2 (5-кубитный чип) — его шумовая модель основана на реальных калибровочных данных IBM Santiago. """ print("🟡 ЗАПУСК 2: ШУМОВОЙ СИМУЛЯТОР (FakeSantiagoV2)") print("-" * 50) try: from qiskit_ibm_runtime.fake_provider import FakeSantiagoV2 # FakeBackend эмулирует: # - Ошибки однокубитных и двухкубитных вентилей (из реальных калибровок) # - Ошибки считывания (readout errors) # - Тепловую релаксацию (T1, T2) — через модель thermal relaxation noisy_backend = FakeSantiagoV2() # Транспиляция под топологию устройства (важно для шумовой модели) compiled = transpile(circuit, noisy_backend) result = noisy_backend.run(compiled, shots=SHOTS).result() counts = result.get_counts() except ImportError: # Fallback: если FakeProvider недоступен, используем ручную модель шума print(" ⚠️ FakeProvider не найден, использую ручную модель деполяризации") from qiskit_aer.noise import NoiseModel, depolarizing_error, ReadoutError noise_model = NoiseModel() # Ошибки однокубитных вентилей (0.1%) depol_1q = depolarizing_error(0.001, 1) noise_model.add_all_qubit_quantum_error(depol_1q, ['u1', 'u2', 'u3', 'h', 'x']) # Ошибки двухкубитных вентилей (1%) depol_2q = depolarizing_error(0.01, 2) noise_model.add_all_qubit_quantum_error(depol_2q, ['cx', 'cz']) # Ошибки считывания (2%) readout_err = ReadoutError([[0.98, 0.02], [0.02, 0.98]]) # 2% вероятность ошибки for i in range(N_QUBITS): noise_model.add_readout_error(readout_err, [i]) simulator = AerSimulator(noise_model=noise_model) compiled = transpile(circuit, simulator) result = simulator.run(compiled, shots=SHOTS).result() counts = result.get_counts() success_count = counts.get(TARGET_STATE, 0) success_prob = (success_count / SHOTS) * 100 print(f" Результаты: {counts}") print(f" Успешных исходов (|{TARGET_STATE}⟩): {success_count} из {SHOTS}") print(f" Вероятность успеха: {success_prob:.1f}%\n") return counts, success_prob, success_count # ============================================================================ # 7. ЗАПУСК НА РЕАЛЬНОМ КВАНТОВОМ ПРОЦЕССОРЕ (ЧЕРЕЗ IBM QUANTUM) # ============================================================================ def run_on_real_device(circuit): """ Запуск на наименее загруженном реальном устройстве IBM Quantum. Требует предварительной аутентификации через QiskitRuntimeService. """ print("🔴 ЗАПУСК 3: РЕАЛЬНОЕ УСТРОЙСТВО IBM QUANTUM") print("-" * 50) try: # Попытка аутентификации # Вариант 1: через переменную окружения token = os.environ.get("IBM_QUANTUM_TOKEN") if token: service = QiskitRuntimeService(channel="ibm_quantum", token=token) else: # Вариант 2: через сохранённую учётную запись service = QiskitRuntimeService() # Поиск наименее загруженного реального устройства с ≥2 кубитов backends = service.backends( filters=lambda b: not b.configuration().simulator and b.configuration().n_qubits >= 2 and b.status().operational ) if not backends: raise RuntimeError("Нет доступных реальных устройств") # Сортируем по длине очереди (наименее загруженное — первое) backend = sorted(backends, key=lambda b: b.status().pending_jobs)[0] print(f" Выбрано устройство: {backend.name}") print(f" Кубитов: {backend.configuration().n_qubits}") print(f" Очередь заданий: {backend.status().pending_jobs}") # Транспиляция под конкретное устройство (учёт топологии связей) compiled = transpile(circuit, backend, optimization_level=1) # Отправка задания print(" Отправка задания...") job = backend.run(compiled, shots=SHOTS) print(f" ID задания: {job.job_id()}") print(" Ожидание результата (может занять несколько минут)...") result = job.result() counts = result.get_counts() except Exception as e: print(f" ❌ Не удалось выполнить запуск на реальном устройстве: {e}") print(" Причины: отсутствует API-токен, нет доступа к IBM Quantum,") print(" или проблемы с сетью/аутентификацией.") print(" Выполняю только два первых сравнения.\n") return None, None, None success_count = counts.get(TARGET_STATE, 0) success_prob = (success_count / SHOTS) * 100 print(f"\n Результаты: {counts}") print(f" Успешных исходов (|{TARGET_STATE}⟩): {success_count} из {SHOTS}") print(f" Вероятность успеха: {success_prob:.1f}%\n") return counts, success_prob, success_count # ============================================================================ # 8. ВЫВОД СВОДНОЙ ТАБЛИЦЫ # ============================================================================ def print_summary_table(results): """Выводит сводную таблицу результатов сравнения.""" print("\n" + "="*70) print("📊 СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ") print("="*70) print(f"{'Бэкенд':<30} {'Успехов':<12} {'Всего':<10} {'Вероятность':<12}") print("-" * 70) for name, (success, total, prob) in results.items(): if success is not None: print(f"{name:<30} {success:<12} {total:<10} {prob:.1f}%") else: print(f"{name:<30} {'—':<12} {total:<10} {'—':<12}") # ============================================================================ # 9. ВИЗУАЛИЗАЦИЯ СРАВНИТЕЛЬНОЙ ГИСТОГРАММЫ # ============================================================================ def visualize_comparison(results_dict): """ Строит сравнительные гистограммы для трёх запусков. Args: results_dict: словарь вида {'название': (counts, success_prob, success_count)} """ # Фильтруем только успешные запуски (где есть counts) valid_results = {name: data[0] for name, data in results_dict.items() if data[0] is not None} if not valid_results: print("Нет данных для визуализации") return # Создаём подграфики (по одному на каждый бэкенд) fig, axes = plt.subplots(1, len(valid_results), figsize=(5*len(valid_results), 4)) # Если только один подграфик, axes не является массивом if len(valid_results) == 1: axes = [axes] for ax, (name, counts) in zip(axes, valid_results.items()): plot_histogram(counts, ax=ax, title=name, color='#4CAF50' if 'идеальный' in name else '#FF9800' if 'шумовой' in name else '#F44336') ax.set_ylabel('Частота') ax.set_xlabel('Состояние') plt.suptitle(f'Сравнение алгоритма Гровера: поиск состояния |{TARGET_STATE}⟩', fontsize=14) plt.tight_layout() plt.show() # ============================================================================ # 10. ОСНОВНАЯ ФУНКЦИЯ # ============================================================================ def main(): # Построение схемы circuit = build_grover_circuit() print("📐 КВАНТОВАЯ СХЕМА ГРОВЕРА (одна итерация):") print(circuit.draw(output='text')) print("\n" + "="*70 + "\n") # Словарь для хранения результатов results = {} # 1. Идеальный симулятор counts_ideal, prob_ideal, success_ideal = run_on_ideal_simulator(circuit) results["Идеальный симулятор (Aer)"] = (success_ideal, SHOTS, prob_ideal) # 2. Шумовой симулятор counts_noisy, prob_noisy, success_noisy = run_on_noisy_simulator(circuit) results["Шумовой симулятор (FakeBackend)"] = (success_noisy, SHOTS, prob_noisy) # 3. Реальное устройство (с обработкой ошибок) counts_real, prob_real, success_real = run_on_real_device(circuit) results["Реальное устройство (IBM Quantum)"] = (success_real, SHOTS, prob_real if prob_real is not None else 0) # Сводная таблица print_summary_table(results) # Сбор данных для визуализации viz_data = { "Идеальный симулятор": (counts_ideal, prob_ideal, success_ideal), "Шумовой симулятор": (counts_noisy, prob_noisy, success_noisy), } if counts_real is not None: viz_data["Реальное устройство"] = (counts_real, prob_real, success_real) # Визуализация visualize_comparison(viz_data) # Итоговый вывод print("\n" + "="*70) print("📌 ВЫВОДЫ") print("="*70) print(f"• Идеальный симулятор: вероятность успеха {prob_ideal:.1f}% — эталон") print(f"• Шумовой симулятор: вероятность успеха {prob_noisy:.1f}%") if counts_real is not None: degradation_ideal = prob_ideal - prob_real degradation_noisy = prob_noisy - prob_real print(f"• Реальное устройство: вероятность успеха {prob_real:.1f}%") print(f"\n Снижение относительно идеала: {degradation_ideal:.1f}%") print(f" Снижение относительно шумового симулятора: {degradation_noisy:.1f}%") print("\n Причина: дополнительные шумы реального устройства,") print(" которые не полностью моделируются FakeBackend,") print(" а также дрейф калибровки и текущее состояние кубитов.") else: print("• Реальное устройство: запуск не выполнен (нет аутентификации)") print("\n✅ АНАЛИЗ ЗАВЕРШЁН") if __name__ == "__main__": main()