Wire Cutting yang Dinyatakan sebagai Arahan `Move` Dua-Qubit
Dalam tutorial ini, kita akan membina semula nilai jangkaan bagi satu Circuit tujuh-Qubit dengan memecahkannya kepada dua Circuit empat-Qubit menggunakan wire cutting.
Ini adalah langkah-langkah yang akan kita ikuti dalam corak Qiskit ini:
- Langkah 1: Petakan masalah kepada Circuit dan operator quantum:
- Petakan hamiltonian ke dalam satu Circuit quantum.
- Langkah 2: Optimumkan untuk perkakasan sasaran [Menggunakan addon cutting]:
- Potong Circuit dan observable.
- Transpile subeksperimen untuk perkakasan.
- Langkah 3: Laksanakan pada perkakasan sasaran:
- Jalankan subeksperimen yang diperoleh dalam Langkah 2 menggunakan primitif
Sampler.
- Jalankan subeksperimen yang diperoleh dalam Langkah 2 menggunakan primitif
- Langkah 4: Pasca-proses keputusan [Menggunakan addon cutting]:
- Gabungkan keputusan Langkah 3 untuk membina semula nilai jangkaan observable yang dikehendaki.
Langkah 1: Petaβ
Cipta Circuit untuk dipotongβ
Pertama, kita mulakan dengan Circuit yang diilhamkan oleh Rajah 1(a) daripada 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")

Nyatakan observableβ
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["ZIIIIII", "IIIZIII", "IIIIIIZ"])
Langkah 2: Optimumkanβ
Cipta Circuit baru di mana arahan Move telah diletakkan di lokasi pemotongan yang dikehendakiβ
Berdasarkan Circuit di atas, kita ingin meletakkan dua wire cut pada garisan Qubit tengah, supaya Circuit boleh dipisahkan menjadi dua Circuit yang masing-masing terdiri daripada empat Qubit. Salah satu cara untuk melakukan ini ialah dengan meletakkan secara manual arahan Move dua-Qubit yang memindahkan keadaan dari satu wayar Qubit ke wayar yang lain. Arahan Move secara konsepnya setara dengan operasi set semula pada Qubit kedua, diikuti oleh Gate SWAP. Kesan arahan ini ialah ia memindahkan keadaan Qubit pertama (sumber) ke Qubit kedua (destinasi), sambil membuang keadaan masuk Qubit kedua. Untuk ini berfungsi seperti yang diharapkan, adalah penting bahawa Qubit kedua (destinasi) tidak berkongsi sebarang keterjalinann dengan bahagian sistem yang lain; jika tidak, operasi set semula akan menyebabkan keadaan bahagian sistem yang lain runtuh separa.
Di sini, kita membina Circuit baru dengan satu Qubit tambahan dan operasi Move di tempatnya. Dalam contoh ini, kita boleh menggunakan semula satu Qubit: Qubit sumber bagi Move pertama menjadi Qubit destinasi bagi operasi Move kedua.
Nota: Sebagai alternatif kepada bekerja secara langsung dengan arahan Move, seseorang boleh memilih untuk menandakan wire cut menggunakan arahan CutWire satu-Qubit. Fungsi cut_wires wujud untuk mengubah CutWire kepada arahan Move pada Qubit yang baru diperuntukkan. Walau bagaimanapun, berbeza dengan kaedah manual, kaedah automatik ini tidak membenarkan penggunaan semula wayar Qubit. Lihat panduan cara CutWire untuk butiran lanjut.
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")

Cipta observable untuk Circuit baruβ
Observable ini sepadan dengan observable, tetapi kita perlu mengambil kira dengan betul wayar Qubit tambahan yang telah ditambah (iaitu, kita memasukkan "I" pada indeks 4). Perhatikan bahawa dalam Qiskit, perwakilan rentetan Qubit-0 sepadan dengan aksara Pauli paling kanan.
observable_expanded = SparsePauliOp(["ZIIIIIII", "IIIIZIII", "IIIIIIIZ"])
Pisahkan Circuit dan observableβ
Seperti dalam tutorial sebelumnya, Qubit yang berkongsi label partition yang sama akan dikumpulkan bersama, dan Gate bukan tempatan yang merentasi lebih daripada satu partition akan dipotong.
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
Visualisasikan masalah yang telah didekomposisiβ
subobservables
{'A': PauliList(['IIII', 'ZIII', 'IIIZ']),
'B': PauliList(['ZIII', 'IIII', 'IIII'])}
subcircuits["A"].draw("mpl")

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

Kira overhed pensampelan bagi pemotongan yang dipilihβ
Di sini kita memotong dua wayar, menghasilkan overhed pensampelan sebesar .
Untuk maklumat lanjut tentang overhed pensampelan yang ditanggung oleh circuit cutting, rujuk bahan penjelasan.
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 256.0
Jana subeksperimen untuk dijalankan pada Backendβ
generate_cutting_experiments menerima argumen circuits/observables sebagai kamus yang memetakan label partition Qubit kepada subcircuit/subobservables yang berkaitan.
Untuk mensimulasikan nilai jangkaan Circuit bersaiz penuh, banyak subeksperimen dijana daripada taburan kuasikemungkinan bersama Gate yang didekomposisi dan kemudiannya dilaksanakan pada satu atau lebih Backend. Bilangan sampel yang diambil daripada taburan dikawal oleh num_samples, dan satu pekali gabungan diberikan bagi setiap sampel unik. Untuk maklumat lanjut tentang cara pekali dikira, rujuk bahan penjelasan.
from qiskit_addon_cutting import generate_cutting_experiments
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits, observables=subobservables, num_samples=np.inf
)
Pilih Backendβ
Di sini kita menggunakan Backend palsu, yang akan menyebabkan Qiskit Runtime berjalan dalam mod tempatan (iaitu, pada simulator tempatan).
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
backend = FakeManilaV2()
Sediakan subeksperimen untuk Backendβ
Kita mesti melakukan Transpile pada Circuit dengan Backend kita sebagai sasaran sebelum menghantarnya ke Qiskit Runtime.
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()
}
Langkah 3: Laksanakanβ
Jalankan subeksperimen menggunakan primitif Sampler 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()}
Langkah 4: Pasca-prosesβ
Bina semula nilai jangkaanβ
Bina semula nilai jangkaan bagi setiap sebutan observable dan gabungkannya untuk membina semula nilai jangkaan bagi observable asal.
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)
Bandingkan nilai jangkaan yang dibina semula dengan nilai jangkaan tepat daripada Circuit dan observable asalβ
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