Přeskočit na hlavní obsah

Řezání drátů formulované jako dvouqubitová instrukce `Move`

V tomto tutoriálu rekonstruujeme očekávané hodnoty sedmiqubitového Circuit tím, že ho rozdělíme na dva čtyřqubitové Circuit pomocí řezání drátů.

Toto jsou kroky, které podnikneme v rámci tohoto Qiskit vzoru:

  • Krok 1: Mapování problému na kvantové Circuit a operátory:
    • Namapuj hamiltonián na kvantový Circuit.
  • Krok 2: Optimalizace pro cílový hardware [Využívá addon pro řezání]:
    • Rozřež Circuit a pozorovatelnou.
    • Transpiluj dílčí experimenty pro hardware.
  • Krok 3: Spuštění na cílovém hardware:
    • Spusť dílčí experimenty získané v Kroku 2 pomocí primitiva Sampler.
  • Krok 4: Post-processing výsledků [Využívá addon pro řezání]:
    • Zkombinuj výsledky Kroku 3 a rekonstruuj očekávanou hodnotu sledované pozorovatelné.

Krok 1: Mapování

Vytvoření Circuit k řezání

Nejprve vyjdeme z Circuit inspirovaného obr. 1(a) z arXiv:2302.03366v1.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
import numpy as np
from qiskit import QuantumCircuit

qc_0 = QuantumCircuit(7)
for i in range(7):
qc_0.rx(np.pi / 4, i)
qc_0.cx(0, 3)
qc_0.cx(1, 3)
qc_0.cx(2, 3)
qc_0.cx(3, 4)
qc_0.cx(3, 5)
qc_0.cx(3, 6)
qc_0.cx(0, 3)
qc_0.cx(1, 3)
qc_0.cx(2, 3)
<qiskit.circuit.instructionset.InstructionSet at 0x7f16ab191a80>
qc_0.draw("mpl")

Quantum circuit diagram

Specifikace pozorovatelné

from qiskit.quantum_info import SparsePauliOp

observable = SparsePauliOp(["ZIIIIII", "IIIZIII", "IIIIIIZ"])

Krok 2: Optimalizace

Vytvoření nového Circuit, kde byly instrukce Move umístěny na požadovaná místa řezu

Na základě výše uvedeného Circuit chceme umístit dva řezy drátů na prostřední qubitovou linku, aby se Circuit mohl rozdělit na dva Circuit o čtyřech Qubitech každý. Jedním ze způsobů, jak toho dosáhnout, je ručně umístit dvouqubitové instrukce Move, které přenesou stav z jednoho qubitového drátu na druhý. Instrukce Move je koncepčně ekvivalentní operaci reset na druhém Qubitu, po níž následuje Gate SWAP. Účinkem této instrukce je přenos stavu prvního (zdrojového) Qubitu na druhý (cílový) Qubit, přičemž příchozí stav druhého Qubitu je zahozen. Aby to fungovalo podle záměru, je důležité, aby druhý (cílový) Qubit nesdílel žádné provázání se zbytkem systému; v opačném případě by operace reset způsobila částečný kolaps stavu zbytku systému.

Zde sestavíme nový Circuit s jedním dalším Qubitem a s operacemi Move na příslušných místech. V tomto příkladu je možné Qubit znovu použít: zdrojový Qubit první instrukce Move se stane cílovým Qubitem druhé instrukce Move.

Poznámka: Jako alternativu k přímé práci s instrukcemi Move lze místa řezů drátů označit pomocí jednoqubitové instrukce CutWire. Funkce cut_wires slouží k transformaci instrukcí CutWire na instrukce Move na nově alokovaných Qubitech. Na rozdíl od ruční metody však tato automatická metoda neumožňuje opětovné použití qubitových drátů. Podrobnosti najdeš v návodech pro CutWire.

from qiskit_addon_cutting.instructions import Move

qc_1 = QuantumCircuit(8)
for i in [*range(4), *range(5, 8)]:
qc_1.rx(np.pi / 4, i)
qc_1.cx(0, 3)
qc_1.cx(1, 3)
qc_1.cx(2, 3)
qc_1.append(Move(), [3, 4])
qc_1.cx(4, 5)
qc_1.cx(4, 6)
qc_1.cx(4, 7)
qc_1.append(Move(), [4, 3])
qc_1.cx(0, 3)
qc_1.cx(1, 3)
qc_1.cx(2, 3)

qc_1.draw("mpl")

Quantum circuit diagram

Vytvoření pozorovatelné k novému Circuit

Tato pozorovatelná odpovídá observable, ale musíme správně zohlednit přidaný extra qubitový drát (tj. vložíme „I" na index 4). Pozor, v Qiskitu odpovídá v řetězcové reprezentaci Qubit-0 znaku Pauliho operátoru zcela vpravo.

observable_expanded = SparsePauliOp(["ZIIIIIII", "IIIIZIII", "IIIIIIIZ"])

Oddělení Circuit a pozorovatelných

Stejně jako v předchozích tutoriálech budou Qubity sdílející stejný štítek oddílu seskupeny dohromady a nelokalní Gate přesahující více než jeden oddíl budou rozřezány.

from qiskit_addon_cutting import partition_problem

partitioned_problem = partition_problem(
circuit=qc_1, partition_labels="AAAABBBB", observables=observable_expanded.paulis
)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
bases = partitioned_problem.bases

Vizualizace rozloženého problému

subobservables
{'A': PauliList(['IIII', 'ZIII', 'IIIZ']),
'B': PauliList(['ZIII', 'IIII', 'IIII'])}
subcircuits["A"].draw("mpl")

Quantum circuit diagram

subcircuits["B"].draw("mpl")

Quantum circuit diagram

Výpočet vzorkovací režie pro zvolené řezy

Zde řežeme dva dráty, což vede ke vzorkovací režii 444^4.

Více informací o vzorkovací režii způsobené řezáním Circuit najdeš ve vysvětlujících materiálech.

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 256.0

Generování dílčích experimentů ke spuštění na Backend

Funkce generate_cutting_experiments přijímá argumenty circuits/observables jako slovníky mapující štítky qubitových oddílů na příslušné subcircuit/subobservables.

Pro simulaci očekávané hodnoty Circuit v plné velikosti se z kvazipravděpodobnostního rozdělení rozložených Gate vygeneruje mnoho dílčích experimentů, které jsou poté spuštěny na jednom nebo více Backend. Počet vzorků odebraných z rozdělení se ovládá parametrem num_samples a pro každý unikátní vzorek je uveden jeden kombinovaný koeficient. Více informací o výpočtu koeficientů najdeš ve vysvětlujících materiálech.

from qiskit_addon_cutting import generate_cutting_experiments

subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits, observables=subobservables, num_samples=np.inf
)

Volba Backend

Zde používáme falešný Backend, jehož výsledkem bude spuštění Qiskit Runtime v lokálním režimu (tj. na lokálním simulátoru).

from qiskit_ibm_runtime.fake_provider import FakeManilaV2

backend = FakeManilaV2()

Příprava dílčích experimentů pro Backend

Před odesláním do Qiskit Runtime musíme Circuit transpilovat s naším Backend jako cílem.

from qiskit.transpiler import generate_preset_pass_manager

# Transpile the subexperiments to ISA circuits
pass_manager = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_subexperiments = {
label: pass_manager.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}

Krok 3: Spuštění

Spuštění dílčích experimentů pomocí primitiva Sampler z Qiskit Runtime

from qiskit_ibm_runtime import SamplerV2, Batch

# Submit each partition's subexperiments to the Qiskit Runtime Sampler
# primitive, in a single batch so that the jobs will run back-to-back.
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
/home/garrison/Qiskit/qiskit-ibm-runtime/qiskit_ibm_runtime/session.py:157: UserWarning: Session is not supported in local testing mode or when using a simulator.
warnings.warn(
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}

Krok 4: Post-processing

Rekonstrukce očekávané hodnoty

Rekonstruuj očekávané hodnoty pro každý člen pozorovatelné a zkombinuj je k rekonstrukci očekávané hodnoty původní pozorovatelné.

from qiskit_addon_cutting import reconstruct_expectation_values

reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)

Porovnání rekonstruované očekávané hodnoty s přesnou očekávanou hodnotou z původního Circuit a pozorovatelné

from qiskit_aer.primitives import EstimatorV2

estimator = EstimatorV2()
exact_expval = estimator.run([(qc_0, observable)]).result()[0].data.evs
print(f"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}")
print(f"Exact expectation value: {np.round(exact_expval, 8)}")
print(f"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}")
print(
f"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}"
)
Reconstructed expectation value: 1.51319069
Exact expectation value: 1.59099026
Error in estimation: -0.07779957
Relative error in estimation: -0.04890009