Formule multi-prodotto per ridurre l'errore di Trotter
Utilizzo stimato della QPU: Quattro minuti su un processore Heron r2 (NOTA: Questa è solo una stima. Il tempo di esecuzione potrebbe variare.)
Contesto
Questo tutorial dimostra come utilizzare una Formula Multi-Prodotto (MPF) per ottenere un errore di Trotter inferiore sull'osservabile rispetto a quello causato dal circuito di Trotter più profondo che effettivamente eseguiremo. Le MPF riducono l'errore di Trotter della dinamica hamiltoniana attraverso una combinazione pesata di diverse esecuzioni di circuiti. Considerate il compito di trovare i valori di aspettazione di osservabili per lo stato quantistico con l'hamiltoniana . Si possono utilizzare le Formule di Prodotto (PF) per approssimare l'evoluzione temporale procedendo come segue:
- Scrivere l'hamiltoniana come dove sono operatori hermitiani tali che ciascun unitario corrispondente possa essere implementato in modo efficiente su un dispositivo quantistico.
- Approssimare i termini che non commutano tra loro.
Quindi, la PF del primo ordine (formula di Lie-Trotter) è:
che ha un termine di errore quadratico . Si possono anche utilizzare PF di ordine superiore (formule di Lie-Trotter-Suzuki), che convergono più rapidamente e sono definite ricorsivamente come:
dove è l'ordine della PF simmetrica e . Per evoluzioni temporali lunghe, si può dividere l'intervallo di tempo in intervalli, chiamati passi di Trotter, di durata e approssimare l'evoluzione temporale in ciascun intervallo con una formula di prodotto di ordine . Quindi, la PF di ordine per l'operatore di evoluzione temporale su passi di Trotter è:
dove il termine di errore diminuisce con il numero di passi di Trotter e l'ordine della PF.
Dato un intero e una formula di prodotto , lo stato approssimato evoluto nel tempo può essere ottenuto da applicando iterazioni della formula di prodotto .
è un'approssimazione di con l'errore di approssimazione di Trotter ||. Se consideriamo una combinazione lineare di approssimazioni di Trotter di :
dove sono i nostri coefficienti di ponderazione, è la matrice densità corrispondente allo stato puro ottenuto evolvendo lo stato iniziale con la formula di prodotto, , che coinvolge passi di Trotter, e indicizza il numero di PF che compongono la MPF. Tutti i termini in utilizzano la stessa formula di prodotto come base. L'obiettivo è migliorare || trovando con ancora più basso.
- non deve necessariamente essere uno stato fisico poiché non deve essere positivo. L'obiettivo qui è minimizzare l'errore nel valore di aspettazione degli osservabili e non trovare una sostituzione fisica per .
- determina sia la profondità del circuito che il livello di approssimazione di Trotter. Valori più piccoli di portano a circuiti più brevi, che incorrono in minori errori di circuito ma saranno un'approssimazione meno accurata dello stato desiderato.
La chiave qui è che l'errore di Trotter residuo dato da è più piccolo dell'errore di Trotter che si otterrebbe semplicemente utilizzando il valore più grande di .
Potete vedere l'utilità di questo da due prospettive:
- Per un budget fisso di passi di Trotter che potete eseguire, potete ottenere risultati con un errore di Trotter che è complessivamente più piccolo.
- Dato un numero target di passi di Trotter che è troppo grande da eseguire, potete utilizzare la MPF per trovare una collezione di circuiti a profondità inferiore da eseguire che risulta in un errore di Trotter simile.
Requisiti
Prima di iniziare questo tutorial, assicuratevi di avere installato quanto segue:
- Qiskit SDK v1.0 o successivo, con supporto per la visualizzazione
- Qiskit Runtime v0.22 o successivo (
pip install qiskit-ibm-runtime) - MPF Qiskit addons (
pip install qiskit_addon_mpf) - Qiskit addons utils (
pip install qiskit_addon_utils) - Libreria Quimb (
pip install quimb) - Libreria Qiskit Quimb (
pip install qiskit-quimb) - Numpy v0.21 per la compatibilità tra i pacchetti (
pip install numpy==0.21)
Parte I. Esempio su piccola scala
Esplorare la stabilità della MPF
Non c'è una restrizione ovvia sulla scelta del numero di passi di Trotter che compongono lo stato MPF . Tuttavia, questi devono essere scelti con attenzione per evitare instabilità nei valori di aspettazione risultanti calcolati da . Una buona regola generale è impostare il passo di Trotter più piccolo in modo che . Se volete saperne di più su questo e su come scegliere gli altri valori , fate riferimento alla guida Come scegliere i passi di Trotter per una MPF.
Nell'esempio seguente esploriamo la stabilità della soluzione MPF calcolando il valore di aspettazione della magnetizzazione per un intervallo di tempi utilizzando diversi stati evoluti nel tempo. Nello specifico, confrontiamo i valori di aspettazione calcolati da ciascuna delle evoluzioni temporali approssimate implementate con i corrispondenti passi di Trotter e i vari modelli MPF (coefficienti statici e dinamici) con i valori esatti dell'osservabile evoluto nel tempo. Innanzitutto, definiamo i parametri per le formule di Trotter e i tempi di evoluzione
# 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)
Per questo esempio utilizzeremo lo stato di Neel come stato iniziale e il modello di Heisenberg su una linea di 10 siti per l'hamiltoniana che governa l'evoluzione temporale
dove è l'intensità dell'accoppiamento per i bordi tra i vicini più prossimi.
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'osservabile che misureremo è la magnetizzazione su una coppia di qubit al centro della catena.
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])
Definiamo un passo del transpiler per raccogliere le rotazioni XX e YY nel circuito come un'unica gate XX+YY. Questo ci permetterà di sfruttare le proprietà di conservazione dello spin di TeNPy durante il calcolo MPO, velocizzando significativamente il calcolo.
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))
Quindi creiamo i circuiti che implementano le evoluzioni temporali approssimate di 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)

Successivamente, calcoliamo i valori di aspettazione evoluti nel tempo dai circuiti di 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)
Calcoliamo anche i valori di aspettazione esatti per il confronto.
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)
Coefficienti MPF statici
Le MPF statiche sono quelle in cui i valori non dipendono dal tempo di evoluzione, . Consideriamo la PF di ordine con passi di Trotter, questa può essere scritta come:
dove sono matrici che dipendono dai commutatori dei termini nella decomposizione dell'hamiltoniana. È importante notare che stessi sono indipendenti dal tempo e dal numero di passi di Trotter . Pertanto, è possibile cancellare i termini di errore di ordine inferiore che contribuiscono a con una scelta attenta dei pesi della combinazione lineare. Per cancellare l'errore di Trotter per i primi termini (questi daranno i contributi maggiori poiché corrispondono al numero inferiore di passi di Trotter) nell'espressione per , i coefficienti devono soddisfare le seguenti equazioni:
con