Kwantowa diagonalizacja Krylova sieciowych hamiltonianów
Szacowany czas wykonania: 20 minut na procesorze Heron r2 (UWAGA: To jedynie szacunek. Rzeczywisty czas może się różnić.)
# 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
Podstawy teoretyczne
Ten samouczek pokazuje, jak zaimplementować algorytm kwantowej diagonalizacji Krylova (KQD) w kontekście wzorców Qiskit. Najpierw poznasz teorię stojącą za algorytmem, a następnie zobaczysz demonstrację jego wykonania na QPU.
W wielu dziedzinach nauki interesujemy się własnościami stanu podstawowego układów kwantowych. Przykłady obejmują rozumienie fundamentalnej natury cząstek i sił, przewidywanie i rozumienie zachowania złożonych materiałów oraz rozumienie biochemicznych interakcji i reakcji. Z uwagi na wykładniczy wzrost przestrzeni Hilberta i korelacje pojawiające się w splątanych układach, klasyczne algorytmy mają trudności z rozwiązaniem tego problemu dla kwantowych układów o rosnącym rozmiarze. Na jednym końcu spektrum są istniejące podejścia wykorzystujące sprzęt kwantowy, skupiające się na wariacyjnych metodach kwantowych (na przykład wariacjnym wyznaczaniu wartości własnych). Te techniki napotykają trudności z obecnymi urządzeniami ze względu na dużą liczbę wywołań funkcji wymaganych w procesie optymalizacji, co powoduje duże nakłady zasobów przy zastosowaniu zaawansowanych technik korekcji błędów, ograniczając ich skuteczność do małych układów. Na drugim końcu spektrum znajdują się tolerujące błędy metody kwantowe z gwarancjami wydajności (na przykład kwantowa estymacja fazy), które wymagają głębokich obwodów możliwych do wykonania jedynie na urządzeniu odpornym na błędy. Z tych powodów przedstawiamy tu algorytm kwantowy oparty na metodach podprzestrzeni (opisany w tym artykule przeglądowym) — algorytm kwantowej diagonalizacji Krylova (KQD). Algorytm ten dobrze skaluje się dla dużych układów [1] na istniejącym sprzęcie kwantowym, posiada podobne gwarancje wydajności jak estymacja fazy, jest kompatybilny z zaawansowanymi technikami łagodzenia błędów i może dostarczać wyniki niedostępne dla klasycznych komputerów.
Wymagania
Przed rozpoczęciem tego samouczka upewnij się, że masz zainstalowane:
- Qiskit SDK v2.0 lub nowszy, z obsługą wizualizacji
- Qiskit Runtime v0.22 lub nowszy (
pip install qiskit-ibm-runtime)
Konfiguracja
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: Odwzorowanie klasycznych danych wejściowych na problem kwantowy
Przestrzeń Krylova
Przestrzeń Krylova rzędu to przestrzeń rozpiętą przez wektory uzyskane przez mnożenie wyższych potęg macierzy , aż do , przez wektor referencyjny .
Jeśli macierz jest hamiltonianem , odpowiadającą przestrzeń nazwiemy potęgową przestrzenią Krylova . W przypadku gdy jest operatorem ewolucji czasowej generowanym przez hamiltonian , przestrzeń nazwiemy unitarną przestrzenią Krylova . Potęgowej podprzestrzeni Krylova używanej klasycznie nie można bezpośrednio wygenerować na komputerze kwantowym, ponieważ nie jest operatorem unitarnym. Zamiast tego możemy użyć operatora ewolucji czasowej , który — jak można wykazać — daje podobne gwarancje zbieżności jak metoda potęgowa. Potęgi stają się wówczas różnymi krokami czasowymi .
Szczegółowe wyprowadzenie tego, jak unitarna przestrzeń Krylova pozwala dokładnie reprezentować niskenergetyczne stany własne, znajdziesz w Dodatku.
Algorytm kwantowej diagonalizacji Krylova
Mając hamiltonian , który chcemy zdiagonalizować, najpierw rozważamy odpowiadającą mu unitarną przestrzeń Krylova . Celem jest znalezienie zwartej reprezentacji hamiltonianu w , którą oznaczymy . Elementy macierzowe , czyli rzutowanie hamiltonianu na przestrzeń Krylova, można obliczyć wyznaczając następujące wartości oczekiwane:
gdzie są wektorami unitarnej przestrzeni Krylova, a są wielokrotnościami wybranego kroku czasowego . Na komputerze kwantowym obliczenie każdego elementu macierzowego można wykonać dowolnym algorytmem pozwalającym uzyskać iloczyn skalarny między stanami kwantowymi. Ten samouczek koncentruje się na teście Hadamarda. Przyjmując, że ma wymiar , hamiltonian rzutowany na podprzestrzeń będzie miał wymiary . Przy wystarczająco małym (zazwyczaj wystarcza do uzyskania zbieżności szacowań energii własnych) możemy wówczas łatwo zdiagonalizować rzutowany hamiltonian . Nie możemy jednak bezpośrednio zdiagonalizować ze względu na nieortogonalność wektorów przestrzeni Krylova. Musimy zmierzyć ich iloczyny skalarne i skonstruować macierz :
Pozwala to rozwiązać zagadnienie własne w przestrzeni nieortogonalnej (zwane też uogólnionym zagadnieniem własnym):
Można następnie uzyskać szacunki wartości własnych i stanów własnych poprzez zbadanie wartości własnych . Na przykład szacunek energii stanu podstawowego otrzymuje się, biorąc najmniejszą wartość własną , a stan podstawowy z odpowiadającego wektora własnego . Współczynniki wyznaczają udział różnych wektorów rozpinających .

Rysunek przedstawia reprezentację obwodową zmodyfikowanego testu Hadamarda — metody stosowanej do obliczania iloczynu skalarnego między różnymi stanami kwantowymi. Dla każdego elementu macierzowego wykonuje się test Hadamarda między stanami i . Jest to zaznaczone na rysunku schematem kolorów elementów macierzy i odpowiadających im operacji , . Do obliczenia wszystkich elementów macierzowych rzutowanego hamiltonianu potrzebny jest zestaw testów Hadamarda dla wszystkich możliwych kombinacji wektorów przestrzeni Krylova. Górna linia w obwodzie testu Hadamarda to qubit pomocniczy (ancilla), mierzony w bazie X lub Y; jego wartość oczekiwana wyznacza wartość iloczynu skalarnego między stanami. Dolna linia reprezentuje wszystkie qubity hamiltonianu układu. Operacja przygotowuje qubit układu w stanie sterowanym stanem qubita pomocniczego (analogicznie ), a operacja reprezentuje dekompozycję Pauliego hamiltonianu układu . Bardziej szczegółowe wyprowadzenie operacji obliczanych przez test Hadamarda podane jest poniżej.
Definicja hamiltonianu
Rozważmy hamiltonian Heisenberga dla qubitów na łańcuchu liniowym:
# 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)]
Ustawianie parametrów algorytmu
Heurystycznie dobieramy wartość kroku czasowego dt (na podstawie górnych ograniczeń normy hamiltonianu). Praca [2] wykazała, że wystarczająco małym krokiem czasowym jest , oraz że do pewnego stopnia lepiej niedoszacować tę wartość niż ją przeszacować — przeszacowanie może bowiem dopuścić wkłady stanów wysokoenergetycznych, które zakłócają nawet optymalny stan w przestrzeni Krylova. Z drugiej strony zbyt małe prowadzi do gorszego uwarunkowania podprzestrzeni Krylova, ponieważ wektory bazy Krylova różnią się od siebie mniej z kroku na krok.
# 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)
Ustawiamy też pozostałe parametry algorytmu. Na potrzeby tego samouczka ograniczymy się do przestrzeni Krylova o zaledwie pięciu wymiarach, co jest dość restrykcyjne.
# Set parameters for quantum Krylov algorithm
krylov_dim = 5 # size of Krylov subspace
num_trotter_steps = 6
dt_circ = dt / num_trotter_steps
Przygotowanie stanu
Wybieramy stan referencyjny , który ma niezerowe nakładanie ze stanem podstawowym. Dla tego hamiltonianu używamy stanu z wzbudzeniem na środkowym qubicie jako naszego stanu referencyjnego.
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)
Ewolucja czasowa
Operator ewolucji czasowej generowany przez dany hamiltonian możemy zrealizować za pomocą aproksymacji Lie-Trottera.
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>
Test Hadamarda

gdzie jest jednym z wyrazów dekompozycji hamiltonianu , a , to sterowane operacje przygotowujące wektory , unitarnej przestrzeni Krylova, przy czym . Aby zmierzyć , najpierw stosujemy ...
... a następnie mierzymy:
Z tożsamości . Analogicznie, pomiar daje:
## 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
Obwód testu Hadamarda może być głębokim obwodem po dekompozycji do bramek natywnych (co jeszcze bardziej wzrośnie przy uwzględnieniu topologii urządzenia).
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: Optymalizacja problemu pod kątem wykonania na sprzęcie kwantowym
Wydajny test Hadamarda
Możemy zoptymalizować głębokie obwody dla testu Hadamarda, które uzyskaliśmy, wprowadzając pewne przybliżenia i opierając się na założeniach dotyczących hamiltoniana modelu. Rozważmy na przykład następujący obwód dla testu Hadamarda:

Załóżmy, że możemy klasycznie obliczyć , wartość własną pod hamiltonianem . Warunek ten jest spełniony, gdy hamiltonian zachowuje symetrię U(1). Choć może się to wydawać silnym założeniem, istnieje wiele przypadków, w których bezpiecznie można przyjąć, że istnieje stan próżni (w tym przypadku odpowiadający stanowi ), na który działanie hamiltonianu nie ma wpływu. Jest tak na przykład w przypadku hamiltonianów chemicznych opisujących stabilną cząsteczkę (gdzie liczba elektronów jest zachowana). Zakładając, że bramka przygotowuje pożądany stan referencyjny , na przykład w celu przygotowania stanu HF dla chemii byłby iloczynem jednoQubitowych bramek NOT, a zatem kontrolowane- jest jedynie iloczynem bramek CNOT. Wówczas powyższy obwód implementuje następujący stan przed pomiarem:
gdzie w trzecim wierszu użyliśmy klasycznie symulowanego przesunięcia fazy . W związku z tym wartości oczekiwane oblicza się jako