Lumaktaw sa pangunahing nilalaman

Magsimula sa circuit cutting gamit ang wire cuts

Mga bersyon ng package

Ang code sa pahinang ito ay binuo gamit ang mga sumusunod na kinakailangan. Inirerekomenda naming gamitin ang mga bersyong ito o mas bago.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17
qiskit-addon-cutting~=0.10.0

Ipinapakita ng gabay na ito ang isang gumaganang halimbawa ng wire cuts gamit ang qiskit-addon-cutting package. Sinasaklaw nito ang pag-reconstruct ng mga expectation value ng isang circuit na may pitong qubit gamit ang wire cutting.

Ang isang wire cut ay kinakatawan sa package na ito bilang isang two-qubit Move na instruksyon, na tinukoy bilang isang reset ng pangalawang qubit na pinapatakbo ng instruksyon, kasunod ng isang swap ng parehong qubit. Ang operasyong ito ay katumbas ng paglipat ng estado ng unang qubit papunta sa pangalawang qubit, habang sabay na itinatatapon ang papasok na estado ng pangalawang qubit.

Ang package ay idinisenyo upang maging pare-pareho sa paraan ng pagtrato mo sa mga wire cut kapag gumagamit ng mga pisikal na qubit. Halimbawa, ang isang wire cut ay maaaring kumuha ng estado ng pisikal na qubit nn at ipagpatuloy ito bilang pisikal na qubit mm pagkatapos ng cut. Maaari mong isipin ang "instruction cutting" bilang isang pinag-isang framework para sa pagsasaalang-alang ng parehong wire at gate cut sa loob ng parehong pormalismo (dahil ang isang wire cut ay isang cut Move na instruksyon lamang). Ang paggamit ng framework na ito para sa wire cutting ay nagbibigay-daan din sa muling paggamit ng qubit, na ipinaliwanag sa seksyon tungkol sa manu-manong pagputol ng wire.

Ang single-qubit CutWire na instruksyon ay nagsisilbing mas abstraktong, mas simpleng interface para sa pagtatrabaho sa mga wire cut. Pinapayagan ka nitong tukuyin kung saan sa circuit dapat putulin ang isang wire sa mataas na antas at hayaan ang circuit cutting addon na mag-insert ng angkop na mga Move na instruksyon para sa iyo.

Ipinapakita ng sumusunod na halimbawa ang expectation value reconstruction pagkatapos ng wire cutting. Gagawa ka ng isang circuit na may ilang non-local na gate at magtutukoy ng mga observable para matantiya.

# 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
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
from qiskit_ibm_runtime import SamplerV2, Batch
from qiskit_aer.primitives import EstimatorV2

from qiskit_addon_cutting.instructions import Move, CutWire
from qiskit_addon_cutting import (
partition_problem,
generate_cutting_experiments,
cut_wires,
expand_observables,
reconstruct_expectation_values,
)

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)

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

# Draw circuit
qc_0.draw("mpl")

Output of the previous code cell

Putulin ang mga wire gamit ang mataas na antas na CutWire na instruksyon​

Sunod, gumawa ng mga wire cut gamit ang single-qubit CutWire na instruksyon sa qubit q3q_3. Kapag handa na ang mga subexperiment para isagawa, gamitin ang cut_wires() na function para gawing Move na instruksyon ang CutWire sa mga bagong inilalaang qubit.

qc_1 = QuantumCircuit(7)
for i in range(7):
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(CutWire(), [3])
qc_1.cx(3, 4)
qc_1.cx(3, 5)
qc_1.cx(3, 6)
qc_1.append(CutWire(), [3])
qc_1.cx(0, 3)
qc_1.cx(1, 3)
qc_1.cx(2, 3)

qc_1.draw("mpl")

Output of the previous code cell

Tala tungkol sa pagpapalawak ng mga observable

Kapag ang isang circuit ay pinalawak sa pamamagitan ng isa o higit pang wire cut, ang observable ay kailangang i-update upang masulit ang mga karagdagang qubit na idinagdag. Ang qiskit-addon-cutting package ay may convenience function na expand_observables(), na tumatanggap ng mga PauliList na object at ng mga orihinal at pinalawak na circuit bilang mga argumento, at nagbabalik ng bagong PauliList.

Ang ibinalik na PauliList na ito ay hindi maglalaman ng anumang impormasyon tungkol sa mga coefficient ng orihinal na observable, ngunit maaari itong balewalain hanggang sa reconstruction ng panghuling expectation value.

# Transform CutWire instructions to Move instructions
qc_2 = cut_wires(qc_1)

# Expand the observable to match the new circuit size
expanded_observable = expand_observables(observable.paulis, qc_0, qc_2)
print(f"Expanded Observable: {expanded_observable}")
qc_2.draw("mpl")
Expanded Observable: ['ZIIIIIIII', 'IIIZIIIII', 'IIIIIIIIZ']

Output of the previous code cell

I-partition ang circuit at observable​

Ngayon ang problema ay maaari nang hatiin sa mga partition. Nagagawa ito gamit ang partition_problem() na function na may opsyonal na hanay ng mga partition label upang tukuyin kung paano ihiwalay ang circuit. Ang mga qubit na may parehong partition label ay pinangkat nang magkasama, at anumang non-local na gate na sumasaklaw sa mahigit isang partition ay pinutol.

Kung walang ibinibigay na partition label, ang partitioning ay awtomatikong matutukoy batay sa koneksyon ng circuit. Basahin ang susunod na seksyon tungkol sa manu-manong pagputol ng wire para sa karagdagang impormasyon tungkol sa pagsasama ng mga partition label.

partitioned_problem = partition_problem(
circuit=qc_2,
observables=expanded_observable,
)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
bases = partitioned_problem.bases

print(f"Subobservables to measure: \n{subobservables}\n")
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
subcircuits[0].draw("mpl")
Subobservables to measure:
{0: PauliList(['IIIII', 'ZIIII', 'IIIIZ']), 1: PauliList(['ZIII', 'IIII', 'IIII'])}

Sampling overhead: 256.0

Output of the previous code cell

subcircuits[1].draw("mpl")

Output of the previous code cell

Sa partitioning scheme na ito, nagputol ka ng dalawang wire, na nagresulta sa sampling overhead na 444^4.

Bumuo ng mga subexperiment para isagawa at i-post-process ang mga resulta​

Para matantiya ang expectation value ng full-sized na circuit, ilang subexperiment ang nabubuo mula sa joint quasi-probability distribution ng mga na-decompose na gate at pagkatapos ay isinasagawa sa isa (o higit pa) na QPU. Ginagawa ito ng generate_cutting_experiments na paraan sa pamamagitan ng pag-ingest ng mga argumento para sa mga subcircuits at subobservables na dictionary na ginawa mo sa itaas, pati na rin ang bilang ng mga sample na kukunin mula sa distribution.

Tala tungkol sa bilang ng mga sample

Ang num_samples na argumento ay nagtutukoy kung ilang sample ang kukunin mula sa quasi-probability distribution at tinutukoy ang katumpakan ng mga coefficient na ginagamit para sa reconstruction. Ang pagpasa ng infinity (np.inf) ay tinitiyak na lahat ng coefficient ay eksaktong kinakalkula. Basahin ang API docs tungkol sa pagbuo ng mga timbang at pagbuo ng mga cutting experiment para sa karagdagang impormasyon.

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

# Set a backend to use and transpile the subexperiments
backend = FakeManilaV2()
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()
}

# 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()
}
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}

Sa huli, ang expectation value ng buong circuit ay maaaring i-reconstruct gamit ang reconstruct_expectation_values() na paraan.

Ang code block sa ibaba ay nire-reconstruct ang mga resulta at inihahambing ang mga ito sa eksaktong expectation value.

reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
# Apply the coefficients of the original observable
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)

# Compute the exact expectation value using the `qiskit_aer` package.
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.45965266
Exact expectation value: 1.59099026
Error in estimation: -0.1313376
Relative error in estimation: -0.08255085
Tala tungkol sa mga coefficient ng observable

Para tumpak na ma-reconstruct ang expectation value, ang mga coefficient ng orihinal na observable (na iba sa output ng generate_cutting_experiments()) ay dapat na ilapat sa output ng reconstruction, dahil ang impormasyong ito ay nawala nang ang mga cutting experiment ay nabuo o nang ang observable ay pinalawak.

Karaniwang ang mga coefficient na ito ay maaaring ilapat sa pamamagitan ng numpy.dot() gaya ng ipinakita nang mas maaga.

Putulin ang mga wire gamit ang mababang antas na Move na instruksyon​

Ang isang limitasyon ng paggamit ng mas mataas na antas na CutWire na instruksyon ay hindi nito pinapayagan ang muling paggamit ng qubit. Kung ito ay nais para sa isang cutting experiment, maaari kang mag-manually na mag-place ng mga Move na instruksyon. Gayunpaman, dahil ang Move na instruksyon ay itinatatapon ang estado ng destination qubit, mahalaga na ang qubit na ito ay hindi nagbabahagi ng anumang entanglement sa natitirang bahagi ng sistema. Kung hindi, ang reset na operasyon ay magiging sanhi ng bahagyang pagbagsak ng estado ng circuit pagkatapos ng wire cut.

Ang code block sa ibaba ay nagsasagawa ng isang wire cut sa qubit q3q_3 para sa parehong halimbawang circuit na ipinakita kanina. Ang pagkakaiba dito ay maaari kang muling gumamit ng qubit sa pamamagitan ng pagbabaligtad ng Move na operasyon kung saan ginawa ang pangalawang wire cut (gayunpaman, hindi ito palaging posible at depende sa circuit na pinutol).

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)

# Expand observable
observable_expanded = SparsePauliOp(["ZIIIIIII", "IIIIZIII", "IIIIIIIZ"])
qc_1.draw("mpl")

Output of the previous code cell

Ang circuit sa itaas ay maaari na ngayong i-partition at mabuo ang mga cutting experiment. Para tahasang tukuyin kung paano dapat i-partition ang circuit, maaari kang magdagdag ng mga partition label sa partition_problem() na function. Ang mga qubit na may parehong partition label ay pinangkat nang magkasama, at anumang non-local na gate na sumasaklaw sa mahigit isang partition ay pinutol. Ang mga key ng dictionary na output ng partition_problem() ay tutugma sa mga tinukoy sa label string.

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

print(f"Subobservables to measure: \n{subobservables}\n")
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
subcircuits["A"].draw("mpl")
Subobservables to measure:
{'A': PauliList(['IIII', 'ZIII', 'IIIZ']), 'B': PauliList(['ZIII', 'IIII', 'IIII'])}

Sampling overhead: 256.0

Output of the previous code cell

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

Output of the previous code cell

Ngayon ang mga cutting experiment ay maaari nang mabuo at ang expectation value ay maaaring i-reconstruct sa parehong paraan gaya ng nakaraang seksyon.

Mga susunod na hakbang​

Mga rekomendasyon