Istanze ed estensioni
Questo capitolo tratta diversi algoritmi variazionali quantistici, tra cui:
- Variational Quantum Eigensolver (VQE)
- Subspace Search VQE (SSVQE)
- Variational Quantum Deflation (VQD)
- Quantum Sampling Regression (QSR)
Utilizzando questi algoritmi, impareremo diverse idee progettuali che possono essere incorporate in algoritmi variazionali personalizzati, come pesi, penalità, sovra-campionamento e sotto-campionamento. Ti incoraggiamo a sperimentare con questi concetti e a condividere i tuoi risultati con la comunità.
Il framework dei pattern Qiskit si applica a tutti questi algoritmi — ma indicheremo esplicitamente i passaggi solo nel primo esempio.
Variational Quantum Eigensolver (VQE)
Il VQE è uno degli algoritmi quantistici variazionali più diffusi e stabilisce un modello su cui altri algoritmi possono basarsi.
Passo 1: Mappare gli input classici in un problema quantistico
Schema teorico
La struttura del VQE è semplice:
- Preparare gli operatori di riferimento
- Si parte dallo stato e si arriva allo stato di riferimento
- Applicare la forma variazionale per creare un ansatz
- Si passa dallo stato a
- Effettuare il bootstrap a se si dispone di un problema simile (tipicamente trovato tramite simulazione classica o campionamento)
- Ogni ottimizzatore viene inizializzato in modo diverso, producendo un insieme iniziale di vettori di parametri (ad esempio, a partire da un punto iniziale ).
- Valutare la funzione di costo per tutti gli stati preparati su un computer quantistico.
- Utilizzare un ottimizzatore classico per selezionare il prossimo insieme di parametri .
- Ripetere il processo fino al raggiungimento della convergenza.
Questo è un semplice ciclo di ottimizzazione classica in cui si valuta la funzione di costo. Alcuni ottimizzatori possono richiedere più valutazioni per calcolare un gradiente, determinare l'iterazione successiva o verificare la convergenza.
Ecco l'esempio per il seguente osservabile:
Implementazione
# 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()
Possiamo usare questa funzione di costo per calcolare i parametri ottimali
# 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
Passo 2: Ottimizzare il problema per l'esecuzione quantistica
Selezioneremo il backend meno occupato e importeremo i componenti necessari da 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')>
Traspileremo il circuito usando il pass manager preimpostato con livello di ottimizzazione 3, e applicheremo il layout corrispondente all'osservabile.
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)
Passo 3: Eseguire usando le primitive di Qiskit Runtime
Siamo ora pronti per eseguire il calcolo sull'hardware IBM Quantum®. Poiché la minimizzazione della funzione di costo è altamente iterativa, avvieremo una sessione Runtime. In questo modo, dovremo aspettare in coda una sola volta. Una volta che il job inizia l'esecuzione, ogni iterazione con aggiornamenti dei parametri verrà eseguita immediatamente.
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)
Passo 4: Post-elaborazione, restituzione del risultato in formato classico
Possiamo vedere che la routine di minimizzazione è terminata con successo, il che significa che abbiamo raggiunto la tolleranza predefinita dell'ottimizzatore classico COBYLA. Se richiedessimo un risultato più preciso, potremmo specificare una tolleranza più piccola. Questo potrebbe effettivamente essere il caso, dato che il risultato si discostava di diversi punti percentuali rispetto al risultato ottenuto dal simulatore sopra.
Il valore di x ottenuto è la migliore stima corrente per i parametri che minimizzano la funzione di costo. Se si itera per ottenere una precisione maggiore, quei valori dovrebbero essere usati al posto dell'x0 inizialmente utilizzato (un vettore di uni).
Infine, notiamo che la funzione è stata valutata 96 volte nel processo di ottimizzazione. Questo potrebbe differire dal numero di passi di ottimizzazione, poiché alcuni ottimizzatori richiedono più valutazioni della funzione in un singolo passo, ad esempio quando si stima un gradiente.
Subspace Search VQE (SSVQE)
Lo SSVQE è una variante del VQE che permette di ottenere i primi autovalori di un osservabile con autovalori , dove . Senza perdita di generalità, assumiamo che . SSVQE introduce una nuova idea aggiungendo pesi per aiutare a dare priorità all'ottimizzazione del termine con il peso maggiore.
Per implementare questo algoritmo, abbiamo bisogno di stati di riferimento mutuamente ortogonali , ovvero tali che per . Questi stati possono essere costruiti usando operatori di Pauli. La funzione di costo di questo algoritmo è quindi:
dove è un numero positivo arbitrario tale che se allora , e è la forma variazionale definita dall'utente.
L'algoritmo SSVQE si basa sul fatto che gli autostati corrispondenti a diversi autovalori sono mutuamente ortogonali. In particolare, il prodotto interno di e può essere espresso come: