Comenzar con compilación cuántica aproximada con redes tensoriales (AQC-Tensor)
Versiones de paquetes
El código de esta página fue desarrollado usando los siguientes requisitos. Recomendamos usar estas versiones o versiones más recientes.
qiskit[all]~=2.3.0
qiskit-aer~=0.17
qiskit-addon-utils~=0.3.0
qiskit-addon-aqc-tensor[aer,quimb-jax]~=0.2.0; sys.platform != 'darwin'
scipy~=1.16.3
Esta guía demuestra un ejemplo de trabajo simple para comenzar con AQC-Tensor. En este ejemplo, tomarás un circuito de Trotter que simula la evolución de un modelo de Ising de campo transverso y usarás el método AQC-Tensor para reducir la profundidad del circuito resultante. Además, este ejemplo requiere el paquete qiskit-addon-utils para el generador del problema, qiskit-aer para la simulación de redes tensoriales, y scipy para la optimización de parámetros.
Para comenzar, recuerda que el Hamiltoniano del modelo de Ising de campo transverso tiene la forma
donde asumiremos condiciones de contorno periódicas que implican que para obtenemos y es la intensidad de acoplamiento entre dos sitios y es la intensidad del campo magnético externo.
El siguiente fragmento de código generará el Hamiltoniano de una cadena de Ising de 10 sitios con condiciones de contorno periódicas.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-aer scipy
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_aer import AerSimulator
from scipy.optimize import OptimizeResult, minimize
# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
# Choose a 10-qubit circle on this coupling map
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 4, 15, 3, 9]
)
# Get a qubit operator describing the Ising field model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)
Dividir la evolución temporal en dos partes para ejecución clásica y cuántica
El objetivo general de este ejemplo es simular la evolución temporal del Hamiltoniano del modelo. Lo hacemos aquí mediante evolución de Trotter, que se dividirá en dos porciones.
- Una porción inicial que es simulable usando estados producto matriciales (MPS). Esto es lo que se 'compilará' usando AQC-Tensor.
- Una porción posterior que se ejecutará en hardware cuántico.
Optaremos por evolucionar el sistema hasta el tiempo y usar AQC-Tensor para comprimir la evolución temporal hasta el tiempo , y luego evolucionar usando pasos de Trotter ordinarios hasta .
A partir de aquí generaremos dos circuitos: uno que será comprimido usando AQC-Tensor y otro que se ejecutará en un QPU. Para el primer circuito, dado que se simulará clásicamente usando estados producto matriciales, usaremos un número generoso de capas para minimizar el error de Trotter. Mientras tanto, el segundo circuito que simula la evolución temporal de a usará muchas menos capas para minimizar la profundidad.
# Generate circuit to be compressed
aqc_evolution_time = 4.0
aqc_target_num_trotter_steps = 45
aqc_target_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
)
# Generate circuit to execute on hardware
subsequent_evolution_time = 1.0
subsequent_num_trotter_steps = 5
subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
Con fines comparativos, también generaremos un tercer circuito. Uno que evoluciona hasta , pero que tiene el mismo número de capas que el segundo circuito que evoluciona de a . Este es el circuito que habríamos ejecutado si no hubiéramos usado la técnica AQC-Tensor. Lo llamaremos el circuito de comparación por ahora.
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
aqc_comparison_num_trotter_steps
comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Generar el ansatz y construir la simulación MPS
A continuación generaremos el ansatz que optimizaremos. Evolucionará hasta el mismo tiempo de evolución que nuestro primer circuito (de a ), pero con menos pasos de Trotter.
Una vez generado el circuito, lo pasamos a la función generate_ansatz_from_circuit() de AQC-Tensor, que analiza la conectividad de dos qubits y devuelve dos cosas. La primera es un circuito ansatz generado con la misma conectividad de dos qubits, y la segunda es un conjunto de parámetros que, al introducirse en el ansatz, producen el circuito de entrada.
aqc_ansatz_num_trotter_steps = 5
aqc_good_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
)
aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit, qubits_initially_zero=True
)
aqc_ansatz.draw("mpl", fold=-1)
A continuación construiremos la representación MPS del estado que AQC debe aproximar. También calcularemos la fidelidad entre el estado preparado por el circuito de comparación y el circuito que genera el estado objetivo (que usó más pasos de Trotter).
# Generate MPS simulator settings and obtain the MPS representation of the target state
simulator_settings = AerSimulator(
method="matrix_product_state",
matrix_product_state_max_bond_dimension=100,
)
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
# Compute the fidelity between the MPS representation of the target state and the state produced by the comparison circuit
comparison_mps = tensornetwork_from_circuit(
comparison_circuit, simulator_settings
)
comparison_fidelity = (
abs(compute_overlap(comparison_mps, aqc_target_mps)) ** 2
)
print(f"Comparison fidelity: {comparison_fidelity}")
Comparison fidelity: 0.9997111919739693
Optimizar los parámetros del ansatz usando el MPS
Por último, optimizaremos el circuito ansatz para que produzca el estado objetivo con una fidelidad mayor que la de nuestro comparison_fidelity. La función de coste a minimizar será MaximizeStateFidelity y se optimizará usando el optimizador L-BFGS de scipy.
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)
stopping_point = 1 - comparison_fidelity
def callback(intermediate_result: OptimizeResult):
print(f"Intermediate result: Fidelity {1 - intermediate_result.fun:.8}")
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration
result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": 100},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)
print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
Intermediate result: Fidelity 0.95084365
Intermediate result: Fidelity 0.98409893
Intermediate result: Fidelity 0.99142033
Intermediate result: Fidelity 0.99521405
Intermediate result: Fidelity 0.99566673
Intermediate result: Fidelity 0.99650054
Intermediate result: Fidelity 0.99683487
Intermediate result: Fidelity 0.99720426
Intermediate result: Fidelity 0.99761726
Intermediate result: Fidelity 0.99809073
Intermediate result: Fidelity 0.99838244
Intermediate result: Fidelity 0.99861841
Intermediate result: Fidelity 0.99874617
Intermediate result: Fidelity 0.99892696
Intermediate result: Fidelity 0.99908129
Intermediate result: Fidelity 0.99917737
Intermediate result: Fidelity 0.99925456
Intermediate result: Fidelity 0.99933134
Intermediate result: Fidelity 0.99947173
Intermediate result: Fidelity 0.99956469
Intermediate result: Fidelity 0.99964488
Intermediate result: Fidelity 0.99967419
Intermediate result: Fidelity 0.99968821
Intermediate result: Fidelity 0.9997448
Done after 24 iterations.
En este punto tenemos un conjunto de parámetros que generan el estado objetivo con una fidelidad mayor que la que habría producido el circuito de comparación sin usar AQC. Con estos parámetros óptimos, el circuito comprimido ahora tiene menos error de Trotter y menos profundidad que el circuito original.
Como paso final, el siguiente fragmento de código construye el circuito completo de evolución temporal que puede pasarse a un pipeline de transpilación y ejecutarse en hardware cuántico.
final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
final_circuit.compose(subsequent_circuit, inplace=True)
final_circuit.draw("mpl", fold=-1)
Próximos pasos
- Prueba el tutorial Compilación cuántica aproximada para circuitos de evolución temporal.