Erste Schritte mit approximativer Quantenkompilierung mit Tensornetzwerken (AQC-Tensor)
Paketversionen
Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.
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
Diese Anleitung demonstriert ein einfaches funktionierendes Beispiel für den Einstieg in AQC-Tensor. In diesem Beispiel nimmst du einen Trotter-Circuit, der die Zeitentwicklung eines transversalen Ising-Modells simuliert, und verwendest die AQC-Tensor-Methode, um die resultierende Circuit-Tiefe zu reduzieren. Außerdem benötigt dieses Beispiel das Paket qiskit-addon-utils für den Problemgenerator, qiskit-aer für die Tensornetzwerk-Simulation und scipy für die Parameteroptimierung.
Zunächst sei daran erinnert, dass der Hamiltonoperator des transversalen Ising-Modells die folgende Form hat:
wobei wir periodische Randbedingungen annehmen, die implizieren, dass für gilt , die Kopplungsstärke zwischen zwei Gitterpunkten ist und die Stärke des externen Magnetfelds.
Der folgende Code-Ausschnitt generiert den Hamiltonoperator einer 10-Gitterpunkt-Ising-Kette mit periodischen Randbedingungen.
# 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),
)
Zeitentwicklung in zwei Teile aufteilen: klassische und Quantenausführung​
Das übergeordnete Ziel dieses Beispiels ist die Simulation der Zeitentwicklung des Modell-Hamiltonoperators. Wir tun dies hier mittels Trotter-Entwicklung, die in zwei Abschnitte aufgeteilt wird.
- Ein anfänglicher Abschnitt, der mithilfe von Matrix-Produkt-Zuständen (MPS) simulierbar ist. Dies ist der Teil, der mit AQC-Tensor „kompiliert" wird.
- Ein anschließender Abschnitt, der auf Quantenhardware ausgeführt wird.
Wir entwickeln das System bis zur Zeit und verwenden AQC-Tensor, um die Zeitentwicklung bis zu komprimieren, und führen dann mit gewöhnlichen Trotter-Schritten bis weiter.
Im nächsten Schritt generieren wir zwei Circuits: einen, der mit AQC-Tensor komprimiert wird, und einen, der auf einem QPU ausgeführt wird. Da der erste Circuit klassisch mithilfe von Matrix-Produkt-Zuständen simuliert wird, verwenden wir eine großzügige Anzahl von Schichten, um den Trotter-Fehler zu minimieren. Der zweite Circuit, der die Zeitentwicklung von bis simuliert, verwendet deutlich weniger Schichten, um die Tiefe zu minimieren.
# 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,
)
Zu Vergleichszwecken generieren wir außerdem einen dritten Circuit, der bis entwickelt, aber dieselbe Anzahl an Schichten hat wie der zweite Circuit, der von bis entwickelt. Dies ist der Circuit, den wir ohne die AQC-Tensor-Technik ausgeführt hätten. Wir bezeichnen ihn vorerst als Vergleichs-Circuit.
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,
)
Ansatz generieren und MPS-Simulation aufbauen​
Als nächstes generieren wir den Ansatz, den wir optimieren werden. Er entwickelt bis zur gleichen Evolutionszeit wie unser erster Circuit (von bis ), aber mit weniger Trotter-Schritten.
Sobald der Circuit generiert wurde, übergeben wir ihn an die Funktion generate_ansatz_from_circuit() von AQC-Tensor, die die Zwei-Qubit-Konnektivität analysiert und zwei Dinge zurückgibt. Erstens einen generierten Ansatz-Circuit mit derselben Zwei-Qubit-Konnektivität, und zweitens einen Satz von Parametern, die, wenn sie in den Ansatz eingesetzt werden, den Eingabe-Circuit ergeben.
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)
Als nächstes erstellen wir die MPS-Darstellung des Zustands, der durch AQC approximiert werden soll. Außerdem berechnen wir die Fidelity zwischen dem vom Vergleichs-Circuit erzeugten Zustand und dem Circuit, der den Zielzustand erzeugt (der mehr Trotter-Schritte verwendet).
# 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
Die Parameter des Ansatzes mithilfe des MPS optimieren​
Abschließend optimieren wir den Ansatz-Circuit so, dass er den Zielzustand mit einer höheren Fidelity erzeugt als unser comparison_fidelity. Die zu minimierende Kostenfunktion ist MaximizeStateFidelity und wird mit dem L-BFGS-Optimierer von scipy optimiert.
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.
An diesem Punkt haben wir einen Parametersatz, der den Zielzustand mit einer höheren Fidelity erzeugt als der Vergleichs-Circuit ohne den Einsatz von AQC. Mit diesen optimalen Parametern hat der komprimierte Circuit nun sowohl weniger Trotter-Fehler als auch eine geringere Tiefe als der ursprüngliche Circuit.
Als letzten Schritt erstellt der folgende Code-Ausschnitt den vollständigen Zeitentwicklungs-Circuit, der an eine Transpiler-Pipeline übergeben und auf Quantenhardware ausgeführt werden kann.
final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
final_circuit.compose(subsequent_circuit, inplace=True)
final_circuit.draw("mpl", fold=-1)
Nächste Schritte​
- Probiere das Tutorial Approximate quantum compilation for time evolution circuits aus.