Skip to main content

unitarylab

A Python quantum circuit simulator for building, executing, analyzing, and exporting quantum circuits.
面向量子电路构建、执行、分析与导出的 Python 量子模拟器。

Python 3.10, 3.11, and 3.12 NumPy, PyTorch, C++, and TensorNet backends Circuit interface UnitaryLab license

English · 中文


English

Introduction

unitarylab provides a high-level Circuit interface, statevector and matrix-product-state execution, circuit drawing and analysis, OpenQASM interoperability, transpilation, and a collection of quantum algorithms.

The top-level user API is:

from unitarylab import Circuit, Register, ClassicalRegister

The simulator uses little-endian qubit ordering when displaying basis-state labels. For example, basis-state strings are interpreted with qubit 0 at the right-hand end of the underlying statevector indexing convention.

Minimal example: statevector index 1 is labeled "01", so qubit 0 = 1 and qubit 1 = 0.

Installation

pip install unitarylab

Verify the installation:

import unitarylab

print(unitarylab.__version__)

The source uses Python 3.10+ syntax. NumPy is a core dependency. PyTorch powers the default execution backend; SciPy is used by TensorNet and algorithm features; Matplotlib is required for Matplotlib circuit drawing. The C++ backend requires the packaged native cppgates extension, and GPU execution requires a compatible PyTorch environment.

For a CPU-only PyTorch installation:

pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install unitarylab

Quick Start

Create and execute a Bell-state circuit:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute()

print(result.state)
print(result.probabilities)

The probability distribution is approximately:

{"00": 0.5, "11": 0.5}

Common gate families include single-qubit gates, rotation gates, controlled and multi-controlled gates, SWAP, and custom unitary matrices.

Measurement and Sampling

Create a classical register and map measured qubits to classical bits:

from unitarylab import Circuit, Register, ClassicalRegister

q = Register("q", 2)
c = ClassicalRegister("c", 2)
circuit = Circuit(q, c)

circuit.h(q[0])
circuit.cx(q[0], q[1])
circuit.measure(q[0:2], c[0:2])

result = circuit.execute(shots=1000, seed=42)

print(result.counts)
print(result.classical_results_map)
print(result.classical_registers)

shots must be a positive integer and defaults to 1. seed defaults to 42; pass None for nondeterministic circuit measurements.

  • counts aggregates measured classical bit strings over all shots.
  • classical_results_map contains the final shot as {classical_bit: value}.
  • classical_registers groups the final-shot values by register name.
  • Unmeasured classical bits are represented by # in count keys and by -1 in register snapshots.

You can also sample an already executed quantum state without adding circuit measurement instructions:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute()
samples = result.sample(shots=10, qubits=[0, 1], seed=7)
print(samples)

sample() does not collapse the stored result state. In contrast, result.measure(...) performs a projective measurement and updates the state.

Execution Result

Circuit.execute() returns an ExecutionResult or, for TensorNet execution, a compatible TensorNetExecutionResult.

Member Purpose
state Dense statevector. TensorNet materializes it on access.
backend_state Native backend representation, such as an MPS.
probabilities Full basis-state probability mapping.
probability(bitstring, qubits=None) Probability of one outcome.
marginal_probabilities(qubits=None) Distribution on selected qubits.
sample(shots, qubits=None, seed=None) Sample without collapsing the state.
expectation(observable, qubits=None) Normalized observable expectation value.
measure(qubits, seed=None) Measure and collapse selected qubits.
counts Classical counts collected by execute(shots=...).
classical_registers Final classical values grouped by register.

Example probability queries:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
result = circuit.execute(backend="numpy")

print(result.probability("00"))
print(result.probability("0", qubits=[0]))
print(result.marginal_probabilities(qubits=[0]))

Computing every dense probability or requesting .state can be expensive for large systems. Prefer targeted probability, marginal, sampling, or expectation queries where possible.

Observables and Expectation Values

Use result.expectation(...) with these supported user-facing forms:

  • A full-length Pauli string, such as "ZZ" for a two-qubit result.
  • A Pauli string plus explicit qubits, such as "XX", qubits=(0, 2).
  • A single-qubit 2×2 Hermitian matrix plus one qubit.
  • A list of (coefficient, observable) or (coefficient, observable, qubits) tuples.
  • A list of mappings with coeff, pauli, and optional qubits keys.
from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
result = circuit.execute(backend="numpy")

print(result.expectation("ZZ"))
print(result.expectation("X", qubits=0))

hamiltonian = [
    (0.5, "ZZ", (0, 1)),
    {"coeff": -0.25, "pauli": "X", "qubits": (0,)},
]
print(result.expectation(hamiltonian))

Pauli labels are limited to I, X, Y, and Z. Matrix observables must be finite, Hermitian, 2×2 matrices and currently apply to exactly one qubit.

Execution Backends

Circuit.execute() accepts initial_state, backend, device, dtype, shots, seed, and backend_options. Its defaults are backend="torch", device="cpu", dtype=np.complex128, shots=1, and seed=42.

Backend Device Native state Main notes
torch cpu, gpu PyTorch tensor GPU requires a compatible PyTorch environment.
numpy cpu NumPy array Dense statevector execution.
cpp cpu NumPy-compatible result Requires the native cppgates extension.
tensornet cpu TensorNetState MPS Supports max_bond, cutoff, and routing options.
from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

torch_result = circuit.execute(backend="torch", device="cpu")
numpy_result = circuit.execute(backend="numpy", device="cpu")
# Requires the compiled C++ extension:
# cpp_result = circuit.execute(backend="cpp", device="cpu")
tensornet_result = circuit.execute(backend="tensornet", device="cpu")

backend_options is accepted only by the TensorNet backend. Torch GPU support depends on the installed PyTorch backend. On Apple MPS, complex128 is rejected and the implementation limits execution to at most 16 qubits.

Density-Matrix Simulation

Use Circuit.execute_density() when the full mixed-state density matrix is required. This is a separate CPU backend backed by the native cppdensity extension; it is not selected through the backend argument of Circuit.execute().

import numpy as np

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute_density(
    initial_state=None,
    device="cpu",
    dtype=np.complex128,
)

print(result.state)          # shape: (2**n, 2**n)
print(result.trace)          # approximately 1
print(result.purity)         # 1 for this pure Bell state
print(result.probabilities)  # {"00": 0.5, "11": 0.5}
print(result.expectation("ZZ"))

initial_state may be None, a statevector, a density matrix, or a DensityMatrixState. complex64 and complex128 are supported. The returned DensityMatrixResult provides state, backend_state, trace, purity, probabilities, probability(), marginal_probabilities(), and expectation().

The density-matrix backend currently supports CPU unitary evolution and does not support circuit measurement, state collapse, shots/counts, or GPU execution.

Noise Models

The density-matrix backend supports five built-in single-qubit channels: BitFlip, PhaseFlip, DepolarizingNoise, AmplitudeDamping, and PhaseDamping. Bind channels to circuit operations with NoiseOperator, add the rules to a NoiseModel, and pass the model to execute_density().

from unitarylab import Circuit
from unitarylab.backend import (
    AmplitudeDamping,
    BitFlip,
    NoiseModel,
    NoiseOperator,
)

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.rx(0.3, 1)

noise_model = NoiseModel([
    # Apply bit flip after matching X/RX gates on their target qubits.
    NoiseOperator(BitFlip(0.02), gate_ids=("x", "rx")),
    # Apply amplitude damping only when qubit 1 is a gate target.
    NoiseOperator(AmplitudeDamping(0.05), qubits=(1,)),
])

result = circuit.execute_density(noise_model=noise_model)
print(result.state)
print(result.trace)
print(result.purity)

NoiseOperator(channel, gate_ids=None, qubits=None) has these V1 semantics:

  • gate_ids=None matches every physical primitive gate; otherwise only the listed gate identifiers match. Configured identifiers are normalized to lowercase.
  • qubits=None applies the channel independently to every target of the matched gate. A qubit tuple filters gate.target only; controls do not match.
  • Noise is inserted after the matched gate. Multiple rules execute in NoiseModel insertion order.
  • A multi-target gate receives independent single-qubit channels in target order. This is not correlated multi-qubit noise.
  • Identity and global-phase operations do not trigger noise.

Depolarizing noise uses E(rho) = (1-p) rho + p I/2; p=1 produces the maximally mixed single-qubit state. Phase damping multiplies off-diagonal coherence by 1-gamma. The underlying Kraus representation is internal. User-defined Kraus channels, before-gate noise, correlated multi-qubit noise, readout error, and GPU noise are not currently exposed.

Circuit Operations

Frequently used circuit operations include:

Operation Behavior
initialize(state, qubits) Append state-preparation gates.
append(other, target, ...) Mutate the circuit by appending a circuit block.
prepend(other, target, ...) Mutate the circuit by prepending a circuit block.
inverse() Return a new inverse circuit.
dagger() Return a new Hermitian-adjoint circuit.
repeat(times) Return a new repeated circuit.
control(num_control_qubits, ...) Return a new controlled circuit.
decompose(n=1, name=None) Return a new circuit with block gates decomposed.
transpile(gates_to_unroll=None, basis="default") Return a transpiled circuit.

append() and prepend() modify the receiving circuit. Transformations such as inverse(), dagger(), repeat(), control(), and decompose() return new circuits and leave the source circuit unchanged.

from unitarylab import Circuit

block = Circuit(2)
block.h(0)
block.cx(0, 1)

circuit = Circuit(2)
circuit.append(block, target=[0, 1])

inverse = circuit.inverse()
repeated = circuit.repeat(2)
controlled = circuit.control(1)
decomposed = circuit.decompose()
transpiled = circuit.transpile()

initialize() accepts a normalized statevector whose dimension matches the selected qubits.

TensorNet

The TensorNet backend stores states as an open-boundary matrix product state (MPS). Use TensorNetState to create, copy, or convert user-level MPS states.

import numpy as np

from unitarylab import Circuit
from unitarylab.backend.tensornet import TensorNetState

dense_state = np.array([1, 0, 0, 0], dtype=np.complex128)
mps_state = TensorNetState.from_statevector(dense_state, max_bond=64)

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute(
    initial_state=mps_state,
    backend="tensornet",
    backend_options={
        "max_bond": 64,
        "cutoff": 1e-10,
        "routing": "auto",
    },
)

print(result.backend_state)
print(result.backend_state.to_statevector())

Each MPS tensor uses (left bond, physical dimension 2, right bond) ordering; the first tensor represents qubit 0. routing may be "auto", "swap", or "mpo".

Explicit max_bond and cutoff values in backend_options override values stored in the input TensorNetState. The input is copied before execution. Statevector backends do not implicitly contract MPS inputs; convert explicitly with to_statevector().

Drawing and Analysis

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

circuit.draw()
print(circuit.draw(output="text"))
circuit.draw(output="latex", filename="bell.tex")
circuit.draw(filename="bell.png", title="Bell State")

info = circuit.analyze(show=False)
print(info.depth())
print(info.count_ops())
info.show()

matrix = circuit.get_matrix(backend="numpy")

Drawing outputs support Matplotlib (mpl/matplotlib), text (text/txt), and LaTeX (latex/tex/quantikz).

Transpiler

Use Circuit.transpile() to unroll gates into a supported basis and apply the configured transpilation pipeline:

from unitarylab import Circuit

circuit = Circuit(3)
circuit.h(0)
circuit.mcx([0, 1], 2)

transpiled = circuit.transpile(basis="default")
print(transpiled.draw(output="text"))

For direct access to the same implementation, use QCompiler.transpile(circuit, ...).

Compilation and Hardware Submission

The Circuit methods cover the common optimization, transpilation, mapping, and hardware-submission flow:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

optimized = circuit.optimize(optimization_level=1)
transpiled = circuit.transpile(basis="default")

# Optimize, transpile, and map without submitting a hardware job.
compiled = circuit.compile(provider="guodun")

# Compile and submit a real hardware job.
result = circuit.submit(
    provider="guodun",
    token=TOKEN,
    shots=1024,
)

optimize() and transpile() return Circuit objects. compile() returns a CompilationResult containing the mapped circuit and executable QCIS; it does not submit a job and consumes no task credits. Real-machine providers still require login credentials, because mapping logs in to download the live chip calibration: provide them through the GUODUN_LOGIN_KEY environment variable or hardware.login_key in a QCompiler config (Circuit.compile has no token argument). submit() returns a provider-neutral HardwareResult; with mitigation enabled through a QCompiler config (execution.mitigation.method), result.counts and result.probabilities return the mitigated observations. Circuit.submit runs without mitigation by default, in which case counts holds the raw counts and probabilities is empty.

Use QCompiler for OpenQASM input or detailed optimization, synthesis, mapping, execution, or mitigation configuration. Its pipeline methods accept OpenQASM or Circuit; direct transpile() accepts Circuit:

from unitarylab import QCompiler

compiler = QCompiler(config)
optimized = compiler.optimize(circuit_or_qasm)
transpiled = compiler.transpile(circuit, basis="default")
compiled = compiler.compile(circuit_or_qasm, provider="guodun")
result = compiler.submit(
    circuit_or_qasm,
    provider="guodun",
    token=TOKEN,
    shots=1024,
)

Advanced artifact-level control remains available through QCompiler.run():

run = compiler.run(
    qcis,
    start_from="mapped_qcis",
    stop_at="hardware_result",
)

The underlying artifact flow is raw_qasm -> optimized_qasm -> mapped_qcis -> hardware_result. Guodun/Tianyan and the other supported providers are routed internally by QCompiler.submit(); callers do not choose between the pipeline and hardware adapters themselves. The non-Guodun adapters require their vendor SDKs to be installed separately: LQCloud needs the lqcloud SDK and QPanda3 needs pyqpanda3 (Quafu needs no separate SDK); a missing SDK is reported at submission time.

Low-level Mapping API

When layout details or a custom submission flow are needed, the mapping module can be called directly (the same interface as the repository's qcompiler.mapping, only the import path differs):

from unitarylab.qcompiler.mapping import map_qasm, submit, plot_probabilities

qcis, layout = map_qasm(qasm_text, token=TOKEN)   # QASM -> mapped QCIS + final layout
result = submit(qcis, token=TOKEN, shots=2000, layout=layout)  # real submission, decoded to logical order
plot_probabilities(result, title="Bell State")

Note: this submit returns a plain dict (probability / raw_probability / lab_id / exp_id / query_id), unlike Circuit.submit's HardwareResult; map_qasm hard-codes the target machine gd_qc1, requires a token, and has no offline cache.

Serialization and OpenQASM

The circuit API supports OpenQASM 2.0 and 3.0, file import/export, and Python source generation:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.rx(0.5, 0)
circuit.cx(0, 1)

qasm3 = circuit.to_qasm()
qasm2 = circuit.to_qasm2()
restored = Circuit.from_qasm(qasm3)

circuit.to_qasm_file("bell.qasm")
from_file = Circuit.from_qasm_file("bell.qasm")

python_source = circuit.to_python(variable_name="bell")
circuit.to_python_file("bell.py", variable_name="bell")

from_qasm() auto-detects OpenQASM 2.0 or 3.0 from the header. to_qasm() and to_qasm_file() emit OpenQASM 3.0; to_qasm2() emits OpenQASM 2.0. Some gates must be decomposed or transpiled before QASM export. Python source generation currently supports native rx, ry, rz, p, and cx gates; unsupported or nested gate structures raise RuntimeError.

Algorithm Library

The following user-facing areas are available below unitarylab.library. Chemistry APIs are intentionally outside the scope of this quick guide.

Area Main entry points
Quantum Fourier transform QFT, IQFT
Quantum phase estimation QPE
Linear combination of unitaries LCU
Block encoding block_encode, with FABLE and Nagy subpackages
Hamiltonian simulation hamiltonian_simulation, QSP/Trotter/Taylor/QDrift methods
Signal and singular-value transformation QSP, QSVT
Linear systems solve, plus HHL/QSVT/Schrödingerization/AQC/VQLS/CKS solvers
Equations Differential operators, parsing, and Schrödingerization APIs
Fermi-Hubbard Hamiltonian construction, ground-state, and magnetic-moment utilities
Pauli operators Decomposition, evolution, products, and matrix conversion

Example:

from unitarylab import Circuit
from unitarylab.library import QFT

qft = QFT(n=4)
circuit = Circuit(4)
circuit.append(qft, target=[0, 1, 2, 3])
print(circuit.draw(output="text"))

FAQ and Troubleshooting

Why does the C++ backend fail to import?
The native cppgates extension must be included and compatible with the active Python environment. Use the NumPy or Torch CPU backend when it is unavailable.

Why does Torch GPU execution reject complex128?
The Apple MPS path explicitly rejects complex128. Use complex64 or CPU. Other GPU behavior depends on the installed PyTorch backend.

Why is TensorNet .state expensive?
Accessing .state contracts the MPS into a dense vector. Prefer probability, sampling, expectation, or backend_state operations for larger systems.

Why does QASM export raise NotImplementedError?
The circuit contains an operation not directly representable in the target QASM format. Try to_qasm(decompose=True, transpile=True) when the operation can be lowered to supported gates.

Are basis labels little-endian?
Yes. Keep the simulator's little-endian display convention in mind when interpreting multi-qubit states, probabilities, measurements, and samples.

Further Documentation


中文

简介

unitarylab 以高层 Circuit 接口为核心,支持状态向量和矩阵乘积态模拟、 电路绘图与分析、OpenQASM 互操作、转译及常用量子算法。

顶层用户入口为:

from unitarylab import Circuit, Register, ClassicalRegister

模拟器显示计算基标签时采用 little-endian 量子比特顺序。最小示例:状态向量 索引 1 的概率标签为 "01",即 qubit 0 = 1qubit 1 = 0

安装

pip install unitarylab

验证安装:

import unitarylab

print(unitarylab.__version__)

源码使用 Python 3.10+ 语法。NumPy 是核心依赖;PyTorch 提供默认执行后端; SciPy 用于 TensorNet 和部分算法能力;Matplotlib 用于 Matplotlib 电路绘图。 C++ 后端要求安装包包含可用的 cppgates 原生扩展,GPU 执行要求兼容的 PyTorch 环境。

快速开始

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute()
print(result.state)
print(result.probabilities)

概率分布约为 {"00": 0.5, "11": 0.5}

测量与采样

from unitarylab import Circuit, Register, ClassicalRegister

q = Register("q", 2)
c = ClassicalRegister("c", 2)
circuit = Circuit(q, c)

circuit.h(q[0])
circuit.cx(q[0], q[1])
circuit.measure(q[0:2], c[0:2])

result = circuit.execute(shots=1000, seed=42)
print(result.counts)
print(result.classical_results_map)
print(result.classical_registers)

shots 必须是正整数,默认值为 1seed 默认值为 42,传入 None 可使用非确定性电路测量。

  • counts 汇总所有 shots 的经典比特字符串。
  • classical_results_map 保存最后一次 shot 的 {经典位: 测量值}
  • classical_registers 按寄存器名称组织最后一次测量值。
  • 未测量经典位在 counts 键中表示为 #,在寄存器快照中表示为 -1

执行后也可以直接采样量子态:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
result = circuit.execute()

print(result.sample(shots=10, qubits=[0, 1], seed=7))

sample() 不会坍缩结果中的状态;result.measure(...) 会执行投影测量并更新状态。

执行结果

成员 用途
state 稠密状态向量;TensorNet 在访问时进行收缩。
backend_state 后端原生状态,例如 MPS。
probabilities 完整计算基概率映射。
probability(bitstring, qubits=None) 查询单个结果概率。
marginal_probabilities(qubits=None) 查询指定量子比特的边缘分布。
sample(shots, qubits=None, seed=None) 不坍缩状态的采样。
expectation(observable, qubits=None) 计算归一化期望值。
measure(qubits, seed=None) 测量并坍缩指定量子比特。
counts execute(shots=...) 收集的经典计数。
classical_registers 按经典寄存器分组的最终值。
from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
result = circuit.execute(backend="numpy")

print(result.probability("00"))
print(result.probability("0", qubits=[0]))
print(result.marginal_probabilities(qubits=[0]))

Observable 与期望值

result.expectation(...) 支持:完整 Pauli 字符串;Pauli 字符串加指定量子比特; 单量子比特 2×2 Hermitian 矩阵;tuple 项或 mapping 项组成的列表。

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
result = circuit.execute(backend="numpy")

print(result.expectation("ZZ"))
print(result.expectation("X", qubits=0))

hamiltonian = [
    (0.5, "ZZ", (0, 1)),
    {"coeff": -0.25, "pauli": "X", "qubits": (0,)},
]
print(result.expectation(hamiltonian))

Pauli 标签仅支持 I/X/Y/Z。矩阵 Observable 必须是有限、Hermitian 的 2×2 矩阵,并且当前只能作用于一个量子比特。

执行后端

execute() 默认使用 backend="torch"device="cpu"dtype=np.complex128shots=1seed=42

后端 device 原生状态 主要限制
torch cpugpu PyTorch tensor GPU 依赖 PyTorch 环境。
numpy cpu NumPy array 稠密状态向量。
cpp cpu NumPy 兼容结果 需要 cppgates 原生扩展。
tensornet cpu TensorNetState MPS 支持截断和 routing 配置。
from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

torch_result = circuit.execute(backend="torch", device="cpu")
numpy_result = circuit.execute(backend="numpy", device="cpu")
# 需要已编译的 C++ 扩展:
# cpp_result = circuit.execute(backend="cpp", device="cpu")
tensornet_result = circuit.execute(backend="tensornet", device="cpu")

backend_options 仅适用于 TensorNet。Apple MPS 路径不支持 complex128, 且源码将执行规模限制为最多 16 个量子比特。

密度矩阵模拟

需要完整混态密度矩阵时,可使用 Circuit.execute_density()。它是由原生 cppdensity 扩展支持的独立 CPU 后端,不通过 Circuit.execute()backend 参数选择。

import numpy as np

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute_density(
    initial_state=None,
    device="cpu",
    dtype=np.complex128,
)

print(result.state)          # shape 为 (2**n, 2**n)
print(result.trace)          # 约等于 1
print(result.purity)         # 当前 Bell 纯态为 1
print(result.probabilities)  # {"00": 0.5, "11": 0.5}
print(result.expectation("ZZ"))

initial_state 可以是 None、状态向量、密度矩阵或 DensityMatrixState, 支持 complex64complex128。返回的 DensityMatrixResult 提供 statebackend_statetracepurityprobabilitiesprobability()marginal_probabilities()expectation()

当前密度矩阵后端支持 CPU unitary 演化,暂不支持电路测量、状态坍缩、 shots/counts 和 GPU 执行。

噪声模型

当前提供五种内置单量子比特 channel:BitFlipPhaseFlipDepolarizingNoiseAmplitudeDampingPhaseDamping。使用 NoiseOperator 将 channel 与门匹配规则绑定,加入 NoiseModel 后传给 execute_density()

from unitarylab import Circuit
from unitarylab.backend import (
    AmplitudeDamping,
    BitFlip,
    NoiseModel,
    NoiseOperator,
)

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.rx(0.3, 1)

noise_model = NoiseModel([
    # 在匹配的 X/RX 门后,对门的 target 添加 bit flip。
    NoiseOperator(BitFlip(0.02), gate_ids=("x", "rx")),
    # 只有当 qubit 1 是门的 target 时才添加 amplitude damping。
    NoiseOperator(AmplitudeDamping(0.05), qubits=(1,)),
])

result = circuit.execute_density(noise_model=noise_model)
print(result.state)
print(result.trace)
print(result.purity)

NoiseOperator(channel, gate_ids=None, qubits=None) 的 V1 语义如下:

  • gate_ids=None 匹配所有实体 primitive gate;否则只匹配列出的 gate id, 配置值会统一转换为小写。
  • qubits=None 对匹配门的每个 target 独立应用 channel;指定 qubit tuple 时只过滤 gate.target,不匹配 control。
  • noise 固定插入到匹配门之后;多条规则严格按照加入 NoiseModel 的顺序执行。
  • 多 target 门按照 target 顺序应用独立单比特 channel,不表示相关多比特噪声。
  • identity 和 global phase 不触发 noise。

Depolarizing noise 使用 E(rho) = (1-p) rho + p I/2,因此 p=1 时得到 单比特完全混合态。Phase damping 将非对角 coherence 乘以 1-gamma。 底层 Kraus 表示目前是内部接口;暂不公开用户自定义 Kraus channel、门前噪声、 相关多比特噪声、readout error 和 GPU noise。

常用电路操作

操作 行为
initialize(state, qubits) 追加状态制备门。
append(other, target, ...) 原地追加电路块。
prepend(other, target, ...) 原地前置电路块。
inverse() / dagger() 返回新的逆电路或共轭转置电路。
repeat(times) 返回新的重复电路。
control(n, ...) 返回新的受控电路。
decompose() 返回新的分解电路。
transpile() 返回转译后的电路。
from unitarylab import Circuit

block = Circuit(2)
block.h(0)
block.cx(0, 1)

circuit = Circuit(2)
circuit.append(block, target=[0, 1])

inverse = circuit.inverse()
repeated = circuit.repeat(2)
controlled = circuit.control(1)
decomposed = circuit.decompose()
transpiled = circuit.transpile()

append()prepend() 修改接收电路;其余表中列出的变换返回新电路。 initialize() 要求输入归一化状态向量,维度与目标量子比特数量匹配。

TensorNet

TensorNet 后端以开放边界矩阵乘积态(MPS)保存状态。TensorNetState 是用户级 MPS 状态入口。

import numpy as np

from unitarylab import Circuit
from unitarylab.backend.tensornet import TensorNetState

dense_state = np.array([1, 0, 0, 0], dtype=np.complex128)
mps_state = TensorNetState.from_statevector(dense_state, max_bond=64)

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

result = circuit.execute(
    initial_state=mps_state,
    backend="tensornet",
    backend_options={
        "max_bond": 64,
        "cutoff": 1e-10,
        "routing": "auto",
    },
)

print(result.backend_state)
print(result.backend_state.to_statevector())

MPS 张量维度顺序为 (左键合, 物理维度 2, 右键合),第一个张量对应 qubit 0。 routing 支持 auto/swap/mpo。显式 max_bondcutoff 会覆盖输入状态中 的配置。状态向量后端不会隐式收缩 MPS,应先调用 to_statevector()

绘图与分析

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

circuit.draw()
print(circuit.draw(output="text"))
circuit.draw(output="latex", filename="bell.tex")
circuit.draw(filename="bell.png", title="Bell State")

info = circuit.analyze(show=False)
print(info.depth())
print(info.count_ops())
info.show()

matrix = circuit.get_matrix(backend="numpy")

绘图支持 Matplotlib、文本和 LaTeX 输出。

Transpiler

from unitarylab import Circuit

circuit = Circuit(3)
circuit.h(0)
circuit.mcx([0, 1], 2)

transpiled = circuit.transpile(basis="default")
print(transpiled.draw(output="text"))

如需直接使用同一底层实现,可调用 QCompiler.transpile(circuit, ...)

编译与硬件提交

Circuit 接口覆盖常用的优化、门展开、mapping 和硬件提交流程:

from unitarylab import Circuit

circuit = Circuit(2)
circuit.h(0)
circuit.cx(0, 1)

optimized = circuit.optimize(optimization_level=1)
transpiled = circuit.transpile(basis="default")

# 优化、门展开和 mapping,但不提交硬件任务
compiled = circuit.compile(provider="guodun")

# 编译并提交真实硬件任务
result = circuit.submit(
    provider="guodun",
    token=TOKEN,
    shots=1024,
)

optimize()transpile() 返回 Circuitcompile() 返回包含映射后 线路和可执行 QCIS 的 CompilationResult;它不提交任务、不消耗任务积分, 但真机 provider 仍需登录凭据,因为映射阶段要登录平台并下载实时芯片校准: 通过环境变量 GUODUN_LOGIN_KEYQCompiler 配置中的 hardware.login_key 提供(Circuit.compile 没有 token 参数)。submit() 统一返回 HardwareResult;开启缓解时(QCompiler 配置 execution.mitigation.methodresult.countsresult.probabilities 返回缓解后结果。Circuit.submit 默认无缓解,此时 counts 为原始计数而 probabilities 为空。

如果输入是 OpenQASM,或需要详细配置优化、综合、mapping、execution 和 mitigation,可直接使用 QCompiler。其 pipeline 方法支持 OpenQASM 或 Circuit,直接调用 transpile() 时输入为 Circuit

from unitarylab import QCompiler

compiler = QCompiler(config)
optimized = compiler.optimize(circuit_or_qasm)
transpiled = compiler.transpile(circuit, basis="default")
compiled = compiler.compile(circuit_or_qasm, provider="guodun")
result = compiler.submit(
    circuit_or_qasm,
    provider="guodun",
    token=TOKEN,
    shots=1024,
)

artifact 级高级控制仍由 QCompiler.run() 提供:

run = compiler.run(
    qcis,
    start_from="mapped_qcis",
    stop_at="hardware_result",
)

底层产物流程为 raw_qasm -> optimized_qasm -> mapped_qcis -> hardware_result。Guodun/Tianyan 与其他支持的厂商均由 QCompiler.submit() 在内部分流,用户无需自行选择 pipeline 或 hardware adapter。 非 Guodun 的适配器需要单独安装厂商 SDK:LQCloud 需要 lqcloud SDK, QPanda3 需要 pyqpanda3(Quafu 无需额外 SDK);SDK 缺失会在提交时报错。

底层 Mapping API

需要布局细节或自定义提交流程时,可单独调用 mapping 模块(与仓库 qcompiler.mapping 同名接口,仅 import 路径不同):

from unitarylab.qcompiler.mapping import map_qasm, submit, plot_probabilities

qcis, layout = map_qasm(qasm_text, token=TOKEN)   # QASM -> 映射后 QCIS + 最终布局
result = submit(qcis, token=TOKEN, shots=2000, layout=layout)  # 真机提交,按布局换算逻辑位序
plot_probabilities(result, title="Bell State")

注意:此处的 submit 返回 dict(probability/raw_probability/lab_id/ exp_id/query_id),与 Circuit.submitHardwareResult 不同; map_qasm 硬编码目标机器 gd_qc1、要求 token 且无离线缓存。

序列化与 OpenQASM

from unitarylab import Circuit

circuit = Circuit(2)
circuit.rx(0.5, 0)
circuit.cx(0, 1)

qasm3 = circuit.to_qasm()
qasm2 = circuit.to_qasm2()
restored = Circuit.from_qasm(qasm3)

circuit.to_qasm_file("bell.qasm")
from_file = Circuit.from_qasm_file("bell.qasm")

python_source = circuit.to_python(variable_name="bell")
circuit.to_python_file("bell.py", variable_name="bell")

from_qasm() 根据头部自动识别 OpenQASM 2.0 或 3.0;to_qasm()to_qasm_file() 输出 3.0,to_qasm2() 输出 2.0。部分量子门必须先分解或 转译才能导出。Python 源码生成当前支持原生 rx/ry/rz/p/cx 门;不支持或 嵌套的门结构会抛出 RuntimeError

算法库概览

以下用户级能力位于 unitarylab.library。本快速指南不介绍 Chemistry。

方向 主要入口
量子傅里叶变换 QFTIQFT
量子相位估计 QPE
线性组合酉算子 LCU
块编码 block_encode,以及 FABLE、Nagy 子包
哈密顿量模拟 hamiltonian_simulation 及 QSP/Trotter/Taylor/QDrift 方法
信号与奇异值变换 QSPQSVT
线性方程组 solve 及 HHL/QSVT/Schrödinger化/AQC/VQLS/CKS 求解器
方程 微分算子、方程解析和 Schrödingerization
Fermi-Hubbard Hamiltonian 构建、基态与磁矩工具
Pauli Operator 分解、演化、乘积和矩阵转换
from unitarylab import Circuit
from unitarylab.library import QFT

qft = QFT(n=4)
circuit = Circuit(4)
circuit.append(qft, target=[0, 1, 2, 3])
print(circuit.draw(output="text"))

FAQ 与故障排查

C++ 后端导入失败?
确认当前 Python 环境包含兼容的 cppgates 原生扩展;不可用时使用 NumPy 或 Torch CPU 后端。

TensorNet 的 .state 为什么开销较大?
访问 .state 会把 MPS 收缩成稠密向量。较大系统应优先使用概率、采样、 期望值查询或直接访问 backend_state

QASM 导出为什么抛出 NotImplementedError
电路包含目标 QASM 无法直接表示的操作。在能够降级为受支持门的情况下,尝试 to_qasm(decompose=True, transpile=True)

计算基标签是否为 little-endian?
是。解释多量子比特状态、概率、测量和采样时应考虑这一显示约定。

更多文档


License

License: LicenseRef-UnitaryLab-LICENSE. The Chinese license text is authoritative; the English version is provided for reference only.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

unitarylab-1.3.1-cp312-cp312-win_amd64.whl (10.1 MB view details)

Uploaded CPython 3.12Windows x86-64

unitarylab-1.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (79.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

unitarylab-1.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (76.7 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

unitarylab-1.3.1-cp312-cp312-macosx_11_0_arm64.whl (10.9 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

unitarylab-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl (12.0 MB view details)

Uploaded CPython 3.12macOS 10.13+ x86-64

unitarylab-1.3.1-cp311-cp311-win_amd64.whl (10.4 MB view details)

Uploaded CPython 3.11Windows x86-64

unitarylab-1.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (78.4 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

unitarylab-1.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (76.7 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

unitarylab-1.3.1-cp311-cp311-macosx_11_0_arm64.whl (10.9 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

unitarylab-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl (12.0 MB view details)

Uploaded CPython 3.11macOS 10.9+ x86-64

unitarylab-1.3.1-cp310-cp310-win_amd64.whl (10.5 MB view details)

Uploaded CPython 3.10Windows x86-64

unitarylab-1.3.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (75.3 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

unitarylab-1.3.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (73.6 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

unitarylab-1.3.1-cp310-cp310-macosx_11_0_arm64.whl (11.1 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

unitarylab-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl (12.2 MB view details)

Uploaded CPython 3.10macOS 10.9+ x86-64

File details

Details for the file unitarylab-1.3.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: unitarylab-1.3.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 10.1 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for unitarylab-1.3.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 f159dfdcb63bc44ef7de04cf0a8f9cff3fb722835e4b06bd4f3b09b7b1a8e1c2
MD5 997d42c5174ccdcb4ee4e2c525abd5eb
BLAKE2b-256 3aa37e900d81066dae07f6b4e7bdde80360ae605350bdf96eb9f5cd9e4ebb70b

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 859da816104f2da0e648f5eb0095eec820e9dda5b35ce837e4a5c71e497b7dd4
MD5 fec2d69c810107424fa4c44aa4d686aa
BLAKE2b-256 34a1cc692313dd4cc6bc6a13ee00d5dab2dae29694abb9dd4c3031f578ce103a

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 60482abdd63ac1a790e856e35c1b05c2faf589c9657acd05f9d790172ab8754b
MD5 4ce3d3dff7970ff2e5958b36a654c4ab
BLAKE2b-256 b00d74f0573c50f019c89f39ac3bc0def34264024f2b07ffcc8f5d06325c6d61

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0e6473cf2f16705cfb647ce47249d28f94214ec27041ed7f0212649311d1cd06
MD5 3d1bd204dd9eecb6d61807514684a82c
BLAKE2b-256 eae9fb3e4212ad2ffc20a40235d2b2bc10d35c3583bab7baebae2d75d5287908

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 8de667e1b956e9a084f220100d7f98c254fd284e49feee56612ae4d56efbf60b
MD5 6070f4aad433091aab56b058f6d90fe7
BLAKE2b-256 31f086e5e2c6c3680c1280285b25b799a0181a3615da5ce2e4d26df5aee5ff44

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: unitarylab-1.3.1-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 10.4 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for unitarylab-1.3.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 604ceaa164e89cd19fb6f536bb5527b4c242f0696815cf60496586dc40944d1f
MD5 855c056f7553683202f3b71ba3959fcb
BLAKE2b-256 4e4d450f94063b9d80848acff0416aaae66311af39e73889014b4a0cdc32b03e

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 23c46ff117f9264e01255d1fbec75e61a46f3e4e0ce7a8c2a869f2c0bcce2822
MD5 074a5069fe64ec29b3283a71fc1ca32b
BLAKE2b-256 bf5dc00c23686d93f49bf329c46ad2e40e8d7164500685201ec92ba485093aef

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 8e00f9c70956e0dd9af1692c89ef293de98977e88448e1930cde4c5bbc96a659
MD5 b399205c261582305cd30e51a235d3c2
BLAKE2b-256 b88d831e6079eb0aa46217a130111e8dccfd7749d9de75eb459b7b802c8affb1

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 95ccca45c9a4831a29660fdf410371e4713eddaa0d6099881a4f4a3c1719054a
MD5 56f138a990d8556c64b221c76d69e2b5
BLAKE2b-256 ea2442f3273b7c7e1d13b17a760687015003f4d30e85447f5924993885c5d0bb

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 d84a67b3f7cdda142c1adabd1a4036fa4bd2bdf9618dc36a3bf59c94b3615220
MD5 6268080f07ccd1d101ffd28e3e53f45c
BLAKE2b-256 a4bab24b0395f418d51c924d84c00f947d0adae6f7bd0e2721b58483e1debe31

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: unitarylab-1.3.1-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 10.5 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for unitarylab-1.3.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 b3dc334c043193ef446a7f1895997f4e4c76194a6e8865dd84b7e8463fdf7f2e
MD5 33d8a273a4ee90b82d36d6d09373746a
BLAKE2b-256 8ebe5b67390be2455c5e6172d90729bb4818f91f2f6dc95df3bd79a9922fd089

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 757eb4d95a652fbb9cec2d3078432f48abf4f998308097d83996d6c87e8ad544
MD5 29788acdee8e0ae3edae7440edd56499
BLAKE2b-256 c24ac94149febcd0d90a3d81ec8883a7aa35c2ed1f53816eeace371d4da11689

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 1f9c389c5c743000a91978f38bf63dff649035b0305e448026e1747b17e16083
MD5 ef3ce72028750d0f6a63544bfa09c732
BLAKE2b-256 351c7028bcaab4eda634e41b484ff43e531567647346242813b4509035952798

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 24d8f16f8f9c9bb530e2449bb48902c1874a1833b18fecd1914ad2774ce5c5b4
MD5 e8937e02f5022d227483ae6f331d455c
BLAKE2b-256 9ecbeb0bdf38f94cb651e557236588ceb47cfa229a27c84e083a413f1b103bca

See more details on using hashes here.

File details

Details for the file unitarylab-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for unitarylab-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 9d3fb9d25cd510c33fa85b55ebf46b52492f63c9772db06940f4ac024c130455
MD5 7de8ad8784351ac212ffe92ecce7e0dd
BLAKE2b-256 1d28f75aea34b6f5176cebc0b112b619729742430cf47c15fb2a5699bd814f0c

See more details on using hashes here.

Release history Release notifications | RSS feed

1.4.2

15 files

1.4.1

15 files

1.4.0

15 files

This release

1.3.1 This release

15 files

1.2.1

15 files

1.2.0

15 files

1.1.6

15 files

1.1.5

15 files

1.1.4

15 files

1.1.3

15 files

1.1.2

15 files

1.1.0

12 files

1.0.1

12 files

1.0.0

12 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page