Skip to content

0.4.1 ​

pyqpanda3 0.4.1 continues and deepens the 0.4.0 hardware workflow. The focus of this release is making fidelity data actually drive transpilation decisions (fidelity-driven approximate decomposition, LightSABRE routing, recoverable transpilation metrics), and filling two long-standing simulator gaps — a matrix-product-state (MPS) simulator and a Heisenberg-picture expectation simulator.

1. Strongly-typed transpilation options and metrics ​

In 0.4.0, transpile() used positional arguments to control the optimization level and layout strategy. 0.4.1 adds TranspilationOptions, which groups every tunable into a single object, and pairs it with TranspilationResult, which returns the transpiled program, compile time, and metrics in one call.

python
from pyqpanda3.core import QProg, H, CZ, measure
from pyqpanda3.transpilation import (
    ChipBackend,
    LayoutMethod,
    RoutingMethod,
    Transpiler,
    TranspilationOptions,
)

prog = QProg()
prog << H(0) << CZ(0, 1) << measure(0, 0) << measure(1, 1)

backend = ChipBackend()
backend.chip_topology_edges = [[0, 1], [1, 2]]
backend.basic_gates = ["RPhi", "CZ"]
backend.single_gate_fidelity = {0: 0.99, 1: 0.99, 2: 0.99}
backend.readout_fidelity = {0: 0.90, 1: 0.90, 2: 0.99}
backend.double_gate_fidelity = {(0, 1): 0.90, (1, 2): 0.99}

options = TranspilationOptions()
options.optimization_level = 2
options.layout_method = LayoutMethod.FIDELITY
options.routing_method = RoutingMethod.LIGHT_SABRE
options.approximation_degree = 0.99
options.execution.routing_trials = 8
options.execution.seed = 42

result = Transpiler().transpile(prog, backend, options)

print(type(result).__name__)
print(result.compile_time_ns > 0)
print(sorted(result.metrics.keys()))
print(len(result.program.operations()) > 0)

Example output shape:

text
TranspilationResult
True
['depth', 'fidelity_exact', 'fidelity_fallback', 'fidelity_unknown', 'log_expected_fidelity', 'two_qubit_gate_count']
True

result.metrics has the same shape as the dict returned by the standalone function transpilation_metrics(prog, backend), with these fields:

FieldTypeDescription
two_qubit_gate_countintNumber of two-qubit gates in the transpiled program.
depthintGate depth of the transpiled program.
log_expected_fidelityfloat or NoneLogarithm of the expected fidelity, accumulated as log(1 - error) over every gate plus one readout per measured qubit; None when any gate or readout fidelity is unknown.
fidelity_exactintNumber of gates whose fidelity data was matched exactly.
fidelity_fallbackintNumber of gates that fell back to an aggregate estimate.
fidelity_unknownintNumber of gates with no fidelity data at all.

2. LightSABRE routing ​

RoutingMethod.LIGHT_SABRE (the string "light_sabre") enables the new layered-lookahead router. It expands the interaction graph layer by layer while routing, which tends to reduce two-qubit gate insertion and swap depth — useful for backends with limited connectivity or deeper circuits.

Besides the strongly-typed entry point, it can also be enabled directly via the positional-argument overload:

python
from pyqpanda3.core import QProg, H, CZ, measure
from pyqpanda3.transpilation import ChipBackend, Transpiler

prog = QProg()
prog << H(0) << CZ(0, 1) << measure(0, 0) << measure(1, 1)

backend = ChipBackend()
backend.chip_topology_edges = [[0, 1], [1, 2]]
backend.basic_gates = ["RPhi", "CZ"]

transpiled = Transpiler().transpile(
    prog,
    backend,
    {},
    2,
    "fidelity",
    "light_sabre",
    "default",
    approximation_degree=0.99,
)

print(type(transpiled).__name__)
print(len(transpiled.operations()) > 0)

Output:

text
QProg
True

3. Heisenberg-picture expectation simulator ​

SparseHeisenbergSimulator computes the expectation value of a Pauli Hamiltonian using a sparse Heisenberg-picture algorithm. It is not a shot-based QVM and does not return a state vector; it is well suited to quickly estimating <H> when the gate set is supported and the initial state is a product state. When the number of operator terms grows, you can truncate with max_terms and truncation_norm.

python
from pyqpanda3.core import QProg, CZ, SparseHeisenbergSimulator

zero = (1 + 0j, 0 + 0j)  # (|0> amplitude, |1> amplitude) for each qubit

prog = QProg() << CZ(0, 1)  # |00> unchanged

sim = SparseHeisenbergSimulator(num_qubits=2)
sim.set_initial_product_state([zero, zero])
sim.add_observable("ZZ", 1.0)
sim.evolve_backward(prog)

print(round(sim.expectation(), 6))

Output:

text
1.0

The free function sparse_heisenberg_expectation(prog, hamiltonian, initial_product_state=...) is equivalent to the flow above and does it in one step:

python
from pyqpanda3.core import QProg, CZ, sparse_heisenberg_expectation
from pyqpanda3.hamiltonian import Hamiltonian

zero = (1 + 0j, 0 + 0j)
prog = QProg() << CZ(0, 1)
ham = Hamiltonian({"Z0 Z1": 1.0})

print(round(sparse_heisenberg_expectation(prog, ham, initial_product_state=[zero, zero]), 6))

Output:

text
1.0

4. Matrix-product-state (MPS) simulator ​

MPSQVM runs quantum programs on a matrix-product-state backend, keeping memory usage low for weakly-entangled circuits even at large qubit counts. After running, retrieve measurement results via result().

python
from pyqpanda3.core import H, MPSQVM, QProg, measure

prog = QProg() << H(0) << measure(0, 0) << measure(1, 1)

qvm = MPSQVM()
qvm.run(prog, 1000)
result = qvm.result()

print(type(result).__name__)
print(sum(result.get_counts().values()) == 1000)

Example output shape:

text
QResult
True

5. Initial state: CPUQVM.init_state ​

0.4.1 adds init_state() to CPUQVM, letting you set the simulator's initial quantum state before running. The simulator resets to this state before each run() (and each evolve()); calling without arguments restores the default |0...0> state.

python
import numpy as np
from pyqpanda3.core import CPUQVM, QProg, measure

qvm = CPUQVM()
qvm.init_state([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)])

prog = QProg() << measure([0, 1], [0, 1])
qvm.run(prog, 1000)
print(qvm.result().get_counts())

qvm.init_state()  # restore default |0...0>
qvm.run(prog, 1000)
print(qvm.result().get_counts())

Output (counts fluctuate slightly due to random sampling):

text
{'00': 524, '11': 476}
{'00': 1000}

Note: MPSQVM's C++ init_state is not exposed in the Python bindings (0.4.1); use CPUQVM.init_state on the Python side.

6. Cloud task cancellation ​

QCloudJob gains cancellation: you can now actively cancel a submitted cloud task.

python
QCloudJob.cancel(self) -> bool
QCloudJob.cancel_task(task_id: str) -> bool

cancel() cancels the current job; the static method cancel_task(task_id) cancels the task with the given ID. Both return True when the cloud service accepted the cancellation request and False when the service responded that the task cannot be cancelled.

Cancelling tasks requires cloud credentials and a real task, so it cannot be executed locally:

python
from pyqpanda3.qcloud import QCloudJob, QCloudService

# Requires cloud credentials and a real task; cannot run locally
service = QCloudService("your_api_key")
job = QCloudJob("your_job_id")

print(job.cancel())                          # cancel the current job
print(QCloudJob.cancel_task("your_task_id"))  # cancel by task ID (static)

API changes ​

Released under the MIT License.