Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Швидкий старт

Цей посібник демонструє мінімальний робочий приклад пакета qiskit-addon-sqd. Ми використовуємо вибіркову квантову діагоналізацію (SQD), щоб уточнити зашумлені вибірки, отримані з квантового circuit, який наближає основний стан Гамільтоніана хімічної системи. Енергії основного стану наближуються діагоналізацією Гамільтоніана в підпросторах, утворених уточненими вибірками.

Щоб побачити докладніші наскрізні робочі процеси, які виконуються на квантовому обладнанні, ознайомся з навчальними посібниками:

1. Підготуй вхідні дані для SQD​

Спочатку ми визначаємо Гамільтоніан хімічної системи та квантовий circuit, який наближає його основний стан. Система, що досліджується в цьому прикладі, це N₂ у рівноважній геометрії в базисі STO-3G із двома замороженими остовними орбіталями. Ця система має 8 просторових орбіталей і 10 електронів, а це означає, що квантовий circuit визначено на 16 кубітах, де кубіти 0-7 представляють орбіталі зі спіном угору, а кубіти 8-15 представляють орбіталі зі спіном униз. Оскільки це система із закритою оболонкою та 10 електронами, ми знаємо, що кожна фізично допустима вибірка має мати вагу Геммінга 10, причому вага Геммінга як лівої, так і правої половини бітового рядка дорівнює 5.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "pyscf": "pyscf", "qiskit_addon_sqd": "qiskit-addon-sqd"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from pyscf import ao2mo, gto, mcscf, scf

# Construct the Hamiltonian, pull out integrals and other information about the system
mol = gto.M(
atom="N 0 0 0; N 0 0 1.09768", basis="sto-3g", spin=0, charge=0, verbose=0
)
mf = scf.RHF(mol).run()
cas = mcscf.CASCI(mf, ncas=8, nelecas=10)
hcore, nuclear_repulsion_energy = cas.get_h1eff()
eri = cas.get_h2eff()
num_orbitals = cas.ncas
eri = ao2mo.restore(1, eri, num_orbitals)
n_alpha, n_beta = cas.nelecas
nelec = (n_alpha, n_beta)

print(
f"Spatial orbitals: {num_orbitals}\nQubits: {num_orbitals * 2}\nElectrons (alpha, beta): {nelec}"
)
Spatial orbitals: 8
Qubits: 16
Electrons (alpha, beta): (5, 5)

2. Згенеруй зашумлені вибірки​

У реальному робочому процесі ти б розробив ansatz circuit, оптимізував його для обладнання та отримав би з нього вибірки на QPU. Тут ми це пропускаємо й натомість беремо 10,000 рівномірно випадкових бітових рядків. Хоча наші вибірки не містять жодної інформації про справжній основний стан, ми побачимо, що протокол SQD все одно може відновити справжній основний стан, ітеративно уточнюючи ці вибірки, діагоналізуючи та використовуючи енергії на кожній ітерації для керування наступним раундом уточнення бітових рядків. Ми зрештою можемо відновити точний основний стан таким чином, бо підпростір, що містить основний стан, достатньо малий, щоб обчислити його точно. Зокрема, оскільки ми знаємо, що кожна половина кожного бітового рядка (8 бітів) має мати вагу Геммінга 5, ми знаємо, що повний підпростір Гільберта системи має розмір (85)2=3136\binom{8}{5}^2=3136.

import numpy as np
from qiskit_addon_sqd.counts import generate_bit_array_uniform

# Generate some uniformly-random bitstrings to simulate QPU samples
rng = np.random.default_rng(24)
bit_array = generate_bit_array_uniform(
10_000, num_orbitals * 2, rand_seed=rng
)
print(
f"Generated {bit_array.num_shots} uniformly-random, {bit_array.num_bits}-qubit samples."
)
Generated 10000 uniformly-random, 16-qubit samples.

3. Постобробка за допомогою SQD​

Мета алгоритму SQD полягає в тому, щоб уточнити зашумлені вибірки, отримані з QPU, і зрештою діагоналізувати Гамільтоніан у підпросторі, натягнутому на ці уточнені вибірки. Це робиться ітеративно таким чином:

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

  • Діагоналізуй у малих підпросторах, натягнутих на уточнені вибірки, і збери інформацію про підпростори, що дають найкращі (найнижчі) енергії.

  • Для кожної ітерації:

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

    • Діагоналізуй у малих підпросторах, натягнутих на уточнені вибірки

    • Збери інформацію про підпростори, що дають найкращі (найнижчі) енергії

З виводу видно, що алгоритм збігається до точної енергії основного стану після 6 ітерацій і що діагоналізація виконується в повному підпросторі Гільберта розмірності (85)2=3136\binom{8}{5}^2=3136. Звісно, діагоналізація в повному підпросторі зазвичай нездійсненна, тому системи, основні стани яких мають розріджену підтримку, найімовірніше виграють від технік діагоналізації в підпросторі, таких як SQD.

Щоб дізнатися більше про функцію diagonalize_fermionic_hamiltonian, відвідай документацію API.

from functools import partial

from pyscf import fci
from qiskit_addon_sqd.fermion import (
SCIResult,
diagonalize_fermionic_hamiltonian,
solve_sci_batch,
)

exact_energy, _ = fci.direct_spin1.kernel(
hcore, eri, int(num_orbitals), nelec
)
exact_energy += nuclear_repulsion_energy
print(f"Exact (FCI) reference energy: {exact_energy:.6f} Ha\n")

sci_solver = partial(solve_sci_batch, spin_sq=0.0, max_cycle=200)

result_history = []

def callback(results: list[SCIResult]):
result_history.append(results)
iteration = len(result_history)
print(f"Iteration {iteration}")
for i, result in enumerate(results):
error = result.energy + nuclear_repulsion_energy - exact_energy
print(f" Subsample {i}")
print(f" Energy: {result.energy + nuclear_repulsion_energy:.6f}")
print(
f" Subspace dimension: {np.prod(result.sci_state.amplitudes.shape)}"
)
print(f" Error vs exact: {error:.6f} Ha")

samples_per_batch = 20
result = diagonalize_fermionic_hamiltonian(
hcore,
eri,
bit_array,
samples_per_batch=samples_per_batch,
norb=num_orbitals,
nelec=nelec,
occupancies_tol=1e-7,
max_iterations=30,
sci_solver=sci_solver,
symmetrize_spin=True,
callback=callback,
seed=np.random.default_rng(32),
)
Exact (FCI) reference energy: -107.652521 Ha

Iteration 1
Subsample 0
Energy: -106.908481
Subspace dimension: 784
Error vs exact: 0.744040 Ha
Iteration 2
Subsample 0
Energy: -106.934921
Subspace dimension: 1764
Error vs exact: 0.717600 Ha
Iteration 3
Subsample 0
Energy: -106.953430
Subspace dimension: 2209
Error vs exact: 0.699091 Ha
Iteration 4
Subsample 0
Energy: -107.651946
Subspace dimension: 2916
Error vs exact: 0.000575 Ha
Iteration 5
Subsample 0
Energy: -107.652003
Subspace dimension: 3025
Error vs exact: 0.000518 Ha
Iteration 6
Subsample 0
Energy: -107.652521
Subspace dimension: 3136
Error vs exact: 0.000000 Ha
Iteration 7
Subsample 0
Energy: -107.652521
Subspace dimension: 3136
Error vs exact: 0.000000 Ha