Estimation de l'énergie de l'état fondamental de la chaîne de Heisenberg avec VQE
Estimation d'utilisation : deux minutes sur un processeur Eagle r3 (REMARQUE : il s'agit d'une estimation uniquement. Votre temps d'exécution peut varier.)
Contexte
Ce tutoriel montre comment construire, déployer et exécuter un Qiskit pattern pour simuler une chaîne de Heisenberg et estimer son énergie de l'état fondamental. Pour plus d'informations sur les Qiskit patterns et comment Qiskit Serverless peut être utilisé pour les déployer dans le cloud en vue d'une exécution gérée, consultez notre page de documentation sur IBM Quantum® Platform.
Prérequis
Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :
- Qiskit SDK v1.2 ou version ultérieure, avec le support de visualisation
- Qiskit Runtime v0.28 ou version ultérieure (
pip install qiskit-ibm-runtime) - Qiskit Serverless (pip install qiskit_serverless)
- IBM Catalog (pip install qiskit-ibm-catalog)
Configuration
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
from typing import Sequence
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives.base import BaseEstimatorV2
from qiskit.circuit.library import XGate
from qiskit.circuit.library import efficient_su2
from qiskit.transpiler import PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes.scheduling import (
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session, Estimator
from qiskit_ibm_catalog import QiskitServerless, QiskitFunction
def visualize_results(results):
plt.plot(results["cost_history"], lw=2)
plt.xlabel("Iteration")
plt.ylabel("Energy")
plt.show()
def build_callback(
ansatz: QuantumCircuit,
hamiltonian: SparsePauliOp,
estimator: BaseEstimatorV2,
callback_dict: dict,
):
def callback(current_vector):
# Keep track of the number of iterations
callback_dict["iters"] += 1
# Set the prev_vector to the latest one
callback_dict["prev_vector"] = current_vector
# Compute the value of the cost function at the current vector
current_cost = (
estimator.run([(ansatz, hamiltonian, [current_vector])])
.result()[0]
.data.evs[0]
)
callback_dict["cost_history"].append(current_cost)
# Print to screen on single line
print(
"Iters. done: {} [Current cost: {}]".format(
callback_dict["iters"], current_cost
),
end="\r",
flush=True,
)
return callback
Étape 1 : Transposer les entrées classiques en un problème quantique
- Entrée : Nombre de spins
- Sortie : Ansatz et hamiltonien modélisant la chaîne de Heisenberg
Construisez un ansatz et un hamiltonien qui modélisent une chaîne de Heisenberg à 10 spins. Tout d'abord, nous importons quelques paquets génériques et créons quelques fonctions utilitaires.
num_spins = 10
ansatz = efficient_su2(num_qubits=num_spins, reps=3)
# Remember to insert your token in the QiskitRuntimeService constructor
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=num_spins, simulator=False
)
coupling = backend.target.build_coupling_map()
reduced_coupling = coupling.reduce(list(range(num_spins)))
edge_list = reduced_coupling.graph.edge_list()
ham_list = []
for edge in edge_list:
ham_list.append(("ZZ", edge, 0.5))
ham_list.append(("YY", edge, 0.5))
ham_list.append(("XX", edge, 0.5))
for qubit in reduced_coupling.physical_qubits:
ham_list.append(("Z", [qubit], np.random.random() * 2 - 1))
hamiltonian = SparsePauliOp.from_sparse_list(ham_list, num_qubits=num_spins)
ansatz.draw("mpl", style="iqp")

Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique
- Entrée : Circuit abstrait, observable
- Sortie : Circuit et observable cibles, optimisés pour le QPU sélectionné
Utilisez la fonction generate_preset_pass_manager de Qiskit pour générer automatiquement une routine d'optimisation pour notre circuit par rapport au QPU sélectionné. Nous choisissons optimization_level=3, qui fournit le niveau d'optimisation le plus élevé des gestionnaires de passes prédéfinis. Nous incluons également les passes de planification ALAPScheduleAnalysis et PadDynamicalDecoupling pour supprimer les erreurs de décohérence.
target = backend.target
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
pm.scheduling = PassManager(
[
ALAPScheduleAnalysis(durations=target.durations()),
PadDynamicalDecoupling(
durations=target.durations(),
dd_sequence=[XGate(), XGate()],
pulse_alignment=target.pulse_alignment,
),
]
)
ansatz_ibm = pm.run(ansatz)
observable_ibm = hamiltonian.apply_layout(ansatz_ibm.layout)
ansatz_ibm.draw("mpl", scale=0.6, style="iqp", fold=-1, idle_wires=False)

Étape 3 : Exécuter à l'aide des primitives Qiskit
- Entrée : Circuit et observable cibles
- Sortie : Résultats de l'optimisation
Minimisez l'énergie estimée de l'état fondamental du système en optimisant les paramètres du circuit. Utilisez la primitive Estimator de Qiskit Runtime pour évaluer la fonction de coût pendant l'optimisation.
Pour cette démonstration, nous exécuterons sur un QPU en utilisant les primitives qiskit-ibm-runtime. Pour exécuter avec les primitives basées sur le vecteur d'état de qiskit, remplacez le bloc de code utilisant les primitives Qiskit IBM Runtime par le bloc commenté.
# SciPy minimizer routine
def cost_func(
params: Sequence,
ansatz: QuantumCircuit,
hamiltonian: SparsePauliOp,
estimator: BaseEstimatorV2,
) -> float:
"""Ground state energy evaluation."""
return (
estimator.run([(ansatz, hamiltonian, [params])])
.result()[0]
.data.evs[0]
)
num_params = ansatz_ibm.num_parameters
params = 2 * np.pi * np.random.random(num_params)
callback_dict = {
"prev_vector": None,
"iters": 0,
"cost_history": [],
}
# Evaluate the problem on a QPU by using Qiskit IBM Runtime
with Session(backend=backend) as session:
estimator = Estimator()
callback = build_callback(
ansatz_ibm, observable_ibm, estimator, callback_dict
)
res = minimize(
cost_func,
x0=params,
args=(ansatz_ibm, observable_ibm, estimator),
callback=callback,
method="cobyla",
options={"maxiter": 100},
)
visualize_results(callback_dict)
Étape 4 : Post-traiter et renvoyer le résultat dans le format classique souhaité
- Entrée : Estimations de l'énergie de l'état fondamental pendant l'optimisation
- Sortie : Énergie estimée de l'état fondamental
print(f'Estimated ground state energy: {res["fun"]}')
Déployer le patron Qiskit dans le cloud
Pour ce faire, déplacez le code source ci-dessus dans un fichier, ./source/heisenberg.py, encapsulez le code dans un script qui prend des entrées et renvoie la solution finale, puis téléversez-le vers un cluster distant en utilisant la classe QiskitFunction de qiskit-ibm-catalog. Pour des conseils sur la spécification des dépendances externes, le passage d'arguments d'entrée, et plus encore, consultez les guides Qiskit Serverless.
L'entrée du patron est le nombre de spins dans la chaîne. La sortie est une estimation de l'énergie de l'état fondamental du système.
# Authenticate to the remote cluster and submit the pattern for remote execution
serverless = QiskitServerless()
heisenberg_function = QiskitFunction(
title="ibm_heisenberg",
entrypoint="heisenberg.py",
working_dir="./source/",
)
serverless.upload(heisenberg_function)
Exécuter le patron Qiskit en tant que service géré
Une fois que nous avons téléversé le patron dans le cloud, nous pouvons facilement l'exécuter en utilisant le client QiskitServerless.
# Run the pattern on the remote cluster
ibm_heisenberg = serverless.load("ibm_heisenberg")
job = serverless.run(ibm_heisenberg)
solution = job.result()
print(solution)
print(job.logs())
Enquête sur le tutoriel
Veuillez répondre à cette courte enquête afin de nous donner votre avis sur ce tutoriel. Vos commentaires nous aideront à améliorer nos contenus et l'expérience utilisateur.