Вступ до Qiskit
У цьому ноутбуці ми розглянемо, як програмувати квантові вентилі та квантові Circuit за допомогою Qiskit, а також як запускати їх на симуляторах і реальних квантових комп'ютерах за допомогою Qiskit patterns. Пізніше ми познайомимось із різними способами кодування інформації та завершимо бонусним прикладом квантової телепортації.
Перш ніж почати
Дотримуйся інструкцій зі встановлення та налаштування, якщо ти ще цього не зробив(-ла), включно з кроками для налаштування роботи з IBM Quantum™ Platform.
Рекомендується використовувати середовище розробки Jupyter для взаємодії з квантовими комп'ютерами. Обов'язково встанови рекомендовану додаткову підтримку візуалізації ('qiskit[visualization]'). Також знадобиться пакет matplotlib для другої частини цього прикладу.
Щоб дізнатися про квантові обчислення загалом, відвідай курс «Основи квантової інформації» в IBM Quantum Learning
Імпорти
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
# Import necessary modules for this notebook
import time
import qiskit
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector, plot_state_qsphere
from qiskit_aer import AerSimulator
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.visualization import plot_histogram
print(qiskit.__version__)
2.3.1
Щоб запускати квантові Circuit на апаратному забезпеченні, спочатку потрібно налаштувати свій акаунт. Це можна зробити так:
- Перейди на оновлену IBM Quantum® Platform.
- Перейди у верхній правий кут (як показано на малюнку вище), створи свій API-токен і скопіюй його у надійне місце.
- У наступній комірці заміни
deleteThisAndPasteYourAPIKeyHereсвоїм API-ключем. - Перейди у нижній лівий кут (як показано на малюнку вище) та створи свій екземпляр. Переконайся, що вибрав(-ла) відкритий план.
- Після створення екземпляра скопіюй його CRN-код. Можливо, потрібно оновити сторінку, щоб побачити екземпляр.
- У комірці нижче заміни
deleteThisAndPasteYourCRNHereсвоїм CRN-кодом.
Докладніше про налаштування акаунту IBM Cloud® дивись у цьому посібнику.
⚠️ Примітка: Постався до свого API-ключа як до надійного пароля. Дивись посібник Cloud setup для отримання докладнішої інформації про використання API-ключа у захищених і ненадійних середовищах.
#your_api_key = "deleteThisAndPasteYourAPIKeyHere"
#your_crn = "deleteThisAndPasteYourCRNHere"
QiskitRuntimeService.save_account(
channel="ibm_quantum_platform",
token=your_api_key,
instance=your_crn,
overwrite=True
)
1. Квантові Gate та квантові Circuit
Квантові Circuit — це моделі квантових обчислень, у яких обчислення є послідовністю квантових Gate. Розглянемо деякі з найпопулярніших квантових Gate.
X Gate
X Gate відповідає обертанню навколо осі X сфери Блоха на радіан. Він відображає в , а в . Це квантовий еквівалент Gate NOT для класичних комп'ютерів, який іноді називають «перекидачем бітів» (bit-flip).
# Let's apply an X-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.draw(output='mpl')
# Let's see Bloch sphere visualization
sv = Statevector(qc)
plot_bloch_multivector(sv)

H Gate
Gate Адамара (H Gate) являє собою обертання на навколо осі, що знаходиться посередині між осями і .
Він відображає базисний стан у , що означає, що вимірювання матиме рівні ймовірності отримати 1 або 0, створюючи «суперпозицію» станів. Цей стан також записується як .
# Let's apply an H-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.h(0)
qc.draw(output='mpl')
# Let's see Bloch sphere visualization
sv = Statevector(qc)
plot_bloch_multivector(sv)

CX Gate (CNOT Gate)
Gate керованого NOT (або CNOT, або CX) діє на два Qubit. Він виконує операцію NOT (еквівалентну застосуванню X Gate) на другому Qubit лише тоді, коли перший Qubit перебуває у стані , в іншому випадку залишаючи його без змін. Примітка: Qiskit нумерує біти в рядку справа наліво.
# Let's apply a CX-gate on |11>
qc = QuantumCircuit(2)
qc.x(0)
qc.x(1)
qc.cx(0,1)
qc.draw(output='mpl')
sv=Statevector(qc)
plot_state_qsphere(sv)

Створи перший стан Белла
# Create a Bell state circuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
# Draw the circuit
qc.draw("mpl")
# Plot the state using q-sphere visualization
sv = Statevector(qc)
plot_state_qsphere(sv)
# q-sphere is useful for visualizing states when Bloch sphere fails to

Створи другий стан Белла
# Create a circuit with the second Bell state
qc = QuantumCircuit(2)
qc.x(0)
qc.h(0)
qc.cx(0,1)
qc.draw("mpl")
Пояснення полягає в тому, що:
# Get the statevector of the circuit
sv = Statevector(qc)
# Plot the state using qsphere visualization
plot_state_qsphere(sv)

Створи тривимірний стан GHZ
# Create a circuit with 3-qubit GHZ state
qc= QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
qc.draw("mpl")
# Get the statevector of the circuit
sv = Statevector(qc)
# Plot the state using qsphere visualization
plot_state_qsphere(sv)

Створи стан логотипу Qiskit
# Create a circuit with the Qiskit logo state
qc = QuantumCircuit(4)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
qc.cx(0,3)
qc.x(1)
# Draw the circuit
qc.draw("mpl")
# Get the statevector of the circuit
sv = Statevector(qc)
# Plot the state using qsphere visualization
plot_state_qsphere(sv)

2. Створи і запусти просту квантову програму
Чотири кроки для написання квантової програми за допомогою Qiskit patterns:
-
Відобрази задачу у квантово-нативний формат.
-
Оптимізуй Circuit та оператори.
-
Виконай, використовуючи квантову примітивну функцію.
-
Проаналізуй результати.
2.1 Відображення задачі у квантово-нативний формат
У квантовій програмі квантові схеми (Circuit) є нативним форматом для представлення квантових інструкцій, а оператори — це спостережувані величини, які потрібно виміряти. Під час створення схеми (Circuit) ти зазвичай створюєш нови й об'єкт QuantumCircuit, а потім послідовно додаєш до нього інструкції.
Наступна комірка коду створює схему (Circuit), яка породжує стан GHZ — стан, у якому три кубіти (Qubit) повністю заплутані між собою.
Qiskit SDK використовує нумерацію бітів LSb 0, де розряд має значення або . Докладніше дивись у темі Порядок бітів у Qiskit SDK.
# Create a GHZ state circuit
qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
# Draw the circuit
qc.draw("mpl")
Перелік усіх доступних операцій дивись у документації QuantumCircuit.
Під час створення квантових схем (Circuit) потрібно також врахувати, який тип даних ти хочеш отримати після виконання. Qiskit надає два способи повернення даних: можна отримати розподіл імовірностей для набору кубітів (Qubit), які ти вибираєш для вимірювання, або отримати значення очікування спостережуваної величини. Підготуй своє завдання для вимірювання схеми (Circuit) одним із цих двох способів за допомогою примітивів Qiskit (детально пояснених у Кроці 3).
У цьому прикладі вимірюються значення очікування з використанням підмодуля qiskit.quantum_info, що задається за допомогою операторів (математичних об'єктів, які представляють дію або процес, що змінює квантовий стан). Наступна комірка коду створює шість тривимірних операторів Паулі: ZZZ, ZZX, ZII, XXI, ZZI та III.
# Set up six different observables.
observables_labels = ["ZZZ", "ZZX", "ZII", "XXI", "ZZI", "III"]
observables = [SparsePauliOp(label) for label in observables_labels]
print(observables)
[SparsePauliOp(['ZZZ'],
coeffs=[1.+0.j]), SparsePauliOp(['ZZX'],
coeffs=[1.+0.j]), SparsePauliOp(['ZII'],
coeffs=[1.+0.j]), SparsePauliOp(['XXI'],
coeffs=[1.+0.j]), SparsePauliOp(['ZZI'],
coeffs=[1.+0.j]), SparsePauliOp(['III'],
coeffs=[1.+0.j])]
Тут щось на кшталт оператора ZZI є скороченням для тензорного добутку , що означає вимірювання Z на кубіті (Qubit) 2 і Z на кубіті (Qubit) 1 разом та отримання інформації про кореляцію між кубітом (Qubit) 2 і кубітом (Qubit) 1. Значення очікування такого виду зазвичай також записуються як .
Якщо спостережуваний стан є тривимірним станом GHZ, то вимірювання має дорівнювати 1.
2.2 Оптимізація схем (Circuit) та операторів
Під час виконання схем (Circuit) на пристрої важливо оптимізувати набір інструкцій, що містить схема (Circuit), та мінімізувати загальну глибину (приблизно кількість інструкцій) схеми (Circuit). Це гарантує отримання найкращих можливих результатів шляхом зменшення впливу помилок і шуму. Крім того, інструкції схеми (Circuit) повинні відповідати набору інструкцій (ISA) Backend-пристрою та враховувати базові вентилі (Gate) і зв'язність кубітів (Qubit) пристрою.
Наступний код створює екземпляр реального пристрою для надсилання завдання та перетворює схему (Circuit) і спостережувані величини відповідно до ISA цього Backend. Якщо ти раніше не зберігав облікові дані, виконай інструкції тут, щоб пройти автентифікацію за допомогою свого API-токена.
# Choose a real backend
service = QiskitRuntimeService(channel='ibm_quantum_platform',)
backend = service.least_busy(min_num_qubits=156)
# print backend details
print(
f"Name: {backend.name}\n"
f"Version: {backend.backend_version}\n"
f"No. of qubits: {backend.num_qubits}\n"
f"Processor type: {backend.processor_type}\n"
)
Name: ibm_marrakesh
Version: 1.0.21
No. of qubits: 156
Processor type: {'family': 'Heron', 'revision': '2'}
# option to use the AerSimulator instead of a real quantum device
seed_sim=42
backend=AerSimulator.from_backend(backend,seed_simulator=seed_sim)
Транспілюй схему (Circuit) в ISA-схему (Circuit)
# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
isa_circuit = pm.run(qc)
isa_circuit.draw("mpl", idle_wires=False)

mapped_observables = [
observable.apply_layout(isa_circuit.layout) for observable in observables
]
print(mapped_observables)
[SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIXIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIXIIIIIIIIIIIIIIIIIIXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])]