Перейти до основного вмісту

Точна симуляція за допомогою примітивів Qiskit SDK

Версії пакетів

Код на цій сторінці розроблено з використанням наведених нижче залежностей. Рекомендуємо використовувати ці або новіші версії.

qiskit[all]~=2.3.0

Референсні примітиви в Qiskit SDK виконують локальну симуляцію вектора стану. Ці симуляції не підтримують моделювання шуму пристрою, але корисні для швидкого прототипування алгоритмів перед переходом до більш просунутих технік симуляції (з використанням Qiskit Aer) або запуску на реальних пристроях (примітиви Qiskit Runtime).

Примітив Estimator може обчислювати очікувані значення схем, а примітив Sampler — виконувати вибірку з вихідних розподілів схем.

У наступних розділах показано, як використовувати референсні примітиви для локального запуску свого робочого процесу.

Використання референсного Estimator

Референсна реалізація EstimatorV2 у qiskit.primitives, що виконується на локальних симуляторах вектора стану, — це клас StatevectorEstimator. Він приймає схеми, спостережувані та параметри як вхідні дані й повертає локально обчислені очікувані значення.

Наступний код готує вхідні дані, які використовуватимуться в прикладах далі. Очікуваний тип вхідних даних для спостережуваних — qiskit.quantum_info.SparsePauliOp. Зауважимо, що схема в прикладі параметризована, але ти також можеш запускати Estimator на непараметризованих схемах.

примітка

Будь-яка схема, що передається до Estimator, не повинна містити вимірювань.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")

Вивід попередньої комірки коду

from qiskit.quantum_info import SparsePauliOp
import numpy as np

# observable(s) whose expected values you want to compute

observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])

# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
Транспіляція до ISA-схем та спостережуваних

Робочий процес примітивів Qiskit Runtime вимагає перетворення схем і спостережуваних так, щоб використовувати лише інструкції, підтримувані QPU (так звані схеми та спостережувані instruction set architecture (ISA)). Референсні примітиви все ще приймають абстрактні інструкції, оскільки покладаються на локальні симуляції вектора стану, але транспіляція схеми може бути корисною з точки зору оптимізації схеми.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

Ініціалізація Estimator

Створи екземпляр qiskit.primitives.StatevectorEstimator.

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

Запуск і отримання результатів

У цьому прикладі використовується лише одна схема (типу QuantumCircuit) та одна спостережувана.

Запусти оцінку, викликавши метод StatevectorEstimator.run, який повертає екземпляр об'єкта PrimitiveJob. Ти можеш отримати результати завдання (як об'єкт qiskit.primitives.PrimitiveResult) за допомогою методу qiskit.primitives.PrimitiveJob.result.

job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>

Отримання очікуваного значення з результату

Результат примітивів виводить масив об'єктів PubResult, де кожен елемент масиву — це об'єкт PubResult, що містить у своїх даних масив оцінок, що відповідають кожній комбінації схема-спостережувана у PUB.

Щоб отримати очікувані значення та метадані для першого (і в цьому випадку єдиного) оцінювання схеми, потрібно звернутися до data для PUB 0:

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
> Expectation value: [4.         3.73205081 2.        ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}

Встановлення параметрів запуску Estimator

За замовчуванням референсний Estimator виконує точне обчислення вектора стану на основі класу quantum_info.Statevector. Проте це можна змінити для введення ефекту накладних витрат вибірки (також відомих як «дробовий шум»).

Estimator приймає аргумент precision, що виражає планки похибки, на які реалізація примітиву має орієнтуватися для оцінок очікуваних значень. Це накладні витрати вибірки, і вони визначаються виключно в методі .run(). Це дозволяє тонко налаштовувати параметр аж до рівня PUB.

# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
[(circuit, observable, parameter_values)], precision=0.05
)

Повний приклад дивись на сторінці Приклади примітивів.

Використання референсного Sampler

Референсна реалізація SamplerV2 у qiskit.primitives — це клас StatevectorSampler. Він приймає схеми та параметри як вхідні дані й повертає результати вибірки з вихідних розподілів ймовірностей у вигляді квазі-ймовірнісного розподілу вихідних станів.

Наступний код готує вхідні дані для прикладів далі. Зауважимо, що ці приклади запускають одну параметризовану схему, але ти також можеш запускати Sampler на непараметризованих схемах.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")

Вивід попередньої комірки коду

примітка

Будь-яка квантова схема, що передається до Sampler, повинна включати вимірювання.

Транспіляція до ISA-схем та спостережуваних

Робочий процес примітивів Qiskit Runtime вимагає перетворення схем так, щоб використовувати лише інструкції, підтримувані QPU (так звані ISA-схеми). Референсні примітиви все ще приймають абстрактні інструкції, оскільки покладаються на локальні симуляції вектора стану, але транспіляція схеми може бути корисною з точки зору оптимізації схеми.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)

Ініціалізація SamplerV2

Створи екземпляр qiskit.primitives.StatevectorSampler:

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

Запуск і отримання результатів

# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

Примітиви приймають декілька PUB як вхідні дані, і кожен PUB отримує власний результат. Таким чином, ти можеш запускати різні схеми з різними комбінаціями параметрів/спостережуваних і отримувати результати PUB:

from qiskit.transpiler import generate_preset_pass_manager

# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()

# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

Отримання розподілу ймовірностей або результату вимірювання

Зразки результатів вимірювань повертаються як бітові рядки або підрахунки. Бітові рядки показують результати вимірювань, зберігаючи порядок знімків у хронологічному порядку вимірювань. Об'єкти результатів Sampler організовують дані за іменами класичних регістрів вхідних схем, для сумісності з динамічними схемами.

примітка

Ім'я класичного регістру за замовчуванням — "meas". Це ім'я буде використано далі для доступу до бітових рядків вимірювань.

# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()
┌───┐      ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'11': 515, '00': 509}

Зміна параметрів запуску

За замовчуванням референсний Sampler виконує точне обчислення вектора стану на основі класу quantum_info.Statevector. Проте це можна змінити для введення ефекту накладних витрат вибірки (також відомих як «дробовий шум»). Для керування цими накладними витратами інтерфейс Sampler приймає аргумент shots, який можна визначити на рівні PUB.

У цьому прикладі передбачається, що ти визначив дві схеми.

# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_circuit2], shots=128)
# Sample two circuits at different amounts of shots. The "None"s are necessary
# as placeholders
# for the lack of parameter values in this example.
sampler.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])
<qiskit.primitives.primitive_job.PrimitiveJob at 0x7fa430e39dd0>

Повний приклад дивись на сторінці Приклади примітивів.

Наступні кроки

Рекомендації