Instances et extensions
Ce chapitre couvre plusieurs algorithmes quantiques variationnels, notamment
- Variational Quantum Eigensolver (VQE)
- Subspace Search VQE (SSVQE)
- Variational Quantum Deflation (VQD)
- Quantum Sampling Regression (QSR)
En utilisant ces algorithmes, tu apprendras plusieurs idées de conception pouvant être intégrées dans des algorithmes variationnels personnalisés, comme les poids, les pénalités, le sur-échantillonnage et le sous-échantillonnage. Nous t'encourageons à expérimenter ces concepts et à partager tes découvertes avec la communauté.
Le framework Qiskit patterns s'applique à tous ces algorithmes — mais nous ne mentionnerons explicitement les étapes que dans le premier exemple.
Variational Quantum Eigensolver (VQE)
VQE est l'un des algorithmes quantiques variationnels les plus utilisés, établissant un modèle sur lequel d'autres algorithmes peuvent s'appuyer.
Étape 1 : Mapper les entrées classiques vers un problème quantique
Organisation théorique
La structure de VQE est simple :
- Préparer les opérateurs de référence
- On part de l'état et on atteint l'état de référence
- Appliquer la forme variationnelle pour créer un ansatz
- On passe de l'état à
- Amorcer à si on dispose d'un problème similaire (généralement trouvé via simulation classique ou échantillonnage)
- Chaque optimiseur sera amorcé différemment, produisant un ensemble initial de vecteurs de paramètres (par exemple, à partir d'un point initial ).
- Évaluer la fonction de coût pour tous les états préparés sur un ordinateur quantique.
- Utiliser un optimiseur classique pour sélectionner le prochain ensemble de paramètres .
- Répéter le processus jusqu'à convergence.
Il s'agit d'une simple boucle d'optimisation classique dans laquelle on évalue la fonction de coût. Certains optimiseurs peuvent nécessiter plusieurs évaluations pour calculer un gradient, déterminer la prochaine itération ou évaluer la convergence.
Voici l'exemple pour l'observable suivante :
Implémentation
# 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()
On peut utiliser cette fonction de coût pour calculer les paramètres optimaux
# 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
Étape 2 : Optimiser le problème pour l'exécution quantique
Nous allons sélectionner le backend le moins occupé et importer les composants nécessaires depuis 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')>
Nous allons transpiler le circuit en utilisant le gestionnaire de passes préconfiguré avec le niveau d'optimisation 3, et appliquer la disposition correspondante à l'observable.
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)
Étape 3 : Exécuter avec les primitives Qiskit Runtime
Nous sommes maintenant prêts à lancer notre calcul sur le matériel IBM Quantum®. Comme la minimisation de la fonction de coût est très itérative, nous allons démarrer une session Runtime. De cette façon, nous n'aurons à attendre dans la file d'attente qu'une seule fois. Une fois la tâche en cours d'exécution, chaque itération avec les mises à jour des paramètres s'exécutera immédiatement.
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)
Étape 4 : Post-traitement, retourner le résultat en format classique
On peut voir que la routine de minimisation s'est terminée avec succès, ce qui signifie que nous avons atteint la tolérance par défaut de l'optimiseur classique COBYLA. Si un résultat plus précis est requis, on peut spécifier une tolérance plus petite. Cela peut effectivement être nécessaire, car le résultat était décalé de plusieurs pourcents par rapport au résultat obtenu par le simulateur ci-dessus.
La valeur de x obtenue est la meilleure estimation actuelle des paramètres qui minimisent la fonction de coût. En cas d'itération pour obtenir une précision plus élevée, ces valeurs devraient être utilisées à la place du x0 initialement utilisé (un vecteur de uns).
Enfin, notons que la fonction a été évaluée 96 fois au cours du processus d'optimisation. Ce nombre peut différer du nombre d'étapes d'optimisation, car certains optimiseurs nécessitent plusieurs évaluations de la fonction en une seule étape, par exemple lors de l'estimation d'un gradient.
Subspace Search VQE (SSVQE)
SSVQE est une variante de VQE qui permet d'obtenir les premières valeurs propres d'une observable avec les valeurs propres , où . Sans perte de généralité, on suppose que . SSVQE introduit une nouvelle idée en ajoutant des poids pour aider à prioriser l'optimisation du terme ayant le poids le plus élevé.
Pour implémenter cet algorithme, nous avons besoin de états de référence mutuellement orthogonaux , c'est-à-dire pour . Ces états peuvent être construits à partir d'opérateurs de Pauli. La fonction de coût de cet algorithme est alors :
où est un nombre positif arbitraire tel que si alors , et est la forme variationnelle définie par l'utilisateur.
L'algorithme SSVQE repose sur le fait que les états propres correspondant à des valeurs propres différentes sont mutuellement orthogonaux. Plus précisément, le produit scalaire de et peut s'exprimer comme suit :
La première égalité tient parce que est un opérateur quantique et est donc unitaire. La dernière égalité tient en raison de l'orthogonalité des états de référence . Le fait que l'orthogonalité soit préservée par les transformations unitaires est profondément lié au principe de conservation de l'information, tel qu'il est exprimé en science de l'information quantique. Sous cet angle, les transformations non unitaires représentent des processus où l'information est soit perdue, soit injectée.
Les poids aident à garantir que tous les états sont des états propres. Si les poids sont suffisamment différents, le terme ayant le plus grand poids (c'est-à-dire ) sera prioritaire lors de l'optimisation par rapport aux autres. Par conséquent, l'état résultant deviendra l'état propre correspondant à . Comme sont mutuellement orthogonaux, les états restants lui seront orthogonaux et, par conséquent, contenus dans le sous-espace correspondant aux valeurs propres .
En appliquant le même raisonnement au reste des termes, la prochaine priorité serait alors le terme de poids , de sorte que serait l'état propre correspondant à , et les autres termes seraient contenus dans l'espace propre de .
En raisonnant par induction, on déduit que sera un état propre approché de pour
Organisation théorique
SSVQE peut se résumer comme suit :
- Préparer plusieurs états de référence en appliquant un unitaire U_R à k états de base computationnels différents
- Cet algorithme nécessite l'utilisation de états de référence mutuellement orthogonaux , tels que pour .
- Appliquer la forme variationnelle à chaque état de référence, donnant l'ansatz .
- Amorcer à si un problème similaire est disponible (généralement trouvé via simulation classique ou échantillonnage).
- Évaluer la fonction de coût pour tous les états préparés sur un ordinateur quantique.
- Cela peut se décomposer en calculant la valeur espérée d'une observable et en multipliant ce résultat par .
- Ensuite, la fonction de coût retourne la somme de toutes les valeurs espérées pondérées.
- Utiliser un optimiseur classique pour déterminer le prochain ensemble de paramètres .
- Répéter les étapes ci-dessus jusqu'à convergence.
Tu vas reconstruire la fonction de coût de SSVQE dans l'évaluation, mais nous avons l'extrait suivant pour t'aider à élaborer ta solution :
import numpy as np
def cost_func_ssvqe(
params, initialized_anastz_list, weights, ansatz, hamiltonian, estimator
):
# """Return estimate of energy from estimator
# Parameters:
# params (ndarray): Array of ansatz parameters
# initialized_anastz_list (list QuantumCircuit): Array of initialised ansatz with reference
# weights (list): List of weights
# ansatz (QuantumCircuit): Parameterized ansatz circuit
# hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
# estimator (Estimator): Estimator primitive instance
# Returns:
# float: Weighted energy estimate
# """
energies = []
# Define SSVQE
weighted_energy_sum = np.dot(energies, weights)
return weighted_energy_sum
Déflexion Quantique Variationnelle (VQD)
VQD est une méthode itérative qui étend VQE pour obtenir les premières valeurs propres d'un observable aux valeurs propres , où , et non plus seulement la première. Pour la suite de cette section, on supposera, sans perte de généralité, que . VQD introduit la notion de coût de pénalité pour guider le processus d'optimisation.
VQD introduit un terme de pénalité, noté , pour équilibrer la contribution de chaque terme de chevauchement au coût. Ce terme de pénalité sert à pénaliser le processus d'optimisation si l'orthogonalité n'est pas atteinte. On impose cette contrainte car les états propres d'un observable, ou d'un opérateur hermitien, correspondant à des valeurs propres différentes sont toujours mutuellement orthogonaux, ou peuvent l'être en cas de dégénérescence ou de valeurs propres répétées. Ainsi, en imposant l'orthogonalité avec l'état propre correspondant à , on optimise effectivement sur le sous-espace correspondant aux valeurs propres restantes . Ici, est la valeur propre la plus basse parmi les valeurs propres restantes et, par conséquent, la solution optimale du nouveau problème peut être obtenue à l'aide du théorème variationnel.
L'idée générale derrière VQD est d'utiliser VQE comme d'habitude pour obtenir la valeur propre la plus basse ainsi que l'état propre (approximatif) correspondant