Langkau ke kandungan utama

Cipta dan transpil menggunakan backend tersuai

# Added by doQumentation β€” required packages for this notebook
!pip install -q numpy qiskit rustworkx
# Don't use SVGs for this file because the images are too large,
# and the SVGs are much larger than their PNGs equivalents.
%config InlineBackend.figure_format='png'
```json

{/* cspell:ignore multichip interchip Lasciate ogne speranza voi ch'intrate */}
{/*
DO NOT EDIT THIS CELL!!!
This cell's content is generated automatically by a script. Anything you add
here will be removed next time the notebook is run. To add new content, create
a new cell before or after this one.
*/}

<details>
<summary><b>Versi pakej</b></summary>

Kod pada halaman ini dibangunkan menggunakan keperluan berikut.
Kami mengesyorkan penggunaan versi ini atau yang lebih baharu.

qiskit[all]~=2.3.0

</details>
{/* cspell:ignore LOCC */}

Salah satu ciri Qiskit yang paling berkuasa ialah keupayaan untuk menyokong konfigurasi peranti yang unik. Qiskit dibina untuk bersifat agnostik terhadap pembekal perkakasan kuantum yang anda gunakan, dan pembekal boleh mengkonfigurasi objek `BackendV2` mengikut sifat peranti mereka yang unik. Topik ini menunjukkan cara mengkonfigurasi backend anda sendiri dan mentranspil litar kuantum menggunakannya.

Anda boleh mencipta objek `BackendV2` yang unik dengan geometri atau gate asas yang berbeza, serta mentranspil litar anda dengan konfigurasi tersebut. Contoh di bawah meliputi backend dengan kekisi qubit yang terpisah, di mana gate asasnya berbeza di tepi berbanding di bahagian dalam.

## Fahami antara muka Provider, BackendV2, dan Target \{#understand-the-provider-backendv2-and-target-interfaces}

Sebelum bermula, adalah berguna untuk memahami penggunaan dan tujuan objek [`Provider`](../api/qiskit/providers), [`BackendV2`](../api/qiskit/qiskit.providers.BackendV2), dan [`Target`](../api/qiskit/qiskit.transpiler.Target).

- Jika anda mempunyai peranti kuantum atau simulator yang ingin diintegrasikan ke dalam Qiskit SDK, anda perlu menulis kelas `Provider` anda sendiri. Kelas ini mempunyai satu tujuan sahaja: untuk mendapatkan objek backend yang anda sediakan. Di sinilah sebarang tugas kelayakan dan/atau pengesahan dikendalikan. Setelah dimulakan, objek provider akan menyediakan senarai backend serta keupayaan untuk mendapatkan/memulakan backend.

- Seterusnya, kelas backend menyediakan antara muka antara Qiskit SDK dan perkakasan atau simulator yang akan melaksanakan litar. Ia mengandungi semua maklumat yang diperlukan untuk menerangkan sebuah backend kepada Transpiler supaya ia boleh mengoptimumkan sebarang litar mengikut kekangannya. Sebuah `BackendV2` dibina daripada empat bahagian utama:
- Sifat [`Target`](../api/qiskit/qiskit.transpiler.Target), yang mengandungi penerangan tentang kekangan backend dan menyediakan model backend untuk Transpiler
- Sifat `max_circuits` yang menentukan had bilangan litar yang boleh dilaksanakan oleh backend dalam satu kerja
- Kaedah `run()` yang menerima penyerahan kerja
- Satu set `_default_options` untuk menentukan pilihan yang boleh dikonfigurasi oleh pengguna dan nilai lalainya

## Cipta BackendV2 tersuai \{#create-a-custom-backendv2}

Objek `BackendV2` ialah kelas abstrak yang digunakan untuk semua objek backend yang dicipta oleh pembekal (sama ada dalam `qiskit.providers` atau perpustakaan lain seperti [`qiskit_ibm_runtime.IBMBackend`](../api/qiskit-ibm-runtime/ibm-backend)). Seperti yang disebutkan di atas, objek ini mengandungi beberapa atribut, termasuk [`Target`](https://docs.quantum.ibm.com/api/qiskit/qiskit.transpiler.Target). `Target` mengandungi maklumat yang menentukan atribut backend β€” seperti [`Coupling Map`](https://docs.quantum.ibm.com/api/qiskit/qiskit.transpiler.CouplingMap), senarai [`Instructions`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.Instruction), dan lain-lain β€” kepada Transpiler. Selain `Target`, seseorang juga boleh menentukan butiran peringkat denyutan seperti [`DriveChannel`](https://docs.quantum.ibm.com/api/qiskit/1.4/qiskit.pulse.channels.DriveChannel) atau [`ControlChannel`](https://docs.quantum.ibm.com/api/qiskit/1.4/qiskit.pulse.channels.ControlChannel).

Contoh berikut menunjukkan penyesuaian ini dengan mencipta backend berbilang cip simulasi, di mana setiap cip mempunyai ketersambungan heavy-hex. Contoh ini menentukan set gate dua-qubit backend sebagai [`CZGates`](../api/qiskit/qiskit.circuit.library.CZGate) dalam setiap cip dan [`CXGates`](../api/qiskit/qiskit.circuit.library.ECRGate) antara cip. Pertama, cipta `BackendV2` anda sendiri dan sesuaikan `Target`-nya dengan gate satu dan dua-qubit mengikut kekangan yang telah diterangkan.

<Admonition type="tip" title="Pustaka graphviz">
Memplot peta gandingan memerlukan pustaka [`graphviz`](https://graphviz.org/) dipasang.
</Admonition>

```python
import numpy as np
import rustworkx as rx

from qiskit.providers import BackendV2, Options
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import XGate, SXGate, RZGate, CZGate, ECRGate
from qiskit.circuit import Measure, Delay, Parameter, Reset
from qiskit import QuantumCircuit, transpile
from qiskit.visualization import plot_gate_map

class FakeLOCCBackend(BackendV2):
"""Fake multi chip backend."""

def __init__(self, distance=3, number_of_chips=3):
"""Instantiate a new fake multi chip backend.

Args:
distance (int): The heavy hex code distance to use for each chips'
coupling map. This number **must** be odd. The distance relates
to the number of qubits by:
:math:`n = \\frac{5d^2 - 2d - 1}{2}` where :math:`n` is the
number of qubits and :math:`d` is the ``distance``
number_of_chips (int): The number of chips to have in the multichip backend
each chip will be a heavy hex graph of ``distance`` code distance.
"""
super().__init__(name="Fake LOCC backend")
# Create a heavy-hex graph using the rustworkx library, then instantiate a new target
self._graph = rx.generators.directed_heavy_hex_graph(
distance, bidirectional=False
)
num_qubits = len(self._graph) * number_of_chips
self._target = Target(
"Fake multi-chip backend", num_qubits=num_qubits
)

# Generate instruction properties for single qubit gates and a measurement, delay,
# and reset operation to every qubit in the backend.
rng = np.random.default_rng(seed=12345678942)
rz_props = {}
x_props = {}
sx_props = {}
measure_props = {}
delay_props = {}

# Add 1q gates. Globally use virtual rz, x, sx, and measure
for i in range(num_qubits):
qarg = (i,)
rz_props[qarg] = InstructionProperties(error=0.0, duration=0.0)
x_props[qarg] = InstructionProperties(
error=rng.uniform(1e-6, 1e-4),
duration=rng.uniform(1e-8, 9e-7),
)
sx_props[qarg] = InstructionProperties(
error=rng.uniform(1e-6, 1e-4),
duration=rng.uniform(1e-8, 9e-7),
)
measure_props[qarg] = InstructionProperties(
error=rng.uniform(1e-3, 1e-1),
duration=rng.uniform(1e-8, 9e-7),
)
delay_props[qarg] = None
self._target.add_instruction(XGate(), x_props)
self._target.add_instruction(SXGate(), sx_props)
self._target.add_instruction(RZGate(Parameter("theta")), rz_props)
self._target.add_instruction(Measure(), measure_props)
self._target.add_instruction(Reset(), measure_props)

self._target.add_instruction(Delay(Parameter("t")), delay_props)
# Add chip local 2q gate which is CZ
cz_props = {}
for i in range(number_of_chips):
for root_edge in self._graph.edge_list():
offset = i * len(self._graph)
edge = (root_edge[0] + offset, root_edge[1] + offset)
cz_props[edge] = InstructionProperties(
error=rng.uniform(7e-4, 5e-3),
duration=rng.uniform(1e-8, 9e-7),
)
self._target.add_instruction(CZGate(), cz_props)

cx_props = {}
# Add interchip 2q gates which are ecr (effectively CX)
# First determine which nodes to connect
node_indices = self._graph.node_indices()
edge_list = self._graph.edge_list()
inter_chip_nodes = {}
for node in node_indices:
count = 0
for edge in edge_list:
if node == edge[0]:
count += 1
if count == 1:
inter_chip_nodes[node] = count
# Create inter-chip ecr props
cx_props = {}
inter_chip_edges = list(inter_chip_nodes.keys())
for i in range(1, number_of_chips):
offset = i * len(self._graph)
edge = (
inter_chip_edges[1] + (len(self._graph) * (i - 1)),
inter_chip_edges[0] + offset,
)
cx_props[edge] = InstructionProperties(
error=rng.uniform(7e-4, 5e-3),
duration=rng.uniform(1e-8, 9e-7),
)

self._target.add_instruction(ECRGate(), cx_props)

@property
def target(self):
return self._target

@property
def max_circuits(self):
return None

@property
def graph(self):
return self._graph

@classmethod
def _default_options(cls):
return Options(shots=1024)

def run(self, circuit, **kwargs):
raise NotImplementedError(
"This backend does not contain a run method"
)

Visualkan backend​

Anda boleh melihat graf ketersambungan kelas baharu ini dengan kaedah plot_gate_map() daripada modul qiskit.visualization. Kaedah ini, bersama plot_coupling_map() dan plot_circuit_layout(), adalah alat yang berguna untuk memvisualisasikan susunan qubit bagi sebuah backend, serta bagaimana sebuah Circuit diletakkan merentasi qubit backend. Contoh ini mencipta backend yang mengandungi tiga cip heavy-hex kecil. Ia menentukan satu set koordinat untuk menyusun qubit, serta satu set warna tersuai untuk gate dua-qubit yang berbeza.

backend = FakeLOCCBackend(3, 3)

target = backend.target
coupling_map_backend = target.build_coupling_map()

coordinates = [
(3, 1),
(3, -1),
(2, -2),
(1, 1),
(0, 0),
(-1, -1),
(-2, 2),
(-3, 1),
(-3, -1),
(2, 1),
(1, -1),
(-1, 1),
(-2, -1),
(3, 0),
(2, -1),
(0, 1),
(0, -1),
(-2, 1),
(-3, 0),
]

single_qubit_coordinates = []
total_qubit_coordinates = []

for coordinate in coordinates:
total_qubit_coordinates.append(coordinate)

for coordinate in coordinates:
total_qubit_coordinates.append(
(-1 * coordinate[0] + 1, coordinate[1] + 4)
)

for coordinate in coordinates:
total_qubit_coordinates.append((coordinate[0], coordinate[1] + 8))

line_colors = ["#adaaab" for edge in coupling_map_backend.get_edges()]
ecr_edges = []

# Get tuples for the edges which have an ecr instruction attached
for instruction in target.instructions:
if instruction[0].name == "ecr":
ecr_edges.append(instruction[1])

for i, edge in enumerate(coupling_map_backend.get_edges()):
if edge in ecr_edges:
line_colors[i] = "#000000"
print(backend.name)
plot_gate_map(
backend,
plot_directed=True,
qubit_coordinates=total_qubit_coordinates,
line_color=line_colors,
)
Fake LOCC backend

Output of the previous code cell

Setiap qubit dilabelkan, dan anak panah berwarna mewakili gate dua-qubit. Anak panah kelabu ialah gate CZ manakala anak panah hitam ialah gate CX antara cip (yang menghubungkan qubit 6β†’216 \rightarrow 21 dan 25β†’4025 \rightarrow 40). Arah anak panah menunjukkan arah lalai pelaksanaan gate ini; ia menentukan qubit mana yang menjadi kawalan/sasaran secara lalai untuk setiap saluran dua-qubit.

Transpil menggunakan backend tersuai​

Memandangkan backend tersuai dengan Target yang unik telah ditakrifkan, adalah mudah untuk mentranspil litar kuantum menggunakan backend ini, kerana semua kekangan yang relevan (gate asas, ketersambungan qubit, dan sebagainya) yang diperlukan untuk laluan Transpiler terkandung dalam atribut ini. Contoh seterusnya membina litar yang mencipta keadaan GHZ yang besar dan mentranspilnya menggunakan backend yang dibina di atas.

from qiskit.transpiler import generate_preset_pass_manager

num_qubits = 50
ghz = QuantumCircuit(num_qubits)
ghz.h(range(num_qubits))
ghz.cx(0, range(1, num_qubits))
op_counts = ghz.count_ops()

print("Pre-Transpilation: ")
print(f"CX gates: {op_counts['cx']}")
print(f"H gates: {op_counts['h']}")
print("\n", 30 * "#", "\n")

pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
transpiled_ghz = pm.run(ghz)
op_counts = transpiled_ghz.count_ops()

print("Post-Transpilation: ")
print(f"CZ gates: {op_counts['cz']}")
print(f"ECR gates: {op_counts['ecr']}")
print(f"SX gates: {op_counts['sx']}")
print(f"RZ gates: {op_counts['rz']}")
Pre-Transpilation:
CX gates: 49
H gates: 50

##############################
Post-Transpilation:
CZ gates: 151
ECR gates: 6
SX gates: 295
RZ gates: 216

Litar yang telah ditranspil kini mengandungi gabungan gate CZ dan ECR, yang kami tentukan sebagai gate asas dalam Target backend. Terdapat juga lebih banyak gate berbanding asal kerana keperluan untuk memasukkan arahan SWAP selepas memilih susun atur. Di bawah, alat visualisasi plot_circuit_layout() digunakan untuk menentukan qubit dan saluran dua-qubit yang digunakan dalam litar ini.

from qiskit.visualization import plot_circuit_layout

plot_circuit_layout(
transpiled_ghz, backend, qubit_coordinates=total_qubit_coordinates
)

Output of the previous code cell

Cipta backend unik​

Pakej rustworkx mengandungi perpustakaan besar pelbagai graf dan membolehkan penciptaan graf tersuai. Kod yang menarik secara visual di bawah mencipta backend yang terinspirasi oleh kod torik. Anda kemudian boleh memvisualisasikan backend menggunakan fungsi daripada bahagian Visualkan backend.

class FakeTorusBackend(BackendV2):
"""Fake multi chip backend."""

def __init__(self):
"""Instantiate a new backend that is inspired by a toric code"""
super().__init__(name="Fake LOCC backend")
graph = rx.generators.directed_grid_graph(20, 20)
for column in range(20):
graph.add_edge(column, 19 * 20 + column, None)
for row in range(20):
graph.add_edge(row * 20, row * 20 + 19, None)
num_qubits = len(graph)
rng = np.random.default_rng(seed=12345678942)
rz_props = {}
x_props = {}
sx_props = {}
measure_props = {}
delay_props = {}
self._target = Target("Fake Kookaburra", num_qubits=num_qubits)
# Add 1q gates. Globally use virtual rz, x, sx, and measure
for i in range(num_qubits):
qarg = (i,)
rz_props[qarg] = InstructionProperties(error=0.0, duration=0.0)
x_props[qarg] = InstructionProperties(
error=rng.uniform(1e-6, 1e-4),
duration=rng.uniform(1e-8, 9e-7),
)
sx_props[qarg] = InstructionProperties(
error=rng.uniform(1e-6, 1e-4),
duration=rng.uniform(1e-8, 9e-7),
)
measure_props[qarg] = InstructionProperties(
error=rng.uniform(1e-3, 1e-1),
duration=rng.uniform(1e-8, 9e-7),
)
delay_props[qarg] = None
self._target.add_instruction(XGate(), x_props)
self._target.add_instruction(SXGate(), sx_props)
self._target.add_instruction(RZGate(Parameter("theta")), rz_props)
self._target.add_instruction(Measure(), measure_props)
self._target.add_instruction(Reset(), measure_props)
self._target.add_instruction(Delay(Parameter("t")), delay_props)
cz_props = {}
for edge in graph.edge_list():
cz_props[edge] = InstructionProperties(
error=rng.uniform(7e-4, 5e-3),
duration=rng.uniform(1e-8, 9e-7),
)
self._target.add_instruction(CZGate(), cz_props)

@property
def target(self):
return self._target

@property
def max_circuits(self):
return None

@classmethod
def _default_options(cls):
return Options(shots=1024)

def run(self, circuit, **kwargs):
raise NotImplementedError("Lasciate ogne speranza, voi ch'intrate")
backend = FakeTorusBackend()
# We set `figsize` to a smaller size to make the documentation website faster
# to load. Normally, you do not need to set the argument.
plot_gate_map(backend, figsize=(4, 4))

Output of the previous code cell

num_qubits = int(backend.num_qubits / 2)
full_device_bv = QuantumCircuit(num_qubits, num_qubits - 1)
full_device_bv.x(num_qubits - 1)
full_device_bv.h(range(num_qubits))
full_device_bv.cx(range(num_qubits - 1), num_qubits - 1)
full_device_bv.h(range(num_qubits))
full_device_bv.measure(range(num_qubits - 1), range(num_qubits - 1))
tqc = transpile(full_device_bv, backend, optimization_level=3)
op_counts = tqc.count_ops()
print(f"CZ gates: {op_counts['cz']}")
print(f"X gates: {op_counts['x']}")
print(f"SX gates: {op_counts['sx']}")
print(f"RZ gates: {op_counts['rz']}")
CZ gates: 867
X gates: 18
SX gates: 1630
RZ gates: 1174
Source: IBM Quantum docs β€” updated 1 Apr 2026
English version on doQumentation β€” updated 7 Mei 2026
This translation based on the English version of 11 Mac 2026