Formules multi-produits pour réduire l'erreur de Trotter
Utilisation estimée du QPU : quatre minutes sur un processeur Heron r2 (REMARQUE : il s'agit d'une estimation uniquement. Votre temps d'exécution peut varier.)
Contexte
Ce tutoriel démontre comment utiliser une Formule Multi-Produit (MPF) pour obtenir une erreur de Trotter plus faible sur notre observable par rapport à celle encourue par le circuit de Trotter le plus profond que nous exécuterons réellement. Les MPF réduisent l'erreur de Trotter de la dynamique hamiltonienne grâce à une combinaison pondérée de plusieurs exécutions de circuits. Considérez la tâche de trouver les valeurs d'espérance d'observable pour l'état quantique avec l'hamiltonien . On peut utiliser les Formules de Produit (PF) pour approximer l'évolution temporelle en procédant comme suit :
- Écrire l'hamiltonien comme où sont des opérateurs hermitiens tels que chaque unitaire correspondant puisse être implémenté efficacement sur un dispositif quantique.
- Approximer les termes qui ne commutent pas entre eux.
Ensuite, la PF de premier ordre (formule de Lie-Trotter) est :
qui a un terme d'erreur quadratique . On peut également utiliser des PF d'ordre supérieur (formules de Lie-Trotter-Suzuki), qui convergent plus rapidement, et qui sont définies récursivement comme :
où est l'ordre de la PF symétrique et . Pour les évolutions temporelles longues, on peut diviser l'intervalle de temps en intervalles, appelés pas de Trotter, de durée et approximer l'évolution temporelle dans chaque intervalle avec une formule de produit d'ordre . Ainsi, la PF d'ordre pour l'opérateur d'évolution temporelle sur pas de Trotter est :
où le terme d'erreur diminue avec le nombre de pas de Trotter et l'ordre de la PF.
Étant donné un entier et une formule de produit , l'état approximatif évolué temporellement peut être obtenu à partir de en appliquant itérations de la formule de produit .
est une approximation de avec l'erreur d'approximation de Trotter ||. Si nous considérons une combinaison linéaire d'approximations de Trotter de :
où sont nos coefficients de pondération, est la matrice densité correspondant à l'état pur obtenu en faisant évoluer l'état initial avec la formule de produit, , impliquant pas de Trotter, et indexe le nombre de PF qui composent la MPF. Tous les termes dans utilisent la même formule de produit comme base. L'objectif est d'améliorer || en trouvant avec un encore plus faible.
- n'a pas besoin d'être un état physique car n'a pas besoin d'être positif. L'objectif ici est de minimiser l'erreur dans la valeur d'espérance des observables et non de trouver un remplacement physique pour .
- détermine à la fois la profondeur du circuit et le niveau d'approximation de Trotter. Des valeurs plus petites de conduisent à des circuits plus courts, qui encourent moins d'erreurs de circuit mais seront une approximation moins précise de l'état désiré.
La clé ici est que l'erreur de Trotter résiduelle donnée par est plus petite que l'erreur de Trotter que l'on obtiendrait en utilisant simplement la plus grande valeur de .
Vous pouvez voir l'utilité de ceci sous deux perspectives :
- Pour un budget fixe de pas de Trotter que vous pouvez exécuter, vous pouvez obtenir des résultats avec une erreur de Trotter qui est plus petite au total.
- Étant donné un nombre cible de pas de Trotter qui est trop grand pour être exécuté, vous pouvez utiliser la MPF pour trouver une collection de circuits de profondeur inférieure à exécuter qui résulte en une erreur de Trotter similaire.
Prérequis
Avant de commencer ce tutoriel, assurez-vous d'avoir les éléments suivants installés :
- Qiskit SDK v1.0 ou version ultérieure, avec le support de visualisation
- Qiskit Runtime v0.22 ou version ultérieure (
pip install qiskit-ibm-runtime) - Addons Qiskit MPF (
pip install qiskit_addon_mpf) - Addons Qiskit utils (
pip install qiskit_addon_utils) - Bibliothèque Quimb (
pip install quimb) - Bibliothèque Qiskit Quimb (
pip install qiskit-quimb) - Numpy v0.21 pour la compatibilité entre les packages (
pip install numpy==0.21)
Partie I. Exemple à petite échelle
Explorer la stabilité de la MPF
Il n'y a pas de restriction évidente sur le choix du nombre de pas de Trotter qui composent l'état MPF . Cependant, ceux-ci doivent être choisis avec soin pour éviter les instabilités dans les valeurs d'espérance résultantes calculées à partir de . Une bonne règle générale est de définir le plus petit pas de Trotter de sorte que . Si vous souhaitez en savoir plus à ce sujet et sur la façon de choisir vos autres valeurs de , consultez le guide Comment choisir les pas de Trotter pour une MPF.
Dans l'exemple ci-dessous, nous explorons la stabilité de la solution MPF en calculant la valeur d'espérance de la magnétisation pour une plage de temps en utilisant différents états évolués temporellement. Plus précisément, nous comparons les valeurs d'espérance calculées à partir de chacune des évolutions temporelles approximatives implémentées avec les pas de Trotter correspondants et les différents modèles MPF (coefficients statiques et dynamiques) avec les valeurs exactes de l'observable évolué temporellement. Tout d'abord, définissons les paramètres pour les formules de Trotter et les temps d'évolution
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-mpf qiskit-addon-utils qiskit-aer qiskit-ibm-runtime rustworkx scipy
import numpy as np
mpf_trotter_steps = [1, 2, 4]
order = 2
symmetric = False
trotter_times = np.arange(0.5, 1.55, 0.1)
exact_evolution_times = np.arange(trotter_times[0], 1.55, 0.05)
Pour cet exemple, nous utiliserons l'état de Néel comme état initial et le modèle de Heisenberg sur une ligne de 10 sites pour l'hamiltonien régissant l'évolution temporelle
où est la force de couplage pour les arêtes de plus proches voisins.
from qiskit.transpiler import CouplingMap
from rustworkx.visualization import graphviz_draw
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
import numpy as np
L = 10
# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_line(L, bidirectional=False)
graphviz_draw(coupling_map.graph, method="circo")
# Get a qubit operator describing the Heisenberg field model
hamiltonian = generate_xyz_hamiltonian(
coupling_map,
coupling_constants=(1.0, 1.0, 1.0),
ext_magnetic_field=(0.0, 0.0, 0.0),
)
print(hamiltonian)
SparsePauliOp(['IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,
1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,
1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])
L'observable que nous mesurerons est la magnétisation sur une paire de qubits au milieu de la chaîne.
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)
print(observable)
SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])
Nous définissons une passe de transpilation pour regrouper les rotations XX et YY dans le circuit en une seule porte XX+YY. Cela nous permettra de tirer parti des propriétés de conservation du spin de TeNPy lors du calcul MPO, accélérant considérablement le calcul.
from qiskit.circuit.library import XXPlusYYGate
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes.optimization.collect_and_collapse import (
CollectAndCollapse,
collect_using_filter_function,
collapse_to_operation,
)
from functools import partial
def filter_function(node):
return node.op.name in {"rxx", "ryy"}
collect_function = partial(
collect_using_filter_function,
filter_function=filter_function,
split_blocks=True,
min_block_size=1,
)
def collapse_to_xx_plus_yy(block):
param = 0.0
for node in block.data:
param += node.operation.params[0]
return XXPlusYYGate(param)
collapse_function = partial(
collapse_to_operation,
collapse_function=collapse_to_xx_plus_yy,
)
pm = PassManager()
pm.append(CollectAndCollapse(collect_function, collapse_function))
Ensuite, nous créons les circuits implémentant les évolutions temporelles approximatives de Trotter.
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit import QuantumCircuit
# Initial Neel state preparation
initial_state_circ = QuantumCircuit(L)
initial_state_circ.x([i for i in range(L) if i % 2 != 0])
all_circs = []
for total_time in trotter_times:
mpf_trotter_circs = [
generate_time_evolution_circuit(
hamiltonian,
time=total_time,
synthesis=SuzukiTrotter(reps=num_steps, order=order),
)
for num_steps in mpf_trotter_steps
]
mpf_trotter_circs = pm.run(
mpf_trotter_circs
) # Collect XX and YY into XX + YY
mpf_circuits = [
initial_state_circ.compose(circuit) for circuit in mpf_trotter_circs
]
all_circs.append(mpf_circuits)
mpf_circuits[-1].draw("mpl", fold=-1)

Ensuite, nous calculons les valeurs d'espérance évoluées temporellement à partir des circuits de Trotter.
from copy import deepcopy
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
aer_sim = AerSimulator()
estimator = Estimator(mode=aer_sim)
mpf_expvals_all_times, mpf_stds_all_times = [], []
for t, mpf_circuits in zip(trotter_times, all_circs):
mpf_expvals = []
circuits = [deepcopy(circuit) for circuit in mpf_circuits]
pm_sim = generate_preset_pass_manager(
backend=aer_sim, optimization_level=3
)
isa_circuits = pm_sim.run(circuits)
result = estimator.run(
[(circuit, observable) for circuit in isa_circuits], precision=0.005
).result()
mpf_expvals = [res.data.evs for res in result]
mpf_stds = [res.data.stds for res in result]
mpf_expvals_all_times.append(mpf_expvals)
mpf_stds_all_times.append(mpf_stds)
Nous calculons également les valeurs d'espérance exactes pour comparaison.
from scipy.linalg import expm
from qiskit.quantum_info import Statevector
exact_expvals = []
for t in exact_evolution_times:
# Exact expectation values
exp_H = expm(-1j * t * hamiltonian.to_matrix())
initial_state = Statevector(initial_state_circ).data
time_evolved_state = exp_H @ initial_state
exact_obs = (
time_evolved_state.conj()
@ observable.to_matrix()
@ time_evolved_state
).real
exact_expvals.append(exact_obs)
Coefficients MPF statiques
Les MPF statiques sont celles où les valeurs ne dépendent pas du temps d'évolution, . Considérons la PF d'ordre avec pas de Trotter, cela peut s'écrire comme :
où sont des matrices qui dépendent des commutateurs des termes dans la décomposition de l'hamiltonien. Il est important de noter que eux-mêmes sont indépendants du temps et du nombre de pas de Trotter