Fórmulas multiproducto para reducir el error de Trotter
Uso estimado de QPU: Cuatro minutos en un procesador Heron r2 (NOTA: Esto es solo una estimación. Su tiempo de ejecución puede variar.)
Antecedentes
Este tutorial demuestra cómo utilizar una Fórmula Multiproducto (MPF, por sus siglas en inglés) para lograr un error de Trotter menor en nuestro observable en comparación con el que se incurriría con el circuito de Trotter más profundo que realmente ejecutaremos. Las MPF reducen el error de Trotter de la dinámica hamiltoniana mediante una combinación ponderada de varias ejecuciones de circuitos. Considera la tarea de encontrar los valores esperados de observables para el estado cuántico con el hamiltoniano . Se pueden utilizar Fórmulas de Producto (PF, por sus siglas en inglés) para aproximar la evolución temporal realizando lo siguiente:
- Escribir el hamiltoniano como donde son operadores hermitianos tales que cada unitario correspondiente puede implementarse eficientemente en un dispositivo cuántico.
- Aproximar los términos que no conmutan entre sí.
Entonces, la PF de primer orden (fórmula de Lie-Trotter) es:
la cual tiene un término de error cuadrático . También se pueden utilizar PF de orden superior (fórmulas de Lie-Trotter-Suzuki), que convergen más rápidamente y se definen recursivamente como:
donde es el orden de la PF simétrica y . Para evoluciones temporales largas, se puede dividir el intervalo de tiempo en intervalos, llamados pasos de Trotter, de duración y aproximar la evolución temporal en cada intervalo con una fórmula de producto de orden . Así, la PF de orden para el operador de evolución temporal sobre pasos de Trotter es:
donde el término de error disminuye con el número de pasos de Trotter y el orden de la PF.
Dado un entero y una fórmula de producto , el estado evolucionado temporalmente aproximado se puede obtener a partir de aplicando iteraciones de la fórmula de producto .
es una aproximación de con el error de aproximación de Trotter ||. Si consideramos una combinación lineal de aproximaciones de Trotter de :
donde son nuestros coeficientes de ponderación, es la matriz de densidad correspondiente al estado puro obtenido al evolucionar el estado inicial con la fórmula de producto , que involucra pasos de Trotter, y indexa el número de PF que componen la MPF. Todos los términos en utilizan la misma fórmula de producto como base. El objetivo es mejorar || encontrando con un aún menor.
- no necesita ser un estado físico ya que no necesita ser positivo. El objetivo aquí es minimizar el error en el valor esperado de los observables y no encontrar un reemplazo físico para .
- determina tanto la profundidad del circuito como el nivel de aproximación de Trotter. Valores más pequeños de conducen a circuitos más cortos, que incurren en menos errores de circuito pero serán una aproximación menos precisa del estado deseado.
La clave aquí es que el error de Trotter residual dado por es menor que el error de Trotter que se obtendría simplemente utilizando el valor más grande de .
puedes ver la utilidad de esto desde dos perspectivas:
- Para un presupuesto fijo de pasos de Trotter que puede ejecutar, puede obtener resultados con un error de Trotter que es menor en total.
- Dado algún número objetivo de pasos de Trotter que es demasiado grande para ejecutar, puede utilizar la MPF para encontrar una colección de circuitos de menor profundidad para ejecutar que resulte en un error de Trotter similar.
Requisitos
Antes de comenzar este tutorial, asegúrate de tener instalado lo siguiente:
- Qiskit SDK v1.0 o posterior, con soporte de visualización
- Qiskit Runtime v0.22 o posterior (
pip install qiskit-ibm-runtime) - Complemento MPF de Qiskit (
pip install qiskit_addon_mpf) - Utilidades de complementos de Qiskit (
pip install qiskit_addon_utils) - Biblioteca Quimb (
pip install quimb) - Biblioteca Qiskit Quimb (
pip install qiskit-quimb) - Numpy v0.21 para compatibilidad entre paquetes (
pip install numpy==0.21)
Parte I. Ejemplo a pequeña escala
Explorar la estabilidad de la MPF
No existe una restricción obvia en la elección del número de pasos de Trotter que componen el estado MPF . Sin embargo, estos deben elegirse cuidadosamente para evitar inestabilidades en los valores esperados resultantes calculados a partir de . Una buena regla general es establecer el paso de Trotter más pequeño de modo que . Si desea obtener más información sobre esto y cómo elegir sus otros valores de , consulta la guía Cómo elegir los pasos de Trotter para una MPF.
En el ejemplo a continuación, exploramos la estabilidad de la solución MPF calculando el valor esperado de la magnetización para un rango de tiempos utilizando diferentes estados evolucionados temporalmente. Específicamente, comparamos los valores esperados calculados a partir de cada una de las evoluciones temporales aproximadas implementadas con los pasos de Trotter correspondientes y los diversos modelos MPF (coeficientes estáticos y dinámicos) con los valores exactos del observable evolucionado temporalmente. Primero, definamos los parámetros para las fórmulas de Trotter y los tiempos de evolución.
# 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)
Para este ejemplo utilizaremos el estado de Néel como estado inicial y el modelo de Heisenberg en una línea de 10 sitios para el hamiltoniano que gobierna la evolución temporal.
donde es la constante de acoplamiento para aristas de vecinos más cercanos.
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])
El observable que mediremos es la magnetización en un par de qubits en el centro de la cadena.
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])
Definimos un pase de transpilación para recopilar las rotaciones XX e YY en el circuito como una sola compuerta XX+YY. Esto nos permitirá aprovechar las propiedades de conservación de espín de TeNPy durante el cálculo del MPO, acelerando significativamente el cálculo.
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))
Luego creamos los circuitos que implementan las evoluciones temporales aproximadas 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)

A continuación, calculamos los valores esperados evolucionados temporalmente a partir de los circuitos 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)
También calculamos los valores esperados exactos para comparación.
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)
Coeficientes MPF estáticos
Las MPF estáticas son aquellas en las que los valores de no dependen del tiempo de evolución, . Consideremos la PF de orden con pasos de Trotter; esto se puede escribir como:
donde son matrices que dependen de los conmutadores de los términos en la descomposición del hamiltoniano. Es importante notar que en sí mismas son independientes del tiempo y del número de pasos de Trotter . Por lo tanto, es posible cancelar los términos de error de orden inferior que contribuyen a con una elección cuidadosa de los pesos de la combinación lineal. Para cancelar el error de Trotter para los primeros términos (estos darán las contribuciones más grandes ya que corresponden al número más bajo de pasos de Trotter) en la expresión de , los coeficientes deben satisfacer las siguientes ecuaciones:
con . La primera ecuación garantiza que no haya sesgo en el estado construido , mientras que la segunda ecuación asegura la cancelación de los errores de Trotter. Para PF de orden superior, la segunda ecuación se convierte en donde para PF simétricas y en caso contrario, con . El error resultante (Refs. [1],[2]) es entonces
Determinar los coeficientes MPF estáticos para un conjunto dado de valores equivale a resolver el sistema lineal de ecuaciones definido por las dos ecuaciones anteriores para las variables : . Donde son nuestros coeficientes de interés, es una matriz que depende de y del tipo de PF que utilizamos (), y es un vector de restricciones. Específicamente:
donde es el order, es si symmetric es True y en caso contrario,