Metadata-Version: 2.4
Name: unitarylab
Version: 1.4.0
Summary: A Python package for quantum simulator from UnitaryLab.
Author: UnitaryLab
License-Expression: LicenseRef-UnitaryLab-LICENSE
Requires-Python: <3.13,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.zh-CN
License-File: LICENSE.en
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: torch
Requires-Dist: matplotlib
Requires-Dist: pylatexenc
Requires-Dist: scikit-learn
Requires-Dist: sympy
Requires-Dist: mpmath
Requires-Dist: cqlib
Requires-Dist: networkx
Provides-Extra: cuda
Requires-Dist: unitarylab-cu12<0.2,>=0.1; extra == "cuda"
Provides-Extra: chemistry
Requires-Dist: unitarylab-chemistry<0.2,>=0.1; extra == "chemistry"
Dynamic: license-file

<div align="center">

<h1>unitarylab</h1>

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

<p>
  <img src="https://img.shields.io/badge/Python-3.10%20%7C%203.11%20%7C%203.12-3b82f6?style=flat-square&logo=python&logoColor=white" alt="Python 3.10, 3.11, and 3.12"/>
  <img src="https://img.shields.io/badge/Backend-NumPy%20%7C%20PyTorch%20%7C%20C%2B%2B%20%7C%20TensorNet-7c3aed?style=flat-square" alt="NumPy, PyTorch, C++, and TensorNet backends"/>
  <img src="https://img.shields.io/badge/Interface-Circuit-f59e0b?style=flat-square" alt="Circuit interface"/>
  <img src="https://img.shields.io/badge/License-UnitaryLab-22c55e?style=flat-square" alt="UnitaryLab license"/>
</p>

<p>
  <a href="#english">English</a>
  &middot;
  <a href="#chinese">中文</a>
</p>

</div>

---

<a id="english"></a>

# 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:

```python
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

```bash
pip install unitarylab
```

For the optional CUDA 12 C++ backend on Linux x86_64, Linux aarch64 server
environments, or Windows x86-64, install the CUDA extra:

```bash
pip install "unitarylab[cuda]"
```

The extra installs the CPU package and the separately distributed
`unitarylab-cu12` wheel selected for the current Python and platform. It
requires a compatible NVIDIA driver, but not a local CUDA Toolkit.

For the optional molecular Chemistry workflow, install the Chemistry extra:

```bash
pip install "unitarylab[chemistry]"
```

The extra installs the separately distributed `unitarylab-chemistry` package.
Import its public API from `unitarylab_chemistry`.

Verify the installation:

```python
import unitarylab

print(unitarylab.__version__)
```

Python 3.10+ is required. NumPy is a core dependency; PyTorch powers the
default backend, while SciPy and Matplotlib support TensorNet, algorithms, and
drawing. The C++ backend requires the packaged native extension.

For a CPU-only PyTorch installation:

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

## Quick Start

Create and execute a Bell-state circuit:

```python
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:

```python
{"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:

```python
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:

```python
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. |

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.

```python
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. |
| `cpp` | `gpu` | NumPy-compatible result | Requires `pip install "unitarylab[cuda]"` and a CUDA 12 runtime. |
| `tensornet` | `cpu` | `TensorNetState` MPS | Supports `max_bond`, `cutoff`, and routing options. |

Select the CUDA backend with
`circuit.execute(backend="cpp", device="gpu")` after installing the CUDA extra.

`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()`.

```python
import numpy as np

from unitarylab import Circuit

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

result = circuit.execute_density(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 provides `BitFlip`, `PhaseFlip`,
`DepolarizingNoise`, `AmplitudeDamping`, and `PhaseDamping`. Attach channels
with `NoiseOperator`, collect them in a `NoiseModel`, and pass the model to
`execute_density()`:

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

noise_model = NoiseModel([
    NoiseOperator(BitFlip(0.02), gate_ids=("x", "rx")),
    NoiseOperator(AmplitudeDamping(0.05), qubits=(1,)),
])

result = circuit.execute_density(noise_model=noise_model)
```

Noise is applied after matching primitive gates. Channels are single-qubit and
do not currently cover correlated noise, readout error, or GPU noise.

## 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.

```python
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.

```python
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

```python
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`).

## Compilation and Hardware Submission

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

```python
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. `transpile()` performs
local basis conversion, while `compile()` continues through mapping and returns
a `CompilationResult` without submitting a job. `submit()` returns a
provider-neutral `HardwareResult`.

Real-machine compilation and submission require provider credentials. Guodun
credentials can be supplied through `GUODUN_LOGIN_KEY` or a `QCompiler`
`hardware.login_key`. Use `QCompiler` when OpenQASM input or detailed pipeline
and mitigation configuration is needed. Non-Guodun providers may require their
vendor SDKs: LQCloud uses `lqcloud`, QPanda3 uses `pyqpanda3`, and Quafu needs no
additional SDK.

## Serialization and OpenQASM

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

```python
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 |

## Further Documentation

- [UnitaryLab website](https://unitarylab.com/)
- [Simulator user manual](https://docs.unitarylab.com/en/docs/unitarylab-simulator-user-manual/)
- [UnitaryLab Algorithms on PyPI](https://pypi.org/project/unitarylab-algorithms/)

---

<a id="chinese"></a>

# 中文

## 简介

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

顶层用户入口为：

```python
from unitarylab import Circuit, Register, ClassicalRegister
```

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

## 安装

```bash
pip install unitarylab
```

在 Linux x86_64、Linux aarch64 服务器环境或 Windows x86-64 上使用可选的
CUDA 12 C++ 后端，可安装 CUDA extra：

```bash
pip install "unitarylab[cuda]"
```

该 extra 会安装 CPU 主包，并根据当前 Python 和平台选择独立发布的
`unitarylab-cu12` wheel。运行需要兼容的 NVIDIA 驱动，但不要求本地安装
CUDA Toolkit。

使用可选的分子 Chemistry 工作流时，可安装 Chemistry extra：

```bash
pip install "unitarylab[chemistry]"
```

该 extra 会安装独立发布的 `unitarylab-chemistry` 包。其公开 API 从
`unitarylab_chemistry` 导入。

验证安装：

```python
import unitarylab

print(unitarylab.__version__)
```

需要 Python 3.10+。NumPy 是核心依赖；PyTorch 提供默认后端，SciPy 和
Matplotlib 用于 TensorNet、算法及绘图。C++ 后端需要安装包中的原生扩展。

## 快速开始

```python
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}`。

## 测量与采样

```python
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` 必须是正整数，默认值为 `1`；`seed` 默认值为 `42`，传入 `None`
可使用非确定性电路测量。

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

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

```python
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` | 按经典寄存器分组的最终值。 |

大型系统应优先使用单项概率、边缘概率、采样或期望值查询，避免不必要地访问
完整概率映射或稠密状态向量。

## Observable 与期望值

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

```python
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.complex128`、`shots=1`、`seed=42`。

| 后端 | device | 原生状态 | 主要限制 |
|---|---|---|---|
| `torch` | `cpu`、`gpu` | PyTorch tensor | GPU 依赖 PyTorch 环境。 |
| `numpy` | `cpu` | NumPy array | 稠密状态向量。 |
| `cpp` | `cpu` | NumPy 兼容结果 | 需要 `cppgates` 原生扩展。 |
| `cpp` | `gpu` | NumPy 兼容结果 | 需要 `pip install "unitarylab[cuda]"` 和 CUDA 12 runtime。 |
| `tensornet` | `cpu` | `TensorNetState` MPS | 支持截断和 routing 配置。 |

安装 CUDA extra 后，通过
`circuit.execute(backend="cpp", device="gpu")` 选择 CUDA 后端。

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

## 密度矩阵模拟

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

```python
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`，
支持 `complex64` 和 `complex128`。返回的 `DensityMatrixResult` 提供
`state`、`backend_state`、`trace`、`purity`、`probabilities`、
`probability()`、`marginal_probabilities()` 和 `expectation()`。

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

### 噪声模型

当前提供 `BitFlip`、`PhaseFlip`、`DepolarizingNoise`、
`AmplitudeDamping` 和 `PhaseDamping`。使用 `NoiseOperator` 将 channel
与门匹配规则绑定，加入 `NoiseModel` 后传给 `execute_density()`：

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

noise_model = NoiseModel([
    NoiseOperator(BitFlip(0.02), gate_ids=("x", "rx")),
    NoiseOperator(AmplitudeDamping(0.05), qubits=(1,)),
])

result = circuit.execute_density(noise_model=noise_model)
```

噪声会在匹配的 primitive gate 后应用。当前 channel 均为单量子比特，暂不覆盖
相关多比特噪声、readout error 或 GPU noise。

## 常用电路操作

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

```python
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 状态入口。

```python
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_bond` 和 `cutoff` 会覆盖输入状态中
的配置。状态向量后端不会隐式收缩 MPS，应先调用 `to_statevector()`。

## 绘图与分析

```python
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 输出。

## 编译与硬件提交

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

```python
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()` 返回 `Circuit`。`transpile()` 只进行本地 basis
转换，`compile()` 会继续完成 mapping，并在不提交任务的情况下返回
`CompilationResult`。`submit()` 返回统一的 `HardwareResult`。

真机编译和提交需要 provider 凭据。Guodun 凭据可通过
`GUODUN_LOGIN_KEY` 或 `QCompiler` 的 `hardware.login_key` 提供。OpenQASM
输入或需要详细 pipeline、mitigation 配置时可使用 `QCompiler`。非 Guodun
provider 可能需要厂商 SDK：LQCloud 使用 `lqcloud`，QPanda3 使用
`pyqpanda3`，Quafu 无需额外 SDK。

## 序列化与 OpenQASM

```python
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。

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

## 更多文档

- [UnitaryLab 官网](https://unitarylab.com/)
- [Simulator 用户手册](https://docs.unitarylab.com/zh/docs/unitarylab-simulator-user-manual/)
- [UnitaryLab Algorithms（PyPI）](https://pypi.org/project/unitarylab-algorithms/)

---

# License

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