Krylov quantum diagonalization ng lattice Hamiltonians
Tantiya sa paggamit: 20 minuto sa Heron r2 (PAALALA: Ito ay tantiya lamang. Maaaring mag-iba ang inyong runtime.)
# Added by doQumentation β required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime scipy sympy
# This cell is hidden from users β it disables some lint rules
# ruff: noqa: E402 E722 F601
Backgroundβ
Ang tutorial na ito ay nagpapakita kung paano ipatupad ang Krylov Quantum Diagonalization Algorithm (KQD) sa konteksto ng Qiskit patterns. Matututunan mo muna ang teorya sa likod ng algorithm at pagkatapos ay makikita mo ang demonstrasyon ng pagpapatupad nito sa QPU.
Sa iba't ibang disiplina, interesado tayong matutunan ang mga katangian ng ground state ng mga quantum system. Kabilang dito ang pag-unawa sa pundamental na kalikasan ng mga particle at pwersa, paghula at pag-unawa sa pag-uugali ng mga komplikadong materyales, at pag-unawa sa mga bio-chemical na interaksyon at reaksyon. Dahil sa exponential growth ng Hilbert space at sa correlation na lumalabas sa mga entangled system, nahihirapan ang mga classical algorithm na lutasin ang problemang ito para sa mga quantum system na lumalaki ang sukat. Sa isang dulo ng spectrum ay ang umiiral na approach na sumasamantala sa quantum hardware na nakatuon sa mga variational quantum method (halimbawa, variational quantum eigensolver). Ang mga pamamaraang ito ay nahaharap sa mga hamon sa mga kasalukuyang device dahil sa mataas na bilang ng function calls na kinakailangan sa proseso ng optimization, na nagdadagdag ng malaking overhead sa mga mapagkukunan kapag ipinakilala ang mga advanced error mitigation technique, kaya nililimitahan ang kanilang epektibidad sa maliliit na sistema. Sa kabilang dulo ng spectrum, may mga fault-tolerant quantum method na may performance guarantees (halimbawa, quantum phase estimation), na nangangailangan ng malalim na circuit na maaari lamang isagawa sa fault-tolerant device. Dahil sa mga kadahilanang ito, ipinapakilala natin dito ang isang quantum algorithm na batay sa subspace method (gaya ng inilarawan sa review paper na ito), ang Krylov quantum diagonalization (KQD) algorithm. Ang algorithm na ito ay gumagana nang maayos sa malaking sukat [1] sa umiiral na quantum hardware, may katulad na performance guarantees tulad ng phase estimation, compatible sa mga advanced error mitigation technique, at maaaring magbigay ng mga resulta na hindi maaabot ng classical.
Requirementsβ
Bago simulan ang tutorial na ito, siguraduhing mayroon kayo ng sumusunod na naka-install:
- Qiskit SDK v2.0 o mas bago, na may suporta sa visualization
- Qiskit Runtime v0.22 o mas bago (
pip install qiskit-ibm-runtime)
Setupβ
import numpy as np
import scipy as sp
import matplotlib.pylab as plt
from typing import Union, List
import itertools as it
import copy
from sympy import Matrix
import warnings
warnings.filterwarnings("ignore")
from qiskit.quantum_info import SparsePauliOp, Pauli, StabilizerState
from qiskit.circuit import Parameter, IfElseOp
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import Target, CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import (
QiskitRuntimeService,
EstimatorV2 as Estimator,
)
def solve_regularized_gen_eig(
h: np.ndarray,
s: np.ndarray,
threshold: float,
k: int = 1,
return_dimn: bool = False,
) -> Union[float, List[float]]:
"""
Method for solving the generalized eigenvalue problem with regularization
Args:
h (numpy.ndarray):
The effective representation of the matrix in the Krylov subspace
s (numpy.ndarray):
The matrix of overlaps between vectors of the Krylov subspace
threshold (float):
Cut-off value for the eigenvalue of s
k (int):
Number of eigenvalues to return
return_dimn (bool):
Whether to return the size of the regularized subspace
Returns:
lowest k-eigenvalue(s) that are the solution of the regularized generalized eigenvalue problem
"""
s_vals, s_vecs = sp.linalg.eigh(s)
s_vecs = s_vecs.T
good_vecs = np.array(
[vec for val, vec in zip(s_vals, s_vecs) if val > threshold]
)
h_reg = good_vecs.conj() @ h @ good_vecs.T
s_reg = good_vecs.conj() @ s @ good_vecs.T
if k == 1:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][0], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][0]
else:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][:k], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][:k]
def single_particle_gs(H_op, n_qubits):
"""
Find the ground state of the single particle(excitation) sector
"""
H_x = []
for p, coeff in H_op.to_list():
H_x.append(set([i for i, v in enumerate(Pauli(p).x) if v]))
H_z = []
for p, coeff in H_op.to_list():
H_z.append(set([i for i, v in enumerate(Pauli(p).z) if v]))
H_c = H_op.coeffs
print("n_sys_qubits", n_qubits)
n_exc = 1
sub_dimn = int(sp.special.comb(n_qubits + 1, n_exc))
print("n_exc", n_exc, ", subspace dimension", sub_dimn)
few_particle_H = np.zeros((sub_dimn, sub_dimn), dtype=complex)
sparse_vecs = [
set(vec) for vec in it.combinations(range(n_qubits + 1), r=n_exc)
] # list all of the possible sets of n_exc indices of 1s in n_exc-particle states
m = 0
for i, i_set in enumerate(sparse_vecs):
for j, j_set in enumerate(sparse_vecs):
m += 1
if len(i_set.symmetric_difference(j_set)) <= 2:
for p_x, p_z, coeff in zip(H_x, H_z, H_c):
if i_set.symmetric_difference(j_set) == p_x:
sgn = ((-1j) ** len(p_x.intersection(p_z))) * (
(-1) ** len(i_set.intersection(p_z))
)
else:
sgn = 0
few_particle_H[i, j] += sgn * coeff
gs_en = min(np.linalg.eigvalsh(few_particle_H))
print("single particle ground state energy: ", gs_en)
return gs_en
Step 1: Map classical inputs to a quantum problemβ
The Krylov spaceβ
Ang Krylov space na may order na ay ang space na natatakpan ng mga vector na nakuha sa pamamagitan ng pagmumultiplika ng mas mataas na power ng matrix , hanggang sa , sa reference vector .
Kung ang matrix ay ang Hamiltonian , tutukuyin natin ang kaukulang space bilang power Krylov space . Sa kaso kung saan ang ay ang time-evolution operator na nabuo ng Hamiltonian , tutukuyin natin ang space bilang unitary Krylov space . Ang power Krylov subspace na ginagamit natin sa classical ay hindi maaaring direktang mabuo sa quantum computer dahil ang ay hindi unitary operator. Sa halip, maaari nating gamitin ang time-evolution operator na maaaring ipakitang nagbibigay ng katulad na convergence guarantees tulad ng power method. Ang mga power ng ay nagiging iba't ibang time step .
Tingnan ang Appendix para sa detalyadong derivation kung paano ang unitary Krylov space ay nagpapahintulot na tumpak na kumatawan sa mga low-energy eigenstate.
Krylov quantum diagonalization algorithmβ
Ibinigay ang Hamiltonian na nais nating i-diagonalize, una ay isasaalang-alang natin ang kaukulang unitary Krylov space . Ang layunin ay makahanap ng compact na representasyon ng Hamiltonian sa , na tutukuyin natin bilang . Ang mga matrix element ng , ang projection ng Hamiltonian sa Krylov space, ay maaaring kalkulahin sa pamamagitan ng pagkalkula ng sumusunod na expectation value
Kung saan ang ay ang mga vector ng unitary Krylov space at ang ay ang mga multiple ng time step na pinili. Sa quantum computer, ang pagkalkula ng bawat matrix element ay maaaring gawin sa pamamagitan ng anumang algorithm na nagpapahintulot na makuha ang overlap sa pagitan ng mga quantum state. Ang tutorial na ito ay nakatuon sa Hadamard test. Dahil ang ay may dimension na , ang Hamiltonian na na-project sa subspace ay magkakaroon ng dimension na . Sa na sapat na maliit (karaniwang ay sapat upang makuha ang convergence ng mga estimate ng eigenenergy) maaari nating madaling i-diagonalize ang projected Hamiltonian . Gayunpaman, hindi natin maaaring direktang i-diagonalize ang dahil sa non-orthogonality ng mga Krylov space vector. Kailangan nating sukatin ang kanilang mga overlap at bumuo ng matrix
Nagpapahintulot ito sa atin na lutasin ang eigenvalue problem sa non-orthogonal space (na tinatawag ding generalized eigenvalue problem)
Maaari kung gayon na makakuha ng mga estimate ng mga eigenvalue at eigenstate ng sa pamamagitan ng pagtingin sa mga ito ng . Halimbawa, ang estimate ng ground state energy ay nakukuha sa pamamagitan ng pagkuha ng pinakamaliit na eigenvalue at ng ground state mula sa kaukulang eigenvector . Ang mga coefficient sa ay tumutukoy sa kontribusyon ng iba't ibang vector na sumasaklaw sa .

Ipinapakita ng Figure ang circuit representation ng modified Hadamard test, isang paraan na ginagamit upang kalkulahin ang overlap sa pagitan ng iba't ibang quantum state. Para sa bawat matrix element , isinasagawa ang Hadamard test sa pagitan ng state , . Ito ay naka-highlight sa figure sa pamamagitan ng color scheme para sa mga matrix element at sa kaukulang , operation. Kaya, kailangan ng isang set ng Hadamard test para sa lahat ng posibleng kombinasyon ng mga Krylov space vector upang kalkulahin ang lahat ng matrix element ng projected Hamiltonian . Ang tuktok na wire sa Hadamard test circuit ay isang ancilla qubit na sinusukat sa X o Y basis, ang expectation value nito ay tumutukoy sa halaga ng overlap sa pagitan ng mga state. Ang ibabang wire ay kumakatawan sa lahat ng qubit ng system Hamiltonian. Ang operation ay naghahanda ng system qubit sa state na kinokontrol ng state ng ancilla qubit (katulad para sa ) at ang operation ay kumakatawan sa Pauli decomposition ng system Hamiltonian . Ang mas detalyadong derivation ng mga operation na kinakalkula ng Hadamard test ay ibinigay sa ibaba.
Define Hamiltonianβ
Isaalang-alang natin ang Heisenberg Hamiltonian para sa qubit sa linear chain:
# Define problem Hamiltonian.
n_qubits = 30
J = 1 # coupling strength for ZZ interaction
# Define the Hamiltonian:
H_int = [["I"] * n_qubits for _ in range(3 * (n_qubits - 1))]
for i in range(n_qubits - 1):
H_int[i][i] = "Z"
H_int[i][i + 1] = "Z"
for i in range(n_qubits - 1):
H_int[n_qubits - 1 + i][i] = "X"
H_int[n_qubits - 1 + i][i + 1] = "X"
for i in range(n_qubits - 1):
H_int[2 * (n_qubits - 1) + i][i] = "Y"
H_int[2 * (n_qubits - 1) + i][i + 1] = "Y"
H_int = ["".join(term) for term in H_int]
H_tot = [(term, J) if term.count("Z") == 2 else (term, 1) for term in H_int]
# Get operator
H_op = SparsePauliOp.from_list(H_tot)
print(H_tot)
[('ZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIZZIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIZZIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIZZIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIZZIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIZZIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 1), ('XXIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IXXIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIXXIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIXXIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIXXIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIXXIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIXXIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIXXIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIXXIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIXXIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIXXIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIXXIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIXXIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIXXIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIXXIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIXXIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIXXIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIXXIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIXXIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIXXIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIXXIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIXXIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIXXIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIXXIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIXXIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIXXIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIXXII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIXXI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIXX', 1), ('YYIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IYYIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIYYIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIYYIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIYYIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIYYIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIYYIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIYYIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIYYIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIYYIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIYYIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIYYIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIYYIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIYYIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIYYIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIYYIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIYYIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIYYIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIYYIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIYYIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIYYIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIYYIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIYYIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIYYIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIYYIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIYYIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIYYII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIYYI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIYY', 1)]
Set parameters for the algorithmβ
Heuristically pumipili tayo ng halaga para sa time-step dt (batay sa upper bound sa Hamiltonian norm). Ang Ref [2] ay nagpakita na ang sapat na maliit na timestep ay , at mas mainam hanggang sa isang punto na masubok-ubok ang halagang ito kaysa sa sobrang tantiya, dahil ang sobrang pagtantiya ay maaaring magpahintulot ng mga kontribusyon mula sa high-energy state na masira kahit na ang optimal state sa Krylov space. Sa kabilang banda, ang pagpili ng na masyadong maliit ay humahantong sa mas masamang conditioning ng Krylov subspace, dahil ang mga Krylov basis vector ay mas kaunting pagkakaiba mula sa timestep hanggang timestep.
# Get Hamiltonian restricted to single-particle states
single_particle_H = np.zeros((n_qubits, n_qubits))
for i in range(n_qubits):
for j in range(i + 1):
for p, coeff in H_op.to_list():
p_x = Pauli(p).x
p_z = Pauli(p).z
if all(
p_x[k] == ((i == k) + (j == k)) % 2 for k in range(n_qubits)
):
sgn = (
(-1j) ** sum(p_z[k] and p_x[k] for k in range(n_qubits))
) * ((-1) ** p_z[i])
else:
sgn = 0
single_particle_H[i, j] += sgn * coeff
for i in range(n_qubits):
for j in range(i + 1, n_qubits):
single_particle_H[i, j] = np.conj(single_particle_H[j, i])
# Set dt according to spectral norm
dt = np.pi / np.linalg.norm(single_particle_H, ord=2)
dt
np.float64(0.10833078115826875)
At itakda ang iba pang parameter ng algorithm. Para sa layunin ng tutorial na ito, limitahan natin ang ating sarili sa paggamit ng Krylov space na may limang dimension lamang, na medyo nakakahangganan.
# Set parameters for quantum Krylov algorithm
krylov_dim = 5 # size of Krylov subspace
num_trotter_steps = 6
dt_circ = dt / num_trotter_steps
State preparationβ
Pumili ng reference state na may overlap sa ground state. Para sa Hamiltonian na ito, ginagamit natin ang state na may excitation sa gitnang qubit bilang ating reference state.
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)
Time evolutionβ
Maaari nating maisakatuparan ang time-evolution operator na nabuo ng ibinigay na Hamiltonian: sa pamamagitan ng Lie-Trotter approximation.
t = Parameter("t")
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=t, synthesis=LieTrotter(reps=num_trotter_steps)
)
qr = QuantumRegister(n_qubits)
qc_evol = QuantumCircuit(qr)
qc_evol.append(evol_gate, qargs=qr)
<qiskit.circuit.instructionset.InstructionSet at 0x11eef9be0>
Hadamard testβ

Kung saan ang ay isa sa mga term sa decomposition ng Hamiltonian at ang , ay mga controlled operation na naghahanda ng , vector ng unitary Krylov space, na may . Upang sukatin ang , ilapat muna ang ...
... pagkatapos ay sukatin:
Mula sa identity na . Katulad nito, ang pagsukat ng ay nagbubunga ng
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
## Create the time-evo op dagger circuit
evol_gate_d = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
evol_gate_d = evol_gate_d.inverse()
# Put pieces together
qc_reg = QuantumRegister(n_qubits)
qc_temp = QuantumCircuit(qc_reg)
qc_temp.compose(qc_state_prep, inplace=True)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate, qargs=qc_reg)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate_d, qargs=qc_reg)
qc_temp.compose(qc_state_prep.inverse(), inplace=True)
# Create controlled version of the circuit
controlled_U = qc_temp.to_gate().control(1)
# Create hadamard test circuit for real part
qr = QuantumRegister(n_qubits + 1)
qc_real = QuantumCircuit(qr)
qc_real.h(0)
qc_real.append(controlled_U, list(range(n_qubits + 1)))
qc_real.h(0)
print(
"Circuit for calculating the real part of the overlap in S via Hadamard test"
)
qc_real.draw("mpl", fold=-1, scale=0.5)
Circuit for calculating the real part of the overlap in S via Hadamard test
Ang Hadamard test circuit ay maaaring maging malalim na circuit kapag na-decompose natin sa native gate (na tataas pa kung isasaalang-alang natin ang topology ng device)
print(
"Number of layers of 2Q operations",
qc_real.decompose(reps=2).depth(lambda x: x[0].num_qubits == 2),
)
Number of layers of 2Q operations 112753
Step 2: Optimize problem for quantum hardware executionβ
Efficient Hadamard testβ
Maaari nating i-optimize ang malalim na circuit para sa Hadamard test na nakuha natin sa pamamagitan ng pagpapakilala ng ilang approximation at pagsandig sa ilang assumption tungkol sa model Hamiltonian. Halimbawa, isaalang-alang ang sumusunod na circuit para sa Hadamard test:

Ipagpalagay natin na maaari nating classically kalkulahin ang , ang eigenvalue ng sa ilalim ng Hamiltonian . Natutugunan ito kapag ang Hamiltonian ay napapanatili ang U(1) symmetry. Bagaman tila ito ay isang malakas na assumption, may maraming kaso kung saan ligtas na ipagpalagay na may vacuum state (sa kasong ito ay nag-map sa state) na hindi naapektuhan ng aksyon ng Hamiltonian. Totoo ito halimbawa para sa chemistry Hamiltonian na naglalarawan ng stable molecule (kung saan ang bilang ng electron ay napapanatili). Dahil ang gate , ay naghahanda ng nais na reference state , halimbawa, upang ihanda ang HF state para sa chemistry ang ay magiging produkto ng single-qubit NOT, kaya ang controlled- ay produkto lamang ng CNOT. Pagkatapos ang circuit sa itaas ay nagpapatupad ng sumusunod na state bago ang pagsukat:
kung saan ginamit natin ang classical simulable phase shift sa ikatlong linya. Kaya ang mga expectation value ay nakukuha bilang
Gamit ang mga assumption na ito ay nakayanang natin na isulat ang mga expectation value ng mga operator na kinapapakinabangan na may mas kaunting controlled operation. Sa katunayan, kailangan lang nating ipatupad ang controlled state preparation at hindi controlled time evolution. Ang pag-reframe ng ating pagkalkula gaya ng sa itaas ay magpapahintulot sa atin na malaki ang mabawas sa lalim ng mga resultang circuit.
Decompose time-evolution operator with Trotter decompositionβ
Sa halip na ipatupad nang eksakto ang time-evolution operator, maaari nating gamitin ang Trotter decomposition upang ipatupad ang approximation nito. Ang pag-uulit ng ilang beses sa isang tiyak na order ng Trotter decomposition ay nagbibigay sa atin ng karagdagang pagbabawas ng error na ipinakilala mula sa approximation. Sa sumusunod, direkta naming binubuo ang Trotter implementation sa pinaka-epektibong paraan para sa interaction graph ng Hamiltonian na ating isinasaalang-alang (nearest neighbor interaction lamang). Sa pagsasagawa inilalagay natin ang mga Pauli rotation , , na may parametrized angle na kumakatawan sa approximate implementation ng . Dahil sa pagkakaiba sa kahulugan ng mga Pauli rotation at sa time-evolution na sinusubukan nating ipatupad, kailangan nating gamitin ang parameter na upang makamit ang time-evolution na . Bukod dito, binabaligtad natin ang order ng mga operation para sa odd number ng mga ulit ng Trotter step, na functionally equivalent ngunit nagpapahintulot na i-synthesize ang magkatabing operation sa isang unitary. Ito ay nagbibigay ng mas mababaw na circuit kaysa sa nakuha gamit ang generic PauliEvolutionGate() functionality.
t = Parameter("t")
# Create instruction for rotation about XX+YY-ZZ:
Rxyz_circ = QuantumCircuit(2)
Rxyz_circ.rxx(t, 0, 1)
Rxyz_circ.ryy(t, 0, 1)
Rxyz_circ.rzz(t, 0, 1)
Rxyz_instr = Rxyz_circ.to_instruction(label="RXX+YY+ZZ")
interaction_list = [
[[i, i + 1] for i in range(0, n_qubits - 1, 2)],
[[i, i + 1] for i in range(1, n_qubits - 1, 2)],
] # linear chain
qr = QuantumRegister(n_qubits)
trotter_step_circ = QuantumCircuit(qr)
for i, color in enumerate(interaction_list):
for interaction in color:
trotter_step_circ.append(Rxyz_instr, interaction)
if i < len(interaction_list) - 1:
trotter_step_circ.barrier()
reverse_trotter_step_circ = trotter_step_circ.reverse_ops()
qc_evol = QuantumCircuit(qr)
for step in range(num_trotter_steps):
if step % 2 == 0:
qc_evol = qc_evol.compose(trotter_step_circ)
else:
qc_evol = qc_evol.compose(reverse_trotter_step_circ)
qc_evol.decompose().draw("mpl", fold=-1, scale=0.5)

Use an optimized circuit for state preparationβ
control = 0
excitation = int(n_qubits / 2) + 1
controlled_state_prep = QuantumCircuit(n_qubits + 1)
controlled_state_prep.cx(control, excitation)
controlled_state_prep.draw("mpl", fold=-1, scale=0.5)
Template circuits for calculating matrix elements of and via Hadamard testβ
Ang tanging pagkakaiba sa pagitan ng mga circuit na ginagamit sa Hadamard test ay ang phase sa time-evolution operator at ang mga observable na sinusukat. Kaya maaari tayong maghanda ng template circuit na kumakatawan sa generic circuit para sa Hadamard test, na may mga placeholder para sa mga gate na nakadepende sa time-evolution operator.
# Parameters for the template circuits
parameters = []
for idx in range(1, krylov_dim):
parameters.append(2 * dt_circ * (idx))
# Create modified hadamard test circuit
qr = QuantumRegister(n_qubits + 1)
qc = QuantumCircuit(qr)
qc.h(0)
qc.compose(controlled_state_prep, list(range(n_qubits + 1)), inplace=True)
qc.barrier()
qc.compose(qc_evol, list(range(1, n_qubits + 1)), inplace=True)
qc.barrier()
qc.x(0)
qc.compose(
controlled_state_prep.inverse(), list(range(n_qubits + 1)), inplace=True
)
qc.x(0)
qc.decompose().draw("mpl", fold=-1)

print(
"The optimized circuit has 2Q gates depth: ",
qc.decompose().decompose().depth(lambda x: x[0].num_qubits == 2),
)
The optimized circuit has 2Q gates depth: 74
Lubhang nabawasan natin ang lalim ng Hadamard test sa pamamagitan ng kombinasyon ng Trotter approximation at uncontrolled unitary
Step 3: Execute using Qiskit primitivesβ
I-instantiate ang backend at itakda ang runtime parameter
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
if (
"if_else" not in backend.target.operation_names
): # Needed as "op_name" could be "if_else"
backend.target.add_instruction(IfElseOp, name="if_else")
print(backend.name)
Transpiling to a QPUβ
Una, pumili tayo ng subset ng coupling map na may "magagandang" performing qubit (kung saan ang "maganda" ay medyo arbitrary dito, gusto lamang nating iwasan ang talagang mahinang performing qubit) at lumikha ng bagong target para sa transpilation
target = backend.target
cmap = target.build_coupling_map(filter_idle_qubits=True)
cmap_list = list(cmap.get_edges())
cust_cmap_list = copy.deepcopy(cmap_list)
for q in range(target.num_qubits):
meas_err = target["measure"][(q,)].error
t2 = target.qubit_properties[q].t2 * 1e6
if meas_err > 0.02 or t2 < 100:
for q_pair in cmap_list:
if q in q_pair:
try:
cust_cmap_list.remove(q_pair)
except:
continue
for q in cmap_list:
op_name = list(target.operation_names_for_qargs(q))[0]
twoq_gate_err = target[f"{op_name}"][q].error
if twoq_gate_err > 0.005:
for q_pair in cmap_list:
if q == q_pair:
try:
cust_cmap_list.remove(q)
except:
continue
cust_cmap = CouplingMap(cust_cmap_list)
cust_target = Target.from_configuration(
basis_gates=backend.configuration().basis_gates,
coupling_map=cust_cmap,
)
Pagkatapos ay i-transpile ang virtual circuit sa pinakamahusay na physical layout sa bagong target na ito
basis_gates = list(target.operation_names)
pm = generate_preset_pass_manager(
optimization_level=3,
target=cust_target,
basis_gates=basis_gates,
)
qc_trans = pm.run(qc)
print("depth", qc_trans.depth(lambda x: x[0].num_qubits == 2))
print("num 2q ops", qc_trans.count_ops())
print(
"physical qubits",
sorted(
[
idx
for idx, qb in qc_trans.layout.initial_layout.get_physical_bits().items()
if qb._register.name != "ancilla"
]
),
)
depth 52
num 2q ops OrderedDict([('rz', 2058), ('sx', 1703), ('cz', 728), ('x', 84), ('barrier', 8)])
physical qubits [91, 92, 93, 94, 95, 98, 99, 108, 109, 110, 111, 113, 114, 115, 119, 127, 132, 133, 134, 135, 137, 139, 147, 148, 149, 150, 151, 152, 153, 154, 155]
Create PUBs for execution with Estimatorβ
# Define observables to measure for S
observable_S_real = "I" * (n_qubits) + "X"
observable_S_imag = "I" * (n_qubits) + "Y"
observable_op_real = SparsePauliOp(
observable_S_real
) # define a sparse pauli operator for the observable
observable_op_imag = SparsePauliOp(observable_S_imag)
layout = qc_trans.layout # get layout of transpiled circuit
observable_op_real = observable_op_real.apply_layout(
layout
) # apply physical layout to the observable
observable_op_imag = observable_op_imag.apply_layout(layout)
observable_S_real = (
observable_op_real.paulis.to_labels()
) # get the label of the physical observable
observable_S_imag = observable_op_imag.paulis.to_labels()
observables_S = [[observable_S_real], [observable_S_imag]]
# Define observables to measure for H
# Hamiltonian terms to measure
observable_list = []
for pauli, coeff in zip(H_op.paulis, H_op.coeffs):
# print(pauli)
observable_H_real = pauli[::-1].to_label() + "X"
observable_H_imag = pauli[::-1].to_label() + "Y"
observable_list.append([observable_H_real])
observable_list.append([observable_H_imag])
layout = qc_trans.layout
observable_trans_list = []
for observable in observable_list:
observable_op = SparsePauliOp(observable)
observable_op = observable_op.apply_layout(layout)
observable_trans_list.append([observable_op.paulis.to_labels()])
observables_H = observable_trans_list
# Define a sweep over parameter values
params = np.vstack(parameters).T
# Estimate the expectation value for all combinations of
# observables and parameter values, where the pub result will have
# shape (# observables, # parameter values).
pub = (qc_trans, observables_S + observables_H, params)
Run circuitsβ
Ang mga circuit para sa ay classically calculable
qc_cliff = qc.assign_parameters({t: 0})
# Get expectation values from experiment
S_expval_real = StabilizerState(qc_cliff).expectation_value(
Pauli("I" * (n_qubits) + "X")
)
S_expval_imag = StabilizerState(qc_cliff).expectation_value(
Pauli("I" * (n_qubits) + "Y")
)
# Get expectation values
S_expval = S_expval_real + 1j * S_expval_imag
H_expval = 0
for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):
# Get expectation values from experiment
expval_real = StabilizerState(qc_cliff).expectation_value(
Pauli(pauli[::-1].to_label() + "X")
)
expval_imag = StabilizerState(qc_cliff).expectation_value(
Pauli(pauli[::-1].to_label() + "Y")
)
expval = expval_real + 1j * expval_imag
# Fill-in matrix elements
H_expval += coeff * expval
print(H_expval)
(25+0j)
Isagawa ang mga circuit para sa at gamit ang Estimator
# Experiment options
num_randomizations = 300
num_randomizations_learning = 30
shots_per_randomization = 100
noise_factors = [1, 1.2, 1.4]
learning_pair_depths = [0, 4, 24, 48]
experimental_opts = {}
experimental_opts["resilience"] = {
"measure_mitigation": True,
"measure_noise_learning": {
"num_randomizations": num_randomizations_learning,
"shots_per_randomization": shots_per_randomization,
},
"zne_mitigation": True,
"zne": {"noise_factors": noise_factors},
"layer_noise_learning": {
"max_layers_to_learn": 10,
"layer_pair_depths": learning_pair_depths,
"shots_per_randomization": shots_per_randomization,
"num_randomizations": num_randomizations_learning,
},
"zne": {
"amplifier": "pea",
"extrapolated_noise_factors": [0] + noise_factors,
},
}
experimental_opts["twirling"] = {
"num_randomizations": num_randomizations,
"shots_per_randomization": shots_per_randomization,
"strategy": "all",
}
estimator = Estimator(mode=backend, options=experimental_opts)
job = estimator.run([pub])
Step 4: Post-process and return result in desired classical formatβ
results = job.result()[0]
Calculate Effective Hamiltonian and Overlap matricesβ
Una ay kalkulahin ang phase na naiipon ng state sa panahon ng uncontrolled time evolution
prefactors = [
np.exp(-1j * sum([c for p, c in H_op.to_list() if "Z" in p]) * i * dt)
for i in range(1, krylov_dim)
]
Kapag mayroon na tayong mga resulta ng circuit execution, maaari nating i-post-process ang data upang kalkulahin ang mga matrix element ng
# Assemble S, the overlap matrix of dimension D:
S_first_row = np.zeros(krylov_dim, dtype=complex)
S_first_row[0] = 1 + 0j
# Add in ancilla-only measurements:
for i in range(krylov_dim - 1):
# Get expectation values from experiment
expval_real = results.data.evs[0][0][
i
] # automatic extrapolated evs if ZNE is used
expval_imag = results.data.evs[1][0][
i
] # automatic extrapolated evs if ZNE is used
# Get expectation values
expval = expval_real + 1j * expval_imag
S_first_row[i + 1] += prefactors[i] * expval
S_first_row_list = S_first_row.tolist() # for saving purposes
S_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)
# Distribute entries from first row across matrix:
for i, j in it.product(range(krylov_dim), repeat=2):
if i >= j:
S_circ[j, i] = S_first_row[i - j]
else:
S_circ[j, i] = np.conj(S_first_row[j - i])
Matrix(S_circ)
At ang mga matrix element ng
# Assemble S, the overlap matrix of dimension D:
H_first_row = np.zeros(krylov_dim, dtype=complex)
H_first_row[0] = H_expval
for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):
# Add in ancilla-only measurements:
for i in range(krylov_dim - 1):
# Get expectation values from experiment
expval_real = results.data.evs[2 + 2 * obs_idx][0][
i
] # automatic extrapolated evs if ZNE is used
expval_imag = results.data.evs[2 + 2 * obs_idx + 1][0][
i
] # automatic extrapolated evs if ZNE is used
# Get expectation values
expval = expval_real + 1j * expval_imag
H_first_row[i + 1] += prefactors[i] * coeff * expval
H_first_row_list = H_first_row.tolist()
H_eff_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)
# Distribute entries from first row across matrix:
for i, j in it.product(range(krylov_dim), repeat=2):
if i >= j:
H_eff_circ[j, i] = H_first_row[i - j]
else:
H_eff_circ[j, i] = np.conj(H_first_row[j - i])
Matrix(H_eff_circ)
Sa wakas, maaari nating lutasin ang generalized eigenvalue problem para sa :
at makakuha ng estimate ng ground state energy
gnd_en_circ_est_list = []
for d in range(1, krylov_dim + 1):
# Solve generalized eigenvalue problem for different size of the Krylov space
gnd_en_circ_est = solve_regularized_gen_eig(
H_eff_circ[:d, :d], S_circ[:d, :d], threshold=9e-1
)
gnd_en_circ_est_list.append(gnd_en_circ_est)
print("The estimated ground state energy is: ", gnd_en_circ_est)
The estimated ground state energy is: 25.0
The estimated ground state energy is: 22.572154819954875
The estimated ground state energy is: 21.691509219286587
The estimated ground state energy is: 21.23882298756386
The estimated ground state energy is: 20.965499325470294
Para sa single-particle sector, maaari tayong epektibong kalkulahin ang ground state ng sector na ito ng Hamiltonian sa classical
gs_en = single_particle_gs(H_op, n_qubits)
n_sys_qubits 30
n_exc 1 , subspace dimension 31
single particle ground state energy: 21.021912418526906
plt.plot(
range(1, krylov_dim + 1),
gnd_en_circ_est_list,
color="blue",
linestyle="-.",
label="KQD estimate",
)
plt.plot(
range(1, krylov_dim + 1),
[gs_en] * krylov_dim,
color="red",
linestyle="-",
label="exact",
)
plt.xticks(range(1, krylov_dim + 1), range(1, krylov_dim + 1))
plt.legend()
plt.xlabel("Krylov space dimension")
plt.ylabel("Energy")
plt.title(
"Estimating Ground state energy with Krylov Quantum Diagonalization"
)
plt.show()
Appendix: Krylov subspace from real time-evolutionsβ
Ang unitary Krylov space ay tinukoy bilang
para sa ilang timestep na tutukuyin natin mamaya. Panandaliang ipagpalagay na ang ay even: kung gayon ay tukuyin ang . Pansinin na kapag ini-project natin ang Hamiltonian sa Krylov space sa itaas, ito ay hindi mapagkakaila mula sa Krylov space