빠른 시작
이 가이드는 qiskit-addon-sqd 패키지의 최소 동작 예제를 보여 줍니다. 샘플 기반 양자 대각화(SQD)를 사용하여 화학 해밀토니안의 바닥 상태를 근사하는 양자 회로에서 얻은 노이즈가 있는 샘플을 정제합니다. 바닥 상태 에너지는 정제된 샘플이 구성하는 부분공간에서 해밀토니안을 대각화하여 근사합니다.
양자 하드웨어에서 실행되는 더 자세한 엔드투엔드 워크플로는 다음 튜토리얼을 참고하세요.
-
샘플 기반 양자 대각화: 화학 해밀토니안 워크플로
-
샘플 기반 크릴로프 양자 대각화: 페르미온 격자 모형 워크플로
1. SQD 입력 준비하기
먼저 화학 해밀토니안과 그 바닥 상태를 근사하는 양자 회로를 정의합니다. 이 예제에서 다루는 시스템은 평형 구조의 N₂이며 STO-3G 기저에서 두 개의 코어 오비탈을 고정했습니다. 이 시스템은 8개의 공간 오비탈과 10개의 전자를 가지므로, 양자 회로는 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. 노이즈가 있는 샘플 생성하기
실제 워크플로에서는 앤사츠 회로를 설계하고, 하드웨어에 맞게 최적화한 뒤, QPU에서 샘플링합니다. 여기서는 이를 건너뛰고 대신 균일한 무작위 비트열 10,000개를 뽑습니다. 이 샘플에는 실제 바닥 상태에 대한 정보가 없지만, SQD 프로토콜은 이 샘플을 반복적으로 정제하고 대각화하며 각 반복의 에너지를 이용해 다음 비트열 정제를 이끄는 방식으로 진짜 바닥 상태를 복원할 수 있음을 보게 될 것입니다. 바닥 상태를 포함하는 부분공간이 정확히 계산할 수 있을 만큼 작기 때문에 이런 방식으로 정확한 바닥 상태를 결국 복원할 수 있습니다. 구체적으로, 각 비트열의 절반(8비트)마다 해밍 가중치가 5여야 한다는 것을 알고 있으므로, 시스템의 전체 힐베르트 부분공간 크기는 입니다.
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에서 얻은 노이즈가 있는 샘플을 정제하고, 정제된 샘플이 span하는 부분공간에서 해밀토니안을 최종적으로 대각화하는 것입니다. 이는 다음과 같이 반복적으로 수행됩니다.
-
스핀 업과 스핀 다운 부분계 모두에서 입자 수가 올바른 샘플만 사후 선택하여 샘플을 정제합니다.
-
정제된 샘플이 span하는 작은 부분공간에서 대각화하고, 가장 좋은(가장 낮은) 에너지를 내는 부분공간에 대한 정보를 수집합니다.
-
각 반복마다:
-
입자 수가 올바르지 않은 샘플의 비트를 확률적으로 뒤집어 노이즈가 있는 샘플을 정제합니다. 확률은 가장 최근 대각화에서 얻은 부분공간 정보를 사용해 계산합니다.
-
정제된 샘플이 span하는 작은 부분공간에서 대각화합니다.
-
가장 좋은(가장 낮은) 에너지를 내는 부분공간에 대한 정보를 수집합니다.
-
출력에서 알고리즘이 6번의 반복 후 정확한 바닥 상태 에너지로 수렴하며, 대각화가 차원 의 전체 힐베르트 부분공간에서 수행되었음을 볼 수 있습니다. 물론 전체 부분공간에서의 대각화는 대개 불가능하므로, 바닥 상태의 지지 집합이 희소한 시스템이 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