diff --git a/docs/Makefile b/docs/Makefile index e69de29..a288b66 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -0,0 +1,15 @@ +SPHINXOPTS ?= +SPHINXBUILD ?= python -m sphinx +SOURCEDIR = . +BUILDDIR = _build + +.PHONY: help html clean + +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) + +html: + @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) + +clean: + @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..8419114 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,32 @@ +project = "Cqlib" +author = "Cqlib contributors" +copyright = "2026, Cqlib contributors" + +from pygments.lexers.special import TextLexer +from sphinx.highlighting import lexers + +extensions = [ + "myst_parser", +] + +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} + +root_doc = "index" +language = "zh_CN" + +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", +] + +html_theme = "classic" +html_title = "Cqlib 文档" +html_show_sourcelink = False + +myst_heading_anchors = 3 + +lexers["mermaid"] = TextLexer() diff --git a/docs/documentation/0_get_started/0_introduction.md b/docs/documentation/0_get_started/0_introduction.md new file mode 100644 index 0000000..72fd2a2 --- /dev/null +++ b/docs/documentation/0_get_started/0_introduction.md @@ -0,0 +1,74 @@ +# Cqlib 简介 + +## 欢迎使用 Cqlib! + +Cqlib 是由中电信量子集团自主研发、专为工程实践打造的量子计算软件开发工具包(SDK)。依托[“天衍”量子计算云平台](https://qc.zdxlz.com),Cqlib 提供了统一、稳定、高性能的量子线路编程接口与表示能力,实现真机算力接入与高效模拟计算服务的无缝转换。 + +本套说明文档涵盖[安装教程](1_installation.md)、[快速入门](2_quickstart.md)、[场景化指南](../1_cqlib/0_circuit/0_overview.md)及多种编程语言的 [API 参考](../../api/python/0_overview.md),致力于帮助您更顺畅地完成量子计算任务的构建、执行与可视化,全面提升开发效率与使用体验。 + +--- + + +## 为什么选择 Cqlib ? + +- **极致性能,Rust 驱动** + 核心层采用 Rust 语言构建,确保[线路编译](../1_cqlib/5_transpilation/0_overview.md)与[中间表示](../1_cqlib/1_ir/0_overview.md)转换的高并发处理能力与内存安全性,为大规模量子线路模拟与复杂逻辑处理提供坚实底座。 + +- **原生生态,天衍直连** + 深度集成国产自主量子指令集 [QCIS](../1_cqlib/1_ir/1_qcis.md),并与 [“天衍”量子计算云平台](https://qc.zdxlz.com) 无缝衔接,支持从本地开发到云端执行的灵活迁移。 + +- **多语协同,开发友好** + 通过高度抽象的 [Python 接口](../../api/python/0_overview.md)与底层 [C 接口](../../api/c/0_overview.md)协同设计,兼顾算法研发效率与系统集成稳定性,支持跨平台、跨语言的混合编程场景。 + +- **统一架构,灵活扩展** + 提供统一的[中间表示](../1_cqlib/1_ir/0_overview.md),支持构建可维护、可扩展的量子软件系统,适配从原型验证到企业级应用开发的完整流程。 + +--- + +## Cqlib 采用什么编程语言? + +Cqlib 采用 Rust、Python 和 C 三种语言,整体遵循“核心统一、接口分层”的设计思路,在保证能力一致性的同时兼顾不同开发场景的使用习惯: + +- **[Rust](../../api/rust/0_overview.md)** + 作为核心实现层,承载量子线路、门与指令、参数系统、中间表示、设备建模、量子信息、错误缓解和编译优化等关键能力。 + +- **[Python](../../api/python/0_overview.md)** + 面向算法开发与快速原型验证,提供更高效、直观的 Python 接口,便于交互式使用和快速迭代。 + +- **[C](../../api/c/0_overview.md)** + 面向系统集成与跨语言调用,提供稳定的 C 接口,便于在异构工程中落地部署。 + +--- + +## Cqlib 能做什么? + +Cqlib 的核心能力主要由以下八个模块组成: + +- [量子线路](../1_cqlib/0_circuit/0_overview.md) + 用于创建和管理量子线路,支持量子门添加、参数化线路、控制流、线路组合、矩阵转换和结构分析等基础功能,是使用 Cqlib 构建量子程序的主要入口。 +- [中间表达](../1_cqlib/1_ir/0_overview.md) + 用于在线路对象和文本格式之间进行转换,支持 QCIS、OpenQASM 2.0 和 OpenQASM 3.0 格式,便于线路保存、跨工具链交换以及后续编译处理。 +- [设备模块](../1_cqlib/2_device/0_overview.md) + 用于描述量子设备相关信息,包括设备拓扑、逻辑比特到物理比特的映射、校准属性、噪声模型和执行结果等,为后端适配和噪声感知编译提供数据基础。 +- [量子信息](../1_cqlib/3_qis/0_overview.md) + 提供量子态、算符和量子信息处理相关工具,包括状态向量、密度矩阵、稳定子模拟、Pauli 算符、哈密顿量、量子演化和信息度量等能力。 +- [编译优化](../1_cqlib/4_compiler/0_overview.md) + 用于将高层量子线路转换为更适合目标设备执行的线路形式,支持门分解、拓扑布局、路由映射、模板匹配、交换优化和 Clifford 相关优化等功能。 +- [可视化](../1_cqlib/5_visualization/0_overview.md) + 用于展示量子线路结构,支持 Unicode 文本图和 SVG 图形渲染,便于用户查看线路层次、门操作顺序和整体结构。 +- [错误缓解](../1_cqlib/6_error_mitigation/0_overview.md) + 用于降低噪声对量子计算结果的影响,提供零噪声外推和虚拟蒸馏等错误缓解能力,适合在含噪模拟或真实设备实验结果处理中使用。 +- [天衍量子云平台客户端](../1_cqlib/7_tianyan/0_overview.md) + 用于连接天衍量子云平台,支持平台认证、后端查询、设备配置获取、QCIS 任务提交、任务状态轮询、执行结果解析和读取误差矫正等能力,适合将本地构建的线路提交到云端真实设备或平台后端执行。 + +--- + + +## 下一步 + +如果您准备开始使用 Cqlib,建议按照以下顺序继续阅读: + +- [安装指南](1_installation.md):安装配置一键搞定 +- [快速开始](2_quickstart.md):从“0”到“1”的第一个量子线路 +- [量子线路](../1_cqlib/0_circuit/0_overview.md):了解 Cqlib 中描述量子程序的基础模块。 +- [天衍量子云平台客户端](../1_cqlib/7_tianyan/0_overview.md):了解如何登录平台、选择后端、提交 QCIS 任务并获取云端执行结果。 diff --git a/docs/documentation/0_get_started/1_installation.md b/docs/documentation/0_get_started/1_installation.md new file mode 100644 index 0000000..002ecb6 --- /dev/null +++ b/docs/documentation/0_get_started/1_installation.md @@ -0,0 +1,143 @@ +# Cqlib 安装与环境配置 + +本指南旨在帮助您快速完成 Cqlib 的安装与开发环境配置。 + +在安装 Cqlib 之前,请确保您的计算机环境满足以下条件: + +- Python 环境:支持 Python 3.10 – 3.14(建议使用 64 位版本); +- 操作系统: + - Linux + - macOS + - Windows + +--- + +## 方式 A:通过 `pip` 快速安装(推荐) + +对于大多数用户,推荐使用 `pip` 直接安装。 + + +> 适用场景:算法研发、原型验证。 +> 注意事项:此方式已包含预编译二进制文件,无需安装 Rust 或 C 编译器。 + +### 第一步:创建并激活虚拟环境 + +建议在独立的环境中进行操作,以避免依赖冲突: + +```bash +python -m venv cqlib-env + +# 激活环境 (Windows) +cqlib-env\Scripts\activate +# 激活环境 (Linux/macOS) +source cqlib-env/bin/activate +``` + +### 第二步:`pip` 一键安装 + +```bash +pip install cqlib +``` + +### 第三步:安装验证 + +安装完成后,可以通过以下方式验证: + +```python +import cqlib +print(cqlib.__version__) +``` +如果能够正确输出版本号,则说明安装成功。 + +--- + +## 方式 B:从源码构建(面向开发者) + +如果您希望参与 Cqlib 的开发,或使用尚未发布的最新功能,可以使用下列方法从源码构建。 + +> 适用场景:定制化开发、贡献代码或使用最新特性。 +> 注意事项:此方式要求本地具备编译环境。 + +### 第一步:配置编译链工具 + +在构建前,请根据您的操作系统安装以下必要组件: + +- 安装 Rust 工具链(Stable 1.85+): + + - Windows:访问官方网站: https://www.rust-lang.org/tools/install 下载并运行安装程序。 + - Linux/macOS:执行 `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` + +- 安装 C 编译器: + - Windows:安装 Visual Studio 生成工具,并勾选“使用 C++ 的桌面开发”。 + - Linux:安装 `build-essential`(Ubuntu/Debian)或 `base-devel`(Arch)。 + - macOS:终端执行 `xcode-select --install`。 + +安装完成后,可通过以下命令验证: + +```bash +rustc --version +cargo --version +``` +如果能够正确输出版本号,则说明 Rust 安装成功。 + +### 第二步:获取源代码 + +从 Gitee 克隆仓库到本地: +```bash +git clone https://gitee.com/zdxlz/cqlib2.git +cd cqlib +``` + +### 第三步:编译并安装 Python 绑定 + +我们使用 `maturin` 跨语言工具将 Rust 核心编译为 Python 模块: + +```bash +# 1. 安装构建工具 +pip install -U maturin + +# 2. 编译并安装到当前环境 +# --release 参数可确保获得最佳运行性能 +maturin develop --release -m crates/binding-python/Cargo.toml +``` +该命令将在当前虚拟环境中安装本地构建版本。 + +### 第四步:构建 Rust 核心与 C 接口(可选) + +```bash +# 构建核心库 +cargo build --release + +# 构建 C 接口 ABI(可选) +cargo build -p binding-c --release +``` + +--- + +## 可选模块:安装天衍量子云平台客户端 + +如果需要将本地线路提交到天衍量子云平台执行,还需要安装 `cqlib-tianyan`。该模块独立于 Cqlib 核心包,主要负责平台认证、后端查询、任务提交、任务轮询和结果解析。 + +```bash +pip install cqlib-tianyan +``` + +安装后可通过以下方式验证: + +```python +from cqlib_tianyan import TianyanPlatform +print(TianyanPlatform) +``` + +`cqlib-tianyan` 通常与 `cqlib` 配合使用:先用 `cqlib.circuit` 构建线路,再用 `cqlib.ir.qcis` 导出 QCIS,最后通过 `cqlib_tianyan` 提交到云端后端。 + +--- + +## 下一步 + +安装完成后,建议继续阅读以下内容: + +- [快速开始](2_quickstart.md):从“0”到“1”的第一个量子线路 +- [量子线路](../1_cqlib/0_circuit/0_overview.md):了解 Cqlib 中描述量子程序的基础模块。 +- [量子门与指令](../1_cqlib/0_circuit/1_gates.md):了解内置门、自定义门、复合门和非酉指令。 +- [天衍量子云平台客户端](../1_cqlib/7_tianyan/0_overview.md):了解云端后端接入、任务提交和结果获取流程。 diff --git a/docs/documentation/0_get_started/2_quickstart.md b/docs/documentation/0_get_started/2_quickstart.md new file mode 100644 index 0000000..3396a00 --- /dev/null +++ b/docs/documentation/0_get_started/2_quickstart.md @@ -0,0 +1,122 @@ +# 快速开始 + +本指南将带您使用 Cqlib 构建一个最小可用的量子线路,并完成一次端到端的“工程闭环”流程。 + +在开始之前,请确保您的环境已准备就绪: + +- Python 环境:支持 Python 3.10 – 3.14 +- 完成 Cqlib 安装与环境配置(安装方式见 “[Cqlib 安装与环境配置](../0_get_started/1_installation.md)”) + +--- + +## 第一个量子线路:Bell 态 + +本节通过一个最小但完整的 Bell 态示例,介绍 Cqlib 构建量子线路的基本流程。通过该示例,您将了解如何创建线路、添加量子门、查看线路结构,并进一步进行状态模拟、测量采样、线路可视化和 IR 导出等操作。 + +Bell 态是量子计算中最常见的双比特纠缠态之一,通常用于演示叠加与纠缠的基本概念。构造 Bell 态需要以下两个步骤: + +- 对第 `0` 个量子比特施加 `H` 门,使其进入叠加态; +- 以第 `0` 个量子比特为控制比特、第 `1` 个量子比特为目标比特施加 CX 门,从而在两个量子比特之间建立纠缠关系。 + +理想情况下,最终得到的量子态为: + +```text +(|00> + |11>) / sqrt(2) +``` + +## 1. 创建线路 + +首先创建一条两比特量子线路,并向其中依次添加 `H` 门和 `CX` 门: + +```python +from cqlib import Circuit + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +print(circuit.num_qubits) # 2 +print(len(circuit)) # 2 +print(circuit.operations) # 查看底层 Operation 列表 +``` + +## 2. 使用文本图查看线路 + +将量子线路渲染为文本图以查看线路结构: + +```python +from cqlib.visualization import draw_text + +print(draw_text(circuit)) +``` + +## 3. 转为矩阵验证 + +对于小规模纯量子门线路,可以将整条线路转换为完整酉矩阵,用于验证线路的数学行为: + +```python +matrix = circuit.to_matrix() +print(matrix) +``` + +## 4. 导出 OpenQASM 2.0 / 3.0 + +此外,Cqlib 还提供了 OpenQASM 2.0 和 OpenQASM 3.0 的导出接口: + +```python +from cqlib.ir import qasm2, qasm3 + +print(qasm2.dumps(circuit)) +print(qasm3.dumps(circuit)) +``` + +## 5. 状态向量模拟 + +使用状态向量模拟来查看线路作用后的量子态分布: + +```python +from cqlib.qis import Statevector + +sv = Statevector.from_circuit(circuit) +print(sv.data) +print(sv.probabilities()) +``` + +对于 Bell 态线路,理想情况下,`probabilities()` 的结果应接近: + +```text +[0.5, 0.0, 0.0, 0.5] +``` + +这表示测量时只会得到 `00` 和 `11` 两种结果,并且二者概率相同;而 `01` 和 `10` 的概率接近 `0`,这体现了 Bell 态中两个量子比特之间的纠缠关联。 + +## 6. 采样测量 + +得到状态向量后,可以进一步进行多次采样,模拟实际测量过程中的统计结果: + +```python +shots = sv.sample_shots(1000) +counts = {} +for outcome in shots: + bitstring = outcome.to_bitstring(2) + counts[bitstring] = counts.get(bitstring, 0) + 1 + +print(counts) +``` + +输出结果类似: + +```text +{'00': 506, '11': 494} +``` + +由于采样过程具有随机性,每次运行得到的计数结果不会完全相同。但对于理想 Bell 态,大量采样后,`00` 和 `11` 的出现次数应大致相近,而 `01` 和 `10` 通常不会出现或概率接近于零。 + +--- + +## 下一步 + +- [量子线路](../1_cqlib/0_circuit/0_overview.md):了解 Cqlib 中描述量子程序的基础模块。 +- [量子门与指令](../1_cqlib/0_circuit/1_gates.md):了解内置门、自定义门、复合门和非酉指令。 +- [线路结构与构造](../1_cqlib/0_circuit/2_structures.md):掌握 `Circuit` 的生命周期、索引、组合和操作表示。 +- [天衍量子云平台客户端](../1_cqlib/7_tianyan/0_overview.md):如果需要把线路提交到云端后端执行,可继续学习 QCIS 导出、后端选择、任务提交和结果获取流程。 diff --git a/docs/documentation/1_cqlib/0_circuit/0_overview.md b/docs/documentation/1_cqlib/0_circuit/0_overview.md new file mode 100644 index 0000000..ba48e15 --- /dev/null +++ b/docs/documentation/1_cqlib/0_circuit/0_overview.md @@ -0,0 +1,155 @@ +# 量子线路 + +`cqlib.circuit` 是 Cqlib 中描述量子程序的基础模块。它负责表达量子比特、门操作、参数表达式、复合线路、非幺正指令、测量结果以及由经典表达式驱动的动态控制流。后续的 [IR 转换](../1_ir/0_overview.md)、[QIS 模拟](../3_qis/0_overview.md)、[编译优化](../4_compiler/0_overview.md)、[设备映射](../2_device/0_overview.md)和[可视化](../5_visualization/0_overview.md)模块,通常都以 `Circuit` 作为输入或中间表示。 + +--- + +## 核心抽象 + +`cqlib.circuit` 围绕 `Circuit` 构建了一组相互配合的对象,用于描述从基础量子门到动态线路控制流的完整线路结构。 + +| 抽象 | 作用 | 常用入口 | +|---|---|---| +| `Qubit` | 量子比特标识符,只保存非负索引 | `Qubit(0)`| +| `Circuit` | 量子线路容器,保存量子比特、参数、经典值和操作序列 | `Circuit(...)` | +| `StandardGate` | Cqlib 内置标准门集合,包括 Pauli、Clifford、旋转门、双比特门等 | `StandardGate.H`, `StandardGate.RX(theta)` | +| `MCGate` | 多控制门,把一个标准门提升为带多个控制比特的门 | `MCGate(2, StandardGate.X())` | +| `UnitaryGate` | 用户自定义幺正门,可通过数值矩阵、符号矩阵或不可变子线路定义 | `UnitaryGate("Oracle", 2)` | +| `CircuitGate` | 由子线路转换得到的复合门 | `sub.to_gate("Block")` | +| `Directive` | 非幺正指令,如 barrier、measure、reset | `circuit.measure(0)` | +| `Parameter` | 符号参数表达式 | `Parameter("theta")` | +| `ClassicalType` / `ClassicalExpr` | 动态线路中的经典类型与经典表达式 | `ClassicalType.bit()`, `m.expr()` | +| `ValueOperation` | 完整操作表示:指令 + 比特 + 参数 + 可选标签 | `circuit.operations[0]` | + +--- + +## 核心功能 + +### 1. 静态线路 + +静态线路是最基础的使用方式。在线路宽度固定的情况下,您可以按照执行顺序依次追加量子门和指令。 + +```python +from cqlib import Circuit + +c = Circuit(3) +c.h(0) +c.cx(0, 1) +c.rzz(1, 2, 0.25) +c.barrier([0, 1, 2]) +c.reset(2) +``` + +静态线路适用于算法原型验证、编译优化、QIS 模拟和 IR 导出等场景。对于不包含测量、重置和控制流的纯酉线路,还可以进一步转换为矩阵表示,用于小规模线路验证。 + +### 2. 参数化线路 + +参数化线路使用 `Parameter` 作为角度或表达式占位符,常用于 VQE、QAOA、量子机器学习、参数扫描和梯度计算等场景。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +c = Circuit(2) +c.rx(0, theta) +c.ry(1, phi) +c.cx(0, 1) + +print(c.symbols) # ['theta', 'phi'] + +bound = c.assign_parameters({"theta": 0.1, "phi": 0.2}) +print(bound.to_matrix()) +``` + +值得注意的是,`assign_parameters()` 会返回一条新的已绑定线路,不会修改原始参数化线路模板。因此,同一个参数化线路可以被多次复用,从而用于不同参数点的扫描和优化。 + +### 3. 子线路与复合门 + +实际量子算法通常由若干可复用的线路模块组成。Cqlib 支持将一条 `Circuit` 转换为 `CircuitGate`,再像普通量子门一样追加到其他线路中。 + +```python +from cqlib import Circuit + +bell = Circuit(2) +bell.h(0) +bell.cx(0, 1) + +bell_gate = bell.to_gate("Bell") + +main = Circuit(4) +main.append_circuit_gate(bell_gate, [0, 1]) +main.append_circuit_gate(bell_gate, [2, 3]) + +flat = main.decompose() +``` + +`CircuitGate` 适合表达算法中的可复用模块,例如纠缠块、特征映射块、oracle 子程序等。此外,可以使用 `decompose()` 将复合门展开为基础操作,便于后续矩阵验证、编译优化或 IR 导出。 + +### 4. 自定义门与多控制门 + +当内置门集合不足以描述某个算法单元时,可以使用 `UnitaryGate` 定义自定义酉门,也可以使用 `MCGate` 构造多控制门。 + + +```python +import numpy as np +from cqlib import Circuit +from cqlib.circuit import MCGate, StandardGate, UnitaryGate + +c = Circuit(3) + +controlled_h = MCGate(2, StandardGate.H()) +c.append_mc_gate(controlled_h, [0, 1, 2]) + +custom_x = UnitaryGate("CustomX", 1).with_matrix([[0, 1], [1, 0]]) +c.append_unitary_gate(custom_x, [2]) +``` + +多控制门和自定义门常用于算法库、oracle 构造、受控子程序以及硬件专用指令建模。请注意,使用自定义酉门时,应确保矩阵维度与作用量子比特数量一致,并且矩阵满足酉性要求。 + +### 5. 动态线路 + +动态线路允许在线路执行过程中进行路中测量,并根据测量结果或经典变量控制后续操作。这里,Cqlib 通过 `ClassicalType`、`ClassicalExpr` 和结构化控制流接口描述这类线路。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr, ClassicalType + +c = Circuit(1) +measurement = c.measure(0) + +condition = measurement.expr().to_bool() +c.if_(condition, lambda body: body.x(0)) + +flag = c.var(ClassicalType.bool()) +c.store(flag, ClassicalExpr.bool_literal(True)) +c.while_(flag.expr(), lambda body: body.break_loop()) + +c.validate() +``` + +请注意,动态线路通常无法表示为单一酉矩阵,因此不能直接使用 `to_matrix()` 进行整体矩阵转换。对于动态线路,应优先使用结构化验证、IR 转换或支持动态执行语义的后端流程。 + +### 6. 线路分析与转换 + +线路构造完成后,可以进一步执行参数绑定、反演、分解、数值矩阵转换或符号矩阵转换等操作。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +c = Circuit(1) +c.rx(0, theta) + +inverse = c.inverse() +symbolic = c.to_symbolic_matrix() +numeric = c.assign_parameters({"theta": 0.3}).to_matrix() +``` +--- + +## 下一步 + +- [量子门与指令](1_gates.md):了解内置门、自定义门、复合门和非酉指令。 +- [线路结构与构造](2_structures.md):掌握 `Circuit` 的生命周期、索引、组合和操作表示。 +- [参数系统](3_parameters.md):学习参数表达式、参数绑定、表达式化简、符号求导和符号矩阵。 \ No newline at end of file diff --git a/docs/documentation/1_cqlib/0_circuit/1_gates.md b/docs/documentation/1_cqlib/0_circuit/1_gates.md new file mode 100644 index 0000000..984c9a4 --- /dev/null +++ b/docs/documentation/1_cqlib/0_circuit/1_gates.md @@ -0,0 +1,451 @@ +# 量子门与指令 + +`cqlib.circuit` 提供了丰富的量子门与线路指令,用于构造从基础量子线路到参数化算法模块的各类量子程序。本篇将详细介绍 Cqlib 中量子门与指令的基本用法,包括标准门、参数门、多比特门、多控制门、自定义酉门、子线路门以及测量、重置、屏障等非幺正指令。 + +通过本篇内容,您可以根据算法需求选择合适的门类型,并将其正确添加到量子线路中。您可以通过 `Circuit` 提供的便捷方法快速添加常用门,也可以使用 `StandardGate`、`MCGate`、`UnitaryGate` 和 `CircuitGate` 等对象显式描述更复杂的门操作。 + +--- + +## 标准门 + +标准门是 Cqlib 内置的基础门集合,覆盖常用单比特门、多比特门、参数化旋转门以及部分硬件相关门。您可以通过以下两种方式执行标准门: + +1. 直接调用 `Circuit` 提供的快捷方法; +2. 显式构造 `StandardGate` 对象,并通过 `append_gate()` 追加到线路中。 + +```python +from cqlib import Circuit +from cqlib.circuit import StandardGate + +c = Circuit(1) +c.h(0) +c.append_gate(StandardGate.X(), [0]) +c.append_gate(StandardGate.RZ(0.5), [0], label="phase-correction") +``` + +`StandardGate` 对象支持属性查询、参数绑定、矩阵计算、求逆以及转换为多控制门等操作。 + +```python +from cqlib.circuit import StandardGate + +gate = StandardGate.RX(0.25) + +print(gate.num_qubits) # 1 +print(gate.num_params) # 1 +print(gate.num_ctrl_qubits) # 0 +print(gate.params) # [Parameter(0.25)] +print(gate.matrix().shape) # (2, 2) + +inverse_gate = gate.inverse() +controlled = StandardGate.X().control(2) +``` + +下面将按照门的作用比特数量和参数形式,对常用标准门进行分类介绍。 + + +### 1. 单比特非参数门 + +单比特非参数门是构造量子线路最基础的一类标准门,其作用于单个量子比特,使用时不需要提供角度参数,门的矩阵形式和作用效果在定义时已经固定。 + +您可以通过这些门完成量子态的基本变换,例如利用 `H` 门构造叠加态,利用 `X` 门实现比特翻转,利用 `Z`、`S`、`T` 等门调整相位,或结合 Clifford 门集构造便于分析和编译优化的基础线路。 + +在实际使用中,单比特非参数门通常作为更复杂线路的基础组成单元,既可以单独用于状态制备、相位修正和线路调试,也可以与双比特门、参数化旋转门组合,用于构造 Bell 态、GHZ 态、变分线路、误差校验线路和硬件原生门分解结果。 + +| `Circuit` 方法 | `StandardGate` | 说明 | +|---|---|---| +| `i(q)` | `I` | 恒等门 | +| `h(q)` | `H` | Hadamard 门 | +| `x(q)` | `X` | Pauli-X / NOT 门 | +| `y(q)` | `Y` | Pauli-Y 门 | +| `z(q)` | `Z` | Pauli-Z 门| +| `s(q)` | `S` | 相位门 | +| `sdg(q)` | `SDG` | `S` 门的逆 | +| `t(q)` | `T` | `pi/4` 相位门 | +| `tdg(q)` | `TDG` | `T` 门的逆 | +| `x2p(q)` | `X2P` | `sqrt(X)`,约定为 `X^(+1/2)` | +| `x2m(q)` | `X2M` | `sqrt(X)` 的逆 | +| `y2p(q)` | `Y2P` | `sqrt(Y)` | +| `y2m(q)` | `Y2M` | `sqrt(Y)` 的逆 | + +```python +from cqlib import Circuit + +c = Circuit(1) +c.h(0) +c.x(0) +c.sdg(0) +c.t(0) +c.x2p(0) +c.y2m(0) + +print([op.instruction.instruction.name for op in c.operations]) +``` + +需要注意的是,`i(q)` 与 `delay(q, duration)` 的语义不同。`i(q)` 表示量子线路中的标准恒等门;`delay(q, duration)` 表示硬件时间线上的空闲等待,通常用于保留调度或时序语义。 + +### 2. 单比特参数门 + +单比特参数门是在单个量子比特上执行的参数化量子门,通常通过一个或多个角度参数控制量子态在 Bloch 球上的旋转方向和旋转幅度。与 `H`、`X` 等固定门不同,参数门的实际作用由传入的数值或符号参数决定,因此可以在保持线路结构不变的情况下,通过调整参数改变线路行为。 + +在 Cqlib 中,单比特参数门既可以接受普通数值参数,也可以接受 Parameter 表达式。前者适合构造已经确定角度的线路;后者适合构造参数化线路模板,并在后续算法流程中进行参数绑定、参数扫描、优化迭代或符号矩阵验证。 + +参数化门广泛用于 VQE、QAOA、量子机器学习、变分线路设计和硬件校准等场景。例如,在变分量子算法中,线路结构通常保持不变,而优化器会不断更新旋转门中的参数值,从而搜索更优的量子态或目标函数值。 + +| `Circuit` 方法 | `StandardGate` | 参数 | 说明 | +|---|---|---|---| +| `rx(q, theta)` | `RX` | 1 | 绕 X 轴旋转 | +| `ry(q, theta)` | `RY` | 1 | 绕 Y 轴旋转 | +| `rz(q, theta)` | `RZ` | 1 | 绕 Z 轴旋转 | +| `phase(q, lambda_)` | `Phase` | 1 | 相位门 | +| `u(q, theta, phi, lambda_)` | `U` | 3 | 通用单比特门 | +| `xy(q, theta)` | `XY` | 1 | XY 系列单比特门 | +| `xy2p(q, theta)` | `XY2P` | 1 | `XY` 的正半角变体 | +| `xy2m(q, theta)` | `XY2M` | 1 | `XY` 的负半角变体 | +| `rxy(q, theta, phi)` | `RXY` | 2 | 在 XY 平面中指定旋转轴 | + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +c = Circuit(1) +c.rx(0, theta) +c.ry(0, 0.25) +c.rz(0, 2 * theta + phi) +c.phase(0, phi) +c.u(0, theta, 0.1, phi) +c.rxy(0, theta, phi) + +print(c.symbols) +bound = c.assign_parameters({"theta": 0.3, "phi": 0.5}) +print(bound.to_matrix()) +``` + +如果线路中仍包含未绑定的符号参数,则无法直接使用 `to_matrix()` 得到数值矩阵。此时可以先通过 `assign_parameters()` 绑定参数,或使用 `to_symbolic_matrix()` 保留符号表达式。 + +`StandardGate.GPhase` 用于表示全局相位。在线路层面,更常用的方式是设置 `Circuit` 的全局相位: + +```python +from cqlib import Circuit, Parameter + +c = Circuit(1) +c.set_global_phase(Parameter("alpha")) +print(c.global_phase) +``` + +### 3. 双比特门与三比特门 + +双比特门与三比特门用于描述多个量子比特之间的相互作用,是构造纠缠态、受控逻辑和量子算法核心结构的核心组件。与单比特门只改变单个量子比特状态不同,多比特门可以在不同量子比特之间建立关联关系,例如通过 `CX`、`CZ` 等受控门构造 Bell 态、GHZ 态和各类纠缠线路,也可以通过 `SWAP` 调整量子比特之间的逻辑位置。 + +在实际量子算法中,多比特门通常承担“连接”不同量子比特信息的作用。例如,QAOA 中常用 `RZZ` 等双比特参数门表达问题哈密顿量中的相互作用项,量子傅里叶变换和相位估计算法中会使用受控相位类操作,纠错和验证线路中也经常使用 `CX`、`CCX` 等门实现条件翻转和辅助比特控制。 + +| `Circuit` 方法 | `StandardGate` | 参数 | 说明 | +|---|---|---|---| +| `cx(control, target)` | `CX` | 0 | 受控 X 门,CNOT | +| `cy(control, target)` | `CY` | 0 | 受控 Y 门 | +| `cz(control, target)` | `CZ` | 0 | 受控 Z 门 | +| `swap(a, b)` | `SWAP` | 0 | 交换两个量子比特状态 | +| `ccx(c1, c2, target)` | `CCX` | 0 | Toffoli 门 | +| `rxx(a, b, theta)` | `RXX` | 1 | `XX` Pauli 旋转 | +| `ryy(a, b, theta)` | `RYY` | 1 | `YY` Pauli 旋转 | +| `rzz(a, b, theta)` | `RZZ` | 1 | `ZZ` Pauli 旋转 | +| `rzx(a, b, theta)` | `RZX` | 1 | `ZX` Pauli 旋转 | +| `crx(control, target, theta)` | `CRX` | 1 | 受控 `RX` 门| +| `cry(control, target, theta)` | `CRY` | 1 | 受控 `RY` 门| +| `crz(control, target, theta)` | `CRZ` | 1 | 受控 `RZ` 门| +| `fsim(a, b, theta, phi)` | `FSIM` | 2 | fSim 双比特门 | + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +c = Circuit(3) +c.cx(0, 1) +c.swap(1, 2) +c.ccx(0, 1, 2) +c.rzz(0, 2, theta) +c.crx(1, 2, 0.25) +c.fsim(0, 1, theta, phi) +``` + +在添加多比特门时,Cqlib 会对操作对象进行合法性检查,包括目标量子比特是否已经在线路中注册、同一次操作是否重复引用同一个量子比特,以及传入参数的数量是否与门定义一致。若检查未通过,系统会抛出 `CircuitError` 或 `ParameterError`,以提示用户修正量子比特索引、作用对象或参数配置。 + +--- + +## 门矩阵与门求逆 + +在量子线路分析、算法验证和编译转换过程中,门的矩阵表示是理解其数学作用的重要依据。Cqlib 支持对标准门和多控制门直接计算矩阵,以便您检查门的维度、验证门的幺正性,以及分析门对量子态的具体作用。 + +在计算矩阵时,需要注意: + +- 对于不含符号参数的门,可以直接调用 `matrix()` 获取其数值矩阵。 +- 对于包含符号参数的门,需要在计算矩阵时提供具体的数值参数,或先通过参数绑定得到数值化后的门对象,再进行矩阵计算。 + +这样既可以保留参数化门在算法模板中的灵活性,也可以在需要数值验证时获得明确的矩阵结果。 + +```python +import numpy as np +from cqlib import Parameter +from cqlib.circuit import StandardGate + +h = StandardGate.H() +h_matrix = h.matrix() +print(h_matrix) + +theta = Parameter("theta") +rx = StandardGate.RX(theta) +rx_matrix = rx.matrix([np.pi / 2]) + +print(rx_matrix) +``` + +此外,Cqlib 还支持对可逆门执行求逆操作。 + +- 对于常见自反门,求逆结果与原门相同。 +- 对于旋转门等参数化门,求逆通常表现为旋转角度取相反数。 + +```python +from cqlib.circuit import StandardGate + +h_inverse = StandardGate.H.inverse() +print(h_inverse) # H + +rx_inverse = StandardGate.RX(0.5).inverse() +print(rx_inverse.params[0].evaluate()) # -0.5 + +s_inverse = StandardGate.S.inverse() +t_inverse = StandardGate.T.inverse() + +print(s_inverse) # SDG +print(t_inverse) # TDG +``` + +常见门的求逆规则如下: + +- `H`、`X`、`Y`、`Z`、`CX`、`CY`、`CZ`、`SWAP` 和 `CCX` 为自反门。 +- `S` 与 `SDG` 互为逆门,`T` 与 `TDG` 互为逆门。 +- 旋转门的逆门通常通过角度取负得到,例如 `RX(theta)^† = RX(-theta)`。 +- `U(theta, phi, lambda)` 的逆门会根据矩阵定义转换参数。 +- `Barrier` 不改变量子态,可视为自身的逆;`Measure` 和 `Reset` 不具备普通幺正逆操作。 + +--- + +## 多控制门 + +`MCGate` 用于将已有的 `StandardGate` 扩展为多控制门,即在原始门的基础上增加一个或多个控制量子比特。只有当所有控制比特满足指定控制条件时,目标门才会作用于对应的目标比特。该机制常用于构造 Toffoli 门、多控制相位门、oracle、条件翻转操作以及量子算法中的受控子程序。 + +在向线路中追加 `MCGate` 时,需要按照约定传入量子比特顺序:**先给出所有控制比特,再给出目标门所作用的目标比特**。 + +例如,对于一个三控制 `X` 门,前三个量子比特为控制比特,最后一个量子比特为目标比特。这样可以保证多控制门的语义清晰,也便于后续进行门分解、编译优化和硬件映射。 + + +```python +from cqlib import Circuit, Parameter +from cqlib.circuit import MCGate, StandardGate + +c = Circuit(4) + +mcx = MCGate(3, StandardGate.X()) +c.append_mc_gate(mcx, [0, 1, 2, 3]) + +theta = Parameter("theta") +mcrz = MCGate(2, StandardGate.RZ(theta)) +c.append_mc_gate(mcrz, [0, 1, 2]) + +print(c[0].instruction.instruction.name) # C3-X +print(c[1].params) # [theta] +``` + +除显式构造 `MCGate` 外,也可以直接从标准门对象调用 `control()` 方法生成多控制门。 + +```python +from cqlib.circuit import StandardGate + +ccx = StandardGate.X().control(2) +controlled_cx = StandardGate.CX().control(1) +``` + +`StandardGate.CX().control(1)` 表示在已有 `CX` 门的基础上再增加一个控制比特,因此其总控制比特数为 2,与三比特 `CCX` 在控制结构上等价。 + +--- + +## 自定义幺正门 + +当算法需要使用内置标准门以外的幺正操作时,可以通过 `UnitaryGate` 定义自定义幺正门。自定义幺正门适用于表示算法中的特殊 oracle、问题相关变换、硬件专用门、已知矩阵形式的量子操作。 + +定义 `UnitaryGate` 时,需要明确指定门名称和作用量子比特数量。若使用矩阵定义该门,则矩阵维度必须为 `2^n × 2^n`,其中 `n` 表示该门作用的量子比特数量,即 `num_qubits`。Cqlib 会根据门定义检查矩阵维度与作用比特数量是否匹配。 + +```python +import numpy as np +from cqlib import Circuit +from cqlib.circuit import UnitaryGate + +h_matrix = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) +custom_h = UnitaryGate("CustomH", 1).with_matrix(h_matrix) + +c = Circuit(2) +c.append_unitary_gate(custom_h, [0]) +``` + +除数值矩阵外,自定义幺正门也可以通过符号矩阵定义。符号矩阵适合描述带参数的门族,可以先定义门的符号形式,再从不同线路或不同位置传入具体参数值。这种方式常用于参数化 oracle、可调相位门、算法模板和符号验证场景。 + +```python +from cqlib import Circuit, Parameter +from cqlib.circuit import SymbolicComplex, SymbolicMatrix, UnitaryGate + +theta = Parameter("theta") +phase = SymbolicComplex.exp_i(theta) + +matrix = SymbolicMatrix( + [ + [SymbolicComplex.one(), SymbolicComplex.zero()], + [SymbolicComplex.zero(), phase], + ] +) + +gate = UnitaryGate("SymbolicPhase", 1, num_params=1).with_symbolic_matrix( + matrix, + ["theta"], +) + +c = Circuit(1) +c.append_unitary_gate(gate, [0], [0.25]) +``` + +需要注意的是,`UnitaryGate` 更适合用于已经具有明确矩阵定义的量子操作。如果目标只是将已有子线路作为一个可复用模块添加到其他线路中,通常更推荐使用 `CircuitGate`。`CircuitGate` 可以直接由子线路封装得到,既避免手动编写矩阵,也更便于后续进行分解、参数绑定和线路结构分析。 + +--- + +## 子线路门 `CircuitGate` + +`CircuitGate` 用于将一段已有子线路封装为可复用的复合门。与 `UnitaryGate` 通过矩阵描述门的行为不同,`CircuitGate` 保留了子线路的结构信息,因此更适合表达算法模块、oracle、ansatz block、重复线路结构以及其他需要在多个位置复用的量子程序片段。 + +在实际使用中,您可以先构造一段子线路,然后通过 `to_gate(name)` 将其转换为 `CircuitGate`。转换后的复合门可以像普通门一样追加到其他线路中,并且可以在需要时通过 `decompose()` 展开为原始子线路操作,便于后续进行线路分析、参数绑定、编译优化或 IR 导出。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") + +sub = Circuit(1) +sub.rx(0, theta) +sub.rz(0, theta / 2) + +block = sub.to_gate("ParamBlock") + +main = Circuit(2) +main.append_circuit_gate(block, [0], [0.3]) +main.append_circuit_gate(block, [1], [0.7]) + +decomposed = main.decompose() +print([op.instruction.instruction.name for op in decomposed.operations]) +``` + +对于带参数的 `CircuitGate`,追加到线路时可以传入具体参数值。参数会按照 `gate.symbols` 中记录的符号顺序进行位置绑定;如果不传入 `params`,则会保留子线路中的原始符号参数,使该复合门仍然保持参数化形式。 + +```python +print(block.name) +print(block.num_qubits) +print(block.num_params) +print(block.symbols) +``` + +除使用 `to_gate(name)` 外,也可以显式使用 `FrozenCircuit` 和 `CircuitGate` 构造复合门。 + +```python +from cqlib import Circuit +from cqlib.circuit import CircuitGate, FrozenCircuit + +sub = Circuit(1) +sub.h(0) + +frozen = FrozenCircuit(sub.qubits, sub.operations) +gate = CircuitGate("HadamardBlock", frozen) +``` + +--- + +## Directive:非幺正指令 + +除普通量子门外,量子线路中还可能包含测量、重置、屏障和延迟等特殊指令。这类指令通常无法对应一个普通的幺正矩阵,因此在 Cqlib 中统一通过 `Directive` 表示。 + +`Directive` 主要用于描述线路执行过程中的辅助语义。例如,`barrier` 用于约束编译优化过程中的门重排,`measure` 用于将量子态信息读出为经典结果,`reset` 用于将量子比特重新初始化到 |0>,`delay` 则用于保留硬件时间调度中的空闲等待语义。 + +常用的非幺正指令可以直接通过 `Circuit` 提供的接口添加到线路中: + +| `Circuit` 方法 | 指令名 | 说明 | +|---|---|---| +| `barrier(qubits)` | `Barrier` | 阻止编译器跨越指定比特重排门 | +| `measure(qubit)` | `measure_bit` | 测量单个比特,返回 `Measurement` | +| `measure_bits(qubits)` | `measure_bits` | 测量多个比特,返回 bit-vector 测量值 | +| `reset(qubit)` | `Reset` | 重置量子比特 | +| `delay(qubit, duration)` | `delay` | 在硬件时间线上保持空闲 | + +```python +from cqlib import Circuit, Parameter +from cqlib.circuit import ClassicalType + +c = Circuit(2) +c.h(0) +c.barrier([0, 1]) + +readout = c.measure(0) +bit_var = c.var(ClassicalType.bit()) +c.measure_into(1, bit_var) + +c.reset(0) +c.delay(1, Parameter("tau")) + +print(readout.width) +print(c.classical_values) +print(c.classical_vars) +``` + +需要注意的是,非幺正指令会影响线路的数学表示和后续处理方式: + +- `barrier` 本身不改变量子态,仅用于表达编译约束。因此,当线路中不包含其他非幺正操作时,带有 `barrier` 的线路仍可用于矩阵转换。 +- `measure`、`measure_into` 和 `measure_bits` 会引入量子测量与经典结果,因此线路不再能用单一幺正矩阵完整表示。 +- `reset` 会将量子比特重新初始化到固定状态,属于非幺正操作,同样不能通过普通幺正矩阵描述。 +- `delay` 主要用于保留硬件执行或调度层面的时间语义。 + +因此,在进行 `to_matrix()`、线路等价性验证或门级优化时,应先确认线路中是否包含测量、重置、延迟等特殊指令。 + +--- + +## 低层指令与 ValueOperation + +在一些更底层或更自动化的开发场景中,Cqlib支持显式构造 `Instruction` 和 `ValueOperation`,从而提供了一种更灵活的开发方式。例如,在编写线路反序列化器、IR 转换器、编译优化测试、自动化线路生成工具或自定义前端接口时,您可以通过此方式直接描述某一条操作的指令类型、作用量子比特、参数列表和标签信息。 + +```python +from cqlib import Circuit, Qubit +from cqlib.circuit import Instruction, StandardGate, ValueOperation + +instruction = Instruction.from_standard_gate(StandardGate.H()) +operation = ValueOperation.from_standard_gate( + StandardGate.RX(0.25), + [Qubit(0)], + label="rx-layer-0", +) + +c = Circuit(1) +c.append(operation) + +print(c[0].instruction) +print(c[0].params) +print(c[0].label) +``` + +从语义上看,`Instruction` 用于描述“执行什么类型的指令”,`ValueOperation` 则用于描述这条指令在线路中的一次具体应用,包括它作用在哪些量子比特上、使用哪些参数,以及是否携带额外标签。 + +这种区分使 Cqlib 能够在高层线路构造、低层操作表示、IR 转换、编译优化和动态控制流处理之间复用统一的数据模型。 + +--- + +## 下一步 + +- [线路结构与构造](2_structures.md):掌握 `Circuit` 的生命周期、索引、组合和操作表示。 +- [参数系统](3_parameters.md):学习参数表达式、参数绑定、表达式化简、符号求导和符号矩阵。 +- [线路分析与转换](4_circuit_analysis.md):使用反演、分解、矩阵转换和操作检查等工具。 diff --git a/docs/documentation/1_cqlib/0_circuit/2_structures.md b/docs/documentation/1_cqlib/0_circuit/2_structures.md new file mode 100644 index 0000000..cbec0cc --- /dev/null +++ b/docs/documentation/1_cqlib/0_circuit/2_structures.md @@ -0,0 +1,350 @@ +# 线路结构与构造 + +`Circuit` 是 `cqlib.circuit` 模块中的核心线路容器,用于表示一段完整的量子程序。它不仅记录量子比特和按顺序排列的线路操作,还负责维护参数表达式、全局相位、经典变量、测量结果以及动态线路所需的经典句柄命名空间。因此,`Circuit` 既是用户构造量子线路的主要入口,也是后续 IR 转换、编译优化、设备映射和结果分析等流程的重要数据基础。 + +从结构上看,一条 `Circuit` 通常包含以下信息: + +* 量子比特列表,用于描述线路中可操作的逻辑量子比特; +* 按追加顺序排列的操作列表,用于记录量子门、指令和控制流结构; +* 线路中的自由参数,用于支持参数化线路、参数绑定和变分算法; +* 全局相位,用于保留线路整体相位信息; +* 动态线路中使用的经典变量和经典值,用于描述测量结果和经典控制逻辑; +* 由 `CircuitId` 标识的经典句柄命名空间,用于保证经典变量和测量值在线路内部的一致性。 + +上述结构共同构成了 Cqlib 量子线路的基础表示,使线路能够在构造、组合、参数绑定、矩阵转换、IR 导出、编译优化和动态控制流分析等不同阶段保持统一的数据语义。 + + +--- + +## 创建线路 + +`Circuit` 支持多种线路创建方式。您既可以直接指定量子比特数量,也可以显式传入量子比特索引列表或 `Qubit` 对象。不同方式适用于不同的建模需求:当量子比特编号连续时,直接传入整数最为简洁;当需要保留特定逻辑编号或与外部系统中的比特编号对齐时,可以使用整数索引列表或显式的 `Qubit` 对象。 + +### 1. 使用量子比特数量 + +最常见的方式是向 `Circuit` 传入一个整数 `n`。此时,Cqlib 会自动创建 `n` 个逻辑量子比特,并按照 `0..n-1` 的顺序分配索引。 + +```python +from cqlib import Circuit + +c = Circuit(3) +print(c.num_qubits) # 3 +print([q.index for q in c.qubits]) # [0, 1, 2] +``` + +在上述示例中,`Circuit(3)` 创建了一条包含 3 个量子比特的线路,对应的逻辑量子比特索引分别为 `0`、`1` 和 `2`。后续添加门操作时,可以直接使用这些整数索引指定门的作用对象,例如 `c.h(0)` 或 `c.cx(0, 1)`。 + +此外,`Circuit(0)` 也是合法的构造方式,表示一条不包含量子比特和操作的零比特线路。零比特线路通常用于边界测试、递归构造、程序化生成线路时的初始占位,或验证接口在空输入下的行为。 + +```python +from cqlib import Circuit + +empty = Circuit(0) +print(empty.width) # 0 +print(len(empty)) # 0 +print(empty.to_matrix()) +``` + +### 2. 使用整数索引列表 + +当线路中的逻辑量子比特编号不是连续的 `0..n-1`,或者需要与外部数据、设备映射、算法模型中的编号保持一致时,可以直接向 `Circuit` 传入整数索引列表。 + +```python +from cqlib import Circuit + +c = Circuit([2, 0, 5]) +print([q.index for q in c.qubits]) # [2, 0, 5] + +c.h(2) +c.cx(2, 5) +``` + +需要注意的是,Cqlib 会保留传入列表中的量子比特顺序。该顺序不仅影响 `c.qubits` 的返回结果,也会影响默认矩阵构造时的量子比特排列方式。除非在矩阵转换时显式指定 `qubits_order`,否则 Cqlib 会按照线路内部保存的 `c.qubits` 顺序解释各个量子比特。 + + +### 3. 使用 `Qubit` 对象 + +除整数索引外,Cqlib 同时支持显式创建 `Qubit` 对象以用于线路构造。`Qubit` 是 `Cqlib` 中表示逻辑量子比特的轻量句柄,主要用于封装非负整数索引。 + +```python +from cqlib import Circuit, Qubit + +q0 = Qubit(10) +q1 = Qubit(11) + +c = Circuit([q0, q1]) +c.h(q0) +c.cx(q0, q1) + +print(q0.index) +print(q0 == Qubit(10)) +``` + +--- + +## 添加量子比特 + +在某些场景下,用户可能需要先构造一条较小的线路,再根据后续算法逻辑、子线路组合或程序化生成结果继续扩展线路宽度。此时可以使用 `add_qubits()` 在线路中添加新的量子比特。 + +`add_qubits()` 会在保留已有操作序列的基础上,将新的量子比特加入当前线路。新增量子比特不会影响已经存在的门操作,但会扩展线路的量子比特列表,并影响后续可添加操作的作用范围以及线路矩阵的维度。 + +```python +from cqlib import Circuit + +c = Circuit(1) +c.h(0) + +c.add_qubits([2, 4]) +print(c.num_qubits) # 3 +print([q.index for q in c.qubits]) # [0, 2, 4] +print(len(c.operations)) # 1 + +c.cx(0, 2) +``` + +在上述示例中,线路最初只包含量子比特 `0`,并已经添加了一个 `H` 门。调用 `add_qubits([2, 4])` 后,线路中新增了索引为 `2` 和 `4` 的量子比特,原有的 `H` 门仍然保留,后续可以继续在新增量子比特上添加操作。 + +使用 `add_qubits()` 时需要注意以下几点: + +- 新增量子比特不能与线路中已有量子比特重复,否则会触发线路结构错误; +- 已有操作不会被重写或重新映射,新增量子比特只影响后续操作; +- 线路的量子比特顺序会影响默认矩阵构造时的比特排列; +- 新增量子比特会扩大线路宽度,因此在线路矩阵转换时,矩阵维度也会随之增加。 + +--- + +## 查询线路信息 + +在构造或调试量子线路时,Cqlib 支持查看线路的基本结构信息,如量子比特数量、已添加的操作、自由参数以及经典句柄命名空间等。`Circuit` 提供了一组常用属性,用于帮助您快速了解当前线路的状态,并为后续参数绑定、线路组合、矩阵转换或 IR 导出提供必要信息。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +c = Circuit(2) +c.rx(0, theta) +c.cx(0, 1) + +print(c.id) # 当前线路的经典句柄命名空间 +print(c.num_qubits) # 2 +print(c.width) # 2 +print(c.qubits) # [Qubit(0), Qubit(1)] +print(c.parameters) # [theta] +print(c.symbols) # ['theta'] +print(c.operations) # [ValueOperation(...), ...] +print(len(c)) # 2 +``` + +其中,`num_qubits` 和 `width` 均表示线路包含的量子比特数量;`qubits` 返回线路中注册的量子比特列表;`operations` 返回按追加顺序排列的操作列表;`len(c)` 返回当前线路中的操作数量。对于包含动态线路结构的场景,`id` 表示当前线路的经典句柄命名空间,用于区分不同线路中的经典变量和测量结果。 + +注意,`parameters` 和 `symbols` 的含义并不完全相同。`parameters` 返回线路中登记的参数表达式对象,而 `symbols` 返回这些表达式中包含的自由符号名称。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +c = Circuit(1) +c.rz(0, 2 * theta + phi) + +print([str(p) for p in c.parameters]) # ['phi + 2*theta'] +print(c.symbols) # ['theta', 'phi'] +``` + +--- + +## 操作序列与索引 + +`Circuit` 会按照操作被追加到线路中的先后顺序维护一条有序操作序列。您可以通过 `operations` 属性查看完整操作列表,也可以使用 `operation(index)` 或 `circuit[index]` 访问指定位置的操作。该机制适用于线路检查、调试、转换器开发以及编译前后的结构对比。 + +```python +from cqlib import Circuit + +c = Circuit(2) +c.h(0) +c.cx(0, 1) + +first = c[0] +second = c.operation(1) + +print(first.instruction.instruction.name) # H +print(second.instruction.instruction.name) # CX +print([q.index for q in second.qubits]) # [0, 1] +``` + +`Circuit` 中的每一项操作通常表示为 `ValueOperation`,它是线路中的完整操作对象,主要包含以下字段: +- `instruction`:操作对应的 `ValueInstruction`,可以表示普通量子门、非幺正指令或经典控制流结构; +- `qubits`:该操作作用的量子比特列表; +- `params`:该次操作携带的参数列表,常用于参数化旋转门、自定义门或复合门; +- `label`:可选的标签信息,通常用于调试、标记线路层、记录转换来源或辅助后续分析。 + +--- + +## 添加操作 + +在线路创建完成后,您可以向 `Circuit` 中持续追加量子门、复合门、非幺正指令或低层操作对象。Cqlib 提供了多种追加操作的方式,以适应不同的使用需求。 + +### 1. 使用便捷门方法 + +使用 `Circuit` 提供的便捷门方法是最常见的线路构造方式。此类方法直接以量子比特索引和必要参数作为输入,语义清晰,代码简洁,适合算法原型和日常量子线路开发。 + +```python +from cqlib import Circuit + +c = Circuit(2) +c.h(0) +c.cx(0, 1) +c.rzz(0, 1, 0.25) +``` + +### 2. 使用显式 gate 对象 + +当需要保存门对象、检查门属性、添加标签,或通过程序逻辑批量生成门操作时,可以先显式构造 `gate` 对象,再通过相应的 `append_*` 方法追加到线路中。 + +```python +from cqlib import Circuit +from cqlib.circuit import MCGate, StandardGate + +c = Circuit(3) +c.append_gate(StandardGate.H(), [0]) +c.append_gate(StandardGate.RZ(0.25), [1], label="rz-calibrated") + +mcx = MCGate(2, StandardGate.X()) +c.append_mc_gate(mcx, [0, 1, 2]) +``` + +常用的显式追加方法如下: + +| 方法 | 用途 | +|---|---| +| `append_gate(gate, qubits, label=None)` | 追加 `StandardGate` | +| `append_mc_gate(gate, qubits, label=None)` | 追加 `MCGate` | +| `append_unitary_gate(gate, qubits, params=None)` | 追加 `UnitaryGate` | +| `append_circuit_gate(gate, qubits, params=None)` | 追加 `CircuitGate` | + +### 3. 使用低层 `ValueOperation` + +在更底层的开发场景中,也可以直接构造并追加完整的 `ValueOperation`。 + +```python +from cqlib import Circuit, Qubit +from cqlib.circuit import StandardGate, ValueOperation + +operation = ValueOperation.from_standard_gate( + StandardGate.RX(0.5), + [Qubit(0)], + label="manual-rx", +) + +c = Circuit(1) +c.append(operation) +print(c[0].label) +``` + +--- + +## 从操作重建线路 + +除常规的逐步构造方式外,Cqlib 还提供了 `Circuit.from_operations()` 接口,用于根据已有的量子比特列表和操作序列重新构造线路。该接口属于较底层的线路构造入口,通常用于从序列化数据、IR 转换结果、编译器中间结果或测试用例中恢复一条完整线路。 + +与直接调用 `h()`、`cx()`、`append_gate()` 等方法逐步追加操作不同,`from_operations()` 适用于已准备好线路所需的量子比特和 `ValueOperation` 操作列表的场景。Cqlib 会基于这些信息重新生成 `Circuit` 对象,并保留原始操作顺序。 + +```python +from cqlib import Circuit + +source = Circuit(2) +source.h(0) +source.cx(0, 1) + +restored = Circuit.from_operations(source.qubits, source.operations) + +print(restored.num_qubits) +print([op.instruction.instruction.name for op in restored.operations]) +``` +--- + +## 组合线路 + +在构造复杂量子程序时,通常需要将多条较小的线路组合成一条完整线路。`compose()` 用于将另一条线路中的操作追加到当前线路末尾,从而实现线路模块之间的顺序组合。 + +默认情况下,`compose()` 会按照两条线路中的量子比特编号进行组合;如果需要将子线路映射到主线路中的特定量子比特位置,可以通过 `qubits` 参数显式指定位置重映射关系。 + +```python +from cqlib import Circuit + +main = Circuit(3) +main.h(0) + +sub = Circuit(2) +sub.cx(0, 1) + +main.compose(sub, [1, 2]) + +print([op.instruction.instruction.name for op in main.operations]) +print([q.index for q in main[1].qubits]) # [1, 2] +``` + +请注意,使用 `compose()` 时需要遵循以下规则: + +- 如果传入 qubits 参数,其长度必须与 other.num_qubits 一致; +- other 中第 i 个量子比特会被映射到 qubits[i] 指定的主线路量子比特; +- 组合后的操作顺序保持为“当前线路已有操作在前,other 的操作在后”; + +--- + +## 子线路封装 + +在构造复杂量子算法时,许多线路片段会被多次复用,例如旋转层、纠缠层、oracle、ansatz block 或特定的算法子模块。对于这类可复用结构,Cqlib支持使用 `to_gate(name)` 将当前线路封装为 `CircuitGate`,再将生成的复合门追加到其他线路中。 + +该复合门可以在主线路的不同量子比特位置多次调用,并且可以在需要时通过 `decompose()` 展开为原始线路操作。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") + +block = Circuit(1) +block.rx(0, theta) +block.rz(0, theta / 2) + +gate = block.to_gate("RotationBlock") + +main = Circuit(2) +main.append_circuit_gate(gate, [0], [0.2]) +main.append_circuit_gate(gate, [1], [0.4]) + +flat = main.decompose() +print([op.instruction.instruction.name for op in flat.operations]) +``` + +--- + +## 全局相位 + +每条 `Circuit` 都包含一个 `global_phase` 属性,用于记录线路整体的全局相位。全局相位不会改变单次测量得到各个计算基态的概率分布,但它是量子线路数学表示的一部分,在矩阵对比、线路等价性判断、编译重写以及某些相位敏感的算法分析中具有重要意义。 + +在 Cqlib 中,`global_phase` 使用 `Parameter` 表示,因此既可以设置为普通数值,也可以设置为符号参数或参数表达式。 + +```python +from cqlib import Circuit, Parameter + +c = Circuit(1) +print(c.global_phase.is_zero()) + +c.set_global_phase(0.25) +print(c.global_phase.evaluate({})) # 0.25 + +alpha = Parameter("alpha") +c.set_global_phase(alpha) +print(c.global_phase) +print(c.symbols) +``` + +--- + +## 下一步 + +- [参数系统](3_parameters.md):学习参数表达式、参数绑定、表达式化简、符号求导和符号矩阵。 +- [线路分析与转换](4_circuit_analysis.md):使用反演、分解、矩阵转换和操作检查等工具。 +- [控制流](5_control_flow.md):使用测量结果、经典变量和结构化控制流构造动态线路。 diff --git a/docs/documentation/1_cqlib/0_circuit/3_parameters.md b/docs/documentation/1_cqlib/0_circuit/3_parameters.md new file mode 100644 index 0000000..ac8728c --- /dev/null +++ b/docs/documentation/1_cqlib/0_circuit/3_parameters.md @@ -0,0 +1,467 @@ +# 参数系统 + +`Parameter` 是 Cqlib 中用于表示数值参数、符号变量和参数表达式的核心类型,常用于描述量子门角度、线路全局相位、自定义符号门以及符号矩阵中的可调元素。通过 `Parameter`,您可以先构造具有符号参数的线路模板,再从后续算法流程中根据不同参数取值进行绑定、求值和验证。 + +`Parameter` 不仅可以表示一个确定的常数,也可以表示一个尚未赋值的符号变量,还可以通过算术运算和数学函数组合成更复杂的表达式。这使得 Cqlib 能够在保持线路结构不变的前提下,对同一条参数化线路执行多次参数绑定、参数扫描、优化迭代或符号分析。 + +--- + +## 创建 `Parameter` + +`Parameter` 可以通过符号名、数值、表达式字符串或内置常量构造器创建。不同创建方式适用于不同场景: +- 符号名适合定义待优化参数; +- 数值适合表示确定角度; +- 表达式字符串适合从配置文件或外部输入中解析参数表达式; +- 常量构造器则用于统一表示 `pi` 和 `e` 等数学常量。 + +### 1. 从符号名创建 + +最常见的方式是使用字符串创建符号参数。符号参数表示一个尚未赋值的变量,通常用于参数化量子门、全局相位、符号矩阵或变分算法中的待优化参数。 + +```python +from cqlib import Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +print(theta.symbols) # ['theta'] +print(theta.as_symbol()) # theta +``` + +需要注意的是,传入的字符串会按照参数表达式进行解析。因此,如果字符串不符合参数表达式语法,Cqlib 会抛出 `ParameterError`,以提示用户检查变量名或表达式格式。 + +### 2. 从数值创建 + +如果参数值已经确定,可以直接使用整数或浮点数创建数值参数。数值参数通常用于固定角度的旋转门、固定全局相位,或在测试与示例中构造确定的参数对象。 + +```python +from cqlib import Parameter + +a = Parameter(1) +b = Parameter(0.25) + +print(a.is_constant()) # True +print(b.evaluate({})) # 0.25 +``` + +由数值创建的 `Parameter` 是常量表达式,不依赖任何符号绑定,因此可以直接求值。 + +### 3. 从表达式字符串创建 + +当参数表达式来自配置文件、用户输入、序列化数据或外部工具时,可以使用 `Parameter.from_expression()` 从字符串解析表达式。 + +```python +from cqlib import Parameter + +expr = Parameter.from_expression("2*x + sin(y) + pi/2") + +print(expr.symbols) # ['x', 'y'] +print(expr.evaluate({"x": 0.1, "y": 0.2})) +``` + +表达式字符串可以包含变量、常量、算术运算和常用数学函数。解析完成后,得到的仍然是一个 `Parameter` 对象,可以继续用于量子门参数、全局相位、符号矩阵或其他参数表达式组合中。 + +当前支持的表达式元素包括: + +- 数字; +- 常量 `pi`、`e`; +- 变量名; +- 运算符 `+`、`-`、`*`、`/`、`**`; +- 括号; +- 函数,如 `sin`、`cos`、`tan`、`exp`、`sqrt`、`ln`、`log` 等。 + +### 4. 常量构造器 + +对于常用数学常量,Cqlib 提供了专门的常量构造器,例如 `Parameter.pi()` 和 `Parameter.e()`。使用这些构造器可以避免手动输入近似浮点数,从而提高表达式的可读性和一致性。 + +```python +from cqlib import Parameter + +pi = Parameter.pi() +e = Parameter.e() + +print(pi.evaluate({})) +print(e.evaluate({})) +``` + +--- + +## 算术表达式 + +`Parameter` 支持常见的 Python 算术运算,您可以像组合普通数值一样组合符号参数、常量和参数表达式。通过这些运算,可以方便地构造门角度、全局相位、符号矩阵元素或算法中需要复用的参数关系。 + +需要注意的是,`Parameter` 表达式是不可变对象。每次算术运算都会返回一个新的 `Parameter`,不会修改原有参数对象。因此,您可以安全地复用同一个符号参数,并基于它构造多个不同的表达式。 + +```python +from cqlib import Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +expr1 = theta + phi +expr2 = 2 * theta - phi / 3 +expr3 = -(theta + 1) +expr4 = theta ** 2 +expr5 = theta.pow(Parameter(0.5)) + +print(expr1) +print(expr2) +print(expr3) +print(expr4) +print(expr5) +``` + +`Parameter` 也支持数值与符号参数混合运算。数字可以出现在表达式的左侧或右侧,Cqlib 会自动将其转换为对应的常量参数表达式。 + + +--- + +## 数学函数 + +除基础算术运算外,`Parameter` 还提供了一组常用数学函数,用于构造更复杂的参数表达式。这些函数可以作用于单个符号参数,也可以作用于由多个参数组合而成的复合表达式,适合描述参数化门角度、符号矩阵元素、算法中的参数变换关系以及需要求导或化简的表达式。 + +常用数学函数如下: + +| 方法 | 说明 | +|---|---| +| `sin()` / `cos()` / `tan()` | 三角函数 | +| `asin()` / `acos()` / `atan()` | 反三角函数 | +| `sinh()` / `cosh()` / `tanh()` | 双曲函数 | +| `exp()` | 指数函数 | +| `ln()` | 自然对数 | +| `log(base=None)` | 对数;当 `base=None` 时,按自然对数处理 | +| `sqrt()` | 平方根 | +| `abs()` | 绝对值 | +| `floor()` / `ceil()` / `round()` | 取整相关函数 | + +```python +from cqlib import Parameter + +x = Parameter("x") +y = Parameter("y") + +expr = (2 * x + y).sin().exp() +grad = expr.derivative("x") + +print(expr) +print(grad) +``` + +上述数学函数支持链式调用,也可以与加减乘除、幂运算等算术操作组合使用。由于每次函数调用都会返回新的 `Parameter` 对象,原始参数不会被修改,因此您可以安全地复用已有符号参数构造多种不同表达式。 + +--- + +## 求值 + +`evaluate(bindings)` 用于将 `Parameter` 表达式计算为具体的浮点数结果。对于包含自由符号的表达式,您需要通过 `bindings` 参数提供每个符号对应的数值;只有当表达式中的所有自由符号都完成绑定后,Cqlib 才能进行数值求值。 + +```python +from cqlib import Parameter + +theta = Parameter("theta") +expr = (2 * theta + 1).sin() + +value = expr.evaluate({"theta": 0.5}) +print(value) +``` + +如果表达式本身不包含自由符号,例如由常量或数学常量构成,则可以传入空字典,也可以省略 `bindings` 参数。 + +```python +from cqlib import Parameter + +expr = Parameter.pi() / 2 + +print(expr.evaluate({})) +print(expr.evaluate()) +``` + +需要注意的是,`evaluate()` 要求表达式能够在实数域内得到有效结果。如果存在以下情况,Cqlib 通常会抛出 `ParameterError`: +- 表达式中存在未绑定的自由符号; +- 绑定字典缺少必要的参数名; +- 绑定值包含 `NaN` 或无穷大等非法数值; +- 表达式在实数域内无法求值。 + +因此,在将参数表达式用于线路矩阵计算、参数绑定或优化器结果验证前,建议先确认所有自由符号均已正确绑定,并确保绑定值位于合法的数值范围内。 + +--- + +## 化简 + +`simplify()` 用于对 `Parameter` 表达式进行代数化简,并返回一个新的参数表达式。该接口常用于减少冗余表达式、规范化参数形式,以及在参数绑定、符号矩阵构造或线路分析前整理表达式结构。 + +需要注意的是,`simplify()` 不会修改原始表达式,而是生成新的 `Parameter` 对象。这样可以保证原始参数表达式在多个线路模板或算法流程中被安全复用。 + +```python +from cqlib import Parameter + +theta = Parameter("theta") + +examples = [ + theta + 0, + theta * 1, + theta - theta, + (Parameter(0)).sin(), + (Parameter(1)).ln(), +] + +for expr in examples: + print(expr, "=>", expr.simplify()) +``` + +--- + +## 求导 + +`derivative(var)` 用于对 `Parameter` 表达式中的指定符号变量进行符号求导,并返回一个新的 `Parameter` 表达式。该接口处理的是经典参数表达式层面的求导关系,适合用于分析门角度、全局相位、符号矩阵元素或其他参数组合关系对某个变量的依赖。 + +```python +from cqlib import Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +expr = (2 * theta + phi).sin() + +d_theta = expr.derivative("theta").simplify() +d_phi = expr.derivative("phi").simplify() + +print(d_theta) +print(d_phi) +print(d_theta.evaluate({"theta": 0.3, "phi": 0.4})) +``` + +需要注意的是,`Parameter.derivative()` 只处理经典符号表达式本身,并不直接计算量子线路关于某个目标函数的梯度。线路整体梯度通常还取决于量子态演化、测量可观测量、模拟器或后端执行结果,以及参数位移规则、伴随法或其他量子梯度计算方法。 + +--- + +## 替换与代入 + +在参数化线路和符号表达式处理中,有时需要将表达式中的某个符号替换为另一个参数表达式。这里,Cqlib 提供了 `replace()` 和 `substitute()` 两类接口,用于对 `Parameter` 表达式进行符号级替换。 + +### 1. `replace()` + +`replace()` 用于替换表达式中的单个符号。您需要指定待替换的符号名称,并提供替换后的 `Parameter` 表达式。 + +```python +from cqlib import Parameter + +x = Parameter("x") +y = Parameter("y") + +expr = x + 2 +new_expr = expr.replace("x", 3 * y) + +print(new_expr) +``` + +### 2. `substitute()` + +`substitute()` 用于同时替换多个符号,适合在一次操作中完成批量符号代入。您可以通过字典指定多个符号与替换表达式之间的对应关系。 + +```python +from cqlib import Parameter + +x = Parameter("x") +y = Parameter("y") +z = Parameter("z") + +expr = x * y + 1 +new_expr = expr.substitute({"x": z + 1, "y": Parameter(2)}) + +print(new_expr.simplify()) +``` + +需要特别注意的是,`replace()` 和 `substitute()` 执行的是符号表达式替换,替换值应为 `Parameter` 对象或可转换为 `Parameter` 的表达式,而非直接用于数值求值的 `float` 结果。 + +--- + +## 等价性判断 + +在参数化线路、符号矩阵和编译优化过程中,有时需要判断两个 `Parameter` 表达式是否表示相同的数学含义。这里,Cqlib 提供了较为保守的等价判断接口。所谓“保守”,是指当接口返回 `True` 时,可以认为两个表达式在当前规则和容差范围内可证明等价;当接口返回 `False` 时,并不一定表示两个表达式必然不等价,也可能只是当前符号规则无法证明它们相等。 + +```python +from cqlib import Parameter + +x = Parameter("x") + +a = (x + 0).simplify() +b = x + +print(a.provably_equal(b)) +``` + +| 方法 | 说明 | +|---|---| +| `provably_equal(other, tolerance=1e-12)` | 在给定容差范围内,保守判断两个表达式是否可以证明相等 | +| `provably_equal_modulo(other, modulus, tolerance=1e-12)` | 在指定模数下判断两个表达式是否等价 | + +需要注意的是,等价性判断通常依赖表达式化简、代数规则和数值容差。对于结构较复杂的表达式,建议先调用 `simplify()` 进行化简,再执行等价性判断。 + +--- + +## 状态检查方法 + +`Parameter` 提供了一组状态检查方法,用于判断表达式是否包含自由符号、是否为常量、是否等于特定数值,或是否可以作为单个符号处理。这些方法常用于参数校验、编译优化、线路规范化、参数绑定前检查以及符号矩阵处理等场景。 + +通过这些接口,您可以在进入矩阵计算、线路转换或后端执行之前,提前确认参数表达式是否满足当前流程的要求。例如,在执行数值矩阵计算前,通常需要确认线路中不存在未绑定符号;在编译优化中,可能需要识别全局相位是否为零,或判断某个旋转角度是否可以被消去。 + +常用状态检查方法如下: + +| 方法 | 说明 | +|---|---| +| `symbols` | 返回表达式中包含的自由符号列表 | +| `canonicalized()` | 返回用于参数驻留和内部比较的规范化表达形式 | +| `is_exact_zero()` | 判断表达式是否精确表示常数 `0` | +| `is_constant()` | 判断表达式是否不包含自由变量 | +| `is_zero()` | 判断表达式在当前条件下是否可求值为 `0` | +| `is_one()` | 判断表达式在当前条件下是否可求值为 `1` | +| `as_symbol()` | 如果表达式恰好是单个符号,则返回该符号名;否则返回 `None` | + +```python +from cqlib import Parameter + +theta = Parameter("theta") +zero = Parameter(0) + +print(theta.is_constant()) # False +print(theta.as_symbol()) # theta +print(zero.is_exact_zero()) # True +print((theta - theta).simplify().is_exact_zero()) +``` + +--- + +## 在线路中使用参数 + +在 Cqlib 中,参数化量子门可以接受普通数值参数或 `Parameter` 表达式作为输入。数值参数适合构造角度已经确定的线路;`Parameter` 表达式则适合构造参数化线路模板,并在后续算法流程中进行参数绑定、参数扫描或优化迭代。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +c = Circuit(2) +c.rx(0, theta) +c.ry(1, phi) +c.rzz(0, 1, 2 * theta - phi) + +print(c.parameters) +print(c.symbols) +``` + +--- + +## 参数绑定 + +参数绑定是指将线路中的符号参数替换为具体数值,生成可以进一步用于矩阵计算、模拟执行、IR 导出或后端运行的线路。Cqlib 通过 `assign_parameters()` 完成参数绑定。该接口接受一个由参数名到数值的映射,如 `dict[str, float]`,并返回绑定后的新线路。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") +phi = Parameter("phi") + +c = Circuit(1) +c.rx(0, theta) +c.rz(0, theta + phi) + +bound = c.assign_parameters({"theta": 0.25, "phi": 0.5}) + +print(bound[0].params) +print(bound[1].params) +print(bound.parameters) +``` + +Cqlib 也支持部分参数绑定。如果绑定字典中只提供了部分符号的取值,则已经绑定的符号会被替换为数值,未绑定的符号会继续保留在线路中。 + +```python +partial = c.assign_parameters({"theta": 0.25}) + +print(partial[0].params) # [0.25] +print(partial[1].params) # [Parameter("0.25 + phi")] +print(partial.symbols) # ['phi'] +``` + +--- + +## 与矩阵转换的关系 + +参数化线路在进行矩阵转换时,分为数值矩阵和符号矩阵两种情况: +- 数值矩阵要求线路中的所有参数都已经绑定为具体数值; +- 符号矩阵则可以保留参数表达式,用于小规模线路的符号分析和验证。 + +### 1. 数值矩阵 + +`to_matrix()` 用于将量子线路转换为数值矩阵。由于数值矩阵中的每个元素都必须是确定的复数值,因此在调用 `to_matrix()` 之前,线路中的所有符号参数都需要完成数值绑定。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") + +c = Circuit(1) +c.rx(0, theta) + +bound = c.assign_parameters({"theta": 0.3}) +matrix = bound.to_matrix() +``` + +### 2. 符号矩阵 + +如果您希望在线路矩阵中保留参数结构,可以使用 `to_symbolic_matrix()`。该接口会生成符号矩阵,其中矩阵元素可以包含 `Parameter` 表达式,适合用于小规模参数化线路验证、符号门定义和编译规则检查。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") + +c = Circuit(1) +c.rz(0, theta) + +symbolic = c.to_symbolic_matrix() +print(symbolic.shape) +print(symbolic.symbols) + +numeric = symbolic.evaluate({"theta": 0.5}) +``` + +--- + +## 在自定义符号门中使用参数 + +Cqlib 支持在自定义符号门中使用参数。通过 `SymbolicComplex` 和 `SymbolicMatrix`,您可以定义包含 `Parameter` 表达式的矩阵,并将该矩阵作为 `UnitaryGate` 的符号矩阵表示。 + +```python +from cqlib import Circuit, Parameter +from cqlib.circuit import SymbolicComplex, SymbolicMatrix, UnitaryGate + +theta = Parameter("theta") + +matrix = SymbolicMatrix( + [ + [SymbolicComplex.one(), SymbolicComplex.zero()], + [SymbolicComplex.zero(), SymbolicComplex.exp_i(theta)], + ] +) + +gate = UnitaryGate("SymbolicPhase", 1, num_params=1).with_symbolic_matrix( + matrix, + ["theta"], +) + +c = Circuit(1) +c.append_unitary_gate(gate, [0], [0.25]) +``` + +需要注意的是,`with_symbolic_matrix(matrix, params)` 中的 `params` 用于定义该自定义门的参数顺序。在线路中调用 `append_unitary_gate()` 时,传入的参数会按照这一顺序与符号矩阵中的参数名称进行绑定。 + +--- + +## 下一步 + +- [线路分析与转换](4_circuit_analysis.md):使用反演、分解、矩阵转换和操作检查等工具。 +- [控制流](5_control_flow.md):使用测量结果、经典变量和结构化控制流构造动态线路。 +- [中间表示](../1_ir/0_overview.md):掌握 Circuit 与 IR 之间的双向转换流程。 + diff --git a/docs/documentation/1_cqlib/0_circuit/4_circuit_analysis.md b/docs/documentation/1_cqlib/0_circuit/4_circuit_analysis.md new file mode 100644 index 0000000..9000e81 --- /dev/null +++ b/docs/documentation/1_cqlib/0_circuit/4_circuit_analysis.md @@ -0,0 +1,258 @@ +# 线路分析与转换 + +完成 Circuit 构造后,线路通常还需要进入进一步的分析、验证与转换流程。您可以根据实际需求检查操作序列、统计门类型、绑定符号参数、展开复合门、生成逆线路、转换为矩阵表示,或对线路结构进行一致性校验。这些能力是连接线路构造、算法验证、IR 转换、编译优化和后端执行的重要基础。 + +本节将系统介绍 `cqlib.circuit` 中与线路分析和结构转换相关的常用接口,帮助您理解一条线路在构造完成后如何被检查、复用、变换和验证。 + +--- + +## 检查操作序列 + +`Circuit.operations` 用于返回线路中的有序 `ValueOperation` 列表。该列表按照操作被追加到线路中以先后顺序保存。 + +每个 `ValueOperation` 都可以进一步拆分为: +- `instruction` 用于描述该操作对应的门、指令或控制流结构; +- `qubits` 表示该操作作用的量子比特; +- `params` 表示本次操作携带的参数; +- `label` 用于记录调试标签或转换来源。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") + +c = Circuit(2) +c.h(0) +c.cx(0, 1) +c.rz(1, theta) + +for index, op in enumerate(c.operations): + if op.instruction.is_instruction: + instruction = op.instruction.instruction + name = instruction.name + else: + name = op.instruction.classical_control.kind + + qubits = [q.index for q in op.qubits] + print(index, name, qubits, op.params, op.label) +``` + +除遍历完整操作列表外,您也可以通过索引访问单个操作。`circuit[i]` 与 `circuit.operation(i)` 都可以用于获取指定位置的 `ValueOperation`。 + +```python +first = c[0] +second = c.operation(1) + +print(first.instruction.instruction.name) # H +print(second.instruction.instruction.name) # CX +``` + +--- + +## 统计线路信息 + +在线路分析和编译优化过程中,通常需要统计线路中的门类型、操作数量或特定指令的出现次数。这类统计信息可用于评估线路规模、比较优化前后的变化,或在测试中验证编译 pass 是否产生了预期结果。 + +在Cqlib中,您可以通过 `Circuit.operations` 实现所需的统计逻辑。由于 `operations` 保存了线路中的完整操作序列,因此可以通过遍历操作列表提取指令名称、作用比特、参数和控制流类型等信息。 + +```python +from collections import Counter +from cqlib import Circuit + +c = Circuit(3) +c.h(0) +c.cx(0, 1) +c.cx(1, 2) +c.rzz(0, 2, 0.5) + +names = [] +for op in c.operations: + if op.instruction.is_instruction: + names.append(op.instruction.instruction.name) + else: + names.append(op.instruction.classical_control.kind) + +print(Counter(names)) +``` + +--- + +## 生成逆线路 + +`inverse()` 用于生成当前线路对应的逆线路。对于一条仅包含可逆量子门的线路,其逆线路表示与原线路相反的量子演化过程。具体而言,Cqlib 会反转原线路中的操作顺序,并将每个操作替换为对应的逆操作,从而得到一条新的 `Circuit`。 + +```python +import numpy as np +from cqlib import Circuit + +c = Circuit(2) +c.h(0) +c.cx(0, 1) +c.rz(1, 0.25) + +inv = c.inverse() + +product = inv.to_matrix() @ c.to_matrix() +print(np.allclose(product, np.eye(4), atol=1e-10)) + +print([op.instruction.instruction.name for op in c.operations]) +print([op.instruction.instruction.name for op in inv.operations]) +``` + +需要注意的是: + +- `inverse()` 不会修改原始线路,而是返回一条新的 `Circuit`; +- `Barrier` 不改变量子态,可视为自身的逆,因此会被保留; +- `Measure`、`Reset`、经典控制流等非可逆结构无法生成普通逆线路。 + +```python +from cqlib import Circuit +from cqlib.circuit import CircuitError + +c = Circuit(1) +c.measure(0) + +try: + c.inverse() +except CircuitError: + print("measurement is not invertible") +``` + +--- + +## 分解复合门 + +`decompose()` 用于展开线路中的复合门,并返回一条新的 `Circuit`。当线路中包含由 `CircuitGate` 表示的复合门时,`decompose()` 会将其替换为该复合门内部定义的原始操作序列。该接口常用于矩阵验证、IR 导出、编译优化以及不支持复合门的后端适配流程。 + +```python +from cqlib import Circuit + +sub = Circuit(2) +sub.h(0) +sub.cx(0, 1) +bell = sub.to_gate("Bell") + +main = Circuit(2) +main.append_circuit_gate(bell, [0, 1]) + +print(len(main)) # 1 + +flat = main.decompose() +print(len(flat)) # 2 +print([op.instruction.instruction.name for op in flat.operations]) +``` + +对于参数化复合门,`decompose()` 会按照追加复合门时传入的位置参数进行绑定和展开。 + +```python +from cqlib import Circuit, Parameter + +theta = Parameter("theta") + +sub = Circuit(1) +sub.rx(0, theta) +block = sub.to_gate("RxBlock") + +main = Circuit(1) +main.append_circuit_gate(block, [0], [0.75]) + +flat = main.decompose() +print(flat[0].params) # [0.75] +``` + +--- + +## 单个操作的矩阵 + +Cqlib 支持对单个 `ValueOperation` 计算矩阵。`ValueOperation.matrix()` 用于获取某一次具体操作对应的矩阵表示,适合在门级测试、编译规则验证、门分解结果检查和局部操作分析中使用。 + +```python +import numpy as np +from cqlib import Qubit +from cqlib.circuit import StandardGate, ValueOperation + +op = ValueOperation.from_standard_gate(StandardGate.X(), [Qubit(0)]) +matrix = op.matrix() + +print(np.allclose(matrix, np.array([[0, 1], [1, 0]], dtype=complex))) +``` + +需要注意的是,该接口仅适用于普通幺正操作。 + +--- + +## 转换为门 + +在量子算法开发中,通常需要将一段已经构造好的线路作为可复用模块,在其他线路中多次调用。`to_gate(name)` 用于将当前线路封装为 `CircuitGate`,从而把一段子线路转换为一个具有名称的复合门。 + +这种方式适合表达结构化算法模块,例如状态制备模块、oracle、ansatz block、纠缠层或重复使用的线路片段。与直接使用 `compose()` 追加线路不同,`to_gate()` 会保留模块边界,使主线路在结构上更加清晰,也便于后续进行复合门分解、参数绑定、IR 导出和编译优化。 + +```python +from cqlib import Circuit + +sub = Circuit(1) +sub.h(0) + +gate = sub.to_gate("HadamardBlock") + +main = Circuit(1) +main.append_circuit_gate(gate, [0]) +``` + +当后续分析流程需要查看复合门内部结构时,可以调用 `decompose()` 将复合门展开为原始操作序列。 + +```python +flat = main.decompose() +print(flat.to_matrix()) +``` + +--- + +## 线路组合与重映射 + +`compose()` 用于将一条线路中的操作追加到另一条线路之后。通过该接口,您可以将多个线路片段按顺序拼接成一条完整线路,也可以在追加过程中对量子比特进行重映射,使子线路中的逻辑比特作用到主线路中的指定比特上。 + +```python +from cqlib import Circuit + +prefix = Circuit(3) +prefix.h(0) + +block = Circuit(2) +block.cx(0, 1) + +prefix.compose(block, [1, 2]) + +print([op.instruction.instruction.name for op in prefix.operations]) +print([q.index for q in prefix[1].qubits]) +``` + +--- + +## 结构校验 + +`validate()` 用于检查线路对象的内部一致性,帮助您在后续分析、转换、编译或执行之前尽早发现潜在结构问题: +- 对于普通静态线路,校验通常关注量子比特引用、操作对象和参数结构是否一致; +- 对于包含测量、经典变量和控制流的动态线路,校验还会进一步检查经典句柄、测量结果引用以及控制流结构是否满足线路语义要求。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr + +c = Circuit(1) +c.measure(0) +c.if_(ClassicalExpr.bool_literal(True), lambda body: body.x(0)) + +c.validate() +``` + +--- + + + +## 下一步 + +- [控制流](5_control_flow.md):使用测量结果、经典变量和结构化控制流构造动态线路。 +- [中间表示](../1_ir/0_overview.md):掌握 Circuit 与 IR 之间的双向转换流程。 +- [QCIS 支持](../1_ir/1_qcis.md):将 Cqlib 线路导出为 QCIS 指令或从 QCIS 文件加载线路。 + diff --git a/docs/documentation/1_cqlib/0_circuit/5_control_flow.md b/docs/documentation/1_cqlib/0_circuit/5_control_flow.md new file mode 100644 index 0000000..f849e01 --- /dev/null +++ b/docs/documentation/1_cqlib/0_circuit/5_control_flow.md @@ -0,0 +1,358 @@ +# 控制流 + +控制流用于描述量子线路中的条件分支、循环结构和多分支选择逻辑。与只按固定顺序执行量子门的普通线路不同,控制流可以在统一的线路表示中保留“在满足某个经典条件时执行某段线路”“按照计数变量重复执行某段线路”或“根据整数值选择不同分支”等结构化语义。 + +通过本节内容,您可以了解如何构造经典表达式,如何使用 `if_`、`if_else`、`while_`、`for_uint` 和 `switch` 等接口组织线路结构,以及如何在构造完成后进行基本校验和结构检查。 + +--- + +## 经典类型 + +`ClassicalType` 用于描述控制流中经典值的类型信息。通过显式的类型定义,您可以在构造线路时检查条件表达式、循环变量、分支目标和普通经典表达式之间的类型是否匹配。 + +常用的经典类型包括以下几类: + +| 类型构造 | 说明 | 典型用途 | +|---|---|---| +| `ClassicalType.bit()` | 单个 bit,取值为 `0` 或 `1` | 单比特经典结果、位表达式 | +| `ClassicalType.bool()` | 逻辑布尔值 | `if`、`while` 条件 | +| `ClassicalType.uint(width)` | 指定位宽无符号整数 | 计数器、循环变量、`switch` 目标 | +| `ClassicalType.bit_vec(width)` | 指定位宽 bit 向量 | 位向量表达式、位打包、位拼接结果 | + +```python +from cqlib.circuit import ClassicalType + +bit_ty = ClassicalType.bit() +bool_ty = ClassicalType.bool() +u3_ty = ClassicalType.uint(3) +bits_ty = ClassicalType.bit_vec(4) + +print(bit_ty.width) # 1 +print(u3_ty.width) # 3 +``` + +类型对象还可以用于创建常用字面量表达式。下列示例创建了 3 位无符号整数类型下的 `0` 和 `1` 字面量: + +```python +zero = ClassicalType.uint(3).zero_literal() +one = ClassicalType.uint(3).one_literal() +``` + +--- + +## 经典变量 + +`Circuit.var(type)` 用于在线路中分配一个可变经典变量。每个经典变量都具有明确的类型信息,如 `Bool`、`UInt` 或 `BitVec`,并且会绑定到创建它的 `Circuit` 对象。 + +经典变量通常用于在控制流中保存和引用经典状态,例如记录分支标志、维护循环计数器,或作为 `switch` 分支判断的目标值。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr, ClassicalType + +c = Circuit(1) + +flag = c.var(ClassicalType.bool()) +counter = c.var(ClassicalType.uint(4)) + +c.store(flag, ClassicalExpr.bool_literal(True)) +c.store(counter, ClassicalExpr.uint_literal(4, 3)) + +print(flag.index) +print(flag.ty) +print(flag.circuit_id == c.id) +``` + +`store(target, value)` 用于向经典变量写入一个经典表达式。写入时,`value` 的类型必须与 `target.ty` 兼容,否则会破坏线路中的经典数据语义。 + +--- + +## 经典表达式 + +`ClassicalExpr` 用于描述控制流中的经典计算逻辑。它可以表示字面量、变量读取、逻辑运算、比较运算、条件选择、位抽取和位拼接等表达式结构。 + +您可以将 `ClassicalExpr` 理解为一类“经典表达式语法树”。它本身不立即执行计算,而是记录经典计算关系,并作为线路结构的一部分参与后续校验、转换和编译处理。 + +### 1. 字面量 + +字面量用于直接构造固定的经典值,例如布尔值、单个 bit、无符号整数或 bit 向量。 + +```python +from cqlib.circuit import ClassicalExpr + +truth = ClassicalExpr.bool_literal(True) +bit_one = ClassicalExpr.bit_literal(True) +u3_five = ClassicalExpr.uint_literal(3, 5) +bits = ClassicalExpr.bit_vec_literal(4, 0b1010) +``` + +其中,`uint_literal(width, value)` 和 `bit_vec_literal(width, value)` 需要显式指定位宽。位宽是经典类型的一部分,会影响后续比较、循环和拼接操作的合法性。 + +### 2. 从变量读取 + +经典变量本身是一个可写入、可引用的句柄。如果需要在表达式中读取变量的当前值,可以使用 `ClassicalExpr.var(var)` 将变量转换为表达式,也可以直接调用变量句柄提供的 `expr()` 方法。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr, ClassicalType + +c = Circuit(1) + +flag = c.var(ClassicalType.bool()) +flag_expr = ClassicalExpr.var(flag) + +# 等价的便捷写法 +same_flag_expr = flag.expr() +``` + +### 3. 逻辑运算 + +`ClassicalExpr` 支持常用逻辑运算。运算符 `~`、`&`、`|`、`^` 分别对应逻辑非、与、或和异或。 + +```python +from cqlib.circuit import ClassicalExpr + +a = ClassicalExpr.bool_literal(True) +b = ClassicalExpr.bool_literal(False) + +expr = (a & ~b) | b +print(expr.simplified()) +``` + +也可以使用方法形式构造同样的表达式: + +```python +expr = a.and_(b.not_()).or_(b) +``` + +### 4. 比较 + +若要构造比较条件,应使用 `ClassicalExpr` 提供的静态方法。 + +```python +from cqlib.circuit import ClassicalExpr + +x = ClassicalExpr.uint_literal(3, 2) +y = ClassicalExpr.uint_literal(3, 5) + +cond1 = ClassicalExpr.lt(x, y) +cond2 = ClassicalExpr.equal(x, y) +cond3 = ClassicalExpr.not_equal(x, y) +cond4 = ClassicalExpr.ge(y, x) +``` + +支持的比较方法包括: + +- `equal(lhs, rhs)` +- `not_equal(lhs, rhs)` +- `lt(lhs, rhs)` +- `le(lhs, rhs)` +- `gt(lhs, rhs)` +- `ge(lhs, rhs)` + +比较结果通常是 `Bool` 表达式,可以直接作为 `if_`、`if_else` 或 `while_` 的条件。 + +### 5. 选择、抽取和拼接 + +除基础逻辑和比较运算外,`ClassicalExpr` 还支持条件选择、位抽取和位拼接等操作,可用于构造更复杂的经典控制逻辑。 + +```python +from cqlib.circuit import ClassicalExpr + +cond = ClassicalExpr.bool_literal(True) +a = ClassicalExpr.uint_literal(3, 1) +b = ClassicalExpr.uint_literal(3, 7) + +selected = ClassicalExpr.select(cond, a, b) + +bits = ClassicalExpr.bit_vec_literal(4, 0b1010) +low_bit = bits.extract_bit(0) +middle = bits.extract_bits(offset=1, width=2) + +packed = ClassicalExpr.pack_bits( + [ + ClassicalExpr.bit_literal(True), + ClassicalExpr.bit_literal(False), + ] +) +``` + +其中: +- `select(cond, a, b)` 表示根据布尔条件在两个表达式之间进行选择:当 `cond` 为真时选择 `a`,否则选择 `b`; +- `extract_bit()` 用于从 `BitVec` 中抽取单个 bit,`extract_bits()` 用于抽取连续的多位片段; +- `pack_bits(bits)` 用于将多个 Bit 表达式打包为一个 BitVec 表达式。 + +--- + +## `if_` 条件分支 + +`if_(condition, body)` 用于在线路中追加一个不包含 `else` 分支的条件控制结构。该接口表示当给定条件满足时,执行分支体中的线路操作;当条件不满足时,跳过该分支体。 + +其中: +- `condition` 必须是 `Bool` 类型的 `ClassicalExpr` 表达式,用于描述条件判断逻辑; +- `body` 是一个回调函数,用于构造条件满足时需要执行的线路内容。回调函数会接收一个临时线路构造器,您可以在其中继续追加量子门或其他受支持的操作。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr + +c = Circuit(1) + +c.if_( + ClassicalExpr.bool_literal(True), + lambda body: body.x(0), +) +``` + +对于只包含一条操作的简单分支,您可以使用 `lambda` 写法,使代码更加简洁。对于包含多条操作的分支体,建议使用普通函数定义,以提高代码可读性和后续维护性。 + +```python +def then_body(body): + body.h(0) + body.x(0) + +c.if_(ClassicalExpr.bool_literal(True), then_body) +``` + +控制流回调采用原子化构造方式。也就是说,只有当回调函数完整执行成功后,分支体才会被提交到线路中;如果回调执行过程中抛出异常,Cqlib 会放弃本次分支体构造,避免在线路中留下不完整或不一致的控制流结构。 + +```python +def failing_body(body): + body.x(0) + raise RuntimeError("construction failed") + +try: + c.if_(ClassicalExpr.bool_literal(True), failing_body) +except RuntimeError: + pass +``` + +--- + +## `if_else` 条件分支 + +`if_else(condition, then_body, else_body)` 用于在线路中构造带有 `else` 分支的条件控制结构。该接口表示当 `condition` 条件为真时,执行 `then_body` 分支;当条件为假时,执行 `else_body` 分支。两个分支均通过回调函数构造,并作为同一个条件控制结构的一部分保存在线路中。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr + +c = Circuit(1) + +c.if_else( + ClassicalExpr.bool_literal(False), + lambda then_body: then_body.x(0), + lambda else_body: else_body.z(0), +) + +control = c[0].instruction.classical_control +print(control.kind) # if +``` + +与 `if_` 类似,`if_else` 的两个分支也采用原子化构造方式。只有当两个回调函数都成功完成构造后,相关控制流结构才会被提交到线路中;如果任一分支在构造过程中抛出异常,本次控制流构造会被放弃,避免在线路中留下不完整或不一致的分支结构。 + +--- + +## `while_` 循环 + +`while_(condition, body)` 用于在线路中构造基于布尔条件的循环结构。该接口表示当 `condition` 条件满足时,执行循环体 `body` 中定义的操作;每轮循环开始前都会根据条件表达式判断是否继续执行。 + +其中: +- `condition` 必须是 `Bool` 类型的 `ClassicalExpr` 表达式; +- `body` 是一个回调函数,用于描述循环体中的线路操作。回调函数会接收一个临时线路构造器,用户可以在其中追加量子门、控制流跳转或其他受支持的操作。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr + +c = Circuit(1) + +c.while_( + ClassicalExpr.bool_literal(True), + lambda body: body.break_loop(), +) +``` + +在循环体中,可以使用以下跳转操作控制循环执行流程: + +- `break_loop()`:退出最近一层循环; +- `continue_loop()`:跳过当前循环体中剩余操作,进入最近一层循环的下一轮判断。 + +```python +def loop_body(body): + body.x(0) + body.continue_loop() + +c = Circuit(1) +c.while_(ClassicalExpr.bool_literal(True), loop_body) +``` + +--- + +## `for_uint` 循环 + +`for_uint(var, start, stop, step, body)` 用于在线路中构造基于无符号整数变量的循环结构,循环范围为 `[start, stop)`:从 `start` 开始,每轮按照 `step` 递增,当循环变量达到或超过 `stop` 时结束循环。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr, ClassicalType + +c = Circuit(1) +loop_var = c.var(ClassicalType.uint(3)) + +def body(builder, index_expr): + # index_expr 是读取 loop_var 的 UInt 表达式 + builder.rx(0, 0.25) + +c.for_uint( + loop_var, + ClassicalExpr.uint_literal(3, 0), + ClassicalExpr.uint_literal(3, 3), + ClassicalExpr.uint_literal(3, 1), + body, +) +``` + +--- + +## `switch` 多分支选择 + +`switch(target, build)` 用于在线路中构造基于整数值匹配的多分支控制结构。该接口会根据 `target` 表达式的取值,在多个已注册分支中选择一个进行执行。`build` 是一个回调函数,用于注册不同的整数匹配分支以及可选的默认分支。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr + +c = Circuit(1) + +def build_switch(builder): + builder.value(0, lambda body: body.x(0)) + builder.value(1, lambda body: body.z(0)) + builder.default(lambda body: body.h(0)) + +c.switch(ClassicalExpr.uint_literal(2, 1), build_switch) +``` + +--- + +## 校验与作用域 + +与普通线性线路相比,控制流结构对线路内部一致性的要求更高。因此,在构造包含控制流的线路后,建议调用 `validate()` 对线路进行结构校验。 + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr + +c = Circuit(1) +c.if_(ClassicalExpr.bool_literal(True), lambda body: body.x(0)) + +c.validate() +``` + +--- + +## 下一步 + +- [中间表示](../1_ir/0_overview.md):掌握 Circuit 与 IR 之间的双向转换流程,支持线路持久化、跨工具链交换和后续编译处理。 +- [QCIS 支持](../1_ir/1_qcis.md):将 Cqlib 线路导出为 QCIS 指令或从 QCIS 文件加载线路。 +- [OpenQASM 2.0 支持](../1_ir/2_qasm2.md):实现 Cqlib 线路与 OpenQASM 2.0 格式之间的导入导出。 \ No newline at end of file diff --git a/docs/documentation/1_cqlib/1_ir/0_overview.md b/docs/documentation/1_cqlib/1_ir/0_overview.md new file mode 100644 index 0000000..f983753 --- /dev/null +++ b/docs/documentation/1_cqlib/1_ir/0_overview.md @@ -0,0 +1,125 @@ +# IR 中间表示总览 + +IR(Intermediate Representation,中间表示)负责把 Cqlib 内部的 `Circuit` 电路对象和外部文本格式连接起来。它不是一个单独的算法模块,而是量子程序在“构建、保存、跨框架交换、硬件提交、调试复现”之间流转的协议层。 + +从用户视角看,IR 模块主要解决四类问题: + +- 将 Cqlib 构建的 `Circuit` 导出为标准或硬件相关的文本格式。 +- 将外部工具生成的量子线路文本导入为 Cqlib `Circuit`。 +- 在 QCIS、OpenQASM 2.0、OpenQASM 3.0 之间做格式转换。 +- 为后续编译优化、可视化、仿真、硬件适配提供统一入口。 + +## 1. Cqlib 的 IR 工作模型 + +Cqlib 的内部核心对象是 `Circuit`。外部文本格式(例如 QCIS 或 OpenQASM)不会绕过 `Circuit` 直接进入模拟器或编译器,而是先被解析成 `Circuit`,后续所有模块都基于同一套电路对象工作。 + +```mermaid +flowchart LR + A["QCIS 文本"] --> D["cqlib.ir.qcis.loads/load"] + B["OpenQASM 2.0 文本"] --> E["cqlib.ir.qasm2.loads/load"] + C["OpenQASM 3.0 文本"] --> F["cqlib.ir.qasm3.loads/load"] + D --> G["Cqlib Circuit"] + E --> G + F --> G + G --> H["编译优化 / 可视化 / 仿真 / 硬件适配"] + G --> I["qcis.dumps/dump"] + G --> J["qasm2.dumps/dump"] + G --> K["qasm3.dumps/dump"] +``` + +这意味着 IR 的核心语义是: + +- `load/loads`:外部文本格式转换为 `Circuit`。 +- `dump/dumps`:`Circuit` 转换为外部文本格式。 +- 格式转换本质上是 `格式 A -> Circuit -> 格式 B`。 +- 如果某个外部格式表达能力不足,导出时会返回明确错误,而不是静默丢失语义。 + +## 2. 支持的格式 + +| 格式 | Python 模块 | 主要用途 | 适合场景 | 注意事项 | +|---|---|---|---|---| +| QCIS | `cqlib.ir.qcis` | 面向硬件控制指令的线路文本 | 国产量子硬件接入、低层指令交换、QCIS 文件解析 | 表达能力偏硬件指令层,不适合复杂经典控制流 | +| OpenQASM 2.0 | `cqlib.ir.qasm2` | 经典量子汇编标准 | 标准线路文件、基准线路、旧版 OpenQASM 工具链互通 | 经典语义有限,主要支持 `if (creg == int) qop` | +| OpenQASM 3.0 | `cqlib.ir.qasm3` | 现代量子程序文本格式 | 动态线路、测量赋值、经典变量、控制流、现代 OpenQASM 工具链互通 | 支持子集以 Cqlib `Circuit` 可表达能力为边界 | + +## 3. 统一 API + +三个 IR 子模块都采用同一套 API 命名:字符串用 `loads/dumps`,文件用 `load/dump`。 + +| 函数 | 输入 | 输出 | 用途 | +|---|---|---|---| +| `loads(text)` | 格式文本字符串 | `Circuit` | 从字符串解析电路 | +| `load(path)` | 文件路径 | `Circuit` | 从文件解析电路 | +| `dumps(circuit)` | `Circuit` | 字符串 | 将电路导出为文本 | +| `dump(circuit, path)` | `Circuit`、文件路径 | `None` | 将电路写入文件 | + +示例: + +```python +from cqlib import Circuit +from cqlib.ir import qasm2, qasm3, qcis + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +qasm2_text = qasm2.dumps(circuit) +qasm3_text = qasm3.dumps(circuit) +qcis_text = qcis.dumps(circuit) + +restored = qasm3.loads(qasm3_text) +print(restored.num_qubits) +``` + +## 4. Cqlib IR 的测量与经典数据模型 + +Cqlib 的 `Circuit` 支持动态线路,因此测量不是简单的“画一个测量门”。内部会区分两类经典数据: + +| 概念 | 含义 | 常见来源 | 是否用户可写 | +|---|---|---|---| +| `ClassicalValue` | 测量产生的不可变临时结果 | `circuit.measure()`、`measure_bits()` | 否 | +| `ClassicalVar` | 用户可变经典存储 | `circuit.var(...)` | 是 | + +例如: + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalType + +circuit = Circuit(2) + +# 只产生一个测量结果值,不指定用户变量。 +measurement = circuit.measure(0) + +# 创建用户可见的 classical bit,并把测量结果写入其中。 +bit = circuit.var(ClassicalType.bit()) +circuit.measure_into(1, bit) +``` + +导出为 OpenQASM 时,Cqlib 会尽量把内部测量值折叠为目标格式的自然写法: + +| Cqlib 操作 | OpenQASM 3 输出倾向 | +|---|---| +| `measure_into(q, bit)` | `c0 = measure q[0];` | +| `measure_bits_into([0, 1], bitvec)` | `c0 = measure q;` | +| 裸测量 `measure(q)` | 生成 `bit[n] meas;` 并写入 `meas[i]`,保证文本可读回 | +| 部分寄存器测量 | 输出 `c0[i] = measure q[j];` 形式,保留顺序 | + +这部分是 Cqlib 当前 IR 修改的重点之一:导出 QASM3 时不会再把内部临时值直接暴露成无意义的 `v0/v1`,而是生成可读回、语义明确的测量目标。 + +## 5. 选择哪种格式 + +| 目标 | 推荐格式 | 原因 | +|---|---|---| +| 与公开数据集或通用工具互通 | OpenQASM 2.0 或 3.0 | 生态通用,工具支持多 | +| 表达现代动态线路、测量赋值、经典变量 | OpenQASM 3.0 | 语义比 2.0 完整 | +| 面向硬件指令或 QCIS 文件交付 | QCIS | 更接近硬件指令格式 | +| 只是保存简单线路并被旧工具读取 | OpenQASM 2.0 | 简单稳定 | +| 保留 Cqlib 特有动态语义 | 优先 OpenQASM 3.0 | 表达能力更接近 `Circuit` | + +## 6. 推荐学习顺序 + +1. 先阅读 [QCIS 支持](1_qcis.md),理解硬件指令风格文本。 +2. 再阅读 [OpenQASM 2.0 支持](2_qasm2.md),掌握最常见的标准格式。 +3. 然后阅读 [OpenQASM 3.0 支持](3_qasm3.md),理解测量、经典变量和动态线路。 +4. 最后阅读 [格式转换工作流](4_conversion_workflow.md),学习如何做跨格式互通和验证。 diff --git a/docs/documentation/1_cqlib/1_ir/1_qcis.md b/docs/documentation/1_cqlib/1_ir/1_qcis.md new file mode 100644 index 0000000..ab0241e --- /dev/null +++ b/docs/documentation/1_cqlib/1_ir/1_qcis.md @@ -0,0 +1,218 @@ +# QCIS 支持 + +QCIS 是面向硬件指令和工程交付的量子线路文本格式。它以“每行一条指令”的方式描述量子操作,适合把 Cqlib 线路导出到硬件相关工具链,或者从已有 QCIS 文件中恢复 `Circuit` 进行仿真、可视化和二次编译。 + +对应 Python 模块: + +```python +from cqlib.ir import qcis +``` + +## 1. API 总览 + +| 函数 | 用途 | 示例 | +|---|---|---| +| `qcis.loads(text)` | 从 QCIS 字符串解析 `Circuit` | `circuit = qcis.loads("H Q0\nM Q0\n")` | +| `qcis.load(path)` | 从 QCIS 文件解析 `Circuit` | `circuit = qcis.load("input.qcis")` | +| `qcis.dumps(circuit)` | 将 `Circuit` 导出为 QCIS 字符串 | `text = qcis.dumps(circuit)` | +| `qcis.dump(circuit, path)` | 将 `Circuit` 写入 QCIS 文件 | `qcis.dump(circuit, "output.qcis")` | + +## 2. QCIS 文本结构 + +QCIS 是行式格式。每行通常由三部分构成: + +```text +OPCODE QUBIT_LIST [PARAMETER_LIST] +``` + +示例: + +```text +H Q0 +CZ Q0 Q1 +RZ Q0 pi/2 +M Q0 Q1 +``` + +含义: + +- `H Q0`:在 `Q0` 上作用 Hadamard 门。 +- `CZ Q0 Q1`:在 `Q0`、`Q1` 上作用 CZ 门。 +- `RZ Q0 pi/2`:在 `Q0` 上作用参数为 `pi/2` 的 RZ 门。 +- `M Q0 Q1`:测量 `Q0`、`Q1`。 + +QCIS 支持以 `//` 开头的注释,也支持行内注释。 + +```text +// prepare Bell state +H Q0 +CZ Q0 Q1 // entangle Q0 and Q1 +``` + +## 3. 从 QCIS 字符串加载 + +```python +from cqlib.ir import qcis + +qcis_code = """ +H Q0 +CZ Q0 Q1 +RZ Q0 pi/2 +M Q0 Q1 +""" + +circuit = qcis.loads(qcis_code) +print(circuit.num_qubits) +print(len(circuit.operations)) +``` + +加载后的对象是标准 Cqlib `Circuit`,可以继续用于可视化、仿真、编译优化或再次导出。 + +## 4. 从 QCIS 文件加载 + +```python +from cqlib.ir import qcis + +circuit = qcis.load("input.qcis") +``` + +如果文件不存在,会抛出 I/O 相关异常;如果 QCIS 内容语法错误或门参数不匹配,会抛出 `ValueError`。 + +## 5. 导出 QCIS 字符串 + +```python +from cqlib import Circuit +from cqlib.ir import qcis + +circuit = Circuit(2) +circuit.h(0) +circuit.cz(0, 1) +circuit.rz(0, 3.141592653589793 / 2) +circuit.measure(0) +circuit.measure(1) + +text = qcis.dumps(circuit) +print(text) +``` + +典型输出: + +```text +H Q0 +CZ Q0 Q1 +RZ Q0 pi/2 +M Q0 +M Q1 +``` + +## 6. 导出 QCIS 文件 + +```python +from cqlib.ir import qcis + +qcis.dump(circuit, "output.qcis") +``` + +写文件失败时会抛出 I/O 异常;如果线路包含 QCIS 无法表示的指令,会抛出 `ValueError`。 + +## 7. 支持的指令类型 + +当前 QCIS 模块覆盖 Cqlib 中可表示为 QCIS 文本的标准门和指令。 + +| 类型 | 支持内容 | +|---|---| +| 单量子比特门 | `H`, `S`, `SD`, `T`, `TD`, `X`, `X2P`, `X2M`, `Y`, `Y2P`, `Y2M`, `Z` | +| 参数化单比特门 | `RX`, `RY`, `RZ`, `RXY`, `U`, `XY`, `XY2P`, `XY2M`, `PHASE` | +| 多量子比特门 | `CX`, `CY`, `CZ`, `SWAP`, `CCX`, `CRX`, `CRY`, `CRZ`, `RXX`, `RYY`, `RZZ`, `RZX`, `FSIM` | +| 指令 | `M` 测量、`B`/`Barrier` 屏障 | +| 延迟 | `I Qn t`,表示在 `Qn` 上延迟 `t` 个 tick | + +别名规则: + +- `SDG` 可被加载,导出时规范化为 `SD`。 +- `TDG` 可被加载,导出时规范化为 `TD`。 + +## 8. QCIS 中的 `I` 不是普通恒等门 + +QCIS 的 `I Qn t` 表示延迟指令,不是 Cqlib 标准门里的 identity gate。 + +```text +I Q0 10 +``` + +含义是在 `Q0` 上空闲指定时长。Cqlib 加载后会把它表示为 `Delay` 指令。导出时,如果用户在线路中直接放了普通恒等门,QCIS dumper 会拒绝导出,避免把“无时长的恒等门”错误解释为“有时长的硬件延迟”。 + +## 9. 测量语义 + +QCIS 只描述测量指令本身,不描述 OpenQASM 那种显式 classical register 赋值。因此: + +```text +M Q0 Q1 +``` + +加载到 Cqlib 后,会变成 `Circuit` 中的测量操作;如果后续导出为 OpenQASM 3,Cqlib 会自动补充可读回的 classical 目标,例如: + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; +bit[2] meas; + +meas[0] = measure q[0]; +meas[1] = measure q[1]; +``` + +这一步是格式转换边界的正常行为:QCIS 没有 classical 赋值语法,OpenQASM 3 有,因此 Cqlib 在导出时补齐了目标寄存器。 + +## 10. QCIS 到 OpenQASM 的转换 + +```python +from cqlib.ir import qcis, qasm3 + +qcis_code = """ +H Q0 +CZ Q0 Q1 +M Q0 Q1 +""" + +circuit = qcis.loads(qcis_code) +qasm3_text = qasm3.dumps(circuit) +print(qasm3_text) +``` + +这个流程适合把硬件侧 QCIS 文件转换成更通用的 OpenQASM 3 文本,然后交给其他支持 OpenQASM 的工具读取。 + +## 11. 不支持或需要先处理的情况 + +QCIS 是硬件指令风格格式,不适合表达所有高级线路语义。以下内容通常不能直接导出为 QCIS: + +- 任意矩阵形式的 `UnitaryGate`。 +- 用户自定义 `CircuitGate`,除非先分解为 QCIS 支持的基础门。 +- 多控制门的泛化形式,除非已经被分解。 +- `if/else`、`for`、`while`、`switch` 等复杂经典控制流。 +- 标准恒等门和全局相位 `GPhase`。 + +推荐处理方式: + +```python +compiled = circuit.decompose() +text = qcis.dumps(compiled) +``` + +如果仍然失败,说明分解后的线路中仍包含 QCIS 无法表达的指令,需要先经过编译优化或目标门集映射。 + +## 12. 常见错误排查 + +| 现象 | 常见原因 | 处理方式 | +|---|---|---| +| `ValueError: QCIS parse error` | 文本中存在未知门、量子比特格式错误或参数数量不匹配 | 检查是否使用 `Q0` 形式,检查每个门的参数数量 | +| 导出时报 unsupported gate | `Circuit` 中包含 QCIS 不支持的门或控制流 | 先 `decompose()`,再做目标门集映射 | +| `I` 指令行为和预期不同 | QCIS 的 `I` 是 delay,不是普通 identity | 如果只是普通恒等门,不建议导出为 QCIS | +| Qubit 数量看起来变化 | Cqlib 根据实际出现的最大 qubit 编号构造线路 | 检查 QCIS 文件是否从 `Q1` 开始而没有 `Q0` | + +## 下一步 + +- [OpenQASM 2.0 支持](2_qasm2.md) +- [OpenQASM 3.0 支持](3_qasm3.md) +- [格式转换工作流](4_conversion_workflow.md) diff --git a/docs/documentation/1_cqlib/1_ir/2_qasm2.md b/docs/documentation/1_cqlib/1_ir/2_qasm2.md new file mode 100644 index 0000000..7536328 --- /dev/null +++ b/docs/documentation/1_cqlib/1_ir/2_qasm2.md @@ -0,0 +1,223 @@ +# OpenQASM 2.0 支持 + +OpenQASM 2.0 是量子计算生态中使用非常广泛的线路交换格式。它适合描述以量子门、寄存器、测量和简单 classical condition 为主的静态线路,也是很多公开 benchmark 和老工具链默认支持的格式。 + +对应 Python 模块: + +```python +from cqlib.ir import qasm2 +``` + +## 1. API 总览 + +| 函数 | 用途 | 示例 | +|---|---|---| +| `qasm2.loads(text)` | 从 OpenQASM 2.0 字符串解析 `Circuit` | `circuit = qasm2.loads(qasm_text)` | +| `qasm2.load(path)` | 从 OpenQASM 2.0 文件解析 `Circuit` | `circuit = qasm2.load("input.qasm")` | +| `qasm2.dumps(circuit)` | 将 `Circuit` 导出为 OpenQASM 2.0 字符串 | `text = qasm2.dumps(circuit)` | +| `qasm2.dump(circuit, path)` | 将 `Circuit` 写入 OpenQASM 2.0 文件 | `qasm2.dump(circuit, "output.qasm")` | + +## 2. 最小 OpenQASM 2.0 程序 + +```text +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[2]; +h q[0]; +cx q[0],q[1]; +``` + +在 Cqlib 中加载: + +```python +from cqlib.ir import qasm2 + +qasm_code = """ +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[2]; +h q[0]; +cx q[0],q[1]; +""" + +circuit = qasm2.loads(qasm_code) +print(circuit.num_qubits) +print(len(circuit.operations)) +``` + +## 3. `qelib1.inc` 的处理 + +OpenQASM 2.0 常见程序会包含: + +```text +include "qelib1.inc"; +``` + +Cqlib 对 `qelib1.inc` 做了内置处理,不需要本地存在这个文件。即使省略 include,Cqlib 也能识别常见 qelib1 门名;但为了与 OpenQASM 2.0 工具链兼容,导出时仍会规范地写出: + +```text +include "qelib1.inc"; +``` + +## 4. 从 Cqlib 导出 OpenQASM 2.0 + +```python +from cqlib import Circuit +from cqlib.ir import qasm2 + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +text = qasm2.dumps(circuit) +print(text) +``` + +典型输出: + +```text +// This file is auto-generated by Cqlib v0.1.0. +OPENQASM 2.0; +include "qelib1.inc"; + +qreg q[2]; + +h q[0]; +cx q[0],q[1]; +``` + +输出会被规范化,不保证保留输入文件的空格、注释、寄存器名称或排版。 + +## 5. 文件读写 + +```python +from cqlib.ir import qasm2 + +circuit = qasm2.load("input.qasm") +qasm2.dump(circuit, "output.qasm") +``` + +`load` 适合读取外部 `.qasm` 文件;`dump` 适合把 Cqlib 线路保存成文件用于提交、归档或传给其他工具。 + +## 6. 参数表达式 + +Cqlib 支持 OpenQASM 2.0 常用参数表达式,例如: + +```text +rx(pi) q[0]; +ry(pi/2) q[0]; +rz(3*pi/4) q[0]; +``` + +加载示例: + +```python +from cqlib.ir import qasm2 + +qasm_code = """ +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[1]; +rx(pi) q[0]; +ry(pi/2) q[0]; +rz(3*pi/4) q[0]; +""" + +circuit = qasm2.loads(qasm_code) +``` + +当前 loader 还接受部分工程中常见的表达式扩展,例如不带小数点的科学计数法 `1e-5`,以及 `asin`、`acos`、`atan` 等函数。 + +## 7. 测量 + +OpenQASM 2.0 使用 `creg` 表示经典寄存器,测量语法是: + +```text +measure q[0] -> c[0]; +``` + +完整示例: + +```python +from cqlib.ir import qasm2 + +qasm_code = """ +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[2]; +creg c[2]; +h q[0]; +cx q[0],q[1]; +measure q[0] -> c[0]; +measure q[1] -> c[1]; +""" + +circuit = qasm2.loads(qasm_code) +``` + +导出时,Cqlib 会把内部测量值映射为 OpenQASM 2.0 可表示的 `creg` 写法。对于 `measure_bits_into([0, 1], c)` 这类多比特测量,会展开为逐比特测量: + +```text +measure q[0] -> c0[0]; +measure q[1] -> c0[1]; +``` + +## 8. 自定义门 + +OpenQASM 2.0 支持 `gate` 定义: + +```text +OPENQASM 2.0; +include "qelib1.inc"; +gate bell a,b { + h a; + cx a,b; +} +qreg q[2]; +bell q[0],q[1]; +``` + +Cqlib 加载时会把自定义门转换为 `CircuitGate`。导出时,如果 `Circuit` 中包含可展开为 OpenQASM 2.0 的自定义线路门,也会生成对应 `gate` 定义。 + +## 9. 条件执行 + +OpenQASM 2.0 支持有限的 classical condition: + +```text +if (c == 1) x q[0]; +``` + +Cqlib 支持这种寄存器整体等于整数的条件形式。需要注意: + +- OpenQASM 2.0 不支持 `else`。 +- OpenQASM 2.0 不支持通用 `while/for/switch`。 +- Cqlib 导出到 QASM2 时会拒绝无法无损表达的复杂控制流。 + +如果线路包含复杂动态语义,优先使用 OpenQASM 3.0。 + +## 10. 支持与限制 + +| 能力 | 加载 | 导出 | 说明 | +|---|---|---|---| +| `qreg`、`creg` | 支持 | 支持 | 导出时寄存器名会规范化 | +| qelib1 标准门 | 支持 | 支持 | `qelib1.inc` 内置处理 | +| 参数化门 | 支持 | 支持 | 支持常见表达式 | +| 自定义 `gate` | 支持 | 支持 | 必须可表示为 OpenQASM 2.0 门体 | +| `measure`、`reset`、`barrier` | 支持 | 支持 | 部分复杂组合会被规范化 | +| `if (creg == int) qop` | 支持 | 支持 | OpenQASM 2.0 的有限条件 | +| `else/while/for/switch` | 不适合 | 不支持无损导出 | 推荐 OpenQASM 3.0 | +| 通用 classical assignment | 不适合 | 不支持 | OpenQASM 2.0 表达能力不足 | + +## 11. 常见错误排查 + +| 现象 | 常见原因 | 处理方式 | +|---|---|---| +| 解析失败 | 漏写 `OPENQASM 2.0;`、语法错误、门参数数量不匹配 | 先用最小示例确认格式,再逐段加入门 | +| 导出失败 | 线路包含 OpenQASM 2.0 无法表示的控制流或指令 | 改用 QASM3,或先分解/移除复杂语义 | +| 其他工具读入后门名不同 | Cqlib 会规范化部分门,例如 `U1/U2/U3` 映射 | 以矩阵或线路语义验证,不要只看文本门名 | +| 测量寄存器名称变化 | 导出器会生成规范寄存器名 | 这是正常行为,不影响测量语义 | + +## 下一步 + +- [OpenQASM 3.0 支持](3_qasm3.md) +- [格式转换工作流](4_conversion_workflow.md) diff --git a/docs/documentation/1_cqlib/1_ir/3_qasm3.md b/docs/documentation/1_cqlib/1_ir/3_qasm3.md new file mode 100644 index 0000000..4bce40b --- /dev/null +++ b/docs/documentation/1_cqlib/1_ir/3_qasm3.md @@ -0,0 +1,276 @@ +# OpenQASM 3.0 支持 + +OpenQASM 3.0 面向现代量子程序设计,比 OpenQASM 2.0 更适合表达测量赋值、经典变量、控制流和更完整的动态线路语义。Cqlib 的 `qasm3` 模块负责在 OpenQASM 3.0 文本和 Cqlib `Circuit` 之间双向转换。 + +对应 Python 模块: + +```python +from cqlib.ir import qasm3 +``` + +## 1. API 总览 + +| 函数 | 用途 | 示例 | +|---|---|---| +| `qasm3.loads(text)` | 从 OpenQASM 3.0 字符串解析 `Circuit` | `circuit = qasm3.loads(qasm_text)` | +| `qasm3.load(path)` | 从 OpenQASM 3.0 文件解析 `Circuit` | `circuit = qasm3.load("input.qasm")` | +| `qasm3.dumps(circuit)` | 将 `Circuit` 导出为 OpenQASM 3.0 字符串 | `text = qasm3.dumps(circuit)` | +| `qasm3.dump(circuit, path)` | 将 `Circuit` 写入 OpenQASM 3.0 文件 | `qasm3.dump(circuit, "output.qasm")` | + +## 2. 最小 OpenQASM 3.0 程序 + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; + +h q[0]; +cx q[0],q[1]; +``` + +加载到 Cqlib: + +```python +from cqlib.ir import qasm3 + +qasm_code = """ +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; + +h q[0]; +cx q[0],q[1]; +""" + +circuit = qasm3.loads(qasm_code) +print(circuit.num_qubits) +``` + +`OPENQASM 3;` 和 `OPENQASM 3.0;` 都可以被加载。 + +## 3. 从 Cqlib 导出 OpenQASM 3.0 + +```python +from cqlib import Circuit +from cqlib.ir import qasm3 + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +text = qasm3.dumps(circuit) +print(text) +``` + +典型输出: + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; + +h q[0]; +cx q[0],q[1]; +``` + +导出器会生成规范化文本,不保留原始输入的空格、注释和变量名。 + +## 4. 文件读写 + +```python +from cqlib.ir import qasm3 + +circuit = qasm3.load("input.qasm") +qasm3.dump(circuit, "output.qasm") +``` + +文件读取失败会抛出 I/O 异常;语法错误、语义错误或 unsupported feature 会抛出 `ValueError`。 + +## 5. `stdgates.inc` + +OpenQASM 3.0 标准门通常通过: + +```text +include "stdgates.inc"; +``` + +引入。Cqlib 依赖 OpenQASM 3 前端识别标准库门。导出时也会写出 `include "stdgates.inc";`,方便外部 OpenQASM 3 工具读取。 + +对于 Cqlib 有、但 `stdgates.inc` 不一定直接提供的扩展门,导出器会在主线路前生成 gate definition。例如 `x2p`、`rxx`、`rzz`、`rzx`、`fsim` 等可能被写成自定义 gate。 + +## 6. 测量赋值 + +OpenQASM 3.0 的测量比 OpenQASM 2.0 更自然,因为它支持赋值表达式: + +```text +bit c; +c = measure q[0]; +``` + +多比特测量: + +```text +bit[2] c; +c = measure q; +``` + +部分 bit-vector 赋值: + +```text +bit[2] c; +c[0] = measure q[2]; +c[1] = measure q[0]; +``` + +Cqlib 当前支持上述测量写法,并在导出时尽量生成可读回的规范形式。 + +## 7. Cqlib 测量到 QASM3 的映射规则 + +Cqlib 内部把测量分为两层: + +- `ClassicalValue`:测量产生的不可变结果。 +- `ClassicalVar`:用户创建的可变经典变量。 + +导出到 QASM3 时会按以下规则处理: + +| Cqlib 操作 | QASM3 输出 | 说明 | +|---|---|---| +| `measure_into(q, bit)` | `c0 = measure q[0];` | 单比特测量写入用户变量 | +| `measure_bits_into([0,1], bitvec)` | `c0 = measure q;` | 全寄存器顺序一致时合并输出 | +| `measure_bits_into([2,0], bitvec)` | `c0[0] = measure q[2];` 和 `c0[1] = measure q[0];` | 非连续或重排测量时拆成 indexed assignment | +| `measure(q)` | `bit[n] meas; meas[i] = measure q[j];` | 裸测量会生成显式寄存器,保证 QASM3 可读回 | + +示例: + +```python +from cqlib import Circuit +from cqlib.ir import qasm3 + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) +circuit.measure(0) +circuit.measure(1) + +print(qasm3.dumps(circuit)) +``` + +输出: + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; +bit[2] meas; + +h q[0]; +cx q[0],q[1]; +meas[0] = measure q[0]; +meas[1] = measure q[1]; +``` + +这样做的原因是:裸测量在 Cqlib 内部可以只产生临时值,但 QASM3 文本如果要稳定地跨工具保存和读回,最好有显式 classical destination。当前实现会生成 `meas` 寄存器,而不是泄漏内部临时名 `v0/v1`。 + +## 8. 标量测量赋值兼容 + +OpenQASM 3.0 中常见的写法也可以加载: + +```text +OPENQASM 3.0; +qubit[1] q; +bit v; +v = measure q[0]; +``` + +以及: + +```text +OPENQASM 3.0; +qubit q; +bit[2] c; +c[0] = measure q; +``` + +Cqlib 会把它们降低为内部的测量操作加 classical store 操作。 + +## 9. reset、barrier 和 global phase + +示例: + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; +reset q[0]; +barrier q[0],q[1]; +gphase(0.25); +``` + +Cqlib 支持将这些语句加载为对应的 `Circuit` 操作。导出时也会尽量保持等价语义。 + +## 10. 自定义门 + +OpenQASM 3.0 自定义门示例: + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +gate bell a, b { + h a; + cx a, b; +} + +qubit[2] q; +bell q[0], q[1]; +``` + +Cqlib 加载后会把 `bell` 视为线路定义门。导出时,如果 `Circuit` 中有可表示为 QASM3 gate body 的 `CircuitGate`,也会输出对应定义。 + +## 11. 控制流支持范围 + +Cqlib 的 QASM3 loader 支持部分可映射到当前 `Circuit` 的控制流: + +| OpenQASM 3 特性 | 当前支持情况 | 说明 | +|---|---|---| +| `if/else` | 支持 | 条件需能转换为 Cqlib classical expression | +| 静态 `for` | 支持 | 例如固定范围 `[0:2]`,可静态展开 | +| `switch` | 支持部分精确值 case | 适用于简单 unsigned integer case | +| `while` | 受前端与 IR 表达能力限制 | 复杂运行时循环可能被拒绝 | +| `break/continue` | 受限 | 取决于是否能映射到当前控制流模型 | + +推荐原则:如果线路主要用于跨框架交换,尽量使用简单、静态、可展开的控制流;复杂动态程序应先验证目标后端是否支持。 + +## 12. 当前支持与限制 + +| 类型 | 支持情况 | +|---|---| +| `qubit`、`qubit[n]` | 支持 | +| `bit`、`bit[n]`、`bool`、`uint[n]` | 支持常用形式 | +| 标准门 | 支持可映射到 Cqlib `StandardGate` 的门 | +| Cqlib 扩展门 | 导出时生成 gate definition | +| 测量赋值 | 支持 scalar、bit-vector、indexed assignment 的常用形式 | +| `reset`、`barrier`、`gphase` | 支持 | +| 自定义 gate | 支持可映射的 gate body | +| calibration、pulse、extern、hardware qubit、alias | 当前不支持 | +| 任意复杂 classical arithmetic | 当前不支持无损 lowering | +| 复杂 lvalue slicing、多维索引 | 当前不支持或仅支持必要子集 | + +## 13. 常见错误排查 + +| 现象 | 常见原因 | 处理方式 | +|---|---|---| +| `QASM3 parse error` | 语法错误、前端语义检查失败、使用了 Cqlib 不支持的特性 | 先用最小 QASM3 程序验证,再逐步加入特性 | +| `unsupported feature` | 使用了 calibration、extern、复杂 lvalue 或 unsupported gate modifier | 改写为基础门,或先在外部工具中展开为基础线路 | +| 导出失败 | `Circuit` 中包含 QASM3 无法表示的 Delay、Unitary 或复杂 store | 先 `decompose()` 或改用更合适的格式 | +| 外部工具无法读取 Cqlib 导出的 QASM3 | 外部工具暂不支持某些扩展门或 OpenQASM 3 子集 | 先分解扩展门,或改用目标工具支持的语法子集 | +| 测量寄存器名变成 `meas` | Cqlib 为裸测量生成显式目标寄存器 | 正常行为,用于保证文本可读回 | + +## 下一步 + +- [格式转换工作流](4_conversion_workflow.md) diff --git a/docs/documentation/1_cqlib/1_ir/4_conversion_workflow.md b/docs/documentation/1_cqlib/1_ir/4_conversion_workflow.md new file mode 100644 index 0000000..cf42d75 --- /dev/null +++ b/docs/documentation/1_cqlib/1_ir/4_conversion_workflow.md @@ -0,0 +1,236 @@ +# 格式转换工作流 + +Cqlib 的 IR 模块可以作为 QCIS、OpenQASM 2.0、OpenQASM 3.0 和外部框架之间的转换中枢。所有转换都遵循同一个原则: + +```text +外部格式 A -> Cqlib Circuit -> 外部格式 B +``` + +也就是说,转换不是字符串替换,而是先把输入格式解析为结构化 `Circuit`,再由目标格式导出器重新生成文本。 + +## 1. 转换总览 + +```mermaid +flowchart LR + A["QCIS"] --> C["Cqlib Circuit"] + B["OpenQASM 2.0"] --> C + D["OpenQASM 3.0"] --> C + E["外部量子线路工具"] --> F["OpenQASM 2/3 text"] --> C + C --> G["QCIS"] + C --> H["OpenQASM 2.0"] + C --> I["OpenQASM 3.0"] + C --> J["可视化 / 仿真 / 编译 / 硬件适配"] +``` + +## 2. QCIS 转 OpenQASM 3.0 + +适用场景:把硬件侧或已有 QCIS 文件转换为现代标准格式,供文档系统、测试工具或其他 OpenQASM 工具消费。 + +```python +from cqlib.ir import qcis, qasm3 + +qcis_code = """ +H Q0 +CX Q0 Q1 +M Q0 Q1 +""" + +circuit = qcis.loads(qcis_code) +qasm3_text = qasm3.dumps(circuit) +print(qasm3_text) +``` + +典型输出: + +```text +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; +bit[2] meas; + +h q[0]; +cx q[0],q[1]; +meas[0] = measure q[0]; +meas[1] = measure q[1]; +``` + +这里 `meas` 是 Cqlib 为 QCIS 测量补充的显式 classical register。QCIS 没有测量赋值语法,但 QASM3 有,所以导出器会生成可读回的目标寄存器。 + +## 3. OpenQASM 3.0 转 QCIS + +适用场景:把标准 QASM3 线路转换为硬件相关 QCIS 指令。 + +```python +from cqlib.ir import qasm3, qcis + +qasm3_code = """ +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; +h q[0]; +cz q[0],q[1]; +""" + +circuit = qasm3.loads(qasm3_code) +qcis_text = qcis.dumps(circuit) +print(qcis_text) +``` + +输出: + +```text +H Q0 +CZ Q0 Q1 +``` + +如果 QASM3 中包含 QCIS 无法表达的控制流、自定义门、复杂 classical assignment 或非目标门集,需要先做分解或编译映射。 + +## 4. OpenQASM 2.0 转 OpenQASM 3.0 + +适用场景:升级老格式线路,或者把 QASM2 benchmark 转成 QASM3 便于表达后续动态测量逻辑。 + +```python +from cqlib.ir import qasm2, qasm3 + +qasm2_code = """ +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[2]; +h q[0]; +cx q[0],q[1]; +""" + +circuit = qasm2.loads(qasm2_code) +qasm3_text = qasm3.dumps(circuit) +print(qasm3_text) +``` + +## 5. OpenQASM 3.0 转 OpenQASM 2.0 + +适用场景:需要输出给只支持 QASM2 的旧工具。 + +```python +from cqlib.ir import qasm3, qasm2 + +qasm3_code = """ +OPENQASM 3.0; +include "stdgates.inc"; + +qubit[2] q; +h q[0]; +cx q[0],q[1]; +""" + +circuit = qasm3.loads(qasm3_code) +qasm2_text = qasm2.dumps(circuit) +print(qasm2_text) +``` + +注意:只有当 QASM3 线路使用的特性也能被 QASM2 表达时,转换才会成功。复杂 `if/else`、`for`、`switch`、通用 classical assignment 等通常不能无损转成 QASM2。 + +## 6. 外部工具与 Cqlib 的通用转换方式 + +外部量子线路工具只要能够导入或导出 OpenQASM 文本,就可以按同一方式与 Cqlib 交换线路: + +```text +外部线路对象 -> OpenQASM 文本 -> cqlib.ir.qasm2/qasm3.loads -> Cqlib Circuit +``` + +```text +Cqlib Circuit -> cqlib.ir.qasm2/qasm3.dumps -> OpenQASM 文本 -> 外部线路对象 +``` + +推荐把 OpenQASM 文本作为工具边界,而不是直接耦合双方内部线路对象。这样可以降低依赖复杂度,也便于在 CI 中保存输入输出文件并做回归测试。 + +如果外部工具无法读取某些 Cqlib 扩展门,可以先在 Cqlib 侧把线路分解为更基础的门集,再导出。 + +## 7. 转换后验证 + +转换完成后建议做三类验证。 + +### 7.1 结构检查 + +```python +print(circuit.num_qubits) +print(len(circuit.operations)) +``` + +### 7.2 可视化检查 + +```python +from cqlib.visualization import draw_text + +print(draw_text(circuit)) +``` + +### 7.3 小规模酉等价检查 + +对不含测量和动态控制的小线路,可以比较矩阵: + +```python +import numpy as np +from cqlib.ir import qasm3 + +before = circuit.to_matrix() +restored = qasm3.loads(qasm3.dumps(circuit)) +after = restored.to_matrix() + +assert np.allclose(before, after) +``` + +含测量或控制流的线路不适合直接比较整体酉矩阵,应改为检查操作序列、测量目标、采样结果或后端执行结果。 + +## 8. 格式选择策略 + +| 输入来源 | 目标 | 推荐路线 | +|---|---|---| +| QCIS 文件 | 通用 OpenQASM 文档或测试文件 | `qcis.load -> qasm3.dump` | +| 外部线路工具 | Cqlib 仿真或编译 | `外部工具导出 OpenQASM -> cqlib.qasm2/qasm3.loads` | +| Cqlib 线路 | 老工具链 | `qasm2.dumps` | +| Cqlib 线路 | 现代工具链或动态线路 | `qasm3.dumps` | +| Cqlib 线路 | 硬件指令交付 | 先编译到目标门集,再 `qcis.dumps` | + +## 9. 转换前检查清单 + +导出前建议确认: + +- 线路中是否包含目标格式不支持的门。 +- 是否存在未绑定参数。 +- 是否包含复杂 classical control flow。 +- 是否需要先 `decompose()` 自定义门。 +- 是否需要先做硬件拓扑映射或目标门集分解。 +- 是否需要保留测量结果,或者只是单纯绘图/仿真。 + +示例: + +```python +compiled = circuit.decompose() +text = qasm3.dumps(compiled) +``` + +## 10. 常见转换问题 + +| 问题 | 根本原因 | 建议 | +|---|---|---| +| QCIS 转 QASM3 后多了 `bit[n] meas` | QCIS 没有 classical 赋值,QASM3 需要显式测量目标以保证可读回 | 正常行为,不需要删除 | +| QASM3 转 QASM2 失败 | QASM3 使用了 QASM2 无法表达的动态语义 | 保持 QASM3,或简化线路 | +| 外部工具读取 Cqlib 导出的 OpenQASM 失败 | 目标工具不支持某些扩展门或语法子集 | 先分解为基础门,或改用目标工具支持的格式 | +| QCIS 导出失败 | 线路包含 QCIS 不支持的门、Unitary、控制流或普通 identity | 先编译/分解到 QCIS 支持门集 | +| 转换后文本和原文本不一样 | 导出器会规范化文本,不保留排版和变量名 | 以线路语义为准,不以字符串完全一致为准 | + +## 11. 推荐工程实践 + +- 在项目中保留源格式和目标格式,便于追溯。 +- 对关键格式转换加入单元测试,至少覆盖 `loads -> dumps -> loads`。 +- 对简单酉线路使用矩阵等价性验证。 +- 对含测量线路检查测量顺序和 classical target。 +- 对硬件提交路径,先用小线路验证门集、qubit 编号和测量输出。 + +## 下一步 + +- [IR 中间表示总览](0_overview.md) +- [QCIS 支持](1_qcis.md) +- [OpenQASM 2.0 支持](2_qasm2.md) +- [OpenQASM 3.0 支持](3_qasm3.md) diff --git a/docs/documentation/1_cqlib/2_device/0_overview.md b/docs/documentation/1_cqlib/2_device/0_overview.md new file mode 100644 index 0000000..9ee98a5 --- /dev/null +++ b/docs/documentation/1_cqlib/2_device/0_overview.md @@ -0,0 +1,111 @@ +# 设备模块 + +`cqlib.device` 是 Cqlib 中用于描述量子后端硬件能力、噪声特性与执行结果的数据模块。 + +在量子计算工作流里,如果说 `Circuit` 表达的是“算法怎么做”,那么 `device` 表达的是“硬件允许怎么做”。它为以下关键工程环节提供统一的底层支撑: + +- 物理约束感知:定义硬件拓扑(哪些比特支持双比特门耦合)。 +- 高保真度建模:精细化管理比特相干时间(T1/T2)、读出误差及门保真度。 +- 动态布局追踪:在编译路由阶段实时维护逻辑比特与物理比特的映射。 +- 噪声数字孪生:构建可用于量子噪声仿真的信道模型。 +- 任务全生命周期管理:追踪任务从提交、入队、运行到结果回传的完整闭环。 + +--- + +## 核心能力 + +- **拓扑模型**:使用 `Topology` 描述物理比特与耦合关系。 +- **设备参数**:使用 `Device`、`QubitProp`、`EdgeProp`、`InstructionProp` 描述标定数据。 +- **拓扑映射**:使用 `Layout` 管理逻辑比特到物理比特的映射关系。 +- **噪声模型**:使用 `NoiseModel` 及噪声通道对象描述读出误差与门误差。 +- **执行结果与状态**:使用 `Outcome`、`Status`、`ExecutionResult` 统一管理任务状态与统计结果。 + +--- + +## 快速示例:从拓扑到结果 + +```python +from cqlib.circuit import Instruction, StandardGate +from cqlib.device import ( + Device, + EdgeProp, + ExecutionResult, + InstructionProp, + Layout, + NoiseModel, + OperationKey, + QubitProp, + ReadoutError, + SingleQubitNoise, + Topology, + TwoQubitNoise, +) + +# 1) 定义硬件拓扑(支持 (u, v) 或 (u, v, gate_name)) +topo = Topology([0, 1, 2], [(0, 1, "CX"), (1, 2, "CZ")]) + +# 2) 创建设备并设置默认参数 + 原生门 +# 注意:Device 构造器现在需要显式传入 qubits 集合 +device = Device("demo_backend", [0, 1, 2], topo) +device.default_t1 = 50.0 +device.default_t2 = 35.0 +device.default_readout_error = 0.05 +device.default_single_qubit_error = 0.001 +device.default_two_qubit_error = 0.01 +device.native_gates = [ + Instruction.from_standard_gate(StandardGate.X), + Instruction.from_standard_gate(StandardGate.CX), +] + +# 3) 覆盖局部标定参数(逐比特、逐边) +q0_prop = QubitProp(readout_error=0.02) +q0_prop.t1 = 80.0 +q0_prop.t2 = 70.0 +device.add_qubit_properties(0, q0_prop) + +cx_prop = InstructionProp( + Instruction.from_standard_gate(StandardGate.CX), + error_rate=0.02 +) +cx_prop.length = 220.0 +edge_prop = EdgeProp() +edge_prop.native_instructions = [cx_prop] +device.add_edge_properties(0, 1, edge_prop) + +# 4) 布局映射(逻辑比特 -> 物理比特) +layout = Layout(logical=[0, 1], physical=[10, 11, 12], init_map={0: 11}) +layout.swap_physical(11, 12) + +# 5) 噪声模型(可选) +noise = NoiseModel() +noise.add_readout_error(0, ReadoutError(0.02, 0.01)) +noise.add_single_qubit_error(StandardGate.X, 0, SingleQubitNoise.bit_flip(0.005)) +noise.add_two_qubit_error(StandardGate.CX, 0, 1, TwoQubitNoise.depolarizing(0.02)) + +# 6) 任务结果管理(示意) +result = ExecutionResult("task-1", [0, 1], 100, 2, "demo_backend") +result.start() +result.finish({"00": 60, "11": 40}) +result.calc_probabilities() + +print(device.name) # demo_backend +print(device.get_t1(0), device.get_t1(2)) # 80.0, 50.0 +print(layout.l2p_map) # 当前映射 +print(noise.get_readout_error(0)) # ReadoutError(...) +print(result.status.kind) # completed +print(result.probabilities) # {'11': 0.4, '00': 0.6} + +# 按操作键查询噪声(可选) +skey = OperationKey.new_single(StandardGate.X, 0) +print(noise.get_single_qubit_errors(skey)) +``` + +--- + +## 下一步 + +- [拓扑建模](1_topology.md) +- [设备属性建模](2_device.md) +- [布局映射](3_layout.md) +- [噪声模型](4_noise.md) +- [执行结果与状态](5_result.md) diff --git a/docs/documentation/1_cqlib/2_device/1_topology.md b/docs/documentation/1_cqlib/2_device/1_topology.md new file mode 100644 index 0000000..bc30276 --- /dev/null +++ b/docs/documentation/1_cqlib/2_device/1_topology.md @@ -0,0 +1,114 @@ +# 拓扑模型 + +`Topology` 是对量子芯片物理比特及其耦合连通性的图论抽象。它是所有高级编译与路由算法的核心输入:只有准确掌握了芯片的硬件约束,编译器才能在有限的连通路径下,寻找最优的门映射方案,从而最大化计算保真度。 + +--- + +## 构造拓扑结构 + +Cqlib 支持通过显式边列表进行精细化建模,也支持针对标准布局的快速生成器。 + +### 1. 显式建模 +您可以通过节点序列与边序列构建自定义的连接关系图: + +```python +from cqlib.device import Topology + +# 定义 3 个比特的连接图 +# 元组结构: (源比特, 目标比特, 默认门类型) +topo = Topology( + [0, 1, 2], + [ + (0, 1), # 默认门类型设为 "CX" + (1, 2, "CZ"), # 显式指定门类型为 "CZ" + ], +) +``` + +### 2. 快速生成器 +针对常见的芯片排列类型,您可以直接调用内置生成器: + +```python +# 快速构建线性链式结构: 0->1->2->3(单向) +line_topo = Topology.line([0, 1, 2, 3]) + +# 双向线型: 0<->1<->2<->3 +bidirectional_topo = Topology.bidirectional_line([0, 1, 2, 3]) + +# 环形: 0<->1<->2<->0 +ring_topo = Topology.ring([0, 1, 2]) + +# 星形: 中心节点 0 与所有其他节点双向连接 +star_topo = Topology.star([0, 1, 2, 3, 4], center=0) + +# 网格: 2x3 双向网格 +grid_topo = Topology.grid([0, 1, 2, 3, 4, 5], rows=2, cols=3) +``` + +--- + +## 拓扑查询与拓扑分析 + +`Topology` 类提供了丰富的高性能查询接口,便于在路由算法中进行实时寻径或连通性判断。 + +```python +# 基础规模查询 +print(f"比特总数: {topo.num_qubits}") +print(f"耦合总数: {topo.num_couplings}") +print(f"节点列表: {topo.qubits}") + +# 连通性分析 +print(topo.supports_directed_coupling(0, 1)) # 检查 (0, 1) 间是否存在直接耦合 +print(topo.supports_coupling_either_direction(0, 1)) # 检查任一方向是否存在耦合 +print(topo.get_coupling_name(1, 2)) # 获取该耦合路径支持的物理门名(如 "CZ") + +# 节点邻序查询(有向图语义) +print(topo.contains_qubit(2)) # 检查特定物理索引是否存在 +print(topo.successors(1)) # 获取比特 1 的后继节点(出边邻居) +print(topo.predecessors(1)) # 获取比特 1 的前驱节点(入边邻居) +print(topo.neighbors_undirected(1)) # 获取比特 1 的无向邻居(合并双向) +print(topo.out_degree(1)) # 获取比特 1 的出度 +print(topo.in_degree(1)) # 获取比特 1 的入度 + +# 获取所有无向边(去重双向边) +print(topo.undirected_edges) # [(Qubit(0), Qubit(1)), (Qubit(1), Qubit(2))] +``` + +--- + +## 动态修改拓扑 +`Topology` 对象支持运行时动态调整,适用于描述多芯片互联或模拟硬件故障(如比特屏蔽)场景: + +```python +# 动态增量更新 +topo.add_qubits([3, 4]) +topo.add_couplings([(2, 3, "CX"), (3, 4, "CZ")]) + +# 动态剔除(用于模拟硬件降级或比特故障) +topo.remove_couplings([(2, 3)]) +topo.remove_qubits([4]) +``` + +--- + +## 有向耦合语义 + +Cqlib 的 `Topology` 默认采用有向图模型。这意味着 (0, 1, "CX") 并不等同于 (1, 0, "CX")。 + +- 在物理层,这通常对应于 Control-Target 的方向限制:例如,硬件可能只支持 0 号对比特 1 号执行受控操作。 +- 无向化建议:如果您使用的算法或仿真后端不考虑耦合方向,建议在构造时显式添加双向边: + +```python +# 表示 0 和 1 互为控制/目标位 +topo = Topology([0, 1], [(0, 1, "CX"), (1, 0, "CX")]) +``` + +--- + +## 下一步 +接下来您可以深入了解以下主题: + +- [设备属性建模](2_device.md) +- [布局映射](3_layout.md) +- [噪声模型](4_noise.md) +- [执行结果与状态](5_result.md) diff --git a/docs/documentation/1_cqlib/2_device/2_device.md b/docs/documentation/1_cqlib/2_device/2_device.md new file mode 100644 index 0000000..234ec6c --- /dev/null +++ b/docs/documentation/1_cqlib/2_device/2_device.md @@ -0,0 +1,161 @@ +# 设备属性 + +`Device` 模块负责汇总后端的全量硬件特征。它将抽象的物理拓扑(`Topology`)与具体的标定参数(如相干时间、门保真度等)相结合,为噪声感知编译和高保真度仿真提供数据支撑。 + +Cqlib 采用 “全局默认 + 局部覆盖” 的策略,允许开发者在定义大规模设备基准的同时,对性能特殊的比特或边进行精细化刻画。 + +--- + +## 核心对象 + +- `InstructionProp`:描述特定指令的物理表现,核心参数包括平均误差率与执行时长。 +- `QubitProp`:描述单比特特性。包含读出误差、相干时间(T1/T2)、共振频率以及该比特支持的原生单比特指令集。 +- `EdgeProp`:描述耦合边特性。主要用于定义该耦合路径支持的原生双比特指令及关联的标定参数。 +- `Device`:顶层实体。整合拓扑结构与上述所有属性,对外提供统一的参数查询接口。 + +--- + +## 构建设备基准 + +在初始化设备时,建议您先设定全局默认值。这可以作为该芯片各参数的期望水平: + +```python +from cqlib.circuit import Instruction, StandardGate +from cqlib.device import Device, Topology + +# 定义基础拓扑 +topo = Topology([0, 1, 2], [(0, 1, "CX"), (1, 2, "CZ")]) + +# 创建设备并注入“全局默认值” +# 注意:Device 构造器现在需要显式传入 qubits 集合 +device = Device("demo_backend", [0, 1, 2], topo) +device.default_t1 = 50.0 # 默认 T1 50μs +device.default_t2 = 35.0 # 默认 T2 35μs +device.default_readout_error = 0.05 # 默认读出误差 5% +device.default_single_qubit_error = 0.001 # 默认单比特门误差 0.1% +device.default_two_qubit_error = 0.01 # 默认双比特门误差 1% +device.native_gates = [ + Instruction.from_standard_gate(StandardGate.X), + Instruction.from_standard_gate(StandardGate.CX), +] +``` + +### 使用快速生成器 + +对于标准拓扑,您可以直接使用设备生成器: + +```python +# 线型设备(单向) +device = Device.line("line_device", num_qubits=5) + +# 双向线型设备 +device = Device.bidirectional_line("bi_line", num_qubits=5) + +# 环形设备 +device = Device.ring("ring_device", num_qubits=4) + +# 星形设备 +device = Device.star("star_device", num_qubits=5, center=0) + +# 网格设备 +device = Device.grid("grid_device", rows=3, cols=4) + +# 从边列表创建设备 +device = Device.from_edges("custom", num_qubits=4, edges=[(0, 1), (1, 2), (2, 3)]) +``` + +--- + +## 注入局部标定数据 + +在实际芯片中,每个比特的表现往往参差不齐。通过 `add_qubit_properties` 和 `add_edge_properties`,您可以覆盖特定的局部参数。 + +```python +from cqlib.device import QubitProp, EdgeProp, InstructionProp + +# 刻画 Q0 的优越性能 +q0_prop = QubitProp(readout_error=0.02) +q0_prop.t1 = 80.0 +q0_prop.t2 = 70.0 +q0_prop.frequency = 5.1 # 频率 5.1 GHz + +x_prop = InstructionProp( + Instruction.from_standard_gate(StandardGate.X), + error_rate=0.001 +) +x_prop.length = 20.0 # X 门脉冲长度 20ns +q0_prop.native_instructions = [x_prop] + +device.add_qubit_properties(0, q0_prop) + +# 刻画 (0, 1) 耦合边的双比特门表现 +cx_prop = InstructionProp( + Instruction.from_standard_gate(StandardGate.CX), + error_rate=0.015 +) +cx_prop.length = 200.0 +edge_prop = EdgeProp() +edge_prop.native_instructions = [cx_prop] +device.add_edge_properties(0, 1, edge_prop) +``` + +--- + +## 参数查询与回退机制 + +`Device` 对象实现了智能查询逻辑。当您请求某个属性时,它会遵循以下优先级: + +- 局部值:检查目标比特/边是否拥有独立的 `QubitProp`/ `EdgeProp`。 +- 全局值:若局部未配置,则自动回退到初始化时设定的 `default` 值。 + +```python +# 参数查询示例 +print(f"Q0 T1: {device.get_t1(0)}") # 输出 80.0 (局部覆盖生效) +print(f"Q2 T1: {device.get_t1(2)}") # 输出 50.0 (回退至全局默认值) + +# 错误率查询 +print(f"Q0 X 门误差: {device.single_qubit_error(0, Instruction.from_standard_gate(StandardGate.X))}") +print(f"(0,1) CX 误差: {device.two_qubit_error(0, 1, Instruction.from_standard_gate(StandardGate.CX))}") +print(f"(0,1) 最佳边误差: {device.edge_error(0, 1)}") +``` + +--- + +## 无效比特管理 + +对于暂时离线或故障的比特,可以将其标记为无效: + +```python +# 标记比特 2 为无效(离线或故障) +device.invalid_qubits = {2} + +# 查询可用比特 +print(f"可用比特数: {device.num_usable_qubits}") +print(f"比特 2 是否可用: {device.is_usable_qubit(2)}") # False +``` + +--- + +## 健壮性校验 + +为了保证硬件模型的一致性,`Device` 会实时校验输入的比特索引是否超出了 `Topology` 定义的范围: + +```python +from cqlib.device import EdgeProp, QubitProp + +# 尝试为不存在的比特 99 添加属性将触发异常 +try: + device.add_qubit_properties(99, QubitProp(0.01)) +except ValueError as e: + print(f"操作拦截: {e}") # 提示比特不在拓扑内 +``` + +--- + +## 下一步 + +接下来您可以深入了解以下主题: + +- [布局映射](3_layout.md) +- [噪声模型](4_noise.md) +- [执行结果与状态](5_result.md) diff --git a/docs/documentation/1_cqlib/2_device/3_layout.md b/docs/documentation/1_cqlib/2_device/3_layout.md new file mode 100644 index 0000000..95cc23b --- /dev/null +++ b/docs/documentation/1_cqlib/2_device/3_layout.md @@ -0,0 +1,141 @@ +# 拓扑映射 + +`Layout` 类提供了一个高性能的、双向一致的映射管理工具,帮助开发者在编译与路由过程中精准追踪比特位置的变化。 + +--- + +## 核心概念 + +- **逻辑比特**:算法代码中定义的比特(`LogicalQubit`)。 +- **物理比特**:硬件芯片上真实的比特索引(`PhysicalQubit`)。 +- **空闲物理比特**:物理比特中未被逻辑比特占用的位置,可用于路由或绑定额外的逻辑比特。 + +--- + +## 构造映射 + +您可以手动定义初始的映射关系,未指定的逻辑比特将被自动分配到剩余的可用物理位置: + +```python +from cqlib.device import Layout + +# 场景:将 2 个逻辑比特映射到 3 比特芯片的特定位置 +layout = Layout( + logical=[0, 1], + physical=[10, 11, 12], + init_map={0: 11} # 指定逻辑比特 0 对应物理比特 11 +) + +print(f"逻辑比特数: {layout.num_logical}") # 2 +print(f"物理比特数: {layout.num_physical}") # 3 +print(f"空闲物理比特数: {layout.num_vacant_physical}") # 1 (物理 10 或 12 其中之一空闲) +``` + +如果 `physical` 数量多于 `logical`,多余的物理比特将保持空闲状态(`vacant`),可用于后续路由或绑定。 + +### 从配对列表构造 + +您也可以通过显式的 `(logical, physical)` 配对列表来构造布局: + +```python +from cqlib.device import Layout + +# 逻辑 0 -> 物理 2, 逻辑 1 -> 物理 0 +# 物理比特总数为 4,因此物理 1 和 3 保持空闲 +layout = Layout.from_pairs([(0, 2), (1, 0)], physical_count=4) +print(layout.num_vacant_physical) # 2 +``` + +--- + +## 查询映射 +`Layout` 维护了严格的双向索引以便于查询: + +```python +# 获取完整的比特列表视图 +print(f"逻辑比特集: {sorted(layout.logical_qubits)}") +print(f"空闲物理比特集: {sorted(layout.vacant_physical_qubits)}") +print(f"物理比特集: {sorted(layout.physical_qubits)}") + +# 正向查询:逻辑 -> 物理 +p_idx = layout.get_physical(0) +print(f"逻辑比特 0 当前位于物理比特: {p_idx}") + +# 反向查询:物理 -> 逻辑 +l_idx = layout.get_logical(11) +print(f"物理比特 11 上当前承载的逻辑比特: {l_idx}") + +# 检查物理比特是否空闲 +print(layout.is_physical_vacant(10)) # True 或 False + +# 获取全量映射快照 (Dict) +l2p = layout.l2p_map # {logical: physical} +p2l = layout.p2l_map # {physical: logical} +``` + +--- + +## 更新映射 + +### 绑定与解绑 + +您可以在运行时动态绑定新的逻辑比特到空闲物理比特,或解绑已有逻辑比特: + +```python +# 绑定新的逻辑比特 2 到空闲物理比特 12 +layout.bind(2, 12) +print(layout.num_vacant_physical) # 0 + +# 解绑逻辑比特 0,释放其占用的物理比特 +released_physical = layout.unbind(0) +print(f"释放的物理比特: {released_physical}") +print(layout.num_vacant_physical) # 1 +``` + +### 交换物理比特 + +在路由算法(如基于最短路径的 SWAP 插入)中,最核心的操作是模拟物理层面的 SWAP 门。通过 `swap_physical`,您可以同步更新双向映射表: + +```python +# 模拟在物理比特 11 和 12 之间执行一次 SWAP +# 操作后,原本在 11 上的逻辑比特将移至 12,反之亦然 +before_11 = layout.get_logical(11) +before_12 = layout.get_logical(12) + +layout.swap_physical(11, 12) + +after_11 = layout.get_logical(11) +after_12 = layout.get_logical(12) + +print(before_11, before_12) +print(after_11, after_12) +``` + +--- + +## 健壮性保障 + +为了防止由于比特索引误操作导致的编译错误,Layout 会在更新时进行严格的成员检查: + +```python +try: + # 尝试交换一个不存在于布局中的物理比特 + layout.swap_physical(11, 99) +except ValueError as e: + print(f"路由校验拦截: {e}") + +try: + # 尝试绑定已占用的物理比特 + layout.bind(3, 11) # 假设 11 已被占用 +except ValueError as e: + print(f"绑定校验拦截: {e}") +``` + +--- + +## 下一步 + +接下来您可以深入了解以下主题: + +- [噪声模型](4_noise.md) +- [执行结果与状态](5_result.md) diff --git a/docs/documentation/1_cqlib/2_device/4_noise.md b/docs/documentation/1_cqlib/2_device/4_noise.md new file mode 100644 index 0000000..936b026 --- /dev/null +++ b/docs/documentation/1_cqlib/2_device/4_noise.md @@ -0,0 +1,169 @@ +# 噪声模型 + +`cqlib.device` 提供了从单比特噪声、双比特噪声到整体噪声容器 `NoiseModel` 的完整建模接口,可用于仿真、误差注入和后处理分析。 + +通过这些接口,您可以构建出与真实芯片特性高度契合的 “噪声数字孪生” ,为误差缓解算法提供完美的实验场。 + +--- + +## 单比特噪声 + +`SingleQubitNoise` 类提供了多种经典的量子信道模型,用于描述比特在演化过程中的信息丢失: + +- `bit_flip(p)`:比特翻转噪声。 +- `phase_flip(p)`:相位翻转噪声。 +- `pauli(px, py, pz)`:一般泡利噪声,独立指定 X、Y、Z 错误概率。 +- `depolarizing(p)`:去极化噪声,最常用的综合噪声模型。 +- `amplitude_damping(gamma)`:能量弛豫(T1 过程)建模。 +- `phase_damping(lambda_)`:纯退相干(T2 过程)建模。 + +```python +from cqlib.device import SingleQubitNoise + +# 创建 1% 强度的去极化噪声 +sq = SingleQubitNoise.depolarizing(0.01) + +# 创建一般泡利噪声 +pauli_noise = SingleQubitNoise.pauli(px=0.001, py=0.0005, pz=0.002) + +# 将噪声转换为 Kraus 算符表示(用于密度矩阵仿真) +kraus_ops = sq.to_kraus() +print(f"Kraus 算符数量: {len(kraus_ops)}") # 去极化噪声对应 4 个算符 +print(f"算符维度: {kraus_ops[0].shape}") # (2, 2) + +# 验证噪声参数 +assert sq.is_valid() # True +``` + +--- + +## 双比特噪声 + +双比特门由于执行时间长、物理交互复杂,通常是电路噪声的主要来源: + +- `depolarizing(p)`:标准双比特去极化噪声,15 个非恒等泡利算符均匀混合。 +- `independent(q0_noise, q1_noise)`:两个比特独立受单比特噪声影响。 +- `correlated_pauli(op_q0, op_q1, p)`:两个比特间存在物理耦合引发的协同泡利错误。 + +```python +from cqlib.device import SingleQubitNoise, TwoQubitNoise +from cqlib.qis import Pauli + +# 标准双比特去极化噪声 +tq = TwoQubitNoise.depolarizing(0.01) + +# 构造相互独立的异构噪声:Q0 发生相位翻转,Q1 发生比特翻转 +tq = TwoQubitNoise.independent( + SingleQubitNoise.phase_flip(0.02), + SingleQubitNoise.bit_flip(0.03), +) + +# 相关泡利噪声:两个比特同时发生 XX 错误 +tq = TwoQubitNoise.correlated_pauli(Pauli.X, Pauli.X, p=0.01) + +# 转换后的 Kraus 算符维度为 (4, 4) +print(f"双比特噪声矩阵维度: {tq.to_kraus()[0].shape}") +``` + +--- + +## 读出误差 + +读出误差描述了测量过程中的比特翻转。它通常由热激发或测量串扰引起,表现为测量结果与真实量子态的不一致: + +```python +from cqlib.device import ReadoutError + +# 参数含义: (P(0|1): 1 测成 0 的概率, P(1|0): 0 测成 1 的概率) +ro = ReadoutError(p_0_given_1=0.02, p_1_given_0=0.01) + +if ro.is_valid(): + print(f"Qubit 1->0 翻转概率: {ro.p_0_given_1}") + print(f"Qubit 0->1 翻转概率: {ro.p_1_given_0}") +``` + +--- + +## NoiseModel:构建整体噪声视图 +`NoiseModel` 是噪声定义的集合容器。您可以将上述定义的各种噪声 “挂载” 到特定的量子比特或量子门操作上: + +```python +from cqlib.circuit import StandardGate +from cqlib.device import ( + NoiseModel, + OperationKey, + ReadoutError, + SingleQubitNoise, + TwoQubitNoise, +) + +nm = NoiseModel() + +# 1) 添加读出误差 +nm.add_readout_error(0, ReadoutError(0.02, 0.01)) + +# 2) 添加单比特门误差 +nm.add_single_qubit_error( + StandardGate.X, + 0, + SingleQubitNoise.bit_flip(0.005), +) + +# 3) 添加双比特门误差 +nm.add_two_qubit_error( + StandardGate.CX, + 0, + 1, + TwoQubitNoise.depolarizing(0.02), +) + +# 4) 查询 +print(nm.get_readout_error(0)) + +skey = OperationKey.new_single(StandardGate.X, 0) +print([n.kind for n in (nm.get_single_qubit_errors(skey) or [])]) + +tkey = OperationKey.new_double(StandardGate.CX, 0, 1) +print([n.kind for n in (nm.get_two_qubit_errors(tkey) or [])]) +``` + +--- + +## 安全性校验 + +Cqlib 会在构造和添加过程中实时监控参数合法性。`NoiseModel` 的 `add_*` 方法现在会返回 `Result` 类型进行参数校验: + +```python +from cqlib.circuit import StandardGate +from cqlib.device import NoiseModel, SingleQubitNoise + +nm = NoiseModel() + +# 尝试设置超过 100% 的错误概率 +try: + nm.add_single_qubit_error( + StandardGate.X, + 0, + SingleQubitNoise.bit_flip(1.5), + ) +except ValueError as e: + print("invalid noise parameter:", e) + +# 尝试使用无效的双比特配置 +try: + nm.add_two_qubit_error( + StandardGate.CX, + 0, 0, # 错误:同一个比特 + TwoQubitNoise.depolarizing(0.01), + ) +except ValueError as e: + print("invalid qubit configuration:", e) +``` + +--- + +## 下一步 + +接下来您可以深入了解以下主题: + +- [执行结果与状态](5_result.md) diff --git a/docs/documentation/1_cqlib/2_device/5_result.md b/docs/documentation/1_cqlib/2_device/5_result.md new file mode 100644 index 0000000..6b790da --- /dev/null +++ b/docs/documentation/1_cqlib/2_device/5_result.md @@ -0,0 +1,153 @@ +# 执行结果与状态 + +`cqlib.device` 的执行结果模块为开发者提供了一套工业级的任务追踪与结果抽象方案,确保从模拟器到真机后端的任务行为高度一致。 + +--- + +## Outcome:量子比特串的语义封装 + +`Outcome` 支持对量子测量观测值的结构化封装。它支持对比特位的高效查询与哈希校验,是构建统计直方图的基础单元: + +```python +from cqlib.device import Outcome + +# 从二进制字符串初始化观测结果 +o = Outcome("101") + +# 语义化查询:检查第 0 位比特是否为 '1' (LSB,即最右边) +print(f"Index 0 is One: {o.is_one(0)}") # True + +# 标准化输出 +print(f"3-bit String: {o.to_bitstring(3)}") # "101" + +# 支持等值比较与哈希,方便作为字典的 Key +o2 = Outcome.from_bitstring("101") +print(f"Match: {o == o2}") # True + +# 从位索引构造(设置第 0 位和第 2 位为 1) +o3 = Outcome.from_indices(width=3, indices=[0, 2]) +print(o3.to_bitstring(3)) # "101" +``` + +--- + +## Status:任务生命周期的状态机 + +量子任务通常是异步执行的。`Status` 对象定义了一套标准的状态机模型,用于描述任务从提交到终止的全过程: + +- `queued()`:已入队,等待资源分配。 +- `running()`:后端正在执行。 +- `completed()`:任务成功完成,结果就绪。 +- `failed(error_msg, error_code)`:执行异常,记录错误信息与错误码。 +- `cancelled()`:用户或系统主动取消执行。 + +```python +from cqlib.device import Status + +queued = Status.queued() +running = Status.running() +completed = Status.completed() +failed = Status.failed("backend down", 500) +cancelled = Status.cancelled() + +print(queued.kind, queued.is_terminal()) # queued False +print(completed.kind, completed.is_success()) # completed True +print(failed.kind, failed.error_msg, failed.error_code) +print(cancelled.kind, cancelled.is_terminal()) +``` + +--- + +## ExecutionResult:全生命周期容器 + +`ExecutionResult` 是任务执行的“黑盒记录仪”。它完整记录了任务的元数据(ID、比特集、采样数)、多维时间戳(创建、开始、完成)以及最终的测量统计: + +```python +from cqlib.device import ExecutionResult + +# 1. 任务初始化:进入 'queued' 状态 +result = ExecutionResult( + task_id="q-task-001", + qubits=[0, 1], + shots=1000, + num_qubits=2, + backend="Tianyan-176-2", +) + +# 2. 模拟任务开始执行 +result.start() +print(f"Started at: {result.started_at}") + +# 3. 任务回传并结束:更新状态为 'completed' 并存入计数字典 +result.finish({"00": 600, "11": 400}) +print(f"Status: {result.status.kind}") # completed + +# 4. 统计分析:计算状态概率分布 +result.calc_probabilities() +print(f"Probabilities: {result.probabilities}") # {'11': 0.4, '00': 0.6} +``` + +### 从计数直接构造 + +如果您已经有完整的计数数据,可以使用便捷构造器直接创建已完成的结果: + +```python +from cqlib.device import ExecutionResult + +# 直接从计数构造已完成的结果 +result = ExecutionResult.from_counts( + task_id="q-task-002", + qubits=[0, 1], + shots=1024, + num_qubits=2, + counts={"00": 512, "11": 512}, + backend="simulator", +) +print(result.status.kind) # completed +print(result.probabilities) # {'00': 0.5, '11': 0.5} +``` + +--- + +## 异常流程处理 + +Cqlib 提供了明确的方法来中止任务或记录失败原因,并对输入数据进行严格校验: + +```python +from cqlib.device import ExecutionResult + +failed = ExecutionResult("task-fail", [0], 10, 1, None) +failed.fail("timeout", 408) +print(failed.status.kind) # failed +print(failed.status.error_msg) # timeout + +cancelled = ExecutionResult("task-cancel", [0], 10, 1, None) +cancelled.cancel() +print(cancelled.status.kind) # cancelled +``` + +--- + +## 输入校验 + +`finish(...)` 方法会严格检查计数字典的有效性。如果 Key 包含非二进制字符(如 "2" 或 "A"),系统将拒绝处理并抛出异常: + +```python +from cqlib.device import ExecutionResult + +r = ExecutionResult("task-invalid", [0], 10, 1, None) +try: + r.finish({"2": 1}) +except ValueError as e: + print("invalid counts:", e) +``` + +--- + +## 下一步 + +接下来您可以深入了解以下主题: + +- [量子信息](../3_information/0_overview.md) +- [错误缓解](../4_mitigation/0_overview.md) +- [编译优化](../5_transpilation/0_overview.md) diff --git a/docs/documentation/1_cqlib/3_qis/0_overview.md b/docs/documentation/1_cqlib/3_qis/0_overview.md new file mode 100644 index 0000000..5d2eda8 --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/0_overview.md @@ -0,0 +1,159 @@ +# QIS 量子信息 + +`cqlib.qis` 提供量子信息科学相关的基础对象和本地模拟能力,用于检查量子态、计算可观测量、构造 Pauli 哈密顿量、生成 Trotter 演化线路,并分析保真度、熵和纠缠指标。 + +本章示例都在本地 Python 环境中运行,不连接云平台,也不提交真实量子硬件任务。开始前需要完成 [安装与环境配置](../../0_get_started/1_installation.md),并确认当前导入的是要测试的 Cqlib 版本。 + +```python +import cqlib + +print(cqlib.__file__) +``` + +--- + +## 常用入口 + +QIS 的 Python 入口集中在 `cqlib.qis`,状态模拟器也可以从 `cqlib.qis.state` 子包导入。 + +```python +from cqlib.qis import ( + Statevector, + DensityMatrix, + DensityMatrixNoise, + StabilizerState, + StabilizerCircuitResult, + RuntimeValue, + ClassicalState, + Phase, + Pauli, + PauliString, + Hamiltonian, + TrotterMode, + metrics, + entropy, +) +``` + +| 对象 | 主要用途 | 常见场景 | +|---|---|---| +| `Statevector` | 纯态模拟,保存 `2^n` 个复振幅 | 理想线路验证、VQE/QAOA 小规模能量计算、状态采样 | +| `DensityMatrix` | 密度矩阵模拟,保存 `2^n × 2^n` 复矩阵 | 混态、Kraus 噪声、部分迹、物理性检查 | +| `DensityMatrixNoise` | 带 `NoiseModel` 的密度矩阵模拟器 | 门噪声、读出误差、含噪线路对比 | +| `StabilizerState` | Clifford 稳定子模拟 | 大一些的 Clifford 线路、稳定子生成元、快速采样 | +| `Pauli` / `PauliString` | 单比特和多比特 Pauli 算符 | 可观测量、对易性判断、测量期望值 | +| `Hamiltonian` | Pauli 项稀疏和式 | VQE、QAOA、Ising 模型、时间演化 | +| `TrotterMode` | Trotter-Suzuki 分解模式 | `e^{-iHt}` 演化线路构造 | +| `metrics` / `entropy` | 量子态距离、纯度、熵和纠缠指标 | 态相似度、混合度、纠缠分析 | + +这些对象通常不是替代 `Circuit`,而是用来执行和解释 `Circuit`。线路负责表达程序结构,QIS 负责把线路变成可分析的状态、可观测量和数值指标。 + +--- + +## 从 Bell 态开始 + +Bell 态是 QIS 入门最合适的例子:它很短,但同时包含叠加、纠缠、概率分布和可观测量期望值。 + +```python +from cqlib import Circuit +from cqlib.qis import Hamiltonian, PauliString, Statevector + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +state = Statevector.from_circuit(circuit) + +hamiltonian = Hamiltonian(2) +hamiltonian.add_term(PauliString.from_str("ZZ"), 1.0) + +print(state.probabilities()) +print(hamiltonian.expectation_statevector(state)) +``` + +理想 Bell 态的计算基概率应集中在 `|00>` 和 `|11>`,因此 `ZZ` 的期望值接近 `1.0`。这类检查常用于确认线路结构、比特索引和可观测量定义是否一致。 + +--- + +## 如何选择状态表示 + +不同状态表示的内存和语义不同。写算法原型时,建议先明确问题需要哪一种状态。 + +| 任务 | 推荐对象 | 原因 | +|---|---|---| +| 只需要理想纯态 | `Statevector` | 内存开销低于密度矩阵,适合快速验证 | +| 需要混态或 Kraus 通道 | `DensityMatrix` | 可以表达相干项、退相干和非纯态 | +| 需要自动套用设备噪声模型 | `DensityMatrixNoise` | 与 `NoiseModel` 结合,门后自动注入噪声 | +| 线路只含 Clifford 门 | `StabilizerState` | 对 Clifford 结构更高效,适合稳定子分析 | +| 只需要从采样概率估计期望值 | `PauliString` / `Hamiltonian` | 可以从 Pauli 测量结果计算期望值 | + +如果线路包含测量、重置或动态控制流,通常不能把整条线路看成单一酉矩阵。此时应根据具体目标选择:调试结构用可视化,分析理想酉片段用 `Statevector`,分析噪声和混态用 `DensityMatrix` 或 `DensityMatrixNoise`。 + +--- + +## QIS 的典型工作流 + +一个常见闭环如下:先构造线路,再选择状态模拟器,接着定义可观测量,最后计算概率、期望值或指标。 + +```python +from cqlib import Circuit +from cqlib.qis import Hamiltonian, PauliString, Statevector, metrics + +ansatz = Circuit(2) +ansatz.ry(0, 0.3) +ansatz.cx(0, 1) +ansatz.ry(1, -0.2) + +state = Statevector.from_circuit(ansatz) + +observable = Hamiltonian.from_list([ + (PauliString.from_str("ZI"), -1.0), + (PauliString.from_str("IZ"), -1.0), + (PauliString.from_str("ZZ"), 0.5), +]) + +print("probabilities:", state.probabilities()) +print("energy:", observable.expectation_statevector(state)) +print("purity:", metrics.purity_pure(state)) +``` + +这个流程对应很多算法的内层计算:VQE 关心 `energy`,QAOA 关心成本哈密顿量期望值,误差诊断关心保真度、迹距离、纯度和熵。 + +--- + +## 本章学习路线 + +阅读顺序如下: + +1. [Statevector 纯态模拟](1_statevector.md):学习纯态构造、门作用、线路执行、采样和期望值。 +2. [DensityMatrix 混态模拟](2_density_matrix.md):学习密度矩阵、Kraus 噪声、部分迹和物理性检查。 +3. [DensityMatrixNoise 含噪模拟](3_density_matrix_noise.md):学习如何把 `NoiseModel` 用于门噪声和读出误差模拟。 +4. [StabilizerState 稳定子模拟](4_stabilizer.md):学习 Clifford 线路的稳定子表示、测量和生成元分析。 +5. [Pauli、PauliString 与 Hamiltonian](5_pauli_and_hamiltonian.md):学习 Pauli 群、对易性、哈密顿量、期望值和 Trotter 演化。 +6. [量子态指标与熵](6_metrics_entropy.md):学习纯度、保真度、迹距离、Von Neumann 熵、Rényi 熵和纠缠指标。 + +建议先阅读前两节,建立纯态和混态的基本语义,再阅读 Pauli 与 Hamiltonian。含噪模拟、稳定子模拟和指标计算可以根据算法需求穿插阅读。 + +--- + +## 什么时候使用 QIS 模块 + +在量子程序开发中,QIS 模块通常用于以下位置: + +- 写完一段小线路后,验证最终概率分布是否符合预期; +- 把 ansatz 绑定到一组参数后,计算可观测量期望值; +- 对比理想态、含噪态和目标态之间的保真度或迹距离; +- 对混态做部分迹,查看子系统状态; +- 用 PauliString 检查对易性、测量基和哈密顿量项; +- 把 Pauli 哈密顿量转换为 Trotter 演化线路; +- 用稳定子模拟器快速检查 Clifford 线路。 + +QIS 数值结果不能替代线路可视化和单元测试。推荐在关键算法模块中同时保留线路图、概率检查、期望值检查和必要的物理性检查。 + +--- + +## 下一步 + +·[Statevector 纯态模拟](1_statevector.md):先用理想纯态检查门序列、概率分布、采样和可观测量期望值。 +·[DensityMatrix 混态模拟](2_density_matrix.md):学习密度矩阵、Kraus 噪声、部分迹和物理性验证。 +·[Pauli、PauliString 与 Hamiltonian](5_pauli_and_hamiltonian.md):掌握 Pauli 表示、哈密顿量建模、期望值计算和 Trotter 演化。 diff --git a/docs/documentation/1_cqlib/3_qis/1_statevector.md b/docs/documentation/1_cqlib/3_qis/1_statevector.md new file mode 100644 index 0000000..4aa1399 --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/1_statevector.md @@ -0,0 +1,196 @@ +# Statevector 纯态模拟 + +`Statevector` 用于本地理想纯态模拟。它把 `n` 比特状态保存为长度为 `2^n` 的复振幅向量,适合验证小中规模酉线路、检查概率分布、计算 Pauli/Hamiltonian 期望值,并模拟有限 shot 采样。 + +`Statevector` 默认初始化为 `|0...0>`。示例都不连接硬件,也不引入噪声。 + +--- + +## 任务:制备并读取 Bell 态 + +```python +from cqlib.qis import Statevector + +state = Statevector(2) +state.apply_h(0) +state.apply_cx(0, 1) + +print(state.data) +print(state.probabilities()) +``` + +`data` 返回复振幅数组,`probabilities()` 返回计算基概率分布。Bell 态的理想概率集中在索引 `0` 和 `3`,即 `|00>` 与 `|11>`。 + +阅读 `Statevector` 结果时,重点确认三件事: + +- 振幅数组长度是否为 `2 ** num_qubits`; +- 概率和是否接近 `1.0`; +- 概率主峰是否落在预期 bitstring 上。 + +--- + +## 从初始振幅构造状态 + +如果已经有一组归一化复振幅,可以用 `from_state()` 直接构造纯态。 + +```python +import numpy as np +from cqlib.qis import Statevector + +plus = Statevector.from_state( + 1, + np.array([1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=complex), +) + +print(plus.probabilities()) +``` + +传入数组长度必须等于 `2 ** num_qubits`,并且状态需要归一化。这个入口适合把外部数值结果转换为 Cqlib 状态对象,再继续计算期望值、保真度或采样结果。 + +--- + +## 从 Circuit 执行纯态模拟 + +更常见的用法是先构造 `Circuit`,再用 `from_circuit()` 执行整条理想线路。 + +```python +from cqlib import Circuit +from cqlib.qis import Statevector + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +state = Statevector.from_circuit(circuit) +print(state.probabilities()) +``` + +`Statevector.from_circuit()` 适合执行不含非酉操作的理想线路片段。如果线路里包含测量、重置或动态控制流,应先确认这些操作是否属于当前模拟目标;必要时改用逐步模拟、密度矩阵模拟或专门的动态执行流程。 + +--- + +## 对已有状态原地作用线路 + +当需要从同一个初态出发反复追加不同线路片段时,可以先创建状态,再调用 `apply_circuit()`。 + +```python +from cqlib import Circuit +from cqlib.qis import Statevector + +prefix = Circuit(2) +prefix.h(0) + +suffix = Circuit(2) +suffix.cx(0, 1) + +state = Statevector(2) +state.apply_circuit(prefix) +state.apply_circuit(suffix) + +print(state.probabilities()) +``` + +这种写法适合分段调试。每执行一个模块后都可以打印概率或计算期望值,定位是哪一段线路改变了状态结构。 + +--- + +## 直接施加常用量子门 + +`Statevector` 提供与标准门相对应的 `apply_*` 方法,包括 Pauli 门、Clifford 门、旋转门、受控门、双比特旋转门、`fSim`、`CCX` 和用户自定义幺正矩阵。 + +```python +import numpy as np +from cqlib.qis import Statevector + +state = Statevector(2) +state.apply_x(0) +state.apply_rz(0, np.pi / 4) +state.apply_cx(0, 1) +state.apply_rzz(0, 1, 0.2) + +print(state.probabilities()) +``` + +对于自定义矩阵,可以使用 `apply_single_qubit_gate()`、`apply_double_qubits_gate()` 或 `apply_unitary_gate()`。 + +```python +import numpy as np +from cqlib.qis import Statevector + +state = Statevector(2) +state.apply_x(0) + +swap = np.array( + [ + [1, 0, 0, 0], + [0, 0, 1, 0], + [0, 1, 0, 0], + [0, 0, 0, 1], + ], + dtype=complex, +) +state.apply_unitary_gate([0, 1], swap) + +print(state.probabilities()) +``` + +使用自定义矩阵时,需要自行保证矩阵维度与作用 qubit 数一致,并满足酉性要求。 + +--- + +## 计算可观测量期望值 + +`Statevector.expectation()` 可以接收 `PauliString` 或 `Hamiltonian`。 + +```python +from cqlib.qis import Hamiltonian, PauliString, Statevector + +state = Statevector(2) +state.apply_h(0) +state.apply_cx(0, 1) + +zz = PauliString.from_str("ZZ") +print(state.expectation(zz)) + +hamiltonian = Hamiltonian.from_list([ + (PauliString.from_str("ZZ"), 1.0), + (PauliString.from_str("XX"), 0.5), +]) +print(state.expectation(hamiltonian)) +``` + +也可以从可观测量一侧调用 `expectation_statevector(state)`。在 VQE 或 QAOA 中,推荐固定使用一种写法,避免在日志和后处理代码中混淆状态对象与可观测量对象。 + +--- + +## 测量、重置和采样 + +`measure()` 会测量指定 qubit 并坍缩当前状态,`measure_all()` 会测量所有 qubit。`sample_shots()` 用于按当前分布采样,不会用于修改原状态的调试场景。 + +```python +from cqlib.qis import Statevector + +state = Statevector(2) +state.apply_h(0) +state.apply_cx(0, 1) + +shots = state.sample_shots(1000) +counts = {} +for outcome in shots: + bitstring = outcome.to_bitstring(state.num_qubits) + counts[bitstring] = counts.get(bitstring, 0) + 1 + +print(counts) +print(state.probabilities()) +``` + +如果需要精确理论概率,用 `probabilities()`;如果需要模拟有限 shot 的统计涨落,用 `sample_shots()`。 + + +--- + +## 下一步 + +·[DensityMatrix 混态模拟](2_density_matrix.md):在需要混态、Kraus 噪声和部分迹时,从纯态模拟切换到密度矩阵。 +·[Pauli、PauliString 与 Hamiltonian](5_pauli_and_hamiltonian.md):学习如何把状态结果连接到可观测量、能量函数和 Trotter 演化。 +·[可视化量子态](../5_visualization/7_state_visualization.md):用 Bloch、state city 和 Pauli vector 图解释纯态模拟结果。 diff --git a/docs/documentation/1_cqlib/3_qis/2_density_matrix.md b/docs/documentation/1_cqlib/3_qis/2_density_matrix.md new file mode 100644 index 0000000..58a6864 --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/2_density_matrix.md @@ -0,0 +1,166 @@ +# DensityMatrix 混态模拟 + +`DensityMatrix` 用密度矩阵表示量子态。它既可以表达纯态,也可以表达混态和噪声后的状态,适合进行 Kraus 通道、部分迹、物理性检查、混态期望值和熵指标分析。 + +密度矩阵的内存规模是 `2^n × 2^n`,通常比 `Statevector` 更重。只有在需要混态语义、噪声通道或子系统分析时,才应优先选择它。 + +--- + +## 任务:用密度矩阵制备 `|+>` 态 + +```python +from cqlib.qis import DensityMatrix + +density = DensityMatrix(1) +density.apply_h(0) + +print(density.data) +print(density.trace()) +print(density.probabilities()) +``` + +`data` 返回二维密度矩阵,`trace()` 应接近 `1.0`,`probabilities()` 返回计算基上的对角概率。对于 `|+>` 态,概率是 `[0.5, 0.5]`,但密度矩阵还包含非对角相干项。 + +--- + +## 从纯态振幅或线路创建 + +`DensityMatrix.from_state()` 接收 qubit 数和纯态振幅;`DensityMatrix.from_circuit()` 会把线路作用到初始 `|0...0>` 后得到密度矩阵。 + +```python +import numpy as np +from cqlib import Circuit +from cqlib.qis import DensityMatrix + +plus = DensityMatrix.from_state( + 1, + np.array([1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=complex), +) +print(plus.data) + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +bell_density = DensityMatrix.from_circuit(circuit) +print(bell_density.probabilities()) +``` + +如果已经有完整密度矩阵,可以用 `from_density_matrix()`。输入矩阵需要满足维度、迹和物理性要求。 + +```python +import numpy as np +from cqlib.qis import DensityMatrix + +mixed = DensityMatrix.from_density_matrix( + 1, + np.array( + [ + 0.7 + 0.0j, 0.0 + 0.0j, + 0.0 + 0.0j, 0.3 + 0.0j, + ], + dtype=complex, + ), +) + +print(mixed.probabilities()) +``` + +--- + +## 施加 Kraus 噪声 + +密度矩阵可以直接施加 Kraus 算符。下面示例对 `|1>` 态施加振幅阻尼。 + +```python +import numpy as np +from cqlib.qis import DensityMatrix + +gamma = 0.05 +k0 = np.array([[1, 0], [0, np.sqrt(1 - gamma)]], dtype=complex) +k1 = np.array([[0, np.sqrt(gamma)], [0, 0]], dtype=complex) + +density = DensityMatrix(1) +density.apply_x(0) + +density.apply_kraus([0], [k0.flatten(), k1.flatten()]) + +print(density.probabilities()) +``` + +Kraus 通道适合手动验证噪声数学形式。如果已经有设备级 `NoiseModel`,可以使用 [DensityMatrixNoise 含噪模拟](3_density_matrix_noise.md) 自动在门后加入噪声。 + +--- + +## 对子系统做部分迹 + +`partial_trace(keep=[...])` 会保留指定 qubit,迹掉其余 qubit。它适合分析纠缠态中的局部子系统。 + +```python +from cqlib import Circuit +from cqlib.qis import DensityMatrix + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +bell_density = DensityMatrix.from_circuit(circuit) +subsystem = bell_density.partial_trace(keep=[0]) + +print(subsystem.data) +print(subsystem.probabilities()) +``` + +对 Bell 态任意单比特做部分迹,会得到接近最大混合态的局部密度矩阵。这个结果说明单个 qubit 本身没有确定 Bloch 方向,但全局二比特状态仍然具有纠缠相关性。 + +--- + +## 物理性检查 + +当密度矩阵来自外部数据、数值重构或自定义噪声通道时,应检查它是否仍然是合法量子态。 + +```python +from cqlib.qis import DensityMatrix + +density = DensityMatrix(1) +density.apply_h(0) + +tol = 1e-10 +print(density.is_hermitian(tol)) +print(density.is_positive_semidefinite(tol)) +density.validate_physical(tol) +``` + +物理密度矩阵至少应满足 Hermitian、半正定和迹为 `1`。如果 `validate_physical()` 抛出异常,通常说明输入矩阵、噪声通道或数值后处理存在问题。 + +--- + +## 期望值、测量和采样 + +`DensityMatrix.expectation()` 同样可以接收 `PauliString` 或 `Hamiltonian`。此外,密度矩阵也提供 `measure()`、`measure_all()`、`sample_shots()`、`sample()` 和 `probs()` 等状态模拟接口。 + +```python +from cqlib.qis import DensityMatrix, Hamiltonian, PauliString + +density = DensityMatrix(2) +density.apply_h(0) +density.apply_cx(0, 1) + +hamiltonian = Hamiltonian.from_list([ + (PauliString.from_str("ZZ"), 1.0), + (PauliString.from_str("XX"), 0.5), +]) + +print(density.expectation(hamiltonian)) +print(density.sample_shots(8)) +``` + +测量方法会改变当前状态;采样方法适合生成有限 shot 结果。做噪声模拟或态指标分析时,建议先保存一份 `copy()`,避免测量坍缩影响后续计算。 + +--- + +## 下一步 + +·[DensityMatrixNoise 含噪模拟](3_density_matrix_noise.md):把手动 Kraus 噪声扩展为基于 `NoiseModel` 的自动含噪线路模拟。 +·[量子态指标与熵](6_metrics_entropy.md):用纯度、熵、保真度和纠缠指标解释密度矩阵结果。 +·[可视化量子态](../5_visualization/7_state_visualization.md):用 state city 和 Pauli vector 检查密度矩阵的相干项和 Pauli 展开。 diff --git a/docs/documentation/1_cqlib/3_qis/3_density_matrix_noise.md b/docs/documentation/1_cqlib/3_qis/3_density_matrix_noise.md new file mode 100644 index 0000000..d8a5823 --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/3_density_matrix_noise.md @@ -0,0 +1,158 @@ +# DensityMatrixNoise 含噪模拟 + +`DensityMatrixNoise` 是带噪声模型的密度矩阵模拟器。它在执行量子门时根据 `NoiseModel` 自动注入单比特门噪声、双比特门噪声和读出误差,适合对比理想线路与含噪线路的概率、期望值和采样结果。 + +底层仍然是密度矩阵,因此规模随 qubit 数增长较快。建议先用小线路验证噪声配置,再扩大到算法实验。 + +--- + +## 任务:给 Bell 态加入门噪声 + +```python +from cqlib import Circuit +from cqlib.circuit import StandardGate +from cqlib.device import NoiseModel, SingleQubitNoise, TwoQubitNoise +from cqlib.qis import DensityMatrixNoise + +noise_model = NoiseModel() +noise_model.add_single_qubit_error( + StandardGate.H, + 0, + SingleQubitNoise.depolarizing(0.001), +) +noise_model.add_two_qubit_error( + StandardGate.CX, + 0, + 1, + TwoQubitNoise.depolarizing(0.01), +) + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +simulator = DensityMatrixNoise.from_circuit(circuit, noise_model) +print(simulator.probabilities()) +``` + +理想 Bell 态只在 `00` 和 `11` 上有主峰。加入噪声后,`01` 和 `10` 可能出现非零概率。读图或读数时,应先确认噪声模型配置在哪些门和哪些 qubit 上。 + +--- + +## 配置常见噪声类型 + +`NoiseModel` 可以分别添加单比特门噪声、双比特门噪声和读出误差。 + +```python +from cqlib.circuit import StandardGate +from cqlib.device import NoiseModel, ReadoutError, SingleQubitNoise, TwoQubitNoise + +noise_model = NoiseModel() + +noise_model.add_single_qubit_error( + StandardGate.RY, + 0, + SingleQubitNoise.bit_flip(0.002), +) +noise_model.add_single_qubit_error( + StandardGate.H, + 1, + SingleQubitNoise.phase_flip(0.001), +) +noise_model.add_two_qubit_error( + StandardGate.CX, + 0, + 1, + TwoQubitNoise.depolarizing(0.01), +) +noise_model.add_readout_error( + 0, + ReadoutError(p_0_given_1=0.02, p_1_given_0=0.01), +) +``` + +单比特噪声和双比特噪声会影响量子态演化;读出误差影响观测概率或采样结果,不等价于门后的量子态退相干。 + +--- + +## 区分态概率和读出概率 + +`probabilities()` 返回不含读出误差的量子态概率;`probabilities_with_readout(qubits)` 会在指定测量 qubit 上叠加读出误差。 + +```python +from cqlib.circuit import StandardGate +from cqlib.device import NoiseModel, ReadoutError +from cqlib.qis import DensityMatrixNoise + +noise_model = NoiseModel() +noise_model.add_readout_error(0, ReadoutError(0.02, 0.03)) + +simulator = DensityMatrixNoise(1, noise_model) +simulator.apply_x(0) + +print("state probabilities:", simulator.probabilities()) +print("readout probabilities:", simulator.probabilities_with_readout([0])) +``` + +如果只想分析量子门噪声后的真实量子态,应查看 `probabilities()` 或底层密度矩阵;如果想模拟实验读数,应查看带 readout 的概率或采样接口。 + +--- + +## 逐门执行含噪模拟 + +除了 `from_circuit()`,也可以像状态模拟器一样逐门调用 `apply_*` 方法。每个门执行后都会根据当前 `NoiseModel` 查找对应噪声。 + +```python +from cqlib.circuit import StandardGate +from cqlib.device import NoiseModel, SingleQubitNoise +from cqlib.qis import DensityMatrixNoise + +noise_model = NoiseModel() +noise_model.add_single_qubit_error( + StandardGate.X, + 0, + SingleQubitNoise.bit_flip(0.01), +) + +simulator = DensityMatrixNoise(1, noise_model) +simulator.apply_x(0) + +print(simulator.probabilities()) +``` + +逐门执行适合定位噪声来源。复杂线路中,如果结果异常,可以把线路拆成若干段,每段后检查概率或期望值。 + +--- + +## 期望值和采样 + +`DensityMatrixNoise` 支持与 `Statevector`、`DensityMatrix` 类似的期望值和采样接口。 + +```python +from cqlib import Circuit +from cqlib.qis import DensityMatrixNoise, Hamiltonian, PauliString + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +simulator = DensityMatrixNoise.from_circuit(circuit) + +observable = Hamiltonian.from_list([ + (PauliString.from_str("ZZ"), 1.0), +]) + +print(simulator.expectation(observable)) +print(simulator.sample_shots(16)) +``` + +如果 `NoiseModel` 里配置了读出误差,普通期望值通常表示噪声演化后的量子态期望值;带读出误差的观测结果应通过 readout 概率或相应采样接口单独处理。 + + +--- + +## 下一步 + +·[DensityMatrix 混态模拟](2_density_matrix.md):回到密度矩阵本身,理解 Kraus 通道、部分迹和物理性检查。 +·[可视化执行结果](../5_visualization/6_result_visualization.md):把含噪采样结果画成 histogram 或概率分布,检查主峰和长尾。 +·[设备噪声](../2_device/4_noise.md):了解 `NoiseModel`、门噪声和读出误差在设备模块中的建模方式。 diff --git a/docs/documentation/1_cqlib/3_qis/4_stabilizer.md b/docs/documentation/1_cqlib/3_qis/4_stabilizer.md new file mode 100644 index 0000000..9cec8be --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/4_stabilizer.md @@ -0,0 +1,151 @@ +# StabilizerState 稳定子模拟 + +`StabilizerState` 用稳定子表表示 Clifford 线路。它适合模拟只包含 Clifford 门的线路,并可以直接读取稳定子生成元、destabilizer、Pauli 期望值和测量结果。 + +当线路包含任意角度旋转、`T` 门、`fSim` 或其他非 Clifford 操作时,应改用 `Statevector` 或 `DensityMatrix`。 + +--- + +## 任务:用稳定子模拟 Bell 态 + +```python +from cqlib.qis import StabilizerState + +state = StabilizerState(2) +state.apply_h(0) +state.apply_cx(0, 1) + +print(state.probabilities()) +print(state.get_stabilizers()) +``` + +Bell 态的稳定子生成元可以用来检查纠缠结构。相比只看概率,稳定子生成元能更直接地表达态满足哪些 Pauli 约束。 + +--- + +## 从 Circuit 构造稳定子态 + +如果已有一条 Clifford 线路,可以直接从线路构造稳定子态。 + +```python +from cqlib import Circuit +from cqlib.qis import StabilizerState + +circuit = Circuit(3) +circuit.h(0) +circuit.cx(0, 1) +circuit.cx(1, 2) + +state = StabilizerState.from_circuit(circuit) +print(state.probabilities()) +``` + +也可以用 `apply_circuit()` 把线路原地作用到已有稳定子态。 + +```python +prefix = Circuit(3) +prefix.h(0) + +suffix = Circuit(3) +suffix.cx(0, 1) +suffix.cx(1, 2) + +state = StabilizerState(3) +state.apply_circuit(prefix) +state.apply_circuit(suffix) + +print(state.get_stabilizers()) +``` + +这种分段方式适合检查 Clifford 编码线路、纠错线路或大规模 GHZ 线路的中间稳定子结构。 + +--- + +## 执行带经典状态的稳定子线路 + +`run_circuit()` 返回 `StabilizerCircuitResult`,其中包含最终稳定子态和经典状态。 + +```python +from cqlib import Circuit +from cqlib.qis import StabilizerState + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) +circuit.measure(0) + +result = StabilizerState.run_circuit(circuit) + +print(result.state) +print(result.classical) +``` + +当线路包含测量或动态线路中的经典值时,`run_circuit()` 比只读取最终量子态更适合保留经典执行结果。 + +--- + +## 测量、重置和采样 + +稳定子态支持单比特测量、全测量、重置和有限 shot 采样。 + +```python +from cqlib.qis import StabilizerState + +state = StabilizerState(2) +state.apply_h(0) +state.apply_cx(0, 1) + +shots = state.sample_shots(32) +counts = {} +for outcome in shots: + bitstring = outcome.to_bitstring(state.num_qubits) + counts[bitstring] = counts.get(bitstring, 0) + 1 + +print(counts) + +measured = state.measure_all() +print(measured.to_bitstring(2)) +``` + +`measure()` 和 `measure_all()` 会坍缩当前稳定子态;`sample_shots()` 适合在不手动处理坍缩过程的情况下生成采样结果。 + +--- + +## 查看稳定子、destabilizer 和 Pauli 期望值 + +```python +from cqlib.qis import PauliString, StabilizerState + +state = StabilizerState(2) +state.apply_h(0) +state.apply_cx(0, 1) + +print("stabilizers:", state.get_stabilizers()) +print("destabilizers:", state.get_destabilizers()) +print("ZZ:", state.pauli_expectation(PauliString.from_str("ZZ"))) +print("XI:", state.pauli_expectation(PauliString.from_str("XI"))) +print(state.to_stim_format()) +``` + +`pauli_expectation()` 返回 `-1`、`0` 或 `1`。这适合快速检查某个 Pauli 可观测量是否被当前稳定子态确定。 + +--- + +## 支持范围 + +`StabilizerState` 适合以下操作: + +- 单比特 Clifford 门:`H`、`S`、`Sdg`、`X`、`Y`、`Z`、`X2p`、`X2m`、`Y2p`、`Y2m`; +- 双比特 Clifford 门:`CX`、`CY`、`CZ`、`SWAP`; +- 稳定子语义下的测量、重置、采样和概率读取。 + +如果线路包含非 Clifford 操作,稳定子模拟器无法精确表达完整态。此时应回到 `Statevector` 或 `DensityMatrix`,或者先把非 Clifford 片段单独隔离出来分析。 + + +--- + +## 下一步 + +·[Statevector 纯态模拟](1_statevector.md):当线路包含非 Clifford 门时,用纯态模拟器继续验证。 +·[Pauli、PauliString 与 Hamiltonian](5_pauli_and_hamiltonian.md):理解稳定子生成元背后的 PauliString 表示和 Pauli 期望值。 +·[用文本图调试线路](../5_visualization/1_draw_text.md):在稳定子线路较长时,先用文本图检查门序和控制位。 diff --git a/docs/documentation/1_cqlib/3_qis/5_pauli_and_hamiltonian.md b/docs/documentation/1_cqlib/3_qis/5_pauli_and_hamiltonian.md new file mode 100644 index 0000000..3ab2be6 --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/5_pauli_and_hamiltonian.md @@ -0,0 +1,193 @@ +# Pauli、PauliString 与 Hamiltonian + +Pauli 算符和 Pauli 哈密顿量是 VQE、QAOA、量子模拟、稳定子分析和测量后处理的基础。`cqlib.qis` 提供单比特 `Pauli`、多比特 `PauliString`、全局相位 `Phase` 和 Pauli 项和式 `Hamiltonian`。 + +本节重点不是线路构造,而是如何描述可观测量、判断对易性、计算期望值,并把哈密顿量转换为演化线路。 + +--- + +## 任务:理解单比特 Pauli 与相位 + +```python +from cqlib.qis import Pauli + +x = Pauli.x() +y = Pauli.y() +z = Pauli.z() + +result, phase = x.mul_with_phase(z) + +print(result) +print(phase) +print(phase.to_complex()) +print(y.to_symplectic()) +print(z.to_matrix()) +``` + +Pauli 乘法会产生全局相位。需要保留相位时使用 `mul_with_phase()`;只关心 Pauli 类型时可以使用普通乘法,但不要用它解释完整的相位关系。 + +--- + +## 构造 PauliString + +`PauliString` 表示多比特 Pauli 算符。可以从字符串创建,也可以逐位设置。 + +```python +from cqlib.qis import Pauli, PauliString + +string = PauliString.from_str("XZI") +print(string.num_qubits) +print(string.x_bits) +print(string.z_bits) +print(string.x_mask) +print(string.z_mask) + +manual = PauliString(3) +manual.set_pauli(0, Pauli.x()) +manual.set_pauli(1, Pauli.z()) +manual.set_pauli(2, Pauli.i()) + +print(manual) +``` + +使用字符串和 bitstring 概率一起做后处理时,需要统一 qubit 顺序约定。特别是从概率字典计算期望值时,应确认结果字符串的每一位对应哪个 qubit。 + +--- + +## 判断对易性和相乘 + +```python +from cqlib.qis import PauliString + +xx = PauliString.from_str("XX") +zz = PauliString.from_str("ZZ") +zi = PauliString.from_str("ZI") +iz = PauliString.from_str("IZ") + +print(xx.commutes_with(zz)) +print(zi.commutes_with(iz)) +print(xx * zz) +``` + +对易性决定了 Pauli 项是否可以在同一测量基中合并处理,也影响 Hamiltonian 的演化分解。构建 VQE 或 QAOA 测量流程时,应尽早检查 Pauli 项的对易结构。 + +--- + +## 从状态或概率计算 Pauli 期望值 + +`PauliString` 可以直接对 `Statevector` 或 `DensityMatrix` 计算期望值,也可以从测量概率字典估计期望值。 + +```python +from cqlib.qis import PauliString, Statevector + +state = Statevector(2) +state.apply_h(0) +state.apply_cx(0, 1) + +zz = PauliString.from_str("ZZ") +print(zz.expectation_statevector(state)) + +probs = { + "00": 0.5, + "11": 0.5, +} +print(zz.expectation(probs)) +``` + +从概率字典计算期望值时,只适合已经在对应 Pauli 测量基下得到的概率。对于含 `X` 或 `Y` 的 PauliString,不能直接把计算基采样概率当作对应测量结果,除非线路中已经完成了正确的换基测量。 + +--- + +## 构造 Hamiltonian + +`Hamiltonian` 是 PauliString 与系数的稀疏和式,形式为 `H = Σ c_k P_k`。 + +```python +from cqlib.qis import Hamiltonian, PauliString + +hamiltonian = Hamiltonian(2) +hamiltonian.add_term(PauliString.from_str("ZZ"), 1.0) +hamiltonian.add_term(PauliString.from_str("XI"), 0.5) +hamiltonian.add_term(PauliString.from_str("ZZ"), -0.2) + +print(hamiltonian.num_terms) +hamiltonian.simplify() +print(hamiltonian.terms) +``` + +也可以从列表一次性创建: + +```python +hamiltonian = Hamiltonian.from_list([ + (PauliString.from_str("ZI"), -1.0), + (PauliString.from_str("IZ"), -1.0), + (PauliString.from_str("ZZ"), 0.5), +]) +``` + +`simplify()` 会合并重复 PauliString,并把 PauliString 内部相位吸收到系数中。构造大哈密顿量后,建议先 `simplify()` 再进入模拟、测量分组或演化分解。 + +--- + +## 计算 Hamiltonian 期望值和方差 + +```python +from cqlib.qis import Hamiltonian, PauliString, Statevector + +state = Statevector(2) +state.apply_h(0) +state.apply_cx(0, 1) + +hamiltonian = Hamiltonian.from_list([ + (PauliString.from_str("ZZ"), 1.0), + (PauliString.from_str("XX"), 0.5), +]) + +print(hamiltonian.expectation_statevector(state)) +print(hamiltonian.variance_statevector(state)) +``` + +如果输入是密度矩阵,可以使用 `expectation_density_matrix()`。如果输入来自真实采样结果,可以使用 `expectation_probs()` 汇总每个 Pauli 测量基下的概率。 + +--- + +## 构造 Trotter 演化线路 + +`Hamiltonian` 可以转换为近似实现 `e^{-iHt}` 的量子线路。`TrotterMode` 提供一阶、二阶和随机化模式。 + +```python +from cqlib.qis import Hamiltonian, PauliString, TrotterMode + +hamiltonian = Hamiltonian.from_list([ + (PauliString.from_str("ZZ"), 0.5), + (PauliString.from_str("XI"), 0.3), +]) + +circuit = hamiltonian.to_trotter_circuit( + time=1.0, + steps=4, + mode=TrotterMode.first_order(), +) + +print(circuit) +``` + +对于对易项,`to_evolution_circuit()` 可以使用更直接的演化分解;对于非对易项,会回退到指定 Trotter 模式。 + +```python +circuit = hamiltonian.to_evolution_circuit( + time=1.0, + steps=4, + mode=TrotterMode.second_order(), +) +``` + +时间演化线路的精度取决于哈密顿量项是否对易、总演化时间、Trotter 步数和分解阶数。正式实验前应先在小规模系统上做误差对比。 + +--- + +## 下一步 + +·[Statevector 纯态模拟](1_statevector.md):把 PauliString 和 Hamiltonian 作用到理想纯态上,验证期望值和能量函数。 +·[DensityMatrix 混态模拟](2_density_matrix.md):把同一组可观测量用于混态和含噪态分析。 +·[量子态指标与熵](6_metrics_entropy.md):在能量之外,继续比较保真度、迹距离、纯度和纠缠指标。 diff --git a/docs/documentation/1_cqlib/3_qis/6_metrics_entropy.md b/docs/documentation/1_cqlib/3_qis/6_metrics_entropy.md new file mode 100644 index 0000000..35f8bd0 --- /dev/null +++ b/docs/documentation/1_cqlib/3_qis/6_metrics_entropy.md @@ -0,0 +1,179 @@ +# 量子态指标与熵 + +`cqlib.qis.metrics` 和 `cqlib.qis.entropy` 提供量子态相似度、混合度、熵和纠缠指标。它们常用于比较理想态与含噪态、判断密度矩阵是否退相干、分析子系统纠缠,以及为算法实验生成辅助评价指标。 + +使用前应先确认输入对象是 `Statevector` 还是 `DensityMatrix`。纯态指标和混态指标的函数名不同,不要混用。 + +--- + +## 常用函数一览 + +| 模块 | 函数 | 输入 | 含义 | +|---|---|---|---| +| `metrics` | `purity_pure` | `Statevector` | 纯态纯度 | +| `metrics` | `purity_mixed` | `DensityMatrix` | 混态纯度 `Tr(ρ²)` | +| `metrics` | `state_fidelity_pure` | 两个 `Statevector` | 纯态保真度 | +| `metrics` | `state_fidelity_pure_mixed` | `Statevector`, `DensityMatrix` | 纯态与混态保真度 | +| `metrics` | `state_fidelity_mixed` | 两个 `DensityMatrix` | 混态保真度 | +| `metrics` | `trace_distance_pure` | 两个 `Statevector` | 纯态迹距离 | +| `metrics` | `trace_distance_mixed` | 两个 `DensityMatrix` | 混态迹距离 | +| `metrics` | `entropy` | `DensityMatrix` | Von Neumann 熵 | +| `metrics` | `partial_transpose` | `DensityMatrix`, qubit 列表 | 部分转置 | +| `metrics` | `logarithmic_negativity` | `DensityMatrix`, 子系统 | 对数负性 | +| `entropy` | `linear_entropy` | `DensityMatrix` | 线性熵 | +| `entropy` | `renyi_entropy` | `DensityMatrix`, `alpha` | Rényi 熵 | +| `entropy` | `entanglement_entropy_pure` | `Statevector`, 子系统 | 纯态纠缠熵 | +| `entropy` | `negativity` | `DensityMatrix`, 子系统 | 负性 | +| `entropy` | `concurrence` | 2-qubit `DensityMatrix` | 两比特 concurrence | +| `entropy` | `entanglement_of_formation` | 2-qubit `DensityMatrix` | 纠缠形成 | + +--- + +## 任务:比较两个纯态 + +```python +from cqlib.qis import Statevector, metrics + +plus = Statevector(1) +plus.apply_h(0) + +minus = Statevector(1) +minus.apply_h(0) +minus.apply_z(0) + +print(metrics.purity_pure(plus)) +print(metrics.state_fidelity_pure(plus, minus)) +print(metrics.trace_distance_pure(plus, minus)) +``` + +保真度越接近 `1`,两个态越相似;迹距离越接近 `0`,两个态越相似。对于正交纯态,保真度应接近 `0`,迹距离应接近 `1`。 + +--- + +## 分析混态纯度和熵 + +```python +import numpy as np +from cqlib.qis import DensityMatrix, entropy, metrics + +mixed = DensityMatrix.from_density_matrix( + 1, + np.array( + [ + 0.7 + 0.0j, 0.0 + 0.0j, + 0.0 + 0.0j, 0.3 + 0.0j, + ], + dtype=complex, + ), +) + +print(metrics.purity_mixed(mixed)) +print(metrics.entropy(mixed)) +print(entropy.linear_entropy(mixed)) +print(entropy.renyi_entropy(mixed, alpha=2.0)) +``` + +纯度越接近 `1`,状态越接近纯态;熵和线性熵越大,状态越混合。解释含噪结果时,建议同时查看概率分布和纯度/熵指标,因为概率相似不一定代表相干结构相同。 + +--- + +## 计算 Bell 态的纠缠熵 + +对纯态,可以通过对子系统做约化得到纠缠熵。 + +```python +from cqlib.qis import Statevector, entropy + +state = Statevector(2) +state.apply_h(0) +state.apply_cx(0, 1) + +print(entropy.entanglement_entropy_pure(state, [0])) +``` + +Bell 态任意一个单比特子系统都接近最大混合态,因此纠缠熵接近 `1` bit。对于乘积态,纠缠熵应接近 `0`。 + +--- + +## 计算负性和对数负性 + +混态纠缠分析常用负性或对数负性。下面先用 Bell 态线路构造密度矩阵,再计算子系统 `[0]` 的纠缠指标。 + +```python +from cqlib import Circuit +from cqlib.qis import DensityMatrix, entropy, metrics + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +density = DensityMatrix.from_circuit(circuit) + +print(entropy.negativity(density, [0])) +print(metrics.logarithmic_negativity(density, sys_a=[0])) +``` + +负性为 `0` 通常表示在对应划分下没有通过 PPT 标准检测到纠缠;负性越大,说明部分转置后出现的负特征值越明显。 + +--- + +## 两比特 concurrence 和纠缠形成 + +`concurrence()` 和 `entanglement_of_formation()` 针对两比特密度矩阵。 + +```python +from cqlib.qis import DensityMatrix, entropy + +state = DensityMatrix(2) +state.apply_h(0) +state.apply_cx(0, 1) + +print(entropy.concurrence(state)) +print(entropy.entanglement_of_formation(state)) +``` + +这两个指标适合两比特纠缠分析。对于更多 qubit 的系统,应改用纠缠熵、负性、对数负性或按子系统划分的其他指标。 + +--- + +## 对比理想态和含噪态 + +下面把理想 Bell 态和一个含噪密度矩阵放在一起比较。 + +```python +from cqlib import Circuit +from cqlib.circuit import StandardGate +from cqlib.device import NoiseModel, TwoQubitNoise +from cqlib.qis import DensityMatrix, DensityMatrixNoise, Statevector, metrics + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) + +ideal_state = Statevector.from_circuit(circuit) +ideal_density = DensityMatrix.from_circuit(circuit) + +noise_model = NoiseModel() +noise_model.add_two_qubit_error( + StandardGate.CX, + 0, + 1, + TwoQubitNoise.depolarizing(0.02), +) +noisy = DensityMatrixNoise.from_circuit(circuit, noise_model) +noisy_density = DensityMatrix.from_density_matrix(2, noisy.state.reshape(-1)) + +print(metrics.state_fidelity_pure_mixed(ideal_state, noisy_density)) +print(metrics.trace_distance_mixed(ideal_density, noisy_density)) +print(metrics.purity_mixed(noisy_density)) +``` + +这类指标适合回答“含噪态离理想态有多远”。如果只看最终 counts,可能会忽略相干项、相位和混合度的变化。 + +--- + +## 下一步 + +·[DensityMatrix 混态模拟](2_density_matrix.md):回到密度矩阵构造、部分迹和物理性验证,理解指标输入从哪里来。 +·[DensityMatrixNoise 含噪模拟](3_density_matrix_noise.md):用含噪模拟生成待比较的混态结果。 +·[可视化量子态](../5_visualization/7_state_visualization.md):把数值指标和 Bloch、state city、Pauli vector 图结合起来解释。 diff --git a/docs/documentation/1_cqlib/4_compiler/0_overview.md b/docs/documentation/1_cqlib/4_compiler/0_overview.md new file mode 100644 index 0000000..f8738c6 --- /dev/null +++ b/docs/documentation/1_cqlib/4_compiler/0_overview.md @@ -0,0 +1,174 @@ +# 编译优化 + +Cqlib 2.0 Python 绑定以 **`cqlib.compile`** 为推荐入口:一次调用完成规范化、知识规则优化、分解、可选设备布局与 SABRE 路由、目标门集翻译。 + +--- + +## 常用入口 + +```python +from cqlib import Circuit +from cqlib.compile import CompileConfig, CompileMode, compile +from cqlib.compile.transform.layout import vf2_perfect_layout, sabre_layout +from cqlib.compile.transform.routing import route_sabre +from cqlib.compile.transform import KnowledgeRewriter, RewriteConfig +``` + +--- + +## 推荐编译管线 + +```python +from cqlib import Circuit +from cqlib.compile import CompileMode, compile +from cqlib.device import Device + +circuit = Circuit(3) +circuit.h(0) +circuit.cx(0, 2) + +device = Device.line("line-3", 3) + +result = compile( + circuit, + mode=CompileMode.enhanced(), + device=device, + target_basis=["H", "CX", "RZ"], + seed=42, +) + +print("changed:", result.changed) +for step in result.steps: + if step.changed or not step.skipped: + print(step.stage, step.name, step.reason) + +compiled = result.circuit +``` + +--- + +## 分步调试管线 + +需要单独观察布局、路由或规则优化时,可拆开调用: + +```python +from cqlib.compile.transform.layout import LayoutObjective, vf2_perfect_layout +from cqlib.compile.transform.routing import route_sabre +from cqlib.compile.sabre import SabreConfig + +objective = LayoutObjective.topology_only() +config = SabreConfig.deterministic_seeded(42) + +# 1) 仅布局(不插 SWAP) +layout_result = vf2_perfect_layout(circuit, device, objective) + +# 2) 布局 + 路由(插 SWAP) +route_result = route_sabre(circuit, device, objective, config) +print("swap_count:", route_result.swap_count) +``` + +--- + +## 编译目标 + +1. 将逻辑线路适配目标设备拓扑(布局 + 路由,必要时插入 SWAP); +2. 减少双比特门数量、线路深度与冗余单比特门; +3. 将门序列 lowering 为目标原生门集。 + +--- + +## 流水线概览 + +```text +逻辑线路 (Circuit) + → canonicalize.input + → decompose.definitions + → optimize.pre_decomposition + → decompose.unitary / mc_gates + → optimize.post_decomposition + → route.sabre + → optimize.post_routing + → translate.target_basis + → optimize.target_cleanup + → canonicalize.output +``` + +--- + +## CompileMode + +| 模式 | 说明 | +|------|------| +| `CompileMode.normal()` | 生产默认可预测:保守 rewrite 预算与 SABRE 试次 | +| `CompileMode.enhanced()` | 更强 rewrite、更多 SABRE trials、路由后/目标基清理 | + +--- + +## CompileConfig 要点 + +| 字段 | 作用 | +|------|------| +| `mode` | `normal` / `enhanced` | +| `device` | 可用比特、拓扑、可选 native gates 与标定 | +| `target_basis` | 显式目标门集(优先于 device.native_gates) | +| `initial_layout` | 跳过自动布局,直接用给定映射做 SABRE 路由 | +| `resource_policy` | 分解阶段辅助比特策略 | +| `seed` | 启发式布局/路由随机试次 | + +--- + +## CompileConfig 与 CompilerWorkflow + +需要复用同一配置编译多条线路时,使用 `CompilerWorkflow`: + +```python +from cqlib.compile import CompileConfig, CompileMode, CompilerWorkflow +from cqlib.device import Device + +config = CompileConfig( + mode=CompileMode.enhanced(), + device=Device.line("line-8", 8), + target_basis=["H", "CX", "RZ"], + seed=42, +) +workflow = CompilerWorkflow(config) + +for circuit in circuits: + result = workflow.run(circuit) + print(result.changed, len(result.circuit.operations)) +``` + +--- + +## 分解与资源策略 + +工作流中的 `decompose.definitions` / `decompose.unitary` / `decompose.mc_gates` 也可单独调用(调试多控门分解时有用): + +```python +from cqlib.compile.resource import ResourcePolicy +from cqlib.compile.transform.decompose import ( + decompose_mc_gates_for_device, + decompose_unitaries, +) + +# 多控门分解(受设备容量约束) +result = decompose_mc_gates_for_device( + circuit, + device, + resource_policy=ResourcePolicy(max_pre_layout_clean_ancillas=2), +) + +# 矩阵酉门分解 +unitary_result = decompose_unitaries(circuit) +``` + +`ResourcePolicy` 控制编译器可创建的 **clean ancilla** 数量;设备 **硬容量** 由 `device.num_usable_qubits` 决定,二者独立。 + +--- + +## 下一步 + +- [初始布局(Layout)](1_layout.md):学习 VF2、greedy、sabre_layout 等初始映射算法。 +- [SABRE 路由映射](2_sabre_mapping.md):了解如何用启发式 SWAP 将线路路由到设备拓扑。 +- [模板匹配与知识规则优化](3_template_optimization.md):掌握 `compile()` 与 `KnowledgeRewriter` 的局部优化能力。 +- [对易与 Clifford-RZ 优化](4_commutative_and_clifford.md):理解对易判定如何支撑旋转合并与规则重排。 diff --git a/docs/documentation/1_cqlib/4_compiler/1_layout.md b/docs/documentation/1_cqlib/4_compiler/1_layout.md new file mode 100644 index 0000000..e7afbcc --- /dev/null +++ b/docs/documentation/1_cqlib/4_compiler/1_layout.md @@ -0,0 +1,136 @@ +# 初始布局(Layout) + +初始布局只负责选择逻辑比特到物理比特的初始映射,不插入 SWAP。 + +--- + +## Python 入口 + +```python +from cqlib import Circuit +from cqlib.compile.transform.layout import ( + LayoutObjective, + Vf2LayoutConfig, + vf2_perfect_layout, + greedy_layout, + sabre_layout, + trivial_layout, +) +from cqlib.device import Device +``` + +--- + +## 快速示例:VF2 完美嵌入 + +```python +circuit = Circuit(3) +circuit.cx(0, 1) +circuit.cx(1, 2) + +device = Device.line("line-5", 5) +objective = LayoutObjective.topology_only() + +result = vf2_perfect_layout(circuit, device, objective) +print(result.layout) # 逻辑 → 物理映射 +print(result.diagnostics.is_perfect) # 是否所有交互都邻接 +``` + +若无完美嵌入,`vf2_perfect_layout` 会抛出 `ValueError`;此时改用 `greedy_layout` 或 `sabre_layout`,再交给 `route_sabre` 或 `route_with_layout`。 + +--- + +## Trivial 布局(恒等映射) + +逻辑比特 `i → 物理 i`,用于基线对比或已对齐拓扑: + +```python +result = trivial_layout(circuit, device, objective) +print(result.layout.l2p_map) +``` + +--- + +## Greedy 布局 + +```python +objective = LayoutObjective.fidelity_aware() +result = greedy_layout(circuit, device, objective) +print("is_perfect:", result.diagnostics.is_perfect) +print("score:", result.score.total if result.score else None) +``` + +特点:确定性、速度快,适合作为 SABRE 布局种子;长链/大扇出上可能 `is_perfect=False`。 + +--- + +## SABRE 初始布局 + +```python +from cqlib.compile.sabre import SabreConfig + +config = SabreConfig.deterministic_seeded(42) +result = sabre_layout(circuit, device, objective, config) +``` + +`sabre_layout` 生成多组候选并经前向/后向试跑精修,仍不对外插入 SWAP。 + +--- + +## LayoutObjective(布局评分) + +| 构造方式 | 行为 | +|----------|------| +| `LayoutObjective.topology_only()` | 仅拓扑距离与方向不匹配 | +| `LayoutObjective.fidelity_aware()` | 默认保真度权重 | +| `LayoutObjective.fidelity_required(device)` | 要求设备有可用标定 | +| `LayoutObjective.auto_from_device(device)` | 有标定则 fidelity,否则 topology | + +Enhanced 模式 `compile(..., device=...)` 在设备有标定时使用 `fidelity_required` 逻辑。 + +--- + +## VF2 配置 + +```python +from cqlib.compile.transform.layout import Vf2EdgeRequirement + +config = Vf2LayoutConfig( + candidate_limit=10, + edge_requirement=Vf2EdgeRequirement.positive_interactions(), +) +result = vf2_perfect_layout(circuit, device, objective, config) +``` + +--- + +## 布局结果交给路由 + +```python +from cqlib.compile.transform.routing import route_with_layout +from cqlib.compile.sabre import SabreConfig + +layout_result = vf2_perfect_layout(circuit, device, objective) +routed = route_with_layout( + circuit, + device, + layout_result.layout, + SabreConfig.deterministic_seeded(42), +) +print("swaps:", routed.swap_count) +``` + +--- + +## 说明 + +- 输入为 **逻辑** `Circuit` + `Device`;输出 `LayoutResult.layout` 为 `cqlib.device.Layout`。 +- 布局阶段 **不改变门语义**,**不插入 SWAP**。 +- 设备标定通过 `Device` 上的 readout / two-qubit 误差字段参与 fidelity 评分。 + +--- + +## 下一步 + +- [SABRE 路由映射](2_sabre_mapping.md):将布局结果交给 `route_sabre` 或 `route_with_layout` 完成物理路由。 +- [编译优化](0_overview.md):回顾 `compile()` 工作流与各编译阶段的整体关系。 diff --git a/docs/documentation/1_cqlib/4_compiler/2_sabre_mapping.md b/docs/documentation/1_cqlib/4_compiler/2_sabre_mapping.md new file mode 100644 index 0000000..c392f2a --- /dev/null +++ b/docs/documentation/1_cqlib/4_compiler/2_sabre_mapping.md @@ -0,0 +1,154 @@ +# SABRE 路由映射 + +SABRE 通过启发式 SWAP 将逻辑两比特门路由到设备允许的物理耦合上。 + +--- + +## 两个入口 + +| API | 作用 | +|-----|------| +| `route_sabre(circuit, device, objective, config)` | 自动 `sabre_layout` + 路由 | +| `route_with_layout(circuit, device, initial_layout, config)` | 仅路由,跳过布局搜索 | + +```python +from cqlib import Circuit +from cqlib.compile.transform.layout import LayoutObjective +from cqlib.compile.transform.routing import route_sabre, route_with_layout +from cqlib.compile.sabre import SabreConfig +from cqlib.device import Device, Layout + +circuit = Circuit(3) +circuit.cx(0, 2) + +device = Device.line("line-3", 3) +objective = LayoutObjective.topology_only() +config = SabreConfig.deterministic_seeded(42) + +result = route_sabre(circuit, device, objective, config) +print("swap_count:", result.swap_count) +print("ops:", len(result.circuit.operations)) +``` + +--- + +## 与 compile 工作流集成 + +```python +from cqlib.compile import CompileMode, compile + +result = compile( + circuit, + mode=CompileMode.enhanced(), + device=device, + seed=42, +) + +for step in result.steps: + if step.name == "route.sabre": + print(step.changed, step.reason) +``` + +`initial_layout` 已提供时,工作流跳过自动布局,仍用相同 SABRE 路由器。 + +--- + +## 示例:仅路由(跳过布局搜索) + +```python +from cqlib.device import Layout + +initial = Layout.from_pairs([(0, 0), (1, 2), (2, 1)], physical_count=3) +routed = route_with_layout(circuit, device, initial, config) +print("swaps:", routed.swap_count) +``` + +--- + +## 示例:compile 传入 initial_layout + +```python +from cqlib.compile import compile + +result = compile( + circuit, + device=device, + initial_layout=initial, + seed=42, +) +``` + +--- + +## SabreConfig + +| 字段 | 含义 | +|------|------| +| `layout_trials` | 随机初始布局试次数 | +| `refinement_iterations` | 每个候选的前向+后向精修轮数 | +| `layout_scoring_trials` | 评分每个精修布局的路由试次数 | +| `routing_trials` | 最终选路的并行试次数 | +| `trial_objective` | `swap_then_depth` / `depth_then_swap` 等 | +| `seed` | 确定性种子 | +| `heuristic` | `SabreHeuristicConfig`:lookahead、decay 等 | + +```python +from cqlib.compile.sabre import SabreConfig, SabreTrialObjective + +config = SabreConfig( + seed=42, + layout_trials=24, + refinement_iterations=2, + routing_trials=12, + trial_objective=SabreTrialObjective.swap_then_depth(), +) +``` + +快捷构造:`SabreConfig.deterministic_seeded(42)`。 + +--- + +## 示例:不同 trial_objective 对比 + +```python +from cqlib.compile.sabre import SabreTrialObjective + +for obj in ( + SabreTrialObjective.swap_count(), + SabreTrialObjective.depth(), + SabreTrialObjective.swap_then_depth(), +): + cfg = SabreConfig(routing_trials=4, seed=123, trial_objective=obj) + routed = route_sabre(circuit, device, objective, cfg) + print(obj, "swaps:", routed.swap_count) +``` + +--- + +## 示例:固定 seed 的可复现记录 + +```python +compile_record = { + "seed": 123, + "layout_trials": 24, + "routing_trials": 12, + "trial_objective": "swap_then_depth", +} +``` + +路由含随机性,正式实验必须固定并记录 `seed`。 + +--- + +## 输出语义 + +- `result.circuit`:物理比特编号上的线路,含插入的 `SWAP`; +- `swap_count`:应与线路中 SWAP 门数量一致; +- 路由保证无向物理邻接。 + +--- + +## 下一步 + +- [模板匹配与知识规则优化](3_template_optimization.md):了解路由后知识规则如何进一步清理与改写线路。 +- [初始布局(Layout)](1_layout.md):复习初始布局算法与 `LayoutObjective` 的评分方式。 diff --git a/docs/documentation/1_cqlib/4_compiler/3_template_optimization.md b/docs/documentation/1_cqlib/4_compiler/3_template_optimization.md new file mode 100644 index 0000000..9028214 --- /dev/null +++ b/docs/documentation/1_cqlib/4_compiler/3_template_optimization.md @@ -0,0 +1,209 @@ +# 模板匹配与知识规则优化 + +模板优化用于在线路中寻找一段局部门序列,并把它替换为语义等价、成本更低或更符合目标门集的序列。日常使用时,您通常不需要手动维护模板列表,直接调用 `compile()` 即可使用内置规则完成常见优化。 + +如果需要观察或调试局部优化过程,可以使用 `KnowledgeRewriter`。它和 `compile()` 使用同一套内置知识规则。规则通常由以下几部分组成: + +- `match`:要识别的源操作序列; +- `require`:可选的参数约束,例如角度相等或模 `2π`、`4π` 相等; +- `rewrite`:替换后的目标操作序列。 + +典型规则包括相邻逆门抵消、旋转合并、零角度归一化、门分解、目标门集改写以及显式对易规则。 + +`compile()` 和 `KnowledgeRewriter` 都不会修改输入线路,而是返回包含新线路的结果对象。优化前后的线路应保持量子语义等价;如果规则涉及全局相位,Cqlib 会显式保留或折叠相应的 `GPhase` 信息。 + +--- + +## 在编译管线中使用 + +推荐从统一的 `compile()` 入口使用模板优化能力。编译工作流会自动组合规范化、定义展开、知识规则重写、门分解、可选路由和目标门集转换。 + +```python +from cqlib.circuit import Circuit +from cqlib.compile import compile + +circuit = Circuit(1) +circuit.h(0) +circuit.h(0) + +result = compile(circuit) +optimized = result.circuit + +print("changed:", result.changed) +print("before:", len(circuit.operations)) +print("after:", len(optimized.operations)) + +for step in result.steps: + if "optimize" in step.name: + print(step.name, step.changed) +``` + +对于多数使用场景,不需要直接调用规则重写器。`compile()` 会选择生产配置,并在输出前再次规范化线路表示。 + +--- + +## 直接运行知识规则重写 + +如果需要在测试、诊断或自定义编译流程中单独运行局部规则优化,可以直接使用 `KnowledgeRewriter`。 + +```python +from cqlib.circuit import Circuit +from cqlib.compile.transform import KnowledgeRewriter + +circuit = Circuit(1) +circuit.x(0) +circuit.x(0) + +result = KnowledgeRewriter.production().run(circuit) +optimized = result.circuit + +print("changed:", result.changed) +print("rounds:", result.stats.rounds_executed) +print("rules:", result.stats.rules_applied) +print("after:", len(optimized.operations)) +``` + +`KnowledgeRewriter.production()` 使用保守优化规则,默认启用简化、抵消、合并和规范化相关规则。重写器只会接受能带来局部收益的改写,避免在线路的等价写法之间来回切换。 + +也可以使用函数式入口: + +```python +from cqlib.compile.transform import rewrite_circuit + +result = rewrite_circuit(circuit) +optimized = result.circuit +``` + +--- + +## 规则示例 + +内建规则使用轻量 DSL 描述。例如 H-H 抵消可以理解为: + +```text +rule cancel_h { + match { H 0, H 0 } + rewrite {} +} +``` + +旋转合并规则可以理解为: + +```text +rule merge_rz { + match { RZ(a) 0, RZ(b) 0 } + rewrite { RZ(a + b) 0 } +} +``` + +带条件的规则会先检查参数关系。例如两个互逆的 `RZ` 只有在角度和满足对应模关系时才会被删去;如果相差一个全局相位,规则会显式生成 `GPhase`,再交给规范化器折叠到线路全局相位中。 + +--- + +## 优化配置 + +`RewriteConfig` 用于控制规则搜索范围和应用方式。Python 侧使用构造函数传入配置项。 + +```python +from cqlib.compile.transform import KnowledgeRewriter, RewriteConfig + +config = RewriteConfig( + max_rounds=8, + max_window_ops=16, + max_pattern_len=8, + recurse_control_flow=True, + skip_labeled_ops=True, +) + +result = KnowledgeRewriter(config).run(circuit) +``` + +几个常用参数的含义如下: + +- `max_rounds`:最多执行多少轮重写;如果提前到达不动点,会提前停止; +- `max_window_ops`:一次局部搜索允许查看的最大操作窗口; +- `max_pattern_len`:可匹配规则的最大长度; +- `recurse_control_flow`:是否递归优化控制流 body; +- `skip_labeled_ops`:是否跳过带 label 的操作,避免破坏调试标记或外部约定。 + +如果只是想使用默认生产配置,可以直接写: + +```python +config = RewriteConfig.production() +result = KnowledgeRewriter(config).run(circuit) +``` + +--- + +## 目标门集改写 + +模板规则不仅用于“减少门数”,也用于把线路改写到指定目标门集。推荐通过 `compile()` 的 `target_basis` 参数指定目标门集: + +```python +from cqlib.circuit import Circuit +from cqlib.compile import compile + +circuit = Circuit(2) +circuit.cx(0, 1) + +result = compile(circuit, target_basis=["H", "CZ"]) +optimized = result.circuit + +print([op.instruction for op in optimized.operations]) +``` + +如果是在自定义编译流程中直接调用知识规则重写,可以使用 lowering 配置并显式传入目标指令: + +```python +from cqlib.circuit import Circuit, Instruction, StandardGate +from cqlib.compile.transform import KnowledgeRewriter, RewriteConfig, RewriteMode + +circuit = Circuit(2) +circuit.cx(0, 1) + +basis = [ + Instruction.from_standard_gate(StandardGate.H), + Instruction.from_standard_gate(StandardGate.CZ), + Instruction.from_standard_gate(StandardGate.RZ), +] + +config = RewriteConfig( + mode=RewriteMode.lowering(), + target_instructions=basis, +) + +result = KnowledgeRewriter(config).run(circuit) +``` + +在完整编译工作流中,目标门集转换发生在物理路由之后,因为路由可能插入新的 `SWAP` 或暴露新的局部清理机会。 + +--- + +## 与传统模板匹配的关系 + +传统模板优化通常从“给定模板线路,在线路中找等价子序列”出发。Cqlib 当前实现更接近“知识规则重写”: + +- 模板被结构化为经过验证的规则; +- 参数关系由 `require` 条件表达; +- 规则按类别启用,便于区分优化、分解和硬件原生门转换; +- 重写器使用局部成本模型控制是否应用规则; +- 工作流会在多个阶段重复调用,直到达到不动点或预算上限。 + +因此,用户可以把模板优化理解为“基于内置知识库的局部门序列改写”。当前文档中的推荐入口是 `compile()`;需要单独观察局部规则效果时,再使用 `KnowledgeRewriter`。 + +--- + +## 使用建议 + +- 普通编译优先使用 `compile()`,不要手动拼接多个优化步骤; +- 自定义流程中优先使用 `KnowledgeRewriter.production()`,只有做目标门集转换时才使用 lowering 模式; +- 添加新规则时必须同时考虑结构合法性、参数约束和语义等价性; +- 优化前后应比较门数、双比特门数、深度和关键线路的矩阵或状态等价性; +- 如果规则可能改变全局相位,应显式保留 `GPhase` 或确认全局相位对任务无影响。 + +--- + +## 下一步 + +- [对易与 Clifford-RZ 优化](4_commutative_and_clifford.md):了解对易判定如何帮助规则重排和旋转合并。 + diff --git a/docs/documentation/1_cqlib/4_compiler/4_commutative_and_clifford.md b/docs/documentation/1_cqlib/4_compiler/4_commutative_and_clifford.md new file mode 100644 index 0000000..56405bc --- /dev/null +++ b/docs/documentation/1_cqlib/4_compiler/4_commutative_and_clifford.md @@ -0,0 +1,180 @@ +# 对易与 Clifford-RZ 优化 + +对易分析用于判断两个具体操作是否可以交换顺序。它本身不一定直接减少门数,但可以为后续规则匹配、旋转合并、取消冗余门和目标门集整理创造条件。Clifford-RZ 优化常见于包含 `H`、`S`、`X`、`Z`、`CX`、`CZ` 与 `RZ`、`Phase`、`T` 等相位门的线路片段。 + +日常使用时,推荐直接调用 `compile()`。编译器会在合适的阶段自动使用对易判断、规范化和知识规则重写。只有在编写自定义分析或调试优化规则时,才需要直接使用 `CommutationChecker`。 + +`compile()` 和对易检查接口都不会修改输入线路;它们会返回新的结果对象或证明对象。对易检查只负责回答“是否能安全交换这两个操作”,并不是一个直接改写整条线路的优化步骤。 + +--- + +## 对易检查入口 + +在 Python 中,对易检查以 `ValueOperation` 为输入。每个 `ValueOperation` 都是一条完整的操作应用,包含门、作用比特和参数。 + +```python +from cqlib.circuit import Parameter, Qubit, StandardGate, ValueOperation +from cqlib.compile.commutation import CommutationChecker + +lhs = ValueOperation.from_standard_gate( + StandardGate.CX, + [Qubit(0), Qubit(1)], +) +rhs = ValueOperation.from_standard_gate( + StandardGate.RZ(Parameter("theta")), + [Qubit(0)], +) + +checker = CommutationChecker.builtin() +proof = checker.check(lhs, rhs) + +if proof is not None: + print("exact:", proof.is_exact()) + print("phase:", proof.phase) +``` + +也可以使用共享的函数式入口: + +```python +from cqlib.compile.commutation import check_commutation + +proof = check_commutation(lhs, rhs) +``` + +返回值可能是: + +- 精确对易证明:表示可以精确交换; +- 带全局相位的对易证明:表示交换会引入一个全局相位; +- `None`:表示当前检查器无法证明可交换,不等价于已经证明不可交换。 + +这种保守语义很重要。编译器只在有证明时使用对易结论,避免因为证明覆盖不足而破坏线路语义。 + +--- + +## 检查顺序 + +内建检查器按固定顺序尝试证明: + +1. 快速局部事实:恒等门、全局相位、作用在不同比特集合上的门、完全相同的操作; +2. 代数证明:Pauli 轴、Pauli 旋转、对角门、受控单轴门和部分对称双比特门; +3. 规则库证明:从内建知识规则中提取 `A; B -> B; A` 形式的显式交换规则; +4. 小规模矩阵检查:在比特数限制内构造局部矩阵,验证 `AB` 与 `BA` 是否相等或只差全局相位。 + +默认配置会启用规则库证明和矩阵回退检查,并将矩阵检查的联合支撑比特数限制为 4。 + +```python +from cqlib.compile.commutation import CommutationChecker, CommutationConfig + +config = CommutationConfig( + enable_rule_oracle=True, + enable_matrix_fallback=True, + max_matrix_qubits=4, +) + +checker = CommutationChecker.with_config(config) +proof = checker.check(lhs, rhs) +``` + +如果在大规模静态分析中更关注速度,可以关闭矩阵回退检查;如果希望完全依赖结构规则,也可以同时关闭规则库证明和矩阵检查,只保留基础结构事实和代数证明。 + +--- + +## 常见对易关系 + +编译器可以识别多类常见关系: + +- 作用在不相交比特集合上的两个门总是精确对易; +- 对角门之间对易,例如 `RZ`、`Phase`、`S`、`T`、`CZ`、`RZZ`; +- 同轴旋转对易,例如连续的 `RZ(a)` 与 `RZ(b)`; +- Pauli 字符串旋转在反对易位置为偶数时对易; +- `CX` 与控制位上的 Z 轴操作对易; +- `CX` 与目标位上的 X 轴操作对易; +- `CZ` 与任一端点上的 Z 轴操作对易; +- `SWAP`、`FSIM`、对称 Pauli 旋转等部分双比特门族在相同无序比特对上有额外的结构化对易事实。 + +这些结论会帮助规则重写器把可合并的门移动到一起。例如: + +```text +RZ(a) q; S q; RZ(b) q +``` + +因为 `RZ` 与 `S` 同属 Z 轴相位族,可以整理为: + +```text +S q; RZ(a) q; RZ(b) q +``` + +随后旋转合并规则可以把两个 `RZ` 合并为 `RZ(a + b)`。 + +--- + +## Clifford-RZ 片段的优化思路 + +Clifford-RZ 线路通常由离散 Clifford 门和 Z 轴旋转交替组成。优化目标不是简单地把所有门重新排序,而是在保持语义的前提下做局部整理: + +- 抵消相邻自反门,例如 `H H`、`X X`、`CX CX`; +- 抵消逆门对,例如 `S SDG`、`T TDG`; +- 合并同轴旋转,例如 `RZ(a) RZ(b) -> RZ(a + b)`; +- 删除零角度旋转和恒等门; +- 把 `S S`、`T T` 等 Clifford/相位组合改写为更短形式; +- 在目标门集转换前后保留或折叠必要的 `GPhase`。 + +这些优化分别由规范化器和知识规则重写器负责。用户一般通过 `compile()` 间接使用它们,不需要寻找单独的 `CliffordRzOptimization` 类。 + +```python +from cqlib.circuit import Circuit +from cqlib.compile import CompileMode, compile + +circuit = Circuit(1) +circuit.rz(0, 0.1) +circuit.rz(0, 0.2) +circuit.rz(0, -0.3) + +result = compile(circuit, mode=CompileMode.enhanced()) +optimized = result.circuit + +print("changed:", result.changed) +print("before:", len(circuit.operations)) +print("after:", len(optimized.operations)) +``` + +`CompileMode.enhanced()` 会使用更高的规则搜索预算,并在路由和目标门集转换后增加清理步骤。对于 Clifford-RZ 片段较多、路由后容易暴露新相邻门的线路,增强模式通常更容易找到额外的合并或抵消机会。 + +--- + +## 与模板规则的配合 + +对易分析和模板优化的关系可以理解为: + +1. 对易检查证明两个相邻操作可以交换; +2. 交换后,原本被隔开的局部模式变成相邻模式; +3. 知识规则库匹配该模式; +4. 重写器应用取消、合并或归一化规则; +5. 规范化器清理参数、全局相位和表示细节。 + +例如 Z 轴门族的对易规则可以把 `RZ`、`S`、`T`、`Phase` 排列到更容易合并或抵消的位置。`CX` 和 `CZ` 的对易规则则能在两比特门附近移动单比特相位门,减少后续目标门集转换中的冗余。 + +--- + +## 全局相位 + +某些交换或抵消只在全局相位意义下成立。Cqlib 用 `Commutation` 的 phase 信息和 `GPhase` 显式表示这类情况。顶层 `GPhase` 在规范化阶段会被合并到线路的 `global_phase`;控制流 body 内的相位不能随意提升为全局相位,因此会保留在 body 表示中。 + +在算法验证、态矢量比较和门矩阵比较中,需要明确是否忽略全局相位。对于采样概率而言,全局相位通常不可观测;对于受控子线路、相位估计和进一步封装为复合门的场景,则应谨慎保留。 + +--- + +## 使用建议 + +- 用户级编译优先使用 `compile()`,让工作流自动安排对易、规则重写和规范化的顺序; +- 自定义分析中使用 `CommutationChecker.builtin()`,并把 `None` 当作“未证明”而不是“不可交换”; +- 大线路批量分析时可以关闭矩阵回退检查,以避免局部矩阵构造带来的开销; +- Clifford-RZ 优化后应记录门数、双比特门数、线路深度和全局相位变化; +- 对关键线路建议使用矩阵等价或状态等价测试确认优化前后语义一致。 + +--- + +## 下一步 + +- [编译优化](0_overview.md):回顾完整 `compile()` 管线与各阶段配置要点。 +- [模板匹配与知识规则优化](3_template_optimization.md):了解知识规则重写如何与对易分析配合。 diff --git a/docs/documentation/1_cqlib/5_visualization/0_overview.md b/docs/documentation/1_cqlib/5_visualization/0_overview.md new file mode 100644 index 0000000..ef8f7f3 --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/0_overview.md @@ -0,0 +1,108 @@ +# 可视化 + +Cqlib 的可视化功能用于在量子程序开发过程中检查三类对象:线路结构、测量结果和量子态。本章从实际使用场景出发,展示如何生成图形、保存结果,并根据图形检查量子语义。 + +开始前需要完成 [安装与环境配置](../../0_get_started/1_installation.md),并确认 Cqlib 可以正常导入。 + +```python +import cqlib + +print(cqlib.__file__) +``` + +如果机器上同时存在多个 Cqlib checkout,请先确认这里打印的是当前要使用的实现。可视化示例依赖 `cqlib.visualization` 的 Python 绑定和本地图形渲染能力,不会连接云平台,也不会提交真实量子硬件任务。 + +--- + +## 从 Bell 态开始 + +Bell 态是最适合入门可视化的例子:线路很短,但同时包含叠加、纠缠、测量和统计结果。 + +```python +from cqlib import Circuit +from cqlib.visualization import draw_text, draw_figure + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) +circuit.measure(0) +circuit.measure(1) + +print(draw_text(circuit)) +draw_figure(circuit, output_path="assets/bell.png") +``` + +文本图输出如下: + +```text + + Q0: ───H──■──M─ + │ + Q1: ──────X──M─ + +``` + +生成的 PNG 线路图如下: + +![Bell state circuit](assets/bell.png) + +这张图检查线路语义是否正确: + +- `H` 门先作用在 `Q0`,创建叠加; +- `CX` 以 `Q0` 为控制位、`Q1` 为目标位,创建纠缠; +- 两个测量都位于纠缠操作之后,没有提前破坏态。 + +快速确认门顺序、控制位和目标位时,文本图通常最快。需要在 Gitee、Markdown、报告或演示材料中稳定展示时,PNG 更兼容;需要可缩放矢量图时,也可以把 `output_path` 改成 `.svg`。 + +--- + +## 本章学习路线 + +阅读顺序如下: + +1. [用文本图调试线路](1_draw_text.md):在终端中检查门顺序、比特顺序、参数和复合门。 +2. [生成 PNG 线路图](2_draw_figure.md):为 Notebook、文档站和报告生成图形文件。 +3. [Notebook 与文档集成](3_notebook_and_docs.md):在 Notebook 和 Markdown 中保存、引用可视化结果。 +4. [复杂线路的可视化策略](4_visualization_practices.md):处理参数化线路、映射前后对比和大线路展示。 +5. [控制流与特殊线路结构](5_control_flow_and_special.md):阅读动态控制流、非幺正指令和自定义门图形。 +6. [可视化执行结果](6_result_visualization.md):用柱状图和概率分布查看采样结果。 +7. [可视化量子态](7_state_visualization.md):用 Bloch、state city 和 Pauli vector 理解状态。 + +建议按这个顺序阅读。前五节解决“线路是否按预期构造”,后两节解决“运行或模拟后的对象如何解释”。 + +--- + +## 什么时候应该画图 + +在量子程序开发中,可视化通常不是最后一步,而是每个关键变换后的检查手段。以下位置适合主动画图: + +- 手动写完多比特门后,检查控制位和目标位; +- 构造参数化 ansatz 后,检查每一层是否按预期重复; +- 将子线路封装为 `CircuitGate` 后,检查模块边界; +- 展开复合门后,检查底层门序列; +- 加入动态控制流后,检查分支、循环和控制转移标记; +- 编译或映射后,检查 SWAP 插入和双比特门位置; +- 采样或模拟后,检查结果分布是否符合预期。 + +可视化不能替代矩阵验证、概率验证或单元测试,但它能很快暴露比特顺序、测量位置、参数遗漏和线路过深等问题。 + +--- + +## 如何选择图形 + +| 开发任务 | 推荐图形 | 观察重点 | +|---|---|---| +| 快速检查线路结构 | 文本线路图 | 门顺序、控制位、目标位、测量位置 | +| 写 Notebook 或报告 | PNG 线路图 | 结构清晰度、模块边界、比特显示顺序 | +| 阅读动态控制流或特殊指令 | PNG 线路图 | 分支、循环、`barrier`、`reset`、`delay`、自定义门标签 | +| 查看采样结果 | Histogram / distribution | 主峰、低概率噪声项、shot 数和归一化概率 | +| 理解单比特态 | Bloch 图 | Bloch 向量方向和长度 | +| 理解多比特或密度矩阵 | State city / Pauli vector | 相干项、Pauli 期望值和纠缠态的全局结构 | + +--- + +## 下一步 + +- [用文本图调试线路](1_draw_text.md):先在终端中快速检查门顺序、控制位、目标位和测量位置。 +- [生成 PNG 线路图](2_draw_figure.md):把需要写进 Notebook、Markdown 或报告的线路保存成图片。 +- [可视化执行结果](6_result_visualization.md):在线路运行或采样后,用结果图检查主峰、噪声项和 bitstring 顺序。 diff --git a/docs/documentation/1_cqlib/5_visualization/1_draw_text.md b/docs/documentation/1_cqlib/5_visualization/1_draw_text.md new file mode 100644 index 0000000..e2fc18f --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/1_draw_text.md @@ -0,0 +1,213 @@ +# 用文本图调试线路 + +文本图适合在终端、日志、Markdown 和单元测试中快速检查线路。 + +--- + +## 任务:检查一个 Bell 态线路 + +先构造一条带测量的 Bell 态线路: + +```python +from cqlib import Circuit +from cqlib.visualization import draw_text + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) +circuit.measure(0) +circuit.measure(1) + +print(draw_text(circuit)) +``` + +输出结果: + +```text + + Q0: ───H──■──M─ + │ + Q1: ──────X──M─ + +``` + +阅读文本图时,重点看三件事: + +- `H` 是否作用在第 `0` 个量子比特; +- `CX` 的控制位是否在 `0`,目标位是否在 `1`; +- 测量是否在纠缠操作之后。 + +调试算法线路时,可以在每增加一层结构后打印一次文本图,避免在线路全部生成后才排查结构问题。 + +文本图更适合快速调试。它的优势是轻量、可复制、方便放进 issue 和测试失败日志;当需要在报告或演示材料中展示完整结构时,可以同时生成 PNG 图。 + +--- + +## 检查比特显示顺序 + +某些论文、后端或前端界面会把高位比特画在上方。`reverse_bits=True` 只改变显示顺序,不改变线路语义。 + +```python +print(draw_text(circuit)) +print(draw_text(circuit, reverse_bits=True)) +``` + +反转显示顺序后的输出如下: + +```text + + Q1: ──────X──M─ + │ + Q0: ───H──■──M─ + +``` + + +--- + +## 调试参数化线路 + +参数化线路容易因为符号名、绑定顺序或表达式过长而变得难读。文本图可以先用于确认线路拓扑,再决定是否显示参数。 + +```python +from cqlib import Circuit, Parameter +from cqlib.visualization import draw_text + +theta = Parameter("theta") +phi = Parameter("phi") + +ansatz = Circuit(2) +ansatz.ry(0, theta) +ansatz.rz(0, phi) +ansatz.cx(0, 1) +ansatz.ry(1, theta + phi) + +print(draw_text(ansatz)) +print(draw_text(ansatz, show_params=False)) +``` + +显示参数时,文本图保留符号表达式: + +```text + + Q0: ───RY(theta)──RZ(phi)──■────────────────── + │ + Q1: ───────────────────────X──RY(phi + theta)─ + +``` + +隐藏参数后,图只保留门和线路拓扑: + +```text + + Q0: ───RY──RZ──■───── + │ + Q1: ───────────X──RY─ + +``` + +这一步常用于变分线路调试:先确认 entangler 连接关系,再单独检查参数表或优化器传入的参数向量。不要用隐藏参数后的图说明具体角度取值。 + +--- + +## 处理长线路 + +线路较深时,默认文本图可能超过终端宽度。可以用 `line_width` 控制折行。 + +```python +layer = Circuit(2) +for _ in range(8): + layer.h(0) + layer.cx(0, 1) + layer.rz(1, 0.2) + +print(draw_text(layer, line_width=80)) +``` + +输出会在超过指定宽度后折行: + +```text + » + Q0: ───H──■─────H─────■─────H─────■─────H─────■─────H─────■─────H─────■─────H─────■─» + │ │ │ │ │ │ │ » + Q1: ──────X──RZ(0.2)──X──RZ(0.2)──X──RZ(0.2)──X──RZ(0.2)──X──RZ(0.2)──X──RZ(0.2)──X─» + » + +« +« Q0: ──────H─────■────────── +« │ +« Q1: ───RZ(0.2)──X──RZ(0.2)─ +« +``` + + + +--- + +## 检查复合门内部结构 + +当线路里包含由 `to_gate()` 封装的复合门时,默认图会保留模块边界。调试内部细节时,可以展开显示。 + +```python +from cqlib import Circuit +from cqlib.visualization import draw_text + +block = Circuit(2) +block.h(0) +block.cx(0, 1) +bell_gate = block.to_gate("Bell") + +main = Circuit(4) +main.append_circuit_gate(bell_gate, [0, 1]) +main.append_circuit_gate(bell_gate, [2, 3]) + +print(draw_text(main)) +print(draw_text(main, decompose_circuit_gates=True)) +``` + +默认显示保留两个 `Bell` 模块: + +```text + ┌──────┐ + Q0: ───│ │─ + │ Bell │ + Q1: ───│ │─ + └──────┘ + Q2: ───│ │─ + │ Bell │ + Q3: ───│ │─ + └──────┘ +``` + +展开复合门后,可以看到模块内部的 `H` 和 `CX`: + +```text + + Q0: ───H──■─ + │ + Q1: ──────X─ + + Q2: ───H──■─ + │ + Q3: ──────X─ + +``` + + +--- + +## 文本图的使用方式 + +- 在 issue、日志和调试输出中使用文本图; +- 对 2 到 4 比特的小线路,文本图通常已经足够清晰; +- 检查结构时可以隐藏参数,检查参数绑定时再显示参数; +- 对复杂线路保留文本图快照,配合数值测试验证行为。 +- 如果图中涉及后端、论文或其他框架的比特顺序约定,需要确认 `reverse_bits` 只改变显示顺序,不改变线路执行语义。 + +--- + +## 下一步 + +- [生成 PNG 线路图](2_draw_figure.md):把已经确认结构的小线路保存成更适合文档和报告的图片。 +- [Notebook 与文档集成](3_notebook_and_docs.md):把生成图片的代码和 Markdown 引用放到同一套实验记录中。 +- [复杂线路的可视化策略](4_visualization_practices.md):在线路变深或模块变多时,用分段、展开和对比图定位结构问题。 diff --git a/docs/documentation/1_cqlib/5_visualization/2_draw_figure.md b/docs/documentation/1_cqlib/5_visualization/2_draw_figure.md new file mode 100644 index 0000000..95770a8 --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/2_draw_figure.md @@ -0,0 +1,169 @@ +# 生成 PNG 线路图 + +PNG 线路图适合 Gitee、Markdown、Notebook、网页文档、报告和演示材料。与文本图相比,图形线路更适合展示较复杂的结构;需要矢量图时,也可以把导出后缀改成 `.svg`。 + +--- + +## 任务:保存一张 Bell 态线路图 + +```python +from cqlib import Circuit +from cqlib.visualization import draw_figure + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) +circuit.measure(0) +circuit.measure(1) + +svg = draw_figure(circuit, output_path="assets/bell.png") +print(svg[:80]) +``` + +生成结果如下: + +![Bell state circuit](assets/bell.png) + +`draw_figure` 返回 SVG 字符串,同时在传入 `output_path` 时写入文件。`output_path` 使用 `.png` 后缀时会写出 PNG 文件,生成的 `assets/bell.png` 可以直接放入 Markdown、HTML、PPT 或论文素材目录。 + +```markdown +![Bell state circuit](assets/bell.png) +``` + +--- + +## 在 Notebook 中内联显示 + +在 Notebook 中,`draw_figure(circuit)` 可以作为单元格最后一行直接显示。 + +```python +from cqlib.visualization import draw_figure + +draw_figure(circuit) +``` + +需要显式控制显示时,可以交给 IPython: + +```python +from IPython.display import SVG, display + +display(SVG(draw_figure(circuit))) +``` + +如果同一段 Notebook 既用于探索又用于沉淀实验记录,建议始终显式传入 `output_path`。这样 Notebook 中看到的图和 Markdown 中引用的 PNG 来自同一段代码。 + +--- + +## 折叠长线路 + +当线路较深时,直接平铺会让图片过宽。可以用 `fold` 控制图形折行。 + +```python +deep = Circuit(2) +for _ in range(12): + deep.h(0) + deep.cx(0, 1) + deep.rz(1, 0.1) + +draw_figure(deep, fold=20, output_path="assets/deep_folded.png") +``` + +折叠后的线路图如下: + +![Folded deep circuit](assets/deep_folded.png) + +折叠只改变画布排版,不改变线路执行顺序。阅读折叠图时,沿每一段末尾和下一段开头的连接方向继续读即可。 + + +--- + +## 控制图中的信息密度 + +参数化线路在图中显示全部表达式时,可能会影响阅读。 + +```python +from cqlib import Circuit, Parameter +from cqlib.visualization import draw_figure + +theta = Parameter("theta") + +ansatz = Circuit(2) +ansatz.ry(0, theta) +ansatz.cx(0, 1) +ansatz.ry(1, 2 * theta) + +draw_figure(ansatz, output_path="assets/ansatz_with_params.png") +draw_figure(ansatz, show_params=False, output_path="assets/ansatz_structure.png") +``` + +保留参数的图: + +![Parameterized ansatz with parameters](assets/ansatz_with_params.png) + +隐藏参数后的结构图: + +![Parameterized ansatz structure](assets/ansatz_structure.png) + +--- + +## 展示初态和比特顺序 + + +```python +draw_figure(circuit, initial_state=True, output_path="assets/bell_initial_state.png") +``` + +带初态标记的图如下: + +![Bell circuit with initial states](assets/bell_initial_state.png) + +需要与后端、论文图或其他框架的显示习惯对齐时,可以反转显示顺序: + +```python +draw_figure(circuit, reverse_bits=True, output_path="assets/bell_reverse_bits.png") +``` + +反转显示顺序后的图如下: + +![Bell circuit with reversed bits](assets/bell_reverse_bits.png) + +这两个选项只影响图形展示,不改变 `Circuit` 的门顺序、比特索引或测量语义。 + +--- + +## 展开复合门用于排查 + +复合门保留模块边界,适合讲解算法结构;展开复合门则适合排查底层门序列。 + +```python +block = Circuit(2) +block.h(0) +block.cx(0, 1) +bell_gate = block.to_gate("Bell") + +main = Circuit(2) +main.append_circuit_gate(bell_gate, [0, 1]) + +draw_figure(main, output_path="assets/bell_gate.png") +draw_figure( + main, + decompose_circuit_gates=True, + output_path="assets/bell_gate_decomposed.png", +) +``` + +保留复合门边界的图: + +![Bell circuit gate](assets/bell_gate.png) + +展开复合门后的图: + +![Bell circuit gate decomposed](assets/bell_gate_decomposed.png) + +--- + +## 下一步 + +- [Notebook 与文档集成](3_notebook_and_docs.md):把图片生成路径、资源目录和 Markdown 引用固定下来。 +- [复杂线路的可视化策略](4_visualization_practices.md):用分阶段图片、模块边界和映射前后对比展示更大的线路。 +- [控制流与特殊线路结构](5_control_flow_and_special.md):阅读包含分支、循环、`reset`、`delay` 和自定义门的线路图。 diff --git a/docs/documentation/1_cqlib/5_visualization/3_notebook_and_docs.md b/docs/documentation/1_cqlib/5_visualization/3_notebook_and_docs.md new file mode 100644 index 0000000..cc8d1d3 --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/3_notebook_and_docs.md @@ -0,0 +1,144 @@ +# Notebook 与文档集成 + +本节展示如何在 Notebook 中显示 Cqlib 生成的 PNG,并把同一张图保存到 Markdown 可引用的资源目录。 + +--- + +## 任务:在 Notebook 中展示并保存同一张图 + +```python +from pathlib import Path + +from cqlib import Circuit +from cqlib.visualization import draw_figure + +assets = Path("assets") +assets.mkdir(exist_ok=True) + +circuit = Circuit(2) +circuit.h(0) +circuit.cx(0, 1) +circuit.measure(0) +circuit.measure(1) + +draw_figure(circuit, output_path=str(assets / "bell.png")) +``` + +在 Notebook 中,上面最后一行会内联显示图形,同时把 PNG 保存到 `assets/bell.png`。这种方式适合实验记录、报告和论文补充材料。 + +生成结果如下: + +![Bell state circuit](assets/bell.png) + +--- + +## 在 Markdown 中引用 + +保存 PNG 后,在 Markdown 中直接引用: + +```markdown +![Bell state circuit](assets/bell.png) +``` + +引用图片后,可以在图下方记录这张线路图的检查点: + +```markdown +图 1 展示了 Bell 态制备线路:先对 q0 施加 H 门,再以 q0 为控制位、q1 为目标位施加 CX 门。 +``` + +量子线路图通常需要明确控制位、目标位、测量位置和比特顺序。否则图本身只能说明线路形状,不能说明检查结论。 + +--- + +## 组织文档资产目录 + +为每组实验或每篇报告保留独立的资源目录,可以避免不同图片互相覆盖: + +```text +docs/ + visualization/ + tutorial.md + assets/ + bell.png + ansatz_structure.png + mapped_before.png + mapped_after.png +``` + +文件名应描述任务,而不是使用 `figure1.png`、`test.png` 这类临时名称。 + +--- + +## 自动重生成图片 + +当一组实验包含多张图时,可以把生成逻辑集中放在同一个代码块或脚本里。 + +```python +from pathlib import Path + +from cqlib import Circuit +from cqlib.visualization import draw_figure + +assets = Path("assets") +assets.mkdir(exist_ok=True) + +bell = Circuit(2) +bell.h(0) +bell.cx(0, 1) + +draw_figure(bell, output_path=str(assets / "bell.png")) +draw_figure(bell, reverse_bits=True, output_path=str(assets / "bell_reverse_bits.png")) +``` + +生成的两张图可以在 Markdown 中分别引用: + +![Bell state circuit](assets/bell.png) + +![Bell state circuit with reversed bits](assets/bell_reverse_bits.png) + +当线路构造代码变更时,重新运行这一段即可同步更新插图。如果生成的图不符合预期,优先检查线路构造和可视化参数,而不是手工修改图片内容。 + +--- + +## 保存可视化结果 + +- 小线路可以同时保留文本图和 PNG; +- 大线路优先保存关键阶段,不必保存每一个中间线路; +- 保存 PNG 原图,避免只保留截图; +- 文件名使用具体任务名,例如 `bell_reverse_bits.png`、`mapped_after.png`; +- 图中涉及比特顺序、测量顺序或后端映射时,需要在实验记录中写清楚对应约定。 + +--- + +## 常见问题 + +Notebook 中没有自动显示图形时,可以显式使用 IPython 显示返回的 SVG 字符串: + +```python +from IPython.display import SVG, display +from cqlib.visualization import draw_figure + +display(SVG(draw_figure(circuit))) +``` + +脚本只保存文件而不显示图时,需要检查 `output_path` 是否写到了预期目录,并确认生成文件可以被本地图片查看器打开。 + +--- + +## 更新图片后的检查 + +修改线路或绘图代码后,可以按下面顺序检查: + +1. 重新运行生成 PNG 的代码块或脚本; +2. 确认 Markdown 中引用的文件名没有变化; +3. 打开生成的 PNG,检查控制位、目标位、测量位置和比特顺序是否仍然符合预期; +4. 如果包含采样结果或状态图,确认数据来源是构造结果、本地模拟还是硬件结果; +5. 检查文件名中没有临时名称,代码块中没有绝对路径或本机用户目录。 + +--- + +## 下一步 + +- [生成 PNG 线路图](2_draw_figure.md):回到绘图参数,调整折叠、参数显示、初态标记和比特显示顺序。 +- [复杂线路的可视化策略](4_visualization_practices.md):为多阶段算法、映射结果和大线路组织一组可维护的图片。 +- [可视化执行结果](6_result_visualization.md):把采样结果图纳入同一套 Notebook 和 Markdown 资产目录。 diff --git a/docs/documentation/1_cqlib/5_visualization/4_visualization_practices.md b/docs/documentation/1_cqlib/5_visualization/4_visualization_practices.md new file mode 100644 index 0000000..dd44c5f --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/4_visualization_practices.md @@ -0,0 +1,210 @@ +# 复杂线路的可视化策略 + +当线路规模变大时,直接绘制全线路可能不易阅读。更实用的方式是先查看关键结构,再用少量图形对比规模扩展、模块展开或编译映射前后的变化。 + +--- + +## 任务:分阶段展示一个 QAOA 风格线路 + +先把线路拆成状态制备、问题层和 mixer 层: + +```python +from cqlib import Circuit +from cqlib.visualization import draw_figure, draw_text + +prepare = Circuit(3) +for q in range(3): + prepare.h(q) + +cost = Circuit(3) +cost.rzz(0, 1, 0.3) +cost.rzz(1, 2, 0.3) + +mixer = Circuit(3) +for q in range(3): + mixer.rx(q, 0.7) +``` + +先分别绘制每一段,便于检查状态制备、问题层和 mixer 层是否各自正确。 + +```python +print(draw_text(prepare)) +print(draw_text(cost)) +print(draw_text(mixer)) + +draw_figure(prepare, output_path="assets/qaoa_prepare.png") +draw_figure(cost, output_path="assets/qaoa_cost.png") +draw_figure(mixer, output_path="assets/qaoa_mixer.png") +``` + +状态制备层: + +![QAOA prepare layer](assets/qaoa_prepare.png) + +问题层: + +![QAOA cost layer](assets/qaoa_cost.png) + +Mixer 层: + +![QAOA mixer layer](assets/qaoa_mixer.png) + +完整 QAOA 线路可以由这些模块按层重复组成。先检查单层结构,再扩展层数,可以更早发现门顺序或作用比特错误。 + +--- + +## 展示模块边界,再展示展开细节 + +对于 ansatz、oracle、特征映射等可复用模块,通常先保留 `CircuitGate` 边界。 + +```python +block = Circuit(2) +block.ry(0, 0.2) +block.cx(0, 1) +block.ry(1, 0.4) +block_gate = block.to_gate("Layer") + +ansatz = Circuit(4) +ansatz.append_circuit_gate(block_gate, [0, 1]) +ansatz.append_circuit_gate(block_gate, [2, 3]) + +draw_figure(ansatz, output_path="assets/ansatz_modules.png") +draw_figure( + ansatz, + decompose_circuit_gates=True, + output_path="assets/ansatz_decomposed.png", +) +``` + +保留模块边界的图: + +![Ansatz modules](assets/ansatz_modules.png) + +展开模块后的图: + +![Ansatz decomposed](assets/ansatz_decomposed.png) + +保留模块边界时,更容易看清 ansatz 的层级结构;展开复合门后,更容易检查底层门序列是否符合预期。 + +--- + +## 映射前后对比 + +编译和路由场景中,图的重点不是每个门的矩阵,而是 SWAP 是否被插入、双比特门是否满足拓扑约束。 + +```python +from cqlib.compile import compile +from cqlib.device import Device + +original = Circuit(3) +original.h(0) +original.cx(0, 2) +original.cx(1, 2) + +result = compile(original, device=Device.line("line-3", 3), seed=42) +mapped = result.circuit + +print("before") +print(draw_text(original, line_width=100)) + +print("after") +print(draw_text(mapped, line_width=100)) + +draw_figure(original, output_path="assets/mapped_before.png") +draw_figure(mapped, output_path="assets/mapped_after.png") +``` + +映射前: + +```text + + Q0: ───H──■──── + │ + Q1: ──────┼──■─ + │ │ + Q2: ──────X──X─ + +``` + +![Circuit before routing](assets/mapped_before.png) + +映射后: + +```text + + Q0: ───H──■──── + │ + Q1: ──────X──X─ + │ + Q2: ─────────■─ + +``` + +![Circuit after routing](assets/mapped_after.png) + +对比映射前后的图时,重点检查是否增加了 SWAP、线路深度是否变大、逻辑比特和物理比特的关系是否需要记录。 + +如果映射结果依赖随机 seed、启发式搜索或设备拓扑,需要固定 seed,并记录示例使用的是哪种拓扑。真实硬件设备的校准、可用 qubit 和门错误率会随时间变化,不能把某一次映射结果当成永久保证。 + +--- + +## 大线路只展示关键切片 + +对于多层 ansatz 或自动生成线路,完整图通常过宽。可以用下面的组合方式查看关键切片: + +```python +print("num operations:", len(ansatz)) +print(draw_text(ansatz, show_params=False, line_width=100)) + +draw_figure( + ansatz, + show_params=False, + fold=80, + output_path="assets/ansatz_overview.png", +) +``` + +概览图如下: + +![Ansatz overview](assets/ansatz_overview.png) + +同时记录一张小表: + +| 内容 | 展示方式 | +|---|---| +| 单层结构 | PNG 线路图 | +| 层数、门数、深度 | 表格 | +| 编译前后差异 | 前后对比图 | +| 参数取值 | 单独表格或公式 | + +这种方式比完整平铺几十层线路更容易定位问题,也更适合放入实验记录或技术报告。 + +--- + +## 可视化不能替代验证 + +可视化适合发现结构问题,但不能证明量子程序正确。关键线路仍应配合数值检查: + +```python +from cqlib import Circuit +from cqlib.qis import Statevector + +check = Circuit(2) +check.h(0) +check.cx(0, 1) + +state = Statevector.from_circuit(check) +print(state.probabilities()) +``` + +对于含随机采样的流程,还需要固定 seed 或使用容差判断。对于硬件执行流程,不能要求真实设备结果与理想模拟图完全一致,应在文档中说明 shot noise、拓扑映射、门误差和测量误差的影响。 + +因此,推荐组合是:小线路用文本图快速检查,关键结构保存 PNG,核心结论再用概率、矩阵或测试断言验证。可视化负责帮助检查结构,不负责单独证明算法正确。 + +--- + +## 下一步 + +- [控制流与特殊线路结构](5_control_flow_and_special.md):把同样的读图方法用于动态控制流、非幺正指令和自定义门。 +- [可视化执行结果](6_result_visualization.md):在线路结构确认后,用 histogram 和 distribution 检查采样结果。 +- [可视化量子态](7_state_visualization.md):需要解释状态本身时,用 Bloch、state city 和 Pauli vector 补充结构图。 diff --git a/docs/documentation/1_cqlib/5_visualization/5_control_flow_and_special.md b/docs/documentation/1_cqlib/5_visualization/5_control_flow_and_special.md new file mode 100644 index 0000000..242e5d1 --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/5_control_flow_and_special.md @@ -0,0 +1,178 @@ +# 控制流与特殊线路结构 + +除了基础门和常规参数化线路,Cqlib 的线路图也可以显示动态控制流、非幺正指令和自定义门。遇到这类线路时,读图重点不只是门顺序,还包括控制流块覆盖了哪些 qubit lane、分支在哪里结束、循环中是否存在 `break` / `continue`,以及特殊门标签是否保留了算法含义。 + +以下示例只生成线路图,不执行真实硬件任务。 + +--- + +## 任务:阅读动态控制流图 + +先构造一条包含 `if/else`、`while`、`for`、`switch`、`break` 和 `continue` 的动态线路。为了让图中的控制流标签保持短而稳定,这里的读图示例使用字面量条件: + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr, ClassicalType +from cqlib.visualization import draw_figure + +dynamic = Circuit(2) +dynamic.h(0) + +def then_body(body): + body.x(1) + +def else_body(body): + body.z(1) + +dynamic.if_else( + ClassicalExpr.bool_literal(True), + then_body, + else_body, +) + +def continue_body(body): + body.measure(1) + body.continue_loop() + +dynamic.while_( + ClassicalExpr.bool_literal(True), + continue_body, +) + +def break_body(body): + body.h(0) + body.break_loop() + +dynamic.while_( + ClassicalExpr.bool_literal(False), + break_body, +) + +loop_var = dynamic.var(ClassicalType.uint(3)) + +def for_body(body, index): + body.rx(0, 0.1) + +dynamic.for_uint( + loop_var, + ClassicalExpr.uint_literal(3, 0), + ClassicalExpr.uint_literal(3, 3), + ClassicalExpr.uint_literal(3, 1), + for_body, +) + +def case_zero(body): + body.x(0) + +def case_one(body): + body.z(1) + +def switch_default(body): + body.h(0) + body.cx(0, 1) + +def build_switch(builder): + builder.value(0, case_zero) + builder.value(1, case_one) + builder.default(switch_default) + +dynamic.switch(ClassicalExpr.uint_literal(2, 1), build_switch) + +draw_figure(dynamic, fold=10, output_path="assets/dynamic_control_flow.png") +``` + +生成的控制流线路图如下: + +![Dynamic control-flow circuit](assets/dynamic_control_flow.png) + +阅读这张图时,可以按控制流块逐段检查: + +- `If`、`Else` 和 `End` 标记给出条件分支的入口、备选分支和结束位置; +- 第一个 `While` 块使用 `true` 条件,循环体末尾的 `Continue` 表示进入下一次循环判断; +- 第二个 `While` 块包含 `Break`,表示循环体内部可以直接退出最近的循环; +- `For` 块会显示循环范围,便于确认迭代边界; +- `Switch` 块显示选择表达式,并列出 case 和 default 分支; +- 控制流块的竖向跨度对应 body 实际使用到的 qubit lane,未参与该 body 的 lane 不应被误读为被该控制块操作。 + +真实动态线路中的条件通常来自路中测量或 classical storage,写法如下: + +```python +from cqlib import Circuit +from cqlib.circuit import ClassicalExpr, ClassicalType +from cqlib.compile import compile +from cqlib.device import Device + +dynamic = Circuit(2) +dynamic.h(0) +measured = dynamic.measure(0) +condition = measured.expr().to_bool() + +def then_body(body): + body.x(1) + +def else_body(body): + body.z(1) + +dynamic.if_else(condition, then_body, else_body) + +keep_running = dynamic.var(ClassicalType.bool()) +dynamic.store(keep_running, ClassicalExpr.bool_literal(True)) + +def loop_body(body): + loop_measurement = body.measure(1) + body.store(keep_running, loop_measurement.expr().to_bool()) + body.continue_loop() + +dynamic.while_(keep_running.expr(), loop_body) + +compiled = compile(dynamic, device=Device.line("line-2", 2), seed=42) +``` + +布局和路由会把控制流体作为结构化子线路处理,并递归处理 body 中的门和控制转移标记。线路图只表达结构、分支位置和控制流边界,不表达一次运行时实际走哪条路径,也不估计分支概率或循环次数。 + +--- + +## 任务:阅读特殊指令和非基础门 + +下面的线路同时包含 `barrier`、`delay`、`reset`、`fSim`、多控制门和自定义 `UnitaryGate`: + +```python +from cqlib import Circuit +from cqlib.circuit import MCGate, StandardGate, UnitaryGate +from cqlib.visualization import draw_figure + +special = Circuit(4) +special.h(0) +special.barrier([0, 1, 2, 3]) +special.delay(0, 40.0) +special.fsim(1, 2, 0.21, -0.44) +special.reset(3) + +special.append_mc_gate(MCGate(2, StandardGate.X), [0, 1, 2]) +special.append_unitary_gate(UnitaryGate("Oracle", 2), [2, 3]) + +draw_figure(special, output_path="assets/special_directives_and_gates.png") +``` + +生成的线路图如下: + +![Special directives and gates](assets/special_directives_and_gates.png) + +这张图可以帮助检查几类常见问题: + +- `barrier` 是竖线,用于保留分段或调度边界,不表示量子门; +- `delay` 会显示等待时长,适合检查硬件时序或空闲段; +- `reset` 显示为重新置到 `|0>`,它是非幺正指令; +- `fSim` 是双比特参数门,图中会跨越两个作用 qubit; +- 多控制门按“控制位在前、目标位在后”的顺序绘制; +- 自定义 `UnitaryGate` 优先显示用户给出的 label,例如这里的 `Oracle`。 + +如果自定义门或复合门标签过长,图形仍然能显示结构,但阅读时应确认 label 是否足够表达算法模块含义。对于需要进入底层门序列排查的场景,可以回到 [生成 PNG 线路图](2_draw_figure.md) 中的 `decompose_circuit_gates=True` 示例。 + +--- + +## 下一步 + +- [生成 PNG 线路图](2_draw_figure.md):回到基础绘图选项,调整折叠、参数显示、初态标记和复合门展开。 +- [复杂线路的可视化策略](4_visualization_practices.md):把控制流或特殊门放入更大的算法线路时,用分段和对比图保持可读性。 +- [可视化执行结果](6_result_visualization.md):运行或采样后,用结果图检查动态线路产生的 bitstring 分布。 diff --git a/docs/documentation/1_cqlib/5_visualization/6_result_visualization.md b/docs/documentation/1_cqlib/5_visualization/6_result_visualization.md new file mode 100644 index 0000000..10229b0 --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/6_result_visualization.md @@ -0,0 +1,190 @@ +# 可视化执行结果 + +下面使用一个构造出的 `ExecutionResult` 展示如何绘制采样计数和归一化概率分布。 + +这里使用本地构造的结果对象,不连接云平台,也不提交真实硬件任务。 + +--- + +## 任务:查看 Bell 态采样结果 + +真实执行或模拟采样后,通常会得到 bitstring 到次数的映射。这里直接从 counts 构造结果对象,便于聚焦在结果图的阅读方式上。 + +```python +from cqlib.device import ExecutionResult +from cqlib.visualization import plot_distribution, plot_histogram + +counts = {"00": 512, "11": 488} + +result = ExecutionResult.from_counts( + "bell-demo", + [0, 1], + sum(counts.values()), + 2, + counts, +) +``` + +`counts` 表示 1000 次采样中只观察到 `00` 和 `11`。这里 bitstring 按结果对象的显示约定直接写入;如果结果来自硬件或其他 SDK,需要先确认测量位到 bitstring 的映射关系。对于理想 Bell 态,`00` 和 `11` 应是主峰,`01`、`10` 不应以高概率出现。 + +--- + +## 画原始计数柱状图 + +```python +plot_histogram( + result, + title="Bell-state counts", + output_path="assets/bell_counts.png", +) +``` + +生成的计数图如下: + +![Bell-state counts](assets/bell_counts.png) + +计数图适合展示真实 shot 数。比较不同 shot 设置、硬件任务或误差缓解前后的原始采样量时,优先使用 histogram。读图时先看主峰,再看是否存在本不应出现的 bitstring。 + +--- + +## 画概率分布 + +```python +plot_distribution( + result, + title="Bell-state probabilities", + output_path="assets/bell_distribution.png", +) +``` + +生成的概率分布如下: + +![Bell-state probabilities](assets/bell_distribution.png) + +概率分布会把计数归一化,更适合与理论概率比较。对于 Bell 态,理想分布应接近: + +```text +P(00) = 0.5 +P(11) = 0.5 +P(01) = 0.0 +P(10) = 0.0 +``` + +实验结果出现少量 `01` 或 `10` 时,不应立刻判断线路错误。需要结合 shot 数、噪声模型、测量误差和后端信息判断。 + +--- + +## 保留主峰并合并长尾 + +当只想保留出现次数最多的若干项时,可以限制展示数量。剩余项会合并,便于查看主峰。 + +```python +noisy_counts = { + "00": 470, + "11": 450, + "01": 45, + "10": 35, +} + +noisy_result = ExecutionResult.from_counts( + "bell-noisy-demo", + [0, 1], + sum(noisy_counts.values()), + 2, + noisy_counts, +) + +plot_distribution( + noisy_result, + number_to_keep=2, + sort="desc", + title="Dominant outcomes", + output_path="assets/bell_dominant_outcomes.png", +) +``` + +只保留主峰后的图如下: + +![Dominant outcomes](assets/bell_dominant_outcomes.png) + +使用这种图时,需要确认被合并的结果是什么,以及是否会影响结论。`number_to_keep` 适合报告主峰结构,但不适合隐藏错误项;如果低概率项本身就是分析重点,应展示完整分布或单独列出被合并的 counts。 + +--- + +## 标记目标结果 + +调试搜索算法或分类任务时,常常需要强调某个目标 bitstring。可以用 `target_string` 标记目标项,并按 Hamming 距离排序。 + +```python +search_counts = { + "100": 420, + "101": 260, + "110": 180, + "000": 90, + "011": 50, +} + +search_result = ExecutionResult.from_counts( + "search-demo", + [0, 1, 2], + sum(search_counts.values()), + 3, + search_counts, +) + +plot_histogram( + search_result, + sort="hamming", + target_string="100", + title="Counts ordered by distance to target", + output_path="assets/search_hamming_counts.png", +) +``` + +目标结果排序后的图如下: + +![Counts ordered by Hamming distance](assets/search_hamming_counts.png) + +这类图适合检查“结果是否集中到目标附近”。使用前需要明确目标 bitstring 的定义和测量位顺序。 + +--- + +## 生成报告用结果图 + +当结果图需要放进报告或演示材料时,可以同时控制画布尺寸、颜色、图例和柱状标签显示。下面仍使用同一组 Bell 态 counts,只调整图形呈现方式: + +```python +plot_histogram( + result, + figsize=(4.8, 3.2), + color=["#0f766e"], + legend=["simulated counts"], + bar_labels=False, + title="Bell-state counts for report", + output_path="assets/bell_report_counts.png", +) +``` + +生成的报告用计数图如下: + +![Bell-state report counts](assets/bell_report_counts.png) + +这种设置适合图很多、版面空间有限的材料。关闭 `bar_labels` 后,图形更简洁;如果需要逐项复核具体 counts,应同时保留原始结果数据或使用带标签的默认图。 + +--- + +## 结果图检查要点 + +- 确认结果来自模拟、构造数据还是真实硬件; +- 记录 shot 数,不要只查看归一化概率; +- 对硬件结果保留误差和噪声解释,不要把偏差简单归因于线路错误; +- 与理论分布对比时,先确认 bitstring 顺序和测量映射; +- 正式报告中可以同时保存 PNG 图和原始 counts 数据。 + +--- + +## 下一步 + +- [可视化量子态](7_state_visualization.md):需要解释模拟态本身时,用 Bloch、state city 和 Pauli vector 补充采样结果。 +- [Notebook 与文档集成](3_notebook_and_docs.md):把结果图、原始 counts 和图片引用一起保存到实验记录中。 +- [复杂线路的可视化策略](4_visualization_practices.md):把结果图和线路图并排检查,确认结构变化是否解释了分布变化。 diff --git a/docs/documentation/1_cqlib/5_visualization/7_state_visualization.md b/docs/documentation/1_cqlib/5_visualization/7_state_visualization.md new file mode 100644 index 0000000..c4f503f --- /dev/null +++ b/docs/documentation/1_cqlib/5_visualization/7_state_visualization.md @@ -0,0 +1,288 @@ +# 可视化量子态 + +状态图用于理解量子态本身,而不是测量采样结果。Cqlib 当前支持从 `Statevector` 或 `DensityMatrix` 绘制 Bloch multivector、state city 和 Pauli vector,也支持直接绘制单个 Bloch 向量。 + +以下示例都在本地模拟中完成,不涉及真实硬件。 + +--- + +## 任务:观察单比特叠加态 + +先构造一个 `|+>` 态: + +```python +from cqlib.qis import Statevector +from cqlib.visualization import ( + plot_bloch_multivector, + plot_bloch_vector, + plot_state_city, + plot_state_paulivec, +) + +state = Statevector(1) +state.apply_h(0) +``` + +`|+>` 态在 Bloch 球上位于 X 轴正方向,因此可以用 Bloch 图快速检查状态制备是否符合预期。这里的状态对象来自本地 `Statevector`,不是采样 counts。 + +```python +plot_bloch_multivector( + state, + title="|+> state", + output_path="assets/plus_bloch.png", +) +``` + +生成的 Bloch 图如下: + +![Bloch multivector for plus state](assets/plus_bloch.png) + +绘制手动给出的 Bloch 向量时,可以直接传入三维向量: + +```python +plot_bloch_vector( + [1.0, 0.0, 0.0], + title="Bloch vector along +X", + output_path="assets/bloch_x.png", +) +``` + +手动向量对应的 Bloch 图如下: + +![Bloch vector along X](assets/bloch_x.png) + +手动传入三维向量适合解释 Bloch 球方向;从线路或状态对象绘图时,应优先使用 `plot_bloch_multivector`,避免手动计算向量时引入约定错误。 + +--- + +## 查看密度矩阵结构 + +`plot_state_city` 展示密度矩阵的实部和虚部。它适合检查态中是否存在相干项,以及混态/纯态结构是否符合预期。 + +```python +plot_state_city( + state, + title="State city for |+>", + output_path="assets/plus_state_city.png", +) +``` + +生成的 state city 图如下: + +![State city for plus state](assets/plus_state_city.png) + +对于 `|+>`,密度矩阵中非对角元不为零,表示 `|0>` 和 `|1>` 之间存在相干性。 + +--- + +## 查看 Pauli 展开 + +Pauli vector 展示状态在 Pauli 基下的期望值,适合连接到 VQE、QAOA、误差诊断和可观测量分析。 + +```python +plot_state_paulivec( + state, + title="Pauli vector for |+>", + output_path="assets/plus_paulivec.png", +) +``` + +生成的 Pauli vector 图如下: + +![Pauli vector for plus state](assets/plus_paulivec.png) + +对 `|+>` 态,`X` 方向期望值应为主导项。这类图比直接看复数振幅更适合解释物理含义。 + +--- + +## Statevector 与 DensityMatrix 使用同一套状态图 + +同一个纯态也可以写成密度矩阵。Cqlib 的状态可视化接口接受 `Statevector` 和 `DensityMatrix`,因此可以用同一段绘图逻辑比较理想态和含噪态。 + +```python +from cqlib.qis import DensityMatrix + +density = DensityMatrix(1) +density.apply_h(0) + +plot_state_city(density, output_path="assets/plus_density_city.png") +plot_state_paulivec(density, output_path="assets/plus_density_paulivec.png") +``` + +密度矩阵输入生成的图如下: + +![Density-matrix state city](assets/plus_density_city.png) + +![Density-matrix Pauli vector](assets/plus_density_paulivec.png) + +使用状态图时,需要先明确输入是状态向量还是密度矩阵。前者通常表示理想纯态,后者可以表达混态和噪声后的状态。 + +下面构造一个简单混态,展示 state city 如何暴露对角结构: + +```python +mixed = DensityMatrix.from_density_matrix( + 1, + [ + 0.7 + 0.0j, + 0.0 + 0.0j, + 0.0 + 0.0j, + 0.3 + 0.0j, + ], +) + +plot_state_city( + mixed, + title="Mixed one-qubit state", + output_path="assets/mixed_state_city.png", +) +``` + +混态的 state city 如下: + +![Mixed one-qubit state city](assets/mixed_state_city.png) + +这个例子没有非对角相干项,因此图中主要信息集中在对角元。用它和 `|+>` 的 state city 对比,可以直观看出相干态与经典概率混合的差别。 + +--- + +## 多比特状态的阅读方式 + +Bloch multivector 对多比特态会为每个 qubit 绘制一个约化 Bloch 向量。以 Bell 态为例: + +```python +from cqlib import Circuit +from cqlib.qis import Statevector +from cqlib.visualization import plot_bloch_multivector, plot_state_city + +bell = Circuit(2) +bell.h(0) +bell.cx(0, 1) + +bell_state = Statevector.from_circuit(bell) + +plot_bloch_multivector(bell_state, output_path="assets/bell_bloch_multivector.png") +plot_state_city(bell_state, output_path="assets/bell_state_city.png") +``` + +Bell 态的约化 Bloch 图如下: + +![Bell-state Bloch multivector](assets/bell_bloch_multivector.png) + +Bell 态的 state city 图如下: + +![Bell-state state city](assets/bell_state_city.png) + +对于最大纠缠态,单个 qubit 的约化态可能看起来接近混合态。此时不要只凭单个 Bloch 球判断全局态是否“没有信息”,应结合 state city、概率分布或纠缠指标一起分析。 + +如果只是阅读每个 qubit 的局部 Bloch 向量,Bell 态会显得“没有方向”;但 state city 仍能展示全局密度矩阵中的相关结构。因此,多比特态通常需要结合多种状态图一起判断。 + +--- + +## 检查多比特标签顺序 + +多比特 state city 和 Pauli vector 的横轴标签依赖 bit order。需要与论文、硬件后端或其他 SDK 的显示习惯对齐时,可以用 `reverse_bits=True` 生成对照图。 + +下面构造一个非对称的 2-qubit 对角密度矩阵,使 `01` 和 `10` 的权重不同: + +```python +asymmetric = DensityMatrix.from_density_matrix( + 2, + [ + 0.05 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, + 0.0 + 0.0j, 0.15 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, + 0.0 + 0.0j, 0.0 + 0.0j, 0.75 + 0.0j, 0.0 + 0.0j, + 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.05 + 0.0j, + ], +) + +plot_state_city( + asymmetric, + title="Asymmetric basis weights", + output_path="assets/asymmetric_state_city.png", +) +plot_state_city( + asymmetric, + reverse_bits=True, + title="Asymmetric basis weights, reversed bits", + output_path="assets/asymmetric_state_city_reverse_bits.png", +) +``` + +默认显示顺序下的 state city: + +![Asymmetric state city](assets/asymmetric_state_city.png) + +反转显示顺序后的 state city: + +![Asymmetric state city with reversed bits](assets/asymmetric_state_city_reverse_bits.png) + +`reverse_bits=True` 不会改变状态本身,只改变图中的 basis label 显示顺序。对于这个非对称状态,`01` 和 `10` 权重不同,因此标签反转会直接影响读图结论。 + +Pauli vector 也可以用同样方式生成对照: + +```python +plot_state_paulivec( + asymmetric, + title="Asymmetric Pauli vector", + output_path="assets/asymmetric_paulivec.png", +) +plot_state_paulivec( + asymmetric, + reverse_bits=True, + title="Asymmetric Pauli vector, reversed bits", + output_path="assets/asymmetric_paulivec_reverse_bits.png", +) +``` + +默认 Pauli vector: + +![Asymmetric Pauli vector](assets/asymmetric_paulivec.png) + +反转显示顺序后的 Pauli vector: + +![Asymmetric Pauli vector with reversed bits](assets/asymmetric_paulivec_reverse_bits.png) + +在多比特状态图中,先确认 basis label 和 Pauli string 的显示顺序,再解释相干项、对角权重或 Pauli 期望值。 + +--- + +## 生成报告用状态图 + +状态图用于报告或演示材料时,可以控制画布尺寸、颜色和透明度。下面以同一个非对称密度矩阵为例,生成更紧凑的 Pauli vector: + +```python +plot_state_paulivec( + asymmetric, + figsize=(5.0, 3.0), + color=["#2563eb", "#dc2626"], + alpha=0.85, + title="Asymmetric Pauli vector for report", + output_path="assets/asymmetric_paulivec_report.png", +) +``` + +生成的报告用 Pauli vector 如下: + +![Asymmetric Pauli vector for report](assets/asymmetric_paulivec_report.png) + +这种图适合放入版面空间有限的报告。调试阶段仍建议先使用默认图,以减少配色、透明度和尺寸设置对读图的干扰。 + +--- + +## 状态图检查要点 + +- 确认状态来自线路模拟、直接构造还是噪声演化; +- 单比特态优先用 Bloch 图解释方向; +- 多比特纠缠态不要只看单 qubit Bloch 向量; +- 多比特图需要先确认 bit order、basis label 和 Pauli string 顺序; +- 密度矩阵图适合解释相干项和混态; +- Pauli vector 适合连接到可观测量和期望值。 +- 与结果图不同,状态图展示的是模拟或构造出的量子态本身;真实硬件通常只能直接给出采样结果,除非另有层析或估计流程。 + +--- + +## 下一步 + +- [可视化执行结果](6_result_visualization.md):对比状态图和采样结果,区分模拟态结构与实际 bitstring 分布。 +- [Notebook 与文档集成](3_notebook_and_docs.md):把状态图和生成代码保存到可重复运行的文档资产目录中。 +- [复杂线路的可视化策略](4_visualization_practices.md):回到线路结构,检查状态图中的异常是否来自门顺序、映射或模块展开。 diff --git a/docs/documentation/1_cqlib/5_visualization/assets/ansatz_decomposed.png b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_decomposed.png new file mode 100644 index 0000000..8accaf2 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_decomposed.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/ansatz_modules.png b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_modules.png new file mode 100644 index 0000000..25b10e7 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_modules.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/ansatz_overview.png b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_overview.png new file mode 100644 index 0000000..25b10e7 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_overview.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/ansatz_structure.png b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_structure.png new file mode 100644 index 0000000..a8ec546 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_structure.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/ansatz_with_params.png b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_with_params.png new file mode 100644 index 0000000..0e3b034 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/ansatz_with_params.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec.png b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec.png new file mode 100644 index 0000000..59102fa Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec_report.png b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec_report.png new file mode 100644 index 0000000..7168b93 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec_report.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec_reverse_bits.png b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec_reverse_bits.png new file mode 100644 index 0000000..7c4af68 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_paulivec_reverse_bits.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_state_city.png b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_state_city.png new file mode 100644 index 0000000..212442c Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_state_city.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_state_city_reverse_bits.png b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_state_city_reverse_bits.png new file mode 100644 index 0000000..43c11b1 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/asymmetric_state_city_reverse_bits.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell.png b/docs/documentation/1_cqlib/5_visualization/assets/bell.png new file mode 100644 index 0000000..1c96801 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_bloch_multivector.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_bloch_multivector.png new file mode 100644 index 0000000..092cebb Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_bloch_multivector.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_counts.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_counts.png new file mode 100644 index 0000000..b0a8234 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_counts.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_distribution.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_distribution.png new file mode 100644 index 0000000..e2eac06 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_distribution.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_dominant_outcomes.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_dominant_outcomes.png new file mode 100644 index 0000000..57a177b Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_dominant_outcomes.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_gate.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_gate.png new file mode 100644 index 0000000..ab18a56 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_gate.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_gate_decomposed.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_gate_decomposed.png new file mode 100644 index 0000000..270c719 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_gate_decomposed.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_initial_state.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_initial_state.png new file mode 100644 index 0000000..9454e25 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_initial_state.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_report_counts.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_report_counts.png new file mode 100644 index 0000000..3f9365a Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_report_counts.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_reverse_bits.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_reverse_bits.png new file mode 100644 index 0000000..104cbfe Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_reverse_bits.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bell_state_city.png b/docs/documentation/1_cqlib/5_visualization/assets/bell_state_city.png new file mode 100644 index 0000000..b78ec40 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bell_state_city.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/bloch_x.png b/docs/documentation/1_cqlib/5_visualization/assets/bloch_x.png new file mode 100644 index 0000000..0d53f45 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/bloch_x.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/deep_folded.png b/docs/documentation/1_cqlib/5_visualization/assets/deep_folded.png new file mode 100644 index 0000000..80a1dc0 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/deep_folded.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/dynamic_control_flow.png b/docs/documentation/1_cqlib/5_visualization/assets/dynamic_control_flow.png new file mode 100644 index 0000000..7c5f91c Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/dynamic_control_flow.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/mapped_after.png b/docs/documentation/1_cqlib/5_visualization/assets/mapped_after.png new file mode 100644 index 0000000..91b668f Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/mapped_after.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/mapped_before.png b/docs/documentation/1_cqlib/5_visualization/assets/mapped_before.png new file mode 100644 index 0000000..a2ddb9d Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/mapped_before.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/mixed_state_city.png b/docs/documentation/1_cqlib/5_visualization/assets/mixed_state_city.png new file mode 100644 index 0000000..8a31482 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/mixed_state_city.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/plus_bloch.png b/docs/documentation/1_cqlib/5_visualization/assets/plus_bloch.png new file mode 100644 index 0000000..31dace8 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/plus_bloch.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/plus_density_city.png b/docs/documentation/1_cqlib/5_visualization/assets/plus_density_city.png new file mode 100644 index 0000000..aede6ec Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/plus_density_city.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/plus_density_paulivec.png b/docs/documentation/1_cqlib/5_visualization/assets/plus_density_paulivec.png new file mode 100644 index 0000000..1728d82 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/plus_density_paulivec.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/plus_paulivec.png b/docs/documentation/1_cqlib/5_visualization/assets/plus_paulivec.png new file mode 100644 index 0000000..0db905f Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/plus_paulivec.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/plus_state_city.png b/docs/documentation/1_cqlib/5_visualization/assets/plus_state_city.png new file mode 100644 index 0000000..69f1088 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/plus_state_city.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/qaoa_cost.png b/docs/documentation/1_cqlib/5_visualization/assets/qaoa_cost.png new file mode 100644 index 0000000..9e94156 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/qaoa_cost.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/qaoa_mixer.png b/docs/documentation/1_cqlib/5_visualization/assets/qaoa_mixer.png new file mode 100644 index 0000000..79237e1 Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/qaoa_mixer.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/qaoa_prepare.png b/docs/documentation/1_cqlib/5_visualization/assets/qaoa_prepare.png new file mode 100644 index 0000000..fab889d Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/qaoa_prepare.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/search_hamming_counts.png b/docs/documentation/1_cqlib/5_visualization/assets/search_hamming_counts.png new file mode 100644 index 0000000..a4104ec Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/search_hamming_counts.png differ diff --git a/docs/documentation/1_cqlib/5_visualization/assets/special_directives_and_gates.png b/docs/documentation/1_cqlib/5_visualization/assets/special_directives_and_gates.png new file mode 100644 index 0000000..4f6572b Binary files /dev/null and b/docs/documentation/1_cqlib/5_visualization/assets/special_directives_and_gates.png differ diff --git a/docs/documentation/1_cqlib/6_error_mitigation/0_overview.md b/docs/documentation/1_cqlib/6_error_mitigation/0_overview.md new file mode 100644 index 0000000..8289b11 --- /dev/null +++ b/docs/documentation/1_cqlib/6_error_mitigation/0_overview.md @@ -0,0 +1,37 @@ +# 错误缓解总览 + +当前源码包的 Rust 核心层包含 `error_mitigation` 模块,主要实现: + +- 零噪声外推 ZNE; +- 虚拟蒸馏 Virtual Distillation; +- 统一的 ErrorMitigation facade。 + +从 Python 绑定目录看,当前 Python 包尚未像 `circuit`、`qis`、`device` 那样公开 `cqlib.error_mitigation` 模块。因此本章以“Rust 核心能力说明 + 后续 Python 绑定建议”为主,避免在官方 Python 教程中误导用户调用尚未暴露的接口。 + +错误缓解的一般流程: + +```text +原始线路 + ↓ +构造缓解所需线路族,例如折叠线路或 copy-swap 线路 + ↓ +在模拟器或真实后端执行 + ↓ +收集 noisy expectation + ↓ +外推或比值估计 + ↓ +得到 mitigated expectation +``` + + +## 误差缓解的典型数据流 + +```python +noise_factors = [1.0, 3.0, 5.0] +expectations = [-0.72, -0.65, -0.58] + +# 目标:根据多个噪声强度下的期望值,估计零噪声处的期望值。 +``` + +误差缓解不是量子纠错。它通常增加执行次数,通过后处理降低期望值偏差,但不能保证恢复完整无噪声分布。 diff --git a/docs/documentation/1_cqlib/6_error_mitigation/1_zne_rust_core.md b/docs/documentation/1_cqlib/6_error_mitigation/1_zne_rust_core.md new file mode 100644 index 0000000..b86e3bd --- /dev/null +++ b/docs/documentation/1_cqlib/6_error_mitigation/1_zne_rust_core.md @@ -0,0 +1,63 @@ +# 零噪声外推 ZNE(Rust 核心) + +ZNE 的基本思想是主动构造不同噪声强度下的等价线路,得到多个 noisy expectation,再外推到零噪声极限。 + +Rust 核心中的关键对象包括: + +- `ZNEMitigation`; +- `ExtrapolateMethod::Polynomial`; +- `ExtrapolateMethod::Exponential`; +- `fold_circuits`; +- `run_em_sequence_with_shots`; +- `extrapolate`。 + +概念流程: + +```text +Circuit U + ↓ fold level = 0,1,2 +U, U(U†U), U(U†U)(U†U) + ↓ +noise factors = 1,3,5 + ↓ +后端估计 + ↓ +多项式/指数外推到 noise_factor = 0 +``` + +未来 Python 绑定建议提供类似接口: + +```python +zne = ZNEMitigation(circuit, fold_levels=[0, 1, 2]) +folded = zne.fold_circuits(gate_set=None) +values = [estimator(c) for c in folded] +mitigated = zne.extrapolate(values, method="polynomial", degree=1) +``` + +在 Python 绑定正式暴露前,官方 Python 教程不应直接使用上述代码作为可运行示例。 + + +## :Python 概念验证 + +```python +import numpy as np + +noise_factors = np.array([1.0, 3.0, 5.0]) +values = np.array([-0.72, -0.65, -0.58]) + +coef = np.polyfit(noise_factors, values, deg=1) +zero_noise_value = np.polyval(coef, 0.0) + +print(zero_noise_value) +``` + +## :Rust Core 侧调用形态示意 + +```rust +use cqlib_core::error_mitigation::zne::ZNEMitigation; + +let zne = ZNEMitigation::default(); +// let result = zne.mitigate(&circuit, &observable, &executor)?; +``` + +正式文档中应以当前 Rust API 为准补全执行器、观测量和返回结果类型。 diff --git a/docs/documentation/1_cqlib/6_error_mitigation/2_virtual_distillation_rust_core.md b/docs/documentation/1_cqlib/6_error_mitigation/2_virtual_distillation_rust_core.md new file mode 100644 index 0000000..5391855 --- /dev/null +++ b/docs/documentation/1_cqlib/6_error_mitigation/2_virtual_distillation_rust_core.md @@ -0,0 +1,58 @@ +# 虚拟蒸馏 Virtual Distillation(Rust 核心) + +虚拟蒸馏通过多份态拷贝和 copy-swap 线路估计: + +```text +Tr(O rho^M) / Tr(rho^M) +``` + +Rust 核心中的 `VirtualDistillation` 支持: + +- 设置 copies 数; +- 构造 copy-swap 线路; +- 分别运行 numerator 和 denominator; +- 根据比值计算缓解后的期望值。 + +概念流程: + +```text +原始 Circuit + ↓ +复制 M 份制备线路 + ↓ +添加 pairwise SWAP + ↓ +估计 numerator: Tr(O rho^M) +估计 denominator: Tr(rho^M) + ↓ +计算比值得到缓解期望值 +``` + +未来 Python 教程可在绑定完成后加入端到端示例: + +```python +vd = VirtualDistillation(circuit, copies=2) +copy_swap = vd.build_copy_swap_circuit() +``` + +当前阶段建议将该内容放在“核心能力说明”或“开发者参考”章节。 + + +## :方法理解 + +```python +# Virtual Distillation 的直观形式: +# rho_mitigated ∝ rho^M +# M 越大,主导本征态成分越突出,但资源开销也越高。 +``` + +## :Rust Core 侧形态示意 + +```rust +use cqlib_core::error_mitigation::virtual_distillation::VirtualDistillation; + +let vd = VirtualDistillation::new(2); +// let value = vd.estimate(&measurements)?; +``` + +该能力更适合作为高级教程或开发者文档,普通入门用户只需理解其适用条件和额外资源开销。 diff --git a/docs/documentation/1_cqlib/6_error_mitigation/3_python_binding_status.md b/docs/documentation/1_cqlib/6_error_mitigation/3_python_binding_status.md new file mode 100644 index 0000000..1a1e754 --- /dev/null +++ b/docs/documentation/1_cqlib/6_error_mitigation/3_python_binding_status.md @@ -0,0 +1,35 @@ +# Python 绑定状态与扩展建议 + +当前 Python 绑定目录中没有独立的 `cqlib.error_mitigation` 运行时模块。因此官方文档应明确: + +- Rust 核心层已有错误缓解实现; +- Python 用户暂时不能直接通过 `import cqlib.error_mitigation` 使用; +- 若后续补齐绑定,应增加正式教程和 API 参考。 + +## 建议的 Python API 形态 + +```python +from cqlib.error_mitigation import ZNEMitigation, ExtrapolateMethod + +zne = ZNEMitigation(circuit, fold_levels=[0, 1, 2]) +folded = zne.fold_circuits() +``` + +## 建议的教程 + +1. ZNE:Bell 态或 1 比特旋转线路; +2. ZNE:选择性门折叠; +3. Virtual Distillation:2 copies 示例; +4. 与 `DensityMatrixNoise` 的联动; +5. 与真实后端 estimator 的联动。 + + +## :发布前必须确认的入口 + +```python +# 在正式写入 Python 用户教程前,请实际验证: +# import cqlib.error_mitigation +# from cqlib.error_mitigation import ZNEMitigation, VirtualDistillation +``` + +如果导入失败,应在教程中保留 Rust Core 说明,并把 Python 示例标注为未来接口草案,避免用户复制后直接报错。 diff --git a/docs/documentation/1_cqlib/7_tianyan/0_overview.md b/docs/documentation/1_cqlib/7_tianyan/0_overview.md new file mode 100644 index 0000000..8b7c2c2 --- /dev/null +++ b/docs/documentation/1_cqlib/7_tianyan/0_overview.md @@ -0,0 +1,149 @@ +# 天衍量子云平台客户端总览 + +`cqlib-tianyan` 是 Cqlib 生态中用于连接天衍量子云平台的客户端库。它的职责不是构建量子线路本身,而是把已经准备好的量子线路提交到云端量子后端,并把云端返回的任务结果转换为 Cqlib 统一的 `ExecutionResult` 对象。 + +从使用链路看,它处在“线路构建 / IR 转换”和“云端执行 / 结果获取”之间: + +```mermaid +flowchart LR + A["Cqlib Circuit"] --> B["cqlib.ir.qcis.dumps"] + C["已有 QCIS 程序"] --> D["QCIS 线路文本"] + B --> D + D --> E["cqlib_tianyan.TianyanBackend.run"] + E --> F["TaskHandle"] + F --> G["wait/status"] + G --> H["cqlib.device.ExecutionResult"] + H --> I["counts / probabilities / backend / task_id"] +``` + +## 1. 模块定位 + +`cqlib-tianyan` 主要提供以下能力: + +| 能力 | 对应对象 | 说明 | +|---|---|---| +| 平台认证 | `TianyanPlatform.login`、`from_credentials` | 使用 API Key 登录,支持本地凭据保存和自动刷新 | +| 后端发现 | `list_backends`、`get_backend` | 获取可用量子后端、设备状态、计费类型、比特数 | +| 设备配置 | `TianyanBackend.device_config` | 下载拓扑、校准、读出误差等设备信息,返回 `cqlib.device.Device` | +| 任务提交 | `run`、`run_raw`、`run_with_mode`、`submit` | 提交 QCIS 线路,支持批量提交 | +| 结果获取 | `TaskHandle.status`、`wait`、`wait_raw` | 查询任务状态,阻塞等待结果,返回 Cqlib 统一结果对象 | +| 读取误差矫正 | `CalibrationMode` | 根据设备校准数据对测量计数做读取误差矫正 | + +## 2. 与 Cqlib 其他模块的关系 + +`cqlib-tianyan` 与 Cqlib 核心模块的关系如下: + +| 模块 | 在天衍执行链路中的作用 | +|---|---| +| `cqlib.circuit` | 构建本地量子线路 | +| `cqlib.ir.qcis` | 把 `Circuit` 转为 QCIS 文本,或加载已有 QCIS 文本 | +| `cqlib_tianyan` | 登录平台、选择后端、提交 QCIS、轮询结果 | +| `cqlib.device` | 承载设备配置和执行结果,例如 `Device`、`ExecutionResult` | +| `cqlib.visualization` | 提交前查看线路结构,排查线路是否符合预期 | + +推荐工作流: + +```text +Circuit 构建 +-> QCIS 导出 +-> Tianyan 后端提交 +-> TaskHandle 等待结果 +-> ExecutionResult 分析 +``` + +## 3. 安装与环境 + +Python 包名为 `cqlib-tianyan`,导入模块名为 `cqlib_tianyan`。 + +```bash +pip install cqlib-tianyan +``` + +如果从源码开发安装: + +```bash +cd crates/binding-python +maturin develop +``` + +需要同时安装 `cqlib`,因为天衍客户端返回的结果对象是 `cqlib.device.ExecutionResult`,设备配置对象是 `cqlib.device.Device`。 + +## 4. 最小示例 + +```python +import os +from cqlib_tianyan import TianyanPlatform + +platform = TianyanPlatform.login(os.environ["TIANYAN_API_KEY"]) +backend = platform.get_backend("tianyan-287") + +qcis = "H Q1\nM Q1" +task = backend.run([qcis], shots=1000) + +results = task.wait(timeout_secs=120.0, poll_interval_secs=5.0) +result = results[0] + +print(result.task_id) +print(result.counts) +print(result.probabilities) +``` + +这个示例做了四件事: + +1. 使用环境变量中的 API Key 登录平台。 +2. 选择一个后端设备。 +3. 提交一条 QCIS 线路。 +4. 等待任务完成并读取测量计数。 + +## 5. 核心对象关系 + +```mermaid +classDiagram + class TianyanPlatform { + +login(api_key) + +from_credentials() + +list_backends() + +get_backend(name) + +submit(circuits, shots, device_name) + } + class TianyanBackend { + +name + +display_name + +status + +toll + +num_qubits + +is_available() + +run(circuits, shots) + +run_raw(circuits, shots) + +run_with_mode(circuits, shots, mode) + +device_config() + } + class TaskHandle { + +task_ids + +device_name + +shots + +submitted_at + +status() + +wait(timeout_secs, poll_interval_secs) + +wait_raw(timeout_secs, poll_interval_secs) + } + class ExecutionResult { + +task_id + +qubits + +shots + +counts + +probabilities + +backend + } + TianyanPlatform --> TianyanBackend + TianyanBackend --> TaskHandle + TaskHandle --> ExecutionResult +``` + +## 下一步 + +- [认证与配置](1_auth_config.md):学习 API Key 登录、凭据保存、自定义域名和配置项。 +- [后端与设备配置](2_backend_device.md):学习后端列表、状态判断、设备拓扑和校准配置。 +- [任务提交与结果获取](3_task_result.md):学习提交 QCIS、批量任务、轮询和结果对象。 +- [QCIS 与 IR 联动](4_qcis_ir_workflow.md):学习如何从 Cqlib `Circuit` 导出 QCIS 并提交到天衍。 +- [读取误差矫正](5_readout_mitigation.md):学习 `CalibrationMode` 和矫正/原始结果的区别。 diff --git a/docs/documentation/1_cqlib/7_tianyan/1_auth_config.md b/docs/documentation/1_cqlib/7_tianyan/1_auth_config.md new file mode 100644 index 0000000..fc3747a --- /dev/null +++ b/docs/documentation/1_cqlib/7_tianyan/1_auth_config.md @@ -0,0 +1,163 @@ +# 认证与配置 + +使用天衍平台前,需要先完成认证。`cqlib-tianyan` 使用 API Key 登录,登录成功后会创建 `TianyanPlatform` 对象。后续的后端查询、任务提交和结果查询都从这个对象开始。 + +对应导入: + +```python +from cqlib_tianyan import TianyanPlatform, TianyanConfig, TianyanError +``` + +## 1. 使用 API Key 登录 + +推荐把 API Key 放在环境变量中,避免写入代码: + +```bash +export TIANYAN_API_KEY="your_api_key" +``` + +Python 示例: + +```python +import os +from cqlib_tianyan import TianyanPlatform + +platform = TianyanPlatform.login(os.environ["TIANYAN_API_KEY"]) +``` + +默认平台域名是: + +```text +qc.zdxlz.com +``` + +默认 base URL 为: + +```text +https://qc.zdxlz.com +``` + +## 2. 凭据保存与复用 + +默认情况下,登录成功后会把凭据保存到本地: + +| 系统 | 默认路径 | +|---|---| +| macOS / Linux | `~/.cqlib/tianyan/credentials.json` | +| Windows | `%APPDATA%\cqlib\tianyan\credentials.json` | + +后续可以直接复用保存的凭据: + +```python +from cqlib_tianyan import TianyanPlatform + +platform = TianyanPlatform.from_credentials() +``` + +如果保存的 token 已过期,默认会使用保存的 API Key 自动刷新。 + +## 3. 不保存凭据 + +如果不希望把凭据写入磁盘,可以关闭 `save_credentials`: + +```python +platform = TianyanPlatform.login( + os.environ["TIANYAN_API_KEY"], + save_credentials=False, +) +``` + +如果希望 token 过期时不要自动刷新,也可以关闭 `auto_refresh`: + +```python +platform = TianyanPlatform.login( + os.environ["TIANYAN_API_KEY"], + save_credentials=False, + auto_refresh=False, +) +``` + +## 4. 自定义凭据路径 + +```python +platform = TianyanPlatform.login( + os.environ["TIANYAN_API_KEY"], + credentials_path="/secure/path/tianyan_credentials.json", +) +``` + +从自定义路径加载: + +```python +platform = TianyanPlatform.from_credentials( + credentials_path="/secure/path/tianyan_credentials.json", +) +``` + +## 5. 自定义平台域名 + +默认情况下不需要设置 `domain`。如果部署环境使用自定义域名,可以这样写: + +```python +platform = TianyanPlatform.login( + os.environ["TIANYAN_API_KEY"], + domain="qc.zdxlz.com", +) +``` + +`domain` 只需要传主机名,不需要写 `https://`。 + +## 6. TianyanConfig + +`TianyanConfig` 用于查看或组织配置项。Python 绑定中,`login` 和 `from_credentials` 已经可以直接接收配置关键字参数,因此普通用户不一定需要显式创建 `TianyanConfig`。 + +```python +from cqlib_tianyan import TianyanConfig + +config = TianyanConfig( + domain="qc.zdxlz.com", + save_credentials=True, + auto_refresh=True, + credentials_path="/secure/path/tianyan_credentials.json", +) + +print(config.base_url) +print(config.credentials_path) +``` + +配置项说明: + +| 参数 | 默认值 | 说明 | +|---|---|---| +| `domain` | `qc.zdxlz.com` | 平台主机名 | +| `save_credentials` | `True` | 登录或刷新后是否保存凭据 | +| `auto_refresh` | `True` | token 过期后是否自动重新登录 | +| `credentials_path` | 平台默认路径 | 本地凭据 JSON 文件路径 | + +## 7. 错误处理 + +平台认证、网络请求、任务提交和结果查询失败时会抛出异常。推荐写法: + +```python +from cqlib_tianyan import TianyanPlatform, TianyanError + +try: + platform = TianyanPlatform.login("invalid_api_key") +except Exception as exc: + print(f"登录失败: {exc}") +``` + +当前 Python abi3 绑定中,实际使用时建议捕获 `Exception`,再根据错误信息进行处理。 + +## 8. 安全建议 + +- 不要把 API Key 写进源码仓库。 +- 优先使用环境变量或密钥管理系统保存 API Key。 +- 多人共享机器上建议设置自定义 `credentials_path`。 +- CI 中建议关闭凭据持久化:`save_credentials=False`。 +- 如果只做临时测试,可以同时关闭 `save_credentials` 和 `auto_refresh`。 + +## 下一步 + +- [后端与设备配置](2_backend_device.md):列举平台后端、选择目标设备,并获取设备拓扑和校准信息。 +- [任务提交与结果获取](3_task_result.md):在完成后端选择后,提交 QCIS 线路并轮询云端执行结果。 diff --git a/docs/documentation/1_cqlib/7_tianyan/2_backend_device.md b/docs/documentation/1_cqlib/7_tianyan/2_backend_device.md new file mode 100644 index 0000000..7a5a3ba --- /dev/null +++ b/docs/documentation/1_cqlib/7_tianyan/2_backend_device.md @@ -0,0 +1,138 @@ +# 后端与设备配置 + +完成认证后,需要选择一个量子后端。天衍平台上的后端由 `TianyanBackend` 表示,它包含后端名称、显示名称、状态、计费类型、可用比特数,以及设备配置下载接口。 + +## 1. 列举后端 + +```python +import os +from cqlib_tianyan import TianyanPlatform + +platform = TianyanPlatform.login(os.environ["TIANYAN_API_KEY"]) +backends = platform.list_backends() + +for backend in backends: + print(backend.name, backend.display_name, backend.status, backend.num_qubits) +``` + +`list_backends()` 会返回 `TianyanBackend` 列表。 + +## 2. 后端对象字段 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `name` | `str` | 后端设备标识,用于提交任务,例如 `tianyan-287` | +| `display_name` | `str` | 用户友好的显示名称 | +| `status` | `DeviceStatus` | 设备运行状态 | +| `toll` | `DeviceToll` | 计费类型 | +| `num_qubits` | `int | None` | 平台返回的物理比特数,可能为空 | + +示例: + +```python +for backend in platform.list_backends(): + print(f"name={backend.name}") + print(f"display={backend.display_name}") + print(f"status={backend.status}") + print(f"toll={backend.toll}") + print(f"qubits={backend.num_qubits}") +``` + +## 3. 后端状态 + +`DeviceStatus` 可以与字符串比较: + +| 值 | 含义 | +|---|---| +| `running` | 设备在线,可接受任务 | +| `calibration` | 设备校准中,任务可能排队 | +| `under_maintenance` | 维护中,暂不可用 | +| `offline` | 离线 | +| `unknown` | 平台返回了未识别状态 | + +推荐提交前先检查: + +```python +backend = platform.get_backend("tianyan-287") + +if not backend.is_available(): + raise RuntimeError(f"后端不可用: {backend.status}") +``` + +也可以直接比较: + +```python +if backend.status == "running": + print("后端可提交") +``` + +## 4. 计费类型 + +`DeviceToll` 表示后端计费类型: + +| 值 | 含义 | +|---|---| +| `free` | 免费任务 | +| `paid` | 消耗额度或计费 | +| `unknown` | 平台返回了未识别计费类型 | + +```python +if backend.toll == "paid": + print("该后端可能消耗额度,请确认后再提交") +``` + +## 5. 获取指定后端 + +```python +backend = platform.get_backend("tianyan-287") +``` + +如果不存在该后端,会抛出异常。 + +## 6. 获取设备配置 + +`device_config()` 会下载设备拓扑、校准和误差信息,并返回 `cqlib.device.Device` 对象: + +```python +device = backend.device_config() + +print(device.name) +print(device.num_usable_qubits) +print(device.topology) +``` + +该结果会在后端对象内部缓存;同一个 `TianyanBackend` 上重复调用时,不需要每次重新下载。 + +## 7. 设备配置的用途 + +设备配置可用于: + +- 查看可用物理比特。 +- 查看耦合拓扑。 +- 辅助编译器做布局映射和门路由。 +- 获取读出保真度,用于读取误差矫正。 +- 分析单比特门、双比特门和测量误差。 + +典型流程: + +```python +backend = platform.get_backend("tianyan-287") +device = backend.device_config() + +# 后续可把 device 交给编译或分析模块使用 +``` + +## 8. 后端选择建议 + +| 场景 | 建议 | +|---|---| +| 只是测试 API 链路 | 选择 `running` 且免费或低成本后端 | +| 需要指定物理比特 | 先查看设备拓扑和可用比特 | +| 需要较好结果质量 | 关注读出误差、双比特门误差和校准时间 | +| 批量提交 | 先用少量线路试跑,再扩大批量 | +| 需要误差矫正 | 确认设备配置中有可用校准数据 | + +## 下一步 + +- [任务提交与结果获取](3_task_result.md):使用选定后端提交 QCIS 线路,查询任务状态并获取结果。 +- [QCIS 与 IR 联动](4_qcis_ir_workflow.md):学习如何从 Cqlib `Circuit` 导出 QCIS,再提交到天衍平台。 diff --git a/docs/documentation/1_cqlib/7_tianyan/3_task_result.md b/docs/documentation/1_cqlib/7_tianyan/3_task_result.md new file mode 100644 index 0000000..ee50d54 --- /dev/null +++ b/docs/documentation/1_cqlib/7_tianyan/3_task_result.md @@ -0,0 +1,189 @@ +# 任务提交与结果获取 + +天衍平台执行线路的基本单位是任务。`cqlib-tianyan` 中,任务提交后会返回 `TaskHandle`,用户可以通过它查询任务状态或阻塞等待结果。 + +## 1. 提交 QCIS 线路 + +Python 绑定中的任务提交接口接收 QCIS 字符串列表: + +```python +qcis = "H Q1\nM Q1" +task = backend.run([qcis], shots=1000) +``` + +参数说明: + +| 参数 | 说明 | +|---|---| +| `circuits` | QCIS 字符串列表 | +| `shots` | 每条线路的采样次数 | + +返回值是 `TaskHandle`。 + +## 2. 通过后端提交 + +```python +import os +from cqlib_tianyan import TianyanPlatform + +platform = TianyanPlatform.login(os.environ["TIANYAN_API_KEY"]) +backend = platform.get_backend("tianyan-287") + +circuits = [ + "H Q1\nM Q1", + "X Q1\nM Q1", +] + +task = backend.run(circuits, shots=1000) +print(task.task_ids) +``` + +## 3. 通过平台直接提交 + +如果已经知道目标设备名,也可以跳过 `get_backend`: + +```python +task = platform.submit( + circuits=["H Q1\nM Q1"], + shots=1000, + device_name="tianyan-287", +) +``` + +这种方式适合服务端或自动化脚本,因为它减少一次后端列表查询。 + +## 4. TaskHandle 字段 + +| 字段 | 说明 | +|---|---| +| `task_ids` | 平台返回的任务 ID 列表 | +| `device_name` | 提交使用的后端名称 | +| `shots` | 每条线路请求的 shots | +| `submitted_at` | 提交时间,ISO 8601 字符串 | + +示例: + +```python +print(task.task_ids) +print(task.device_name) +print(task.shots) +print(task.submitted_at) +``` + +## 5. 非阻塞查询 + +`status()` 只查询一次,返回已经完成的结果。尚未完成的线路不会出现在返回列表中。 + +```python +partial_results = task.status() + +print(f"已完成 {len(partial_results)} / {len(task.task_ids)}") +for result in partial_results: + print(result.task_id, result.counts) +``` + +适合自己实现轮询逻辑,或者在 UI 中定期刷新任务状态。 + +## 6. 阻塞等待 + +`wait()` 会轮询直到所有线路完成,或直到超时。 + +```python +results = task.wait( + timeout_secs=120.0, + poll_interval_secs=5.0, +) + +for result in results: + print(result.task_id) + print(result.counts) + print(result.probabilities) +``` + +参数说明: + +| 参数 | 说明 | +|---|---| +| `timeout_secs` | 最大等待秒数 | +| `poll_interval_secs` | 轮询间隔秒数,默认 `5.0` | + +`wait()` 在阻塞等待时会释放 Python GIL,因此不会阻塞其他 Python 线程运行。 + +## 7. 获取原始结果 + +默认 `wait()` 会按照提交时的 `CalibrationMode` 决定是否应用读取误差矫正。若只想拿原始计数,使用: + +```python +raw_results = task.wait_raw( + timeout_secs=120.0, + poll_interval_secs=5.0, +) +``` + +## 8. 结果对象 + +`wait()`、`wait_raw()` 和 `status()` 返回的是 `cqlib.device.ExecutionResult` 列表。 + +常用字段: + +| 字段 | 说明 | +|---|---| +| `task_id` | 平台任务 ID | +| `qubits` | 被测量的量子比特 | +| `shots` | 实际 shots 数 | +| `num_qubits` | 结果对象中的量子比特数 | +| `backend` | 后端名称 | +| `counts` | 测量计数字典 | +| `probabilities` | 按 counts 归一化后的概率 | +| `status` | 执行状态 | + +示例: + +```python +result = results[0] + +print(result.task_id) +print(result.backend) +print(result.qubits) +print(result.counts) +print(result.probabilities) +``` + +## 9. 批量提交 + +天衍平台单次请求最多接受 50 条线路。`cqlib-tianyan` 会在内部自动拆分更大的输入: + +```python +circuits = ["H Q1\nM Q1"] * 120 + +task = backend.run(circuits, shots=1000) +print(len(task.task_ids)) # 120 +``` + +内部会按 `50 + 50 + 20` 分批提交,并把所有任务 ID 合并到同一个 `TaskHandle` 中。 + +## 10. 提交前检查 + +建议提交前检查以下内容: + +- 后端是否 `running`。 +- QCIS 文本是否能被 `cqlib.ir.qcis.loads` 正确解析。 +- 线路中是否包含目标设备不支持的门。 +- 是否已经添加测量。 +- shots 是否符合实验需求和平台限制。 +- 批量任务是否有必要先小规模试跑。 + +## 11. 常见问题 + +| 现象 | 常见原因 | 处理方式 | +|---|---|---| +| 提交失败 | 后端不可用、QCIS 格式错误、API Key 无效或网络问题 | 检查 `backend.status`、QCIS 文本和认证状态 | +| `wait` 超时 | 队列等待时间长或任务未完成 | 增大 `timeout_secs`,或改用 `status()` 分批查询 | +| 返回结果少于提交线路数 | 使用了 `status()`,它只返回已完成结果 | 使用 `wait()` 等待全部完成 | +| counts 为空 | 平台结果尚未完成或解析失败 | 检查任务状态和原始错误信息 | +| 概率和 counts 不一致 | 概率由 counts 归一化得到,矫正模式可能改变 counts | 对比 `wait()` 和 `wait_raw()` | + +## 下一步 + +- [QCIS 与 IR 联动](4_qcis_ir_workflow.md):把 Cqlib `Circuit` 导出为 QCIS,并接入天衍任务提交流程。 +- [读取误差矫正](5_readout_mitigation.md):了解 `CalibrationMode`、原始结果和矫正结果的区别。 diff --git a/docs/documentation/1_cqlib/7_tianyan/4_qcis_ir_workflow.md b/docs/documentation/1_cqlib/7_tianyan/4_qcis_ir_workflow.md new file mode 100644 index 0000000..5a7b173 --- /dev/null +++ b/docs/documentation/1_cqlib/7_tianyan/4_qcis_ir_workflow.md @@ -0,0 +1,172 @@ +# QCIS 与 IR 联动 + +天衍平台任务提交使用 QCIS 线路文本。因此,在 Cqlib 中通常有两种方式准备提交内容: + +- 使用已有 QCIS 程序文本,例如硬件侧工具链、历史实验文件或外部系统生成的 QCIS。 +- 先用 `cqlib.circuit.Circuit` 构建线路,再通过 `cqlib.ir.qcis.dumps` 导出 QCIS。 + +本页重点说明第二种方式,因为它能把 Cqlib 的线路构建、可视化、IR 转换和天衍执行串成一个完整流程。 + +## 1. 从 Circuit 到 QCIS + +```python +from cqlib import Circuit +from cqlib.ir import qcis + +circuit = Circuit(2) +circuit.h(0) +circuit.cz(0, 1) +circuit.measure(0) +circuit.measure(1) + +qcis_text = qcis.dumps(circuit) +print(qcis_text) +``` + +可能输出: + +```text +H Q0 +CZ Q0 Q1 +M Q0 +M Q1 +``` + +## 2. 提交导出的 QCIS + +```python +import os +from cqlib import Circuit +from cqlib.ir import qcis +from cqlib_tianyan import TianyanPlatform + +circuit = Circuit(2) +circuit.h(0) +circuit.cz(0, 1) +circuit.measure(0) +circuit.measure(1) + +qcis_text = qcis.dumps(circuit) + +platform = TianyanPlatform.login(os.environ["TIANYAN_API_KEY"]) +backend = platform.get_backend("tianyan-287") + +task = backend.run([qcis_text], shots=1000) +results = task.wait(timeout_secs=120.0) + +print(results[0].counts) +``` + +## 3. 提交前先验证 QCIS + +如果 QCIS 来自外部系统、历史实验文件或人工编写的程序文本,推荐先用 Cqlib IR 解析一次: + +```python +from cqlib.ir import qcis + +qcis_text = "H Q1\nM Q1" +circuit = qcis.loads(qcis_text) + +print(circuit.num_qubits) +print(len(circuit.operations)) +``` + +如果 `qcis.loads` 失败,说明文本本身可能不符合 Cqlib 当前 QCIS 支持范围,不建议直接提交到云端。 + +## 4. 可视化检查 + +提交前可以用 Cqlib 可视化模块查看线路: + +```python +from cqlib.ir import qcis +from cqlib.visualization import draw_text + +circuit = qcis.loads("H Q1\nM Q1") +print(draw_text(circuit)) +``` + +这一步适合排查: + +- 量子比特编号是否正确。 +- 门顺序是否正确。 +- 测量是否存在。 +- 线路是否和预期一致。 + +## 5. QCIS 门集边界 + +`qcis.dumps(circuit)` 会拒绝无法表示为 QCIS 的线路。常见情况: + +| 情况 | 说明 | 处理方式 | +|---|---|---| +| 自定义 `CircuitGate` | QCIS 不直接表达用户自定义门 | 先 `decompose()` | +| 任意矩阵 `UnitaryGate` | QCIS 是门指令文本,不保存任意矩阵 | 先分解到基础门 | +| 复杂控制流 | QCIS 不表达通用 classical control flow | 改用其他格式或先编译展开 | +| 普通 identity gate | QCIS 的 `I Qn t` 表示 delay,不是普通恒等门 | 不要把普通 I 当成 QCIS delay | +| 全局相位 | 硬件指令一般不需要全局相位 | 提交前去除或忽略 | + +## 6. 物理比特编号 + +天衍 QCIS 使用物理比特编号,例如: + +```text +H Q1 +M Q1 +``` + +这里的 `Q1` 是物理比特编号,不一定等同于本地逻辑线路中的第 1 个逻辑比特。实际提交前需要确认目标设备拓扑和可用比特。 + +如果你直接用 `Circuit(2)` 构造本地线路,再导出 QCIS,默认会得到 `Q0`、`Q1`。如果目标设备希望使用特定物理比特,例如 `Q1`、`Q8`,需要在构造或映射阶段显式处理。 + +手写指定物理比特的 QCIS: + +```python +qcis_text = "H Q1\nH Q8\nCZ Q1 Q8\nH Q8\nM Q1\nM Q8" +``` + +这个示例使用 `H + CZ + H` 在目标比特上等价实现 CNOT 结构。 + +## 7. 与 OpenQASM 的关系 + +如果已有 OpenQASM 线路,可以通过 Cqlib IR 先转为 `Circuit`,再导出 QCIS: + +```python +from cqlib.ir import qasm3, qcis + +qasm3_text = """ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +h q[0]; +cz q[0],q[1]; +""" + +circuit = qasm3.loads(qasm3_text) +qcis_text = qcis.dumps(circuit) +``` + +注意:只有当 OpenQASM 线路中的门和语义能被 QCIS 表达时,转换才会成功。 + +## 8. 推荐执行链路 + +```mermaid +flowchart TD + A["构建 Circuit 或准备已有 QCIS 程序"] --> B["qcis.loads / qcis.dumps 验证"] + B --> C["可视化检查"] + C --> D["选择 TianyanBackend"] + D --> E["backend.run"] + E --> F["TaskHandle.wait"] + F --> G["ExecutionResult counts/probabilities"] +``` + +## 9. 工程建议 + +- 对关键实验保存原始 `Circuit`、导出的 QCIS 和最终结果。 +- 使用外部 QCIS 程序文本时,先用 `qcis.loads` 解析验证。 +- 使用物理比特前,先查看 `backend.device_config()`。 +- 如果 QCIS 导出失败,不要强行字符串拼接,优先回到线路分解或编译阶段处理。 +- 对云端任务保留 `task.task_ids`,方便后续追踪。 + +## 下一步 + +- [读取误差矫正](5_readout_mitigation.md):基于设备校准数据获取矫正后的测量结果。 +- [QCIS 格式说明](../1_ir/1_qcis.md):了解 Cqlib 对 QCIS 指令、导入和导出的支持范围。 diff --git a/docs/documentation/1_cqlib/7_tianyan/5_readout_mitigation.md b/docs/documentation/1_cqlib/7_tianyan/5_readout_mitigation.md new file mode 100644 index 0000000..0e1c5ef --- /dev/null +++ b/docs/documentation/1_cqlib/7_tianyan/5_readout_mitigation.md @@ -0,0 +1,182 @@ +# 读取误差矫正 + +真实量子设备的测量存在读取误差。`cqlib-tianyan` 在获取结果时可以利用设备校准数据对测量计数做读取误差矫正。该能力由 `CalibrationMode` 控制。 + +对应导入: + +```python +from cqlib_tianyan import CalibrationMode +``` + +## 1. 为什么需要读取误差矫正 + +理想情况下,如果某个量子比特真实处于 `|0>`,测量结果应该总是 `0`;真实处于 `|1>`,测量结果应该总是 `1`。但硬件读出会有错误: + +```text +真实 0 -> 可能读成 1 +真实 1 -> 可能读成 0 +``` + +平台校准数据通常会给出每个量子比特的读出保真度: + +| 符号 | 含义 | +|---|---| +| `f00` | 制备为 0 时读出为 0 的概率 | +| `f11` | 制备为 1 时读出为 1 的概率 | + +`cqlib-tianyan` 会基于这些数据构建混淆矩阵,并对观测计数做近似反演。 + +## 2. CalibrationMode + +`CalibrationMode` 有三种模式: + +| 模式 | 行为 | +|---|---| +| `auto` | 默认模式。有校准数据且测量比特数不超过阈值时自动矫正,否则回退到原始计数 | +| `enabled` | 强制矫正。没有校准数据或资源不足时返回错误 | +| `disabled` | 不做矫正,始终返回原始计数 | + +创建方式: + +```python +from cqlib_tianyan import CalibrationMode + +mode = CalibrationMode("auto") +assert mode == "auto" +``` + +大多数情况下可以直接传字符串: + +```python +task = backend.run_with_mode( + circuits=["H Q1\nM Q1"], + shots=1000, + mode="disabled", +) +``` + +## 3. 默认行为 + +`backend.run()` 默认使用 `auto` 模式: + +```python +task = backend.run(["H Q1\nM Q1"], shots=1000) +results = task.wait(timeout_secs=120.0) +``` + +`auto` 模式会在满足条件时自动应用读取误差矫正。 + +## 4. 获取原始结果 + +如果只想获取原始计数,不希望做任何矫正,可以使用 `run_raw`: + +```python +task = backend.run_raw(["H Q1\nM Q1"], shots=1000) +raw_results = task.wait(timeout_secs=120.0) +``` + +或者在已有任务上使用 `wait_raw`: + +```python +task = backend.run(["H Q1\nM Q1"], shots=1000) +raw_results = task.wait_raw(timeout_secs=120.0) +``` + +## 5. 显式指定矫正模式 + +```python +# 自动矫正 +task = backend.run_with_mode(["H Q1\nM Q1"], shots=1000, mode="auto") + +# 强制矫正 +task = backend.run_with_mode(["H Q1\nM Q1"], shots=1000, mode="enabled") + +# 禁用矫正 +task = backend.run_with_mode(["H Q1\nM Q1"], shots=1000, mode="disabled") +``` + +也可以传 `CalibrationMode` 对象: + +```python +mode = CalibrationMode("disabled") +task = backend.run_with_mode(["H Q1\nM Q1"], shots=1000, mode=mode) +``` + +## 6. auto 模式的比特数阈值 + +读取误差矫正需要构建混淆矩阵。若测量比特数为 `n`,完整矩阵规模是: + +```text +2^n x 2^n +``` + +内存复杂度接近: + +```text +O(4^n) +``` + +因此 `auto` 模式默认只在测量比特数不超过 14 时自动矫正。超过阈值时会回退到原始计数,避免内存开销过大。 + +如果确实需要强制矫正,可以使用: + +```python +task = backend.run_with_mode(circuits, shots=1000, mode="enabled") +``` + +但这要求调用方自己确认内存资源足够。 + +## 7. 对比矫正前后结果 + +```python +qcis = "H Q1\nM Q1" + +task = backend.run([qcis], shots=1000) + +calibrated = task.wait(timeout_secs=120.0) +raw = task.wait_raw(timeout_secs=120.0) + +print("矫正后:", calibrated[0].counts, calibrated[0].probabilities) +print("原始值:", raw[0].counts, raw[0].probabilities) +``` + +注意:矫正后的 counts 仍会以 `ExecutionResult` 的 `counts` 字段返回,`probabilities` 是基于 counts 归一化得到的概率。 + +## 8. 与设备配置的关系 + +读取误差矫正依赖设备校准数据。可以先查看设备配置: + +```python +device = backend.device_config() +``` + +如果没有可用校准数据: + +- `auto` 会回退到原始计数。 +- `enabled` 会报错。 +- `disabled` 不受影响。 + +## 9. 选择建议 + +| 场景 | 推荐模式 | +|---|---| +| 普通实验 | `auto` | +| 只想看硬件原始输出 | `disabled` 或 `wait_raw()` | +| 做误差矫正算法验证 | `enabled` | +| 大量测量比特 | `disabled`,或自行评估内存后使用 `enabled` | +| 对比硬件与矫正效果 | 同时使用 `wait()` 和 `wait_raw()` | + +## 10. 常见问题 + +| 现象 | 原因 | 处理方式 | +|---|---|---| +| `auto` 没有明显改变结果 | 无校准数据、测量比特数超过阈值,或读出误差本身较小 | 使用 `enabled` 验证,或查看设备校准数据 | +| `enabled` 报错 | 缺少校准数据或资源不足 | 改用 `auto` 或 `disabled` | +| 结果中出现很小概率项 | 逆矩阵矫正会重新分配概率 | 根据实验需求设置后处理阈值 | +| 大比特数任务很慢 | 矫正矩阵规模随比特数指数增长 | 禁用矫正或拆分实验 | + +## 下一步 + +- [Cqlib 教程总览](../../README.md):回到教程目录,继续查看其他 Cqlib 模块。 +- [QCIS 格式说明](../1_ir/1_qcis.md):了解 QCIS 文本格式、导入导出接口和支持边界。 +- [设备模块](../2_device/0_overview.md):了解设备拓扑、设备配置、噪声模型和执行结果对象。 diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..a129f9b --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,97 @@ +Cqlib 文档 +========== + +.. toctree:: + :maxdepth: 2 + :caption: 快速开始 + + documentation/0_get_started/0_introduction + documentation/0_get_started/1_installation + documentation/0_get_started/2_quickstart + +.. toctree:: + :maxdepth: 2 + :caption: 量子线路 + + documentation/1_cqlib/0_circuit/0_overview + documentation/1_cqlib/0_circuit/1_gates + documentation/1_cqlib/0_circuit/2_structures + documentation/1_cqlib/0_circuit/3_parameters + documentation/1_cqlib/0_circuit/4_circuit_analysis + documentation/1_cqlib/0_circuit/5_control_flow + +.. toctree:: + :maxdepth: 2 + :caption: 中间表示 + + documentation/1_cqlib/1_ir/0_overview + documentation/1_cqlib/1_ir/1_qcis + documentation/1_cqlib/1_ir/2_qasm2 + documentation/1_cqlib/1_ir/3_qasm3 + documentation/1_cqlib/1_ir/4_conversion_workflow + +.. toctree:: + :maxdepth: 2 + :caption: 设备与后端 + + documentation/1_cqlib/2_device/0_overview + documentation/1_cqlib/2_device/1_topology + documentation/1_cqlib/2_device/2_device + documentation/1_cqlib/2_device/3_layout + documentation/1_cqlib/2_device/4_noise + documentation/1_cqlib/2_device/5_result + +.. toctree:: + :maxdepth: 2 + :caption: 量子信息 + + documentation/1_cqlib/3_qis/0_overview + documentation/1_cqlib/3_qis/1_statevector + documentation/1_cqlib/3_qis/2_density_matrix + documentation/1_cqlib/3_qis/3_density_matrix_noise + documentation/1_cqlib/3_qis/4_stabilizer + documentation/1_cqlib/3_qis/5_pauli_and_hamiltonian + documentation/1_cqlib/3_qis/6_metrics_entropy + +.. toctree:: + :maxdepth: 2 + :caption: 编译优化 + + documentation/1_cqlib/4_compiler/0_overview + documentation/1_cqlib/4_compiler/1_layout + documentation/1_cqlib/4_compiler/2_sabre_mapping + documentation/1_cqlib/4_compiler/3_template_optimization + documentation/1_cqlib/4_compiler/4_commutative_and_clifford + +.. toctree:: + :maxdepth: 2 + :caption: 可视化 + + documentation/1_cqlib/5_visualization/0_overview + documentation/1_cqlib/5_visualization/1_draw_text + documentation/1_cqlib/5_visualization/2_draw_figure + documentation/1_cqlib/5_visualization/3_notebook_and_docs + documentation/1_cqlib/5_visualization/4_visualization_practices + documentation/1_cqlib/5_visualization/5_control_flow_and_special + documentation/1_cqlib/5_visualization/6_result_visualization + documentation/1_cqlib/5_visualization/7_state_visualization + +.. toctree:: + :maxdepth: 2 + :caption: 错误缓解 + + documentation/1_cqlib/6_error_mitigation/0_overview + documentation/1_cqlib/6_error_mitigation/1_zne_rust_core + documentation/1_cqlib/6_error_mitigation/2_virtual_distillation_rust_core + documentation/1_cqlib/6_error_mitigation/3_python_binding_status + +.. toctree:: + :maxdepth: 2 + :caption: 天衍平台 + + documentation/1_cqlib/7_tianyan/0_overview + documentation/1_cqlib/7_tianyan/1_auth_config + documentation/1_cqlib/7_tianyan/2_backend_device + documentation/1_cqlib/7_tianyan/3_task_result + documentation/1_cqlib/7_tianyan/4_qcis_ir_workflow + documentation/1_cqlib/7_tianyan/5_readout_mitigation diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..f086509 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,24 @@ +@ECHO OFF + +pushd %~dp0 + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. + echo.Make sure you have activated the Conda environment and installed docs requirements: + echo. + echo. conda activate cqlib_dev_2.0 + echo. python -m pip install -r requirements.txt + echo. + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +popd diff --git a/docs/requirements.txt b/docs/requirements.txt index 094bad4..bd6fa57 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ sphinx>=9.0 +myst-parser>=5.0,<6.0