Instancje i rozszerzenia
Ten rozdział obejmie kilka kwantowych algorytmów wariacyjnych, w tym
- Wariacyjny kwantowy solver wartości własnych (VQE)
- Wariacyjny kwantowy solver wartości własnych z wyszukiwaniem podprzestrzeni (SSVQE)
- Wariacyjna kwantowa deflacja (VQD)
- Regresja przez próbkowanie kwantowe (QSR)
Korzystając z tych algorytmów, poznamy kilka pomysłów projektowych, które można wykorzystać w niestandardowych algorytmach wariacyjnych, takich jak wagi, kary, nadpróbkowanie i podpróbkowanie. Zachęcamy do eksperymentowania z tymi koncepcjami i dzielenia się swoimi odkryciami ze społecznością.
Framework Qiskit patterns ma zastosowanie do wszystkich tych algorytmów — ale kroki omówimy wyraźnie tylko w pierwszym przykładzie.
Wariacyjny kwantowy solver wartości własnych (VQE)
VQE jest jednym z najczęściej używanych wariacyjnych algorytmów kwantowych, stanowiąc szablon, na którym budowane są inne algorytmy.
Krok 1: Mapowanie klasycznych danych wejściowych na problem kwantowy
Układ teoretyczny
Układ VQE jest prosty:
- Przygotuj operatory referencyjne
- Zaczynamy od stanu i przechodzimy do stanu referencyjnego
- Zastosuj formę wariacyjną , aby utworzyć ansatz
- Przechodzimy ze stanu do
- Zainicjuj (bootstrap) przy , jeśli mamy podobny problem (zazwyczaj znaleziony poprzez klasyczną symulację lub próbkowanie)
- Każdy optymalizator będzie inicjowany inaczej, co daje początkowy zestaw wektorów parametrów (na przykład z punktu początkowego ).
- Oblicz funkcję kosztu dla wszystkich przygotowanych stanów na komputerze kwantowym.
- Użyj klasycznego optymalizatora, aby wybrać następny zestaw parametrów .
- Powtarzaj proces, aż do osiągnięcia zbieżności.
Jest to prosta klasyczna pętla optymalizacyjna, w której oceniamy funkcję kosztu. Niektóre optymalizatory mogą wymagać wielokrotnych ewaluacji w celu obliczenia gradientu, wyznaczenia kolejnej iteracji lub oceny zbieżności.
Oto przykład dla następującej obserwabli:
Implementacja
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime scipy
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import TwoLocal
import numpy as np
theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()
observable = SparsePauliOp.from_list([("II", 2), ("XX", -2), ("YY", 3), ("ZZ", -3)])
reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)
variational_form = TwoLocal(
2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)
ansatz = reference_circuit.compose(variational_form)
ansatz.decompose().draw("mpl")
def cost_func_vqe(parameters, ansatz, hamiltonian, estimator):
"""Return estimate of energy from estimator
Parameters:
params (ndarray): Array of ansatz parameters
ansatz (QuantumCircuit): Parameterized ansatz circuit
hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
estimator (Estimator): Estimator primitive instance
Returns:
float: Energy estimate
"""
estimator_job = estimator.run([(ansatz, hamiltonian, [parameters])])
estimator_result = estimator_job.result()[0]
cost = estimator_result.data.evs[0]
return cost
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
Możemy użyć tej funkcji kosztu do obliczenia optymalnych parametrów
# SciPy minimizer routine
from scipy.optimize import minimize
x0 = np.ones(8)
result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="COBYLA"
)
result
message: Optimization terminated successfully.
success: True
status: 1
fun: -5.999999982445723
x: [ 1.741e+00 9.606e-01 1.571e+00 2.115e-05 1.899e+00
1.243e+00 6.063e-01 6.063e-01]
nfev: 136
maxcv: 0.0
Krok 2: Optymalizacja problemu do wykonania kwantowego
Wybierzemy najmniej obciążony backend i zaimportujemy niezbędne komponenty z qiskit_ibm_runtime.
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Session, EstimatorOptions
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
print(backend)
<IBMBackend('ibm_brisbane')>
Przetranspilujemy obwód za pomocą predefiniowanego menedżera przebiegów (pass manager) z poziomem optymalizacji 3 i zastosujemy odpowiadający mu układ (layout) do obserwabli.
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_ansatz = pm.run(ansatz)
isa_observable = observable.apply_layout(layout=isa_ansatz.layout)
Krok 3: Wykonanie przy użyciu prymitywów Qiskit Runtime
Jesteśmy teraz gotowi do uruchomienia naszych obliczeń na sprzęcie IBM Quantum®. Ponieważ minimalizacja funkcji kosztu jest wysoce iteracyjna, rozpoczniemy sesję Runtime. W ten sposób będziemy musieli poczekać w kolejce tylko raz. Gdy zadanie zacznie się wykonywać, każda iteracja z aktualizacjami parametrów zostanie uruchomiona natychmiast.
x0 = np.ones(8)
estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)
with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
result = minimize(
cost_func_vqe,
x0,
args=(isa_ansatz, isa_observable, estimator),
method="COBYLA",
options={"maxiter": 200, "disp": True},
)
session.close()
print(result)
Krok 4: Przetwarzanie końcowe, zwrócenie wyniku w formacie klasycznym
Widzimy, że procedura minimalizacji zakończyła się pomyślnie, co oznacza, że osiągnęliśmy domyślną tolerancję klasycznego optymalizatora COBYLA. Jeśli wymagamy bardziej precyzyjnego wyniku, możemy określić mniejszą tolerancję. Może to być rzeczywiście przypadek, ponieważ wynik różnił się o kilka procent w porównaniu z wynikiem uzyskanym przez powyższy symulator.
Uzyskana wartość x jest aktualnym najlepszym przybliżeniem parametrów minimalizujących funkcję kosztu. Jeśli iterujemy w celu uzyskania wyższej precyzji, wartości te powinny być użyte zamiast początkowo użytego x0 (wektora jedynek).
Na koniec zauważamy, że funkcja została obliczona 96 razy w procesie optymalizacji. Może to różnić się od liczby kroków optymalizacji, ponieważ niektóre optymalizatory wymagają wielu ocen funkcji w pojedynczym kroku, np. przy estymacji gradientu.
Subspace Search VQE (SSVQE)
SSVQE to wariant VQE, który umożliwia uzyskanie pierwszych wartości własnych obserwabli o wartościach własnych , gdzie . Bez utraty ogólności zakładamy, że . SSVQE wprowadza nową ideę, dodając wagi, które pomagają ustalić priorytet optymalizacji dla składnika o największej wadze.
Aby zaimplementować ten algorytm, potrzebujemy wzajemnie ortogonalnych stanów referencyjnych , co oznacza, że dla . Stany te można skonstruować za pomocą operatorów Pauli. Funkcja kosztu tego algorytmu jest następująca:
gdzie jest dowolną liczbą dodatnią taką, że jeśli , to , a jest zdefiniowaną przez użytkownika formą wariacyjną.
Algorytm SSVQE opiera się na fakcie, że stany własne odpowiadające różnym wartościom własnym są wzajemnie ortogonalne. Konkretnie, iloczyn skalarny i można wyrazić jako:
Pierwsza równość zachodzi, ponieważ jest operatorem kwantowym i w związku z tym jest unitarny. Ostatnia równość zachodzi ze względu na ortogonalność stanów referencyjnych . Fakt, że ortogonalność jest zachowana poprzez transformacje unitarne, jest ściśle związany z zasadą zachowania informacji, wyrażoną w kwantowej nauce o informacji. W tym ujęciu transformacje nieunitarne reprezentują procesy, w których informacja jest albo tracona, albo wstrzykiwana.
Wagi pomagają zapewnić, że wszystkie stany są stanami własnymi. Jeśli wagi są wystarczająco różne, składnikowi o największej wadze (to znaczy ) zostanie nadany priorytet podczas optymalizacji w stosunku do pozostałych. W rezultacie powstały stan stanie się stanem własnym odpowiadającym . Ponieważ