Krylovova kvantová diagonalizace mřížkových Hamiltonů
Odhadovaný čas: 20 minut na Heron r2 (POZNÁMKA: Jedná se pouze o odhad. Skutečný čas se může lišit.)
# 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
Pozadí
Tento tutoriál ukazuje, jak implementovat algoritmus Krylovovy kvantové diagonalizace (KQD) v rámci vzorů Qiskitu. Nejprve se dozvíš o teorii za algoritmem a pak uvidíš ukázku jeho spuštění na QPU.
Napříč obory nás zajímají vlastnosti základního stavu kvantových systémů. Mezi příklady patří pochopení základní povahy částic a sil, předpovídání a porozumění chování složitých materiálů a pochopení bio-chemických interakcí a reakcí. Kvůli exponenciálnímu růstu Hilbertova prostoru a korelacím, které vznikají v provázaných systémech, mají klasické algoritmy problém toto řešit pro kvantové systémy rostoucí velikosti. Na jednom konci spektra jsou stávající přístupy využívající kvantový hardware zaměřené na variační kvantové metody (například variační kvantový eigensolver). Tyto techniky čelí výzvám na současných zařízeních kvůli vysokému počtu volání funkcí potřebných v optimalizačním procesu, což přidává velkou režii na zdroje při použití pokročilých technik potlačení chyb, a tak omezuje jejich účinnost na malé systémy. Na druhém konci spektra jsou metody odolné vůči chybám s výkonnostními zárukami (například kvantová fázová estimace), které vyžadují hluboké Circuit, spustitelné pouze na zařízení odolném vůči chybám. Z těchto důvodů zde představujeme kvantový algoritmus založený na metodách podprostoru (jak je popsáno v tomto přehledovém článku), algoritmus Krylovovy kvantové diagonalizace (KQD). Tento algoritmus funguje dobře ve velkém měřítku [1] na stávajícím kvantovém hardwaru, sdílí podobné výkonnostní záruky jako fázová estimace, je kompatibilní s pokročilými technikami potlačení chyb a mohl by poskytovat výsledky klasicky nedosažitelné.
Požadavky
Před zahájením tohoto tutoriálu se ujisti, že máš nainstalováno následující:
- Qiskit SDK v2.0 nebo novější, s podporou vizualizace
- Qiskit Runtime v0.22 nebo novější (
pip install qiskit-ibm-runtime)
Nastavení
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
Krok 1: Mapování klasických vstupů na kvantový problém
Krylovův prostor
Krylovův prostor řádu je prostor rozepnutý vektory získanými násobením vyšších mocnin matice , až do , s referenčním vektorem .
Pokud je matice Hamiltonián , budeme odpovídající prostor nazývat mocninový Krylovův prostor . V případě, kdy je operátor časového vývoje generovaný Hamiltonánem , budeme prostor nazývat unitární Krylovův prostor . Mocninový Krylovův podprostor, který klasicky používáme, nelze přímo generovat na kvantovém počítači, protože není unitární operátor. Místo toho můžeme použít operátor časového vývoje , u nějž lze ukázat, že poskytuje podobné záruky konvergence jako mocninová metoda. Mocniny se pak stávají různými časovými kroky .
Viz Přílohu pro podrobné odvození toho, jak unitární Krylovův prostor umožňuje přesně reprezentovat nízkoenergetické vlastní stavy.
Algoritmus Krylovovy kvantové diagonalizace
Pro daný Hamiltonián , který chceme diagonalizovat, nejprve uvažujeme odpovídající unitární Krylovův prostor . Cílem je najít kompaktní reprezentaci Hamiltoniánu v , kterou budeme označovat . Maticové prvky , projekce Hamiltoniánu do Krylovova prostoru, lze vypočítat výpočtem následujících střední hodnot
Kde jsou vektory unitárního Krylovova prostoru a jsou násobky zvoleného časového kroku . Na kvantovém počítači lze výpočet každého maticového prvku provést libovolným algoritmem, který umožňuje získat překryv mezi kvantovými stavy. Tento tutoriál se zaměřuje na Hadamardův test. Vzhledem k tomu, že má dimenzi , bude mít Hamiltonián promítnutý do podprostoru rozměry . Při dostatečně malém (obecně stačí k dosažení konvergence odhadů vlastních energií) pak snadno diagonalizujeme promítnutý Hamiltonián . Nemůžeme však přímo diagonalizovat kvůli neortogonalitě vektorů Krylovova prostoru. Musíme změřit jejich překryvy a sestavit matici
To nám umožňuje řešit problém vlastních hodnot v neortogonálním prostoru (také nazývaný zobecněný problém vlastních hodnot)
Lze pak získat odhady vlastních hodnot a vlastních stavů pohledem na vlastní hodnoty . Například odhad energie základního stavu se získá jako nejmenší vlastní hodnota a základní stav z odpovídajícího vlastního vektoru . Koeficienty v určují příspěvek různých vektorů, které rozpínají .

Obrázek ukazuje reprezentaci Circuit modifikovaného Hadamardova testu, metody používané k výpočtu překryvu mezi různými kvantovými stavy. Pro každý maticový prvek se provede Hadamardův test mezi stavy , . To je zvýrazněno v obrázku barevným schématem pro maticové prvky a odpovídající operace , . Tedy pro výpočet všech maticových prvků promítnutého Hamiltoniánu je potřeba sada Hadamardových testů pro všechny možné kombinace vektorů Krylovova prostoru. Horní drát v obvodu Hadamardova testu je ancilla Qubit, který se měří buď v základně X nebo Y, jeho střední hodnota určuje hodnotu překryvu mezi stavy. Dolní drát představuje všechny Qubit systémového Hamiltoniánu. Operace připraví systémové Qubit ve stavu řízeném stavem ancilla Qubit (podobně pro ) a operace představuje Pauliho rozklad systémového Hamiltoniánu . Podrobnější odvození operací počítaných Hadamardovým testem je uvedeno níže.
Definice Hamiltoniánu
Uvažujme Heisenbergův Hamiltonián pro Qubit na lineárním řetězci:
# 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)]
Nastavení parametrů algoritmu
Heuristicky volíme hodnotu časového kroku dt (na základě horních odhadů normy Hamiltoniánu). Ref [2] ukázal, že dostatečně malý časový krok je , a že je výhodnější tuto hodnotu spíše podhodnotit než přehodnotit, protože přehodnocení může umožnit příspěvkům z vysokoenergetických stavů zkreslit i optimální stav v Krylovově prostoru. Na druhé straně příliš malá hodnota vede k horšímu podmínění Krylovova podprostoru, protože vektory Krylovovy základny se mezi časovými kroky méně liší.
# 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)
A nastavíme další parametry algoritmu. Pro účely tohoto tutoriálu se omezíme na použití Krylovova prostoru s pouhými pěti dimenzemi, což je docela omezující.
# Set parameters for quantum Krylov algorithm
krylov_dim = 5 # size of Krylov subspace
num_trotter_steps = 6
dt_circ = dt / num_trotter_steps
Příprava stavu
Zvolíme referenční stav , který má nějaký překryv se základním stavem. Pro tento Hamiltonián použijeme jako referenční stav stav s excitací ve středním Qubit .
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)
Časový vývoj
Operátor časového vývoje generovaný daným Hamiltonánem: lze realizovat pomocí Lie-Trotterovy aproximace.
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ův test

Kde je jeden z členů v rozkladu Hamiltoniánu a , jsou řízené operace, které připravují vektory , unitárního Krylovova prostoru, přičemž . Pro měření nejprve aplikuj ...
... pak změř:
Z identity . Podobně měření dává
## 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
Circuit Hadamardova testu může být hluboký Circuit po rozkladu na nativní Gate (který se ještě více prohloubí, pokud vezmeme v úvahu topologii zařízení)
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
Krok 2: Optimalizace problému pro spouštění na kvantovém hardwaru
Efektivní Hadamardův test
Hluboké obvody pro Hadamardův test, které jsme získali, můžeme optimalizovat zavedením určitých aproximací a využitím předpokladů o modelovém Hamiltoniánu. Uvažuj například následující obvod pro Hadamardův test:

Předpokládejme, že dokážeme klasicky vypočítat , vlastní hodnotu pod Hamiltoniánem . To platí, pokud Hamiltonián zachovává symetrii U(1). Ačkoli se to může zdát silným předpokladem, existuje mnoho případů, kdy je bezpečné předpokládat, že existuje vakuový stav (v tomto případě se zobrazí na stav ), který není ovlivněn působením Hamiltoniánu. To platí například pro chemické Hamiltoniány popisující stabilní molekuly (kde je zachováno množství elektronů). Pokud hradlo připravuje požadovaný referenční stav , například pro přípravu stavu HF v chemii je součinem jednoqubitových NOT, takže řízené je jen součinem CNOT. Obvod výše pak implementuje následující stav před měřením:
kde jsme ve třetím řádku použili klasicky simulovatelný fázový posun . Střední hodnoty jsou proto získány jako
Díky těmto předpokladům jsme schopni vyjádřit střední hodnoty sledovaných operátorů s menším počtem řízených operací. Ve skutečnosti potřebujeme implementovat pouze řízenou přípravu stavu , nikoli řízené časové evoluce. Přeformulování výpočtu tímto způsobem nám umožní výrazně snížit hloubku výsledných obvodů.
Rozložení operátoru časové evoluce Trotterovou dekompozicí
Místo přesné implementace operátoru časové evoluce můžeme použít Trotterovu dekompozici pro implementaci jeho aproximace. Opakováním Trotterovy dekompozice určitého řádu několikrát dosáhneme dalšího snížení chyby způsobené aproximací. V následujícím kódu přímo sestavíme Trotterovu implementaci nejefektivnějším způsobem pro interakční graf uvažovaného Hamiltoniánu (pouze interakce nejbližších sousedů). V praxi vkládáme Pauliho rotace , , s parametrizovaným úhlem , které odpovídají přibližné implementaci . Kvůli rozdílu v definici Pauliho rotací a časové evoluce, kterou se snažíme implementovat, musíme použít parametr , abychom dosáhli časové evoluce . Navíc pro lichý počet opakování Trotterových kroků obrátíme pořadí operací, což je funkčně ekvivalentní, ale umožňuje syntézovat sousední operace do jediného unitáru . Tím získáme mnohem mělčí obvod než ten, který by byl získán pomocí obecné funkce PauliEvolutionGate().
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)

Použití optimalizovan ého obvodu pro přípravu stavu
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)
Šablonové obvody pro výpočet maticových prvků a pomocí Hadamardova testu
Jediný rozdíl mezi obvody použitými v Hadamardově testu bude fáze v operátoru časové evoluce a měřené observably. Proto můžeme připravit šablonový obvod reprezentující obecný obvod pro Hadamardův test se zástupnými symboly pro hradla závislá na operátoru časové evoluce.
# 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
Kombinací Trotterovy aproximace a neřízených unitárů jsme výrazně snížili hloubku Hadamardova testu.
Krok 3: Spuštění pomocí Qiskit primitiv
Inicializuj Backend a nastav parametry běhového prostředí
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)
Transpilace na QPU
Nejprve vybereme podmnožiny coupling map s „dobře" fungujícími qubity (kde „dobře" je zde poměrně libovolné — chceme hlavně vyloučit velmi špatně fungující qubity) a vytvoříme nový target pro Transpiler.
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,
)
Pak transpilujeme virtuální obvod na nejlepší fyzické rozmístění v tomto novém targetu.
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]