Skip to content

Fidelity Layout with QCloud ​

This tutorial shows the complete pyqpanda3 0.4.0 hardware-aware path:

  1. Fetch live QCloud chip metadata.
  2. Convert the metadata into a ChipBackend.
  3. Use the same ChipBackend for fidelity-aware layout and transpilation.
  4. Export a scheduled instruction stream with timing derived from the chip metadata.
  5. Preview the timing diagram locally.
  6. Submit the exact instruction stream with run_instruction().

Do not hard-code API keys in source files. The examples below read QPANDA_QCLOUD_API_KEY from the environment.

Why chip_info() Matters ​

run_instruction() accepts instruction JSON, so it is tempting to hand-write a short instruction list. That is useful for debugging, but it is not the recommended hardware workflow.

For a real QCloud backend, build the instruction stream from chip_info() instead:

The important invariant is simple: the backend used for layout should also be the backend used for instruction export and schedule drawing.

One-Step Fidelity Transpile, Schedule, and Submit ​

This path lets Transpiler call fidelity layout internally through layout_method="fidelity".

python
import os

from pyqpanda3.core import CZ, H, QProg, measure
from pyqpanda3.qcloud import QCloudOptions, QCloudService
from pyqpanda3.transpilation import Transpiler, decompose
from pyqpanda3.visualization import draw_time_scheduling


def build_program():
    prog = QProg()
    prog << H(0)
    prog << H(1)
    prog << CZ(0, 1)
    prog << CZ(1, 2)
    prog << measure(0, 0)
    prog << measure(1, 1)
    prog << measure(2, 2)
    return prog


service = QCloudService(os.environ["QPANDA_QCLOUD_API_KEY"])
qcloud_backend = service.backend(os.environ.get("QPANDA_QCLOUD_BACKEND", "WK_C180"))

chip_info = qcloud_backend.chip_info()
chip_backend = chip_info.get_chip_backend()
print("backend:", qcloud_backend.name())
print("timing:", chip_backend.single_gate_timing, chip_backend.double_gate_timing)

prog = build_program()
decomposed = decompose(prog, ["U3", "CZ"])
transpiled = Transpiler().transpile(decomposed, chip_backend, {}, 2, "fidelity")

# is_scheduling=True makes to_instruction use timing from chip_backend, including
# single_gate_timing and double_gate_timing, instead of relying on hand-written timestamps.
instruction = transpiled.to_instruction(
    chip_backend,
    offset=1,
    is_scheduling=True,
    compensate_pulse=False,
)

figures = draw_time_scheduling(instruction, backend=chip_backend, output="plotly")
print("schedule figure:", type(figures).__name__)

options = QCloudOptions()
options.set_mapping(False)
options.set_optimization(False)
options.set_amend(False)

job = qcloud_backend.run_instruction([instruction], 1000, options)
result = job.result()

print(result.get_counts_list())

Example output shape:

text
backend: WK_C180
timing: 40 80
schedule figure: Figure
[{'000': 263, '001': 229, '010': 241, '011': 227, '100': 6, '101': 4, '110': 15, '111': 15}]

Timing and counts vary by backend and calibration snapshot. The key point is that the instruction timestamps are generated from the ChipBackend returned by QCloud, not from constants in the document.

Inspect the Layout Before Routing ​

Call fidelity_layout() directly when you want to inspect, store, or adjust the initial mapping before routing. The same chip_backend should still be passed to transpile(), to_instruction(), and draw_time_scheduling().

python
from pyqpanda3.transpilation import Transpiler, decompose, fidelity_layout

decomposed = decompose(prog, ["U3", "CZ"])

init_mapping = fidelity_layout(
    decomposed,
    chip_backend.chip_topology_edges,
    chip_backend.single_gate_fidelity,
    chip_backend.readout_fidelity,
    chip_backend.double_gate_fidelity,
)
print("initial mapping:", init_mapping)

transpiled = Transpiler().transpile(
    decomposed,
    chip_backend,
    init_mapping,
    2,
)

instruction = transpiled.to_instruction(
    chip_backend,
    offset=1,
    is_scheduling=True,
    compensate_pulse=False,
)
figures = draw_time_scheduling(instruction, backend=chip_backend, output="plotly")

job = qcloud_backend.run_instruction([instruction], 1000, options)
print(type(figures).__name__)
print(job.result().get_counts_list())

Example output shape:

text
initial mapping: {1: 106, 0: 96, 2: 115}
Figure
[{'000': 229, '001': 203, '010': 210, '011': 189, '100': 39, '101': 50, '110': 45, '111': 35}]

init_mapping is a Python dictionary whose keys are logical qubits and whose values are physical chip qubits.

How Fidelity Layout Works ​

Build the Virtual Interaction Graph ​

The layout pass scans the decomposed program. Single-qubit gates increase the importance of their logical qubits. Measurements make readout fidelity relevant. Two-qubit gates create weighted virtual interaction edges, and repeated interactions receive higher weight.

Search Connected Physical Blocks ​

The chip topology comes from chip_backend.chip_topology_edges. Instead of trying every possible assignment on the full chip, the algorithm first searches connected physical candidate blocks with the same size as the active logical qubit set.

Score Candidate Mappings ​

Each candidate mapping is scored by minimizing the estimated error cost:

  • Single-gate error matters more for logical qubits with many single-qubit operations.
  • Readout error matters for logical qubits that are measured.
  • Two-qubit gate error matters for logical edges with frequent two-qubit gates.
  • Non-adjacent physical distance adds a penalty when frequently interacting logical qubits are far apart.
text
cost =
  one_qubit_count  * single_gate_error(physical_qubit)
+ measure_count    * readout_error(physical_qubit)
+ two_qubit_count  * pair_cost(logical_edge, physical_qubits)

where pair_cost depends on whether the logical qubits are mapped to adjacent physical qubits:

  • If the two physical qubits are adjacent, pair_cost = double_gate_error(adjacent_physical_edge).
  • If they are farther apart, the pair cost adds a nonlocal-interaction penalty, a distance penalty, and the average two-qubit error.

Missing fidelity values use the average error from the available values of the same fidelity class. If that class is empty, the average is 0.0.

Hand the Mapping to Routing ​

Fidelity layout chooses the initial mapping only. It does not replace routing. If the selected mapping places the main interactions on adjacent, high-fidelity edges, routing usually inserts fewer SWAPs. If the circuit interaction graph and chip topology differ substantially, routing may still need SWAPs.

Tuning Search Parameters ​

python
init_mapping = fidelity_layout(
    decomposed,
    chip_backend.chip_topology_edges,
    chip_backend.single_gate_fidelity,
    chip_backend.readout_fidelity,
    chip_backend.double_gate_fidelity,
    max_candidate_blocks=32,
    max_mappings_per_block=256,
    distance_penalty_weight=1.0,
)
ParameterMeaning
max_candidate_blocksNumber of connected physical blocks to consider. Larger values search more thoroughly and take longer.
max_mappings_per_blockNumber of mappings tried inside each candidate block. Larger values can find better mappings but increase combinatorial cost.
distance_penalty_weightPenalty for physical distance. Larger values favor fewer routing SWAPs; smaller values favor fidelity more directly.

Run the Sample ​

The source package includes a runnable sample:

powershell
$env:QPANDA_QCLOUD_API_KEY="your_api_key"
$env:QPANDA_QCLOUD_BACKEND="WK_C180"
python samples/python/fidelity_layout_qcloud.py

If QPANDA_QCLOUD_BACKEND is not set, the sample uses WK_C180.

Beyond 0.4.0: fidelity-driven approximation and metrics (0.4.1) ​

This tutorial focuses on the 0.4.0 fidelity_layout() positional-argument workflow. 0.4.1 builds on it to actually let fidelity data drive transpilation decisions:

  • TranspilationOptions groups the layout / routing / optimization strategies into one object, and TranspilationResult.metrics recovers the two-qubit gate count, depth, log-expected fidelity, and more.
  • At optimization_level >= 1, approximation_degree decides whether ORACLE KAK synthesis is exact or approximate (1.0 exact, None uses the target two-qubit gate error rate).
  • ChipBackend.gate_fidelity (per-instruction fidelity) is auto-populated from cloud metadata and matched first by transpilation_metrics().
  • RoutingMethod.LIGHT_SABRE enables layered-lookahead routing, which usually reduces two-qubit gate insertion.

See Transpilation tutorial §4.5 and the Transpilation Options & Metrics API.

Released under the MIT License.