Přeskočit na hlavní obsah

Snížení hloubky Circuit pomocí doplňku AQC-Tensor pro Qiskit

V tomto notebooku si projdeme kroky Qiskit vzoru a přitom použijeme přibližnou kvantovou kompilaci s tenzorními sítěmi (AQC-Tensor), abychom dosáhli nižší hloubky Circuit, než by bylo jinak potřeba pro provádění Trotterovy evoluce.

Toto jsou kroky, které podnikneme:

  • Krok 1: Mapování na kvantový problém
    • Inicializace Hamiltoniánu problému a pozorovatelných veličin
    • Vygenerování cílového stavu tenzorové sítě pro počáteční část Circuit
    • Vygenerování Circuit s nízkou hloubkou, který aproximuje komprimovanou část
    • Vygenerování obecného ansatzu z tohoto Circuit
    • Optimalizace parametrů tak, aby se ansatz co nejvíce přiblížil cíli
    • Přidání následných Trotterových kroků k optimalizovanému ansatzu
  • Krok 2: Optimalizace pro cílový hardware
    • Transpilace Circuit pro hardware
  • Krok 3: Provedení experimentů
    • Použití falešného Backend pro jednoduchost
  • Krok 4: Rekonstrukce výsledků
    • Není potřeba; místo toho pouze vypíšeme naměřenou pozorovatelnou veličinu

Krok 1: Mapování na kvantový Circuit a operátor

Nastavení modelového Hamiltoniánu a pozorovatelné veličiny

V tomto notebooku používáme Isingův model na kružnici o 10 místech:

H^Ising=i=110Ji,(i+1)ZiZ(i+1)+hiXi,\hat{\mathcal{H}}_{\text{Ising}} = \sum_{i=1}^{10} J_{i,(i+1)} Z_i Z_{(i+1)} + h_i X_i \, ,

kde periodické okrajové podmínky znamenají, že pro i=10i=10 dostaneme i+1=111i+1=11\rightarrow1, JJ je síla vazby mezi dvěma místy a hh je vnější magnetické pole.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-ibm-runtime quimb scipy
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian

# 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),
)

Pozorovatelná veličina, kterou budeme měřit, je celková magnetizace.

from qiskit.quantum_info import SparsePauliOp

L = reduced_coupling_map.size()
observable = SparsePauliOp.from_sparse_list([("Z", [i], 1 / L / 2) for i in range(L)], num_qubits=L)

Určení, jak velkou část časové evoluce simulovat klasicky

Náš celkový cíl je simulovat časovou evoluci výše uvedeného modelového Hamiltoniánu. Děláme to pomocí Trotterovy evoluce, kterou rozdělíme na dvě části:

  1. Počáteční část, která je simulovatelná pomocí stavů maticového součinu (MPS). Tuto část „zkompilujeme" pomocí AQC, jak je popsáno v https://arxiv.org/abs/2301.08609.
  2. Následná část Circuit, která bude provedena na hardware. Plánujeme použít AQC-Tensor ke kompresi našeho Circuit časové evoluce až do času t=4t=4 a poté provádět evoluci pomocí běžných Trotterových kroků až do t=5t=5.

Generování Circuits před a po rozdělení

Nyní, když jsme se rozhodli rozdělit v t=4t=4, vygenerujeme dva Circuits:

  1. „Cílový" Circuit pro část AQC evoluce, od ti=0t_i=0 do tf=4t_f=4. Protože je simulován simulátorem tenzorové sítě, počet vrstev ovlivňuje dobu provádění pouze konstantním faktorem, takže s klidným svědomím můžeme použít velkorysý počet vrstev, abychom minimalizovali Trotterovu chybu.
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit

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,
)
  1. Následný Circuit evoluce, který provádí evoluci od ti=4t_i=4 do tf=5t_f=5. Protože bude spuštěn na kvantovém hardware, je žádoucí použít co nejméně Trotterových vrstev.
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,
)

Pro účely pozdějšího srovnání vygenerujeme také třetí Circuit: takový, který provádí evoluci po dobu aqc_evolution_time, ale má stejnou dobu evoluce na Trotterův krok jako následující Circuit. To je Circuit, se kterým bychom pracovali, kdybychom nepoužili velkorysý počet Trotterových kroků pro cílový Circuit. Budeme ho označovat jako srovnávací Circuit.

aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps / subsequent_evolution_time * aqc_evolution_time
)
aqc_comparison_num_trotter_steps
20
comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)

Generování ansatzu a počátečních parametrů z Trotterova Circuit s menším počtem kroků

Nejprve vytvoříme „dobrý" Circuit, který má stejnou dobu evoluce jako cílový Circuit, ale s menším počtem Trotterových kroků (a tedy menším počtem vrstev).

Poté tento „dobrý" Circuit předáme funkci generate_ansatz_from_circuit z AQC-Tensor. Tato funkce analyzuje dvou-Qubitovou konektivitu Circuit a vrátí dvě věci:

  1. obecný, parametrizovaný ansatz Circuit se stejnou dvou-Qubitovou konektivitou jako vstupní Circuit; a,
  2. parametry, které po vložení do ansatzu dají vstupní (dobrý) Circuit.

Tyto parametry brzy vezmeme a iterativně je upravíme, aby se ansatz Circuit co nejvíce přiblížil cílovému MPS.

from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit

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)

Quantum circuit diagram

print(f"Comparison circuit: depth {comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters")
Comparison circuit: depth 120
Target circuit: depth 270
Ansatz circuit: depth 23, with 515 parameters

Výběr nastavení pro simulaci tenzorové sítě

Zde používáme simulátor tenzorové sítě založený na quimb. V tomto příkladu používáme simulátor stavů maticového součinu (MPS) z quimb a používáme JAX pro automatickou diferenciaci. Více informací o použití simulátoru quimb najdeš v dokumentaci API.

from functools import partial

import quimb.tensor

from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator

simulator_settings = QuimbSimulator(
partial(quimb.tensor.CircuitMPS, max_bond=100, cutoff=1e-8),
autodiff_backend="jax",
)

Konstrukce reprezentace cílového stavu AQC jako maticového součinu

Dále sestavíme reprezentaci maticového součinu stavu, který má být aproximován pomocí AQC.

from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit

aqc_target_mps = tensornetwork_from_circuit(aqc_target_circuit, simulator_settings)

Všimni si, že protože jsme pro cílový stav zvolili velkorysý počet Trotterových kroků, má ve skutečnosti menší Trotterovu chybu než srovnávací Circuit. Můžeme vypočítat věrnost (ψ1ψ22| \langle \psi_1 | \psi_2 \rangle |^2) stavu připraveného srovnávacím Circuit vůči cílovému stavu:

from qiskit_addon_aqc_tensor.simulation import compute_overlap

comparison_mps = tensornetwork_from_circuit(comparison_circuit, simulator_settings)
comparison_fidelity = abs(compute_overlap(comparison_mps, aqc_target_mps)) ** 2
comparison_fidelity
0.9996761790297157

Optimalizace parametrů ansatzu pomocí výpočtů MPS

Zde minimalizujeme nejjednodušší možnou ztrátovou funkci, MaximizeStateFidelity, pomocí optimalizátoru L-BFGS ze scipy.

Zvolíme zastavovací bod pro věrnost tak, aby byla vyšší, než jaké by dosáhl srovnávací Circuit bez použití AQC. Jakmile je tohoto bodu dosaženo, má komprimovaný Circuit menší Trotterovu chybu i menší hloubku než původní Circuit. S více procesním časem lze provést další optimalizační kroky, aby se věrnost ještě zvýšila.

from scipy.optimize import OptimizeResult, minimize

from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity

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.loss_function,
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.95080335
Intermediate result: Fidelity 0.98408927
Intermediate result: Fidelity 0.99140876
Intermediate result: Fidelity 0.9951876
Intermediate result: Fidelity 0.99563147
Intermediate result: Fidelity 0.99646297
Intermediate result: Fidelity 0.99679298
Intermediate result: Fidelity 0.99715793
Intermediate result: Fidelity 0.99756604
Intermediate result: Fidelity 0.99804283
Intermediate result: Fidelity 0.99832283
Intermediate result: Fidelity 0.99856583
Intermediate result: Fidelity 0.99868698
Intermediate result: Fidelity 0.998867
Intermediate result: Fidelity 0.99902237
Intermediate result: Fidelity 0.99912174
Intermediate result: Fidelity 0.99919705
Intermediate result: Fidelity 0.99926724
Intermediate result: Fidelity 0.99938605
Intermediate result: Fidelity 0.99951297
Intermediate result: Fidelity 0.99956172
Intermediate result: Fidelity 0.99962274
Intermediate result: Fidelity 0.99963919
Intermediate result: Fidelity 0.99967423
Intermediate result: Fidelity 0.9997101
Done after 25 iterations.

Konstrukce finálního Circuit pro předání Transpileru

final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
final_circuit.compose(subsequent_circuit, inplace=True)
final_circuit.draw("mpl", fold=-1)

Quantum circuit diagram

Krok 2: Transpilace pro spuštění na cílovém hardware

V kroku 2 Qiskit vzoru transpilujeme tento Circuit a požadované pozorovatelné veličiny pro spuštění na cílovém zařízení. Zde používáme falešný Backend poskytovaný qiskit-ibm-runtime.

from qiskit import transpile
from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2

backend = FakeMelbourneV2()

isa_circuit = transpile(final_circuit, backend)
isa_observable = observable.apply_layout(isa_circuit.layout)

Výsledný ISA Circuit lze poté odeslat ke spuštění na Backend (krok 3 Qiskit vzoru).

Krok 3: Spuštění na kvantovém hardware

from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(backend)
job = estimator.run([(isa_circuit, isa_observable)])
pub_result = job.result()[0]

Krok 4: Rekonstrukce

Rekonstrukce v našem případě není nutná. Stačí se podívat na výsledek.

pub_result.data.evs[()]
np.float64(0.047998046875000006)