Langkau ke kandungan utama
Projek ini dalam fasa beta. Jumpa pepijat? Buat isu GitHub. Ada soalan atau idea? Tanya dalam Discussions — kami suka dengar maklum balas anda!
Bagaimana kualiti terjemahan pada halaman ini?

Simulasi litar secara klasik dengan OBP

Dalam panduan ini, anda akan mempelajari cara mensimulasikan contoh QuantumCircuit secara klasik untuk menganggar nilai jangkaan sepenuhnya melalui OBP.

Memandangkan OBP akan mengambil observable dan merambat baliknya melalui litar tertentu, "simulasi" sesuatu litar bermaksud mengira nilai jangkaan observable sasaran berkenaan litar ini. Seperti yang akan anda lihat nanti, pakej qiskit-addon-obp malah mampu mengendalikan model hingar yang ringkas, membolehkan anda mengira nilai jangkaan berhingar juga!

Membina litar contoh​

Bagi tujuan panduan ini, kami akan menggunakan litar contoh yang sama seperti dalam panduan pemangkasan sebutan Pauli:

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_obp": "qiskit-addon-obp", "qiskit_addon_utils": "qiskit-addon-utils", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "rustworkx": "rustworkx"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
PauliOrderStrategy,
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types

# we generate a linear chain of 10 qubits
num_qubits = 10
linear_chain = rustworkx.generators.path_graph(num_qubits)

# we use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
linear_chain,
coupling_constants=(0.05, 0.02, 0.0),
ext_magnetic_field=(0.02, 0.08, 0.0),
pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# we evolve for some time
circuit = generate_time_evolution_circuit(
hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)

Walau bagaimanapun, litar di atas semata-mata menerangkan evolusi masa di bawah Hamiltonian yang dipilih. Kita juga memerlukan keadaan awal sebagai permulaan, yang berkenaannya kita mengira nilai jangkaan observable kita.

Sudah tentu, kita boleh memilih keadaan semua-sifar (atau vakum) sebagai keadaan awal kita, tetapi untuk menunjukkan cara memasukkan keadaan awal sendiri, kita memilih yang berbeza di bawah.

Satu kemungkinan ialah menambah keadaan awal di hadapan litar evolusi masa kita di atas: circuit.compose(initial_state, front=True). Tetapi memandangkan kita telah pun menghiris circuit kita, lebih mudah untuk sekadar memasukkan keadaan awal sebagai hirisan pertama, yang kita lakukan di bawah.

Dengan cara ini, kita boleh menggantikan hirisan pertama dengan keadaan awal lain, jika kita mahu menukarnya pada masa hadapan, tanpa perlu mengira semula hirisan kita.

from qiskit.circuit import QuantumCircuit

initial_state = QuantumCircuit(num_qubits)
for i in range(0, num_qubits, 2):
initial_state.x(i)

slices.insert(0, initial_state)
# for visualization purposes only, we recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)

Output of the previous code cell

Mensimulasikan nilai jangkaan tanpa hingar​

Sebagai observable sasaran, kita memilih observable ZZ pada qubit tengah:

from qiskit.quantum_info import SparsePauliOp

obs = SparsePauliOp("IIIIZZIIII")

Pada ketika ini, kita sudah bersedia untuk mensimulasikan nilai jangkaan secara klasik menggunakan OBP. Untuk berbuat demikian, kita hanya perlu memberikan semua hirisan kepada kaedah backpropagate, seperti berikut:

from qiskit_addon_obp import backpropagate

vacuum_state_obs, _, metadata = backpropagate(obs, slices)

Kini kita telah merambat balik observable sasaran kita obs melalui keseluruhan litar (termasuk initial_state yang kita letakkan pada slices[0]) menghasilkan SparsePauliOp baharu yang nilai jangkaannya kita peroleh dengan memproyeksikannya pada keadaan vakum (|00...00>).

Ini boleh dicapai dengan cara yang mudah dengan menjumlahkan pekali semua sebutan Pauli yang ditakrifkan dalam asas pengiraan:

vacuum_state_obs.coeffs[~vacuum_state_obs.paulis.x.any(axis=1)].sum()
np.complex128(-0.8285688012239535+4.9487770271457865e-20j)

Sebagai semakan kewarasan (dan untuk membuktikan bahawa ini berfungsi) kita boleh membandingkan keputusan kita dengan Statevector Qiskit:

from qiskit.quantum_info import Statevector

Statevector(combine_slices(slices)).expectation_value(obs)
np.complex128(-0.8285687255430366+0j)

Beberapa catatan tentang prestasi​

Kecekapan pengiraan panggilan backpropagate di atas akan sangat bergantung pada banyak perkara, termasuk:

  • struktur circuit
  • kaedah menghiris litar
  • observable sasaran
  • parameter pemangkasan

Memandangkan kaedah backpropagate memudahkan observable selepas setiap hirisan diterapkan, bilangan gate dalam sesuatu hirisan boleh mempengaruhi beban pengiraan dengan ketara. Strategi paling agresif dari segi pemudahan operator boleh dicapai dengan menghiris litar anda kepada hirisan gate individu.

Selain itu, anda boleh memanfaatkan semua mekanisme pemangkasan yang terbina dalam kaedah backpropagate. Kami tidak berbuat demikian di atas, sehingga menghasilkan nilai jangkaan yang tepat, tetapi anda boleh mempelajari caranya dalam panduan pemangkasan sebutan Pauli.

Mensimulasikan nilai jangkaan berhingar​

Pakej qiskit-addon-obp turut menyokong pengendalian model hingar dalam bentuk PauliLindbladError. Ini amat berguna apabila anda telah mencirikan model hingar lapisan 2 qubit dalam litar anda, contohnya menggunakan NoiseLearner.

Dalam bahagian ini, anda akan melihat cara menggunakan objek LayerError yang dikembalikan oleh NoiseLearner untuk mengira nilai jangkaan berhingar menggunakan OBP.

Mendapatkan model hingar​

Lazimnya, anda akan melaksanakan NoiseLearner untuk mendapatkan model hingar bagi litar khusus anda. Untuk mengelakkan kerumitan (dan kerawakan) dalam tutorial ini, kami tidak akan berbuat demikian, sebaliknya mengekodkan secara tetap beberapa model hingar untuk litar kita di bawah.

Walau bagaimanapun, kami memastikan struktur data kita sepadan dengan NoiseLearnerResult. Dalam bentuknya sekarang (tidak ditranspil), litar kita mengandungi 4 lapisan unik gate 2 qubit:

  • slices[1]: yang mempunyai gate Rxx bertindak pada semua pasangan qubit ganjil
  • slices[2]: yang mempunyai gate Rxx bertindak pada semua pasangan qubit genap
  • slices[3]: yang mempunyai gate Ryy bertindak pada semua pasangan qubit ganjil
  • slices[4]: yang mempunyai gate Ryy bertindak pada semua pasangan qubit genap

Dalam sel di bawah, kami membina secara manual 4 contoh LayerError untuk setiap satu lapisan ini dengan beberapa kadar ralat rawak.

import numpy as np
from qiskit.quantum_info import PauliList
from qiskit_ibm_runtime.utils.noise_learner_result import (
LayerError,
PauliLindbladError,
)

# fmt: off
pauli_errors_even = ['IIIIIIIIIX', 'IIIIIIIIIY', 'IIIIIIIIIZ', 'IIIIIIIIXI', 'IIIIIIIIXX', 'IIIIIIIIXY', 'IIIIIIIIXZ', 'IIIIIIIIYI', 'IIIIIIIIYX', 'IIIIIIIIYY', 'IIIIIIIIYZ', 'IIIIIIIIZI', 'IIIIIIIIZX', 'IIIIIIIIZY', 'IIIIIIIIZZ', 'IIIIIIIXII', 'IIIIIIIXXI', 'IIIIIIIXYI', 'IIIIIIIXZI', 'IIIIIIIYII', 'IIIIIIIYXI', 'IIIIIIIYYI', 'IIIIIIIYZI', 'IIIIIIIZII', 'IIIIIIIZXI', 'IIIIIIIZYI', 'IIIIIIIZZI', 'IIIIIIXIII', 'IIIIIIXXII', 'IIIIIIXYII', 'IIIIIIXZII', 'IIIIIIYIII', 'IIIIIIYXII', 'IIIIIIYYII', 'IIIIIIYZII', 'IIIIIIZIII', 'IIIIIIZXII', 'IIIIIIZYII', 'IIIIIIZZII', 'IIIIIXIIII', 'IIIIIXXIII', 'IIIIIXYIII', 'IIIIIXZIII', 'IIIIIYIIII', 'IIIIIYXIII', 'IIIIIYYIII', 'IIIIIYZIII', 'IIIIIZIIII', 'IIIIIZXIII', 'IIIIIZYIII', 'IIIIIZZIII', 'IIIIXIIIII', 'IIIIXXIIII', 'IIIIXYIIII', 'IIIIXZIIII', 'IIIIYIIIII', 'IIIIYXIIII', 'IIIIYYIIII', 'IIIIYZIIII', 'IIIIZIIIII', 'IIIIZXIIII', 'IIIIZYIIII', 'IIIIZZIIII', 'IIIXIIIIII', 'IIIXXIIIII', 'IIIXYIIIII', 'IIIXZIIIII', 'IIIYIIIIII', 'IIIYXIIIII', 'IIIYYIIIII', 'IIIYZIIIII', 'IIIZIIIIII', 'IIIZXIIIII', 'IIIZYIIIII', 'IIIZZIIIII', 'IIXIIIIIII', 'IIXXIIIIII', 'IIXYIIIIII', 'IIXZIIIIII', 'IIYIIIIIII', 'IIYXIIIIII', 'IIYYIIIIII', 'IIYZIIIIII', 'IIZIIIIIII', 'IIZXIIIIII', 'IIZYIIIIII', 'IIZZIIIIII', 'IXIIIIIIII', 'IXXIIIIIII', 'IXYIIIIIII', 'IXZIIIIIII', 'IYIIIIIIII', 'IYXIIIIIII', 'IYYIIIIIII', 'IYZIIIIIII', 'IZIIIIIIII', 'IZXIIIIIII', 'IZYIIIIIII', 'IZZIIIIIII', 'XIIIIIIIII', 'XXIIIIIIII', 'XYIIIIIIII', 'XZIIIIIIII', 'YIIIIIIIII', 'YXIIIIIIII', 'YYIIIIIIII', 'YZIIIIIIII', 'ZIIIIIIIII', 'ZXIIIIIIII', 'ZYIIIIIIII', 'ZZIIIIIIII']
pauli_errors_odd = ['IIIIIIIIXI', 'IIIIIIIIYI', 'IIIIIIIIZI', 'IIIIIIIXII', 'IIIIIIIXXI', 'IIIIIIIXYI', 'IIIIIIIXZI', 'IIIIIIIYII', 'IIIIIIIYXI', 'IIIIIIIYYI', 'IIIIIIIYZI', 'IIIIIIIZII', 'IIIIIIIZXI', 'IIIIIIIZYI', 'IIIIIIIZZI', 'IIIIIIXIII', 'IIIIIIXXII', 'IIIIIIXYII', 'IIIIIIXZII', 'IIIIIIYIII', 'IIIIIIYXII', 'IIIIIIYYII', 'IIIIIIYZII', 'IIIIIIZIII', 'IIIIIIZXII', 'IIIIIIZYII', 'IIIIIIZZII', 'IIIIIXIIII', 'IIIIIXXIII', 'IIIIIXYIII', 'IIIIIXZIII', 'IIIIIYIIII', 'IIIIIYXIII', 'IIIIIYYIII', 'IIIIIYZIII', 'IIIIIZIIII', 'IIIIIZXIII', 'IIIIIZYIII', 'IIIIIZZIII', 'IIIIXIIIII', 'IIIIXXIIII', 'IIIIXYIIII', 'IIIIXZIIII', 'IIIIYIIIII', 'IIIIYXIIII', 'IIIIYYIIII', 'IIIIYZIIII', 'IIIIZIIIII', 'IIIIZXIIII', 'IIIIZYIIII', 'IIIIZZIIII', 'IIIXIIIIII', 'IIIXXIIIII', 'IIIXYIIIII', 'IIIXZIIIII', 'IIIYIIIIII', 'IIIYXIIIII', 'IIIYYIIIII', 'IIIYZIIIII', 'IIIZIIIIII', 'IIIZXIIIII', 'IIIZYIIIII', 'IIIZZIIIII', 'IIXIIIIIII', 'IIXXIIIIII', 'IIXYIIIIII', 'IIXZIIIIII', 'IIYIIIIIII', 'IIYXIIIIII', 'IIYYIIIIII', 'IIYZIIIIII', 'IIZIIIIIII', 'IIZXIIIIII', 'IIZYIIIIII', 'IIZZIIIIII', 'IXIIIIIIII', 'IXXIIIIIII', 'IXYIIIIIII', 'IXZIIIIIII', 'IYIIIIIIII', 'IYXIIIIIII', 'IYYIIIIIII', 'IYZIIIIIII', 'IZIIIIIIII', 'IZXIIIIIII', 'IZYIIIIIII', 'IZZIIIIIII']
# fmt: on

np.random.seed(42)

layer_error_odd_xx = LayerError(
circuit=slices[1],
qubits=list(range(num_qubits)),
error=PauliLindbladError(
PauliList(pauli_errors_odd),
0.0001 + 0.0004 * np.random.rand(len(pauli_errors_odd)),
),
)

layer_error_even_xx = LayerError(
circuit=slices[2],
qubits=list(range(num_qubits)),
error=PauliLindbladError(
PauliList(pauli_errors_even),
0.0001 + 0.0004 * np.random.rand(len(pauli_errors_even)),
),
)

layer_error_odd_yy = LayerError(
circuit=slices[3],
qubits=list(range(num_qubits)),
error=PauliLindbladError(
PauliList(pauli_errors_odd),
0.0001 + 0.0004 * np.random.rand(len(pauli_errors_odd)),
),
)

layer_error_even_yy = LayerError(
circuit=slices[4],
qubits=list(range(num_qubits)),
error=PauliLindbladError(
PauliList(pauli_errors_even),
0.0001 + 0.0004 * np.random.rand(len(pauli_errors_even)),
),
)

Jika anda telah menggunakan NoiseLearner untuk mengenal pasti lapisan gate 2 qubit yang unik dalam litar anda dan mencirikan hingarnya, anda akan memperoleh objek NoiseLearnerResult. Hasil ini akan mengandungi senarai objek LayerError, sama seperti yang telah kami bina secara manual di atas.

Bagi setiap lapisan 2 qubit yang unik, LayerError mengandungi:

  • QuantumCircuit yang mewakili lapisan gate 2 qubit tersebut
  • indeks qubit yang menjadi sasaran litar ini
  • PauliLindbladError yang mewakili model hingar tercirikan bagi lapisan ini

PauliLindbladError akan mengandungi dua objek:

  • senarai ralat Pauli yang telah dicirikan
  • kadar ralat yang sepadan dengan setiap satu ralat Pauli tersebut

Lazimnya, senarai ralat Pauli akan jarang. Lebih khusus lagi, ia akan mengandungi ralat Pauli satu qubit pada semua qubit yang mempunyai gate bertindak ke atasnya serta ralat Pauli dua qubit pada semua qubit yang bersambung.

Memasukkan lapisan berhingar ke dalam litar kita​

Dalam bahagian sebelumnya, kita telah membina secara khusus satu LayerError bagi setiap lapisan gate 2 qubit unik yang kita ketahui. Ini bermakna kita tahu LayerError mana yang sepadan tepat dengan satu hirisan tertentu kita.

Lazimnya, apabila menggunakan LayerError, anda perlu mengetahui apakah lapisan gate 2 qubit yang unik itu, dan di mana ia berlaku dalam litar anda. Anda kemudian perlu melaraskan circuit dan/atau slices anda untuk memasukkan LayerError dengan sewajarnya. Cara melakukannya dalam kes am adalah di luar skop panduan cara ini. TODO: link to external documentation, once it exists! Di sini, keadaan kita lebih mudah kerana kita tahu hirisan mana yang sepadan dengan sesuatu LayerError. Oleh itu, kini ia hanya soal memasukkan hirisan baharu untuk mewakili hingar.

Perhatikan bahawa kita mesti membungkus setiap PauliLindbladError daripada LayerError.error dalam PauliLindbladErrorInstruction supaya ia menjadi arahan QuantumCircuit yang sah.

from qiskit_addon_obp.utils.noise import PauliLindbladErrorInstruction

noisy_slices = []
for slice_ in slices:
if slice_ == layer_error_even_xx.circuit:
noisy_slices.append(
QuantumCircuit.from_instructions(
[
(
PauliLindbladErrorInstruction(
layer_error_even_xx.error
),
slice_.qubits,
)
]
)
)
elif slice_ == layer_error_odd_xx.circuit:
noisy_slices.append(
QuantumCircuit.from_instructions(
[
(
PauliLindbladErrorInstruction(
layer_error_odd_xx.error
),
slice_.qubits,
)
]
)
)
elif slice_ == layer_error_even_yy.circuit:
noisy_slices.append(
QuantumCircuit.from_instructions(
[
(
PauliLindbladErrorInstruction(
layer_error_even_yy.error
),
slice_.qubits,
)
]
)
)
elif slice_ == layer_error_odd_yy.circuit:
noisy_slices.append(
QuantumCircuit.from_instructions(
[
(
PauliLindbladErrorInstruction(
layer_error_odd_yy.error
),
slice_.qubits,
)
]
)
)
noisy_slices.append(slice_)

Kita boleh menyemak kerja kita dan melukis litar di bawah:

combine_slices(noisy_slices, include_barriers=True).draw(
"mpl", fold=100, scale=0.8
)

Output of the previous code cell

Mensimulasikan nilai jangkaan berhingar​

Pada ketika ini, mensimulasikan nilai jangkaan secara klasik berfungsi dengan cara yang sama seperti sebelum ini, cuma

vacuum_state_noisy_obs, _, metadata = backpropagate(obs, noisy_slices)
vacuum_state_noisy_obs.coeffs[
~vacuum_state_noisy_obs.paulis.x.any(axis=1)
].sum()
np.complex128(-0.7230801696448901+7.082755280463563e-19j)

Kami nyatakan sekali lagi bahawa beberapa kebimbangan prestasi perlu dipertimbangkan. Sila kembali ke bahagian yang sepadan di atas.