diff --git a/README.md b/README.md index a12fbe0d8c5f396e511b483e20dc95d9925e555a..18241d09fdca8cb8aca0c45601dd08e4e93e6739 100644 --- a/README.md +++ b/README.md @@ -1,156 +1,312 @@ Origin Quantum is pleased to support the open source community by making pyqpanda-algorithm available.
+ Copyright (c) 2026 Origin Quantum. All rights reserved.
+ This source code is licensed under the Apache License Version 2.0
+ +

+ pyqpanda-algorithm 算法软件包 +

+ +

QPanda3框架 高开发效率 高可靠和稳定性 高性能
集合了在量子算法中常用的基本量子算法和函数

+ + + + + + ## 介绍 + + pyqpanda-algorithm 是由本源量子(Origin Quantum)开发的量子算法软件包,旨在为量子计算开发者提供一套标准化、模块化、高性能的基础算法库。该库集成了多种在金融、机器学习、组合优化、科学计算等领域广泛应用的量子算法,帮助用户快速实现从理论到代码的转化,提升开发效率并确保算法在不同量子平台上的可移植性。 + + 软件包官网: [https://qcloud.originqc.com.cn/zh/programming/pyqpanda-algorithm] + + ------ + + ## **核心特点** + + 1. **模块化与高复用性** + 所有算法以独立模块形式组织,便于开发者按需调用。例如,`QAOA`、`Grover`、`QSVM` 等算法均可独立导入与使用,支持在不同项目中重复利用。 + 2. **高性能实现** + 域名特定算法经过算法优化与工程加速,结合 QPanda3 的底层优化(如 OriginBIS 指令集、硬件感知编译),显著提升在模拟器与真实量子硬件上的执行效率。 + 3. **跨平台兼容性** + 与 QPanda3 框架深度集成,支持在 CPU 模拟器、量子云服务(如本源悟空)及真实量子处理器上运行,实现“一次编写,多端部署”。 + 4. **完善的文档与示例** + 提供详尽的 API 文档、使用示例与注释代码,降低学习门槛,特别适合初学者与研究者快速上手机器学习与组合优化任务。 + 5. **生态整合性强** + 与本源量子的其他工具链(如 VQNet、本源悟空、本源量禹)无缝对接,支持从算法设计到实际运行的完整工作流。 + + ------ + + ## 软件包种类 + + ### 1. **优化与搜索算法包** + + 适用于组合优化、大规模搜索问题,在路径规划、资源调度、投资组合优化等领域有广泛应用。 + + - **QUBO(无约束二进制优化)** + 将组合优化问题转化为二次无约束二元优化问题,是量子退火和变分量子算法的通用建模形式。 + + - **QAOA(量子近似优化算法)** + 混合量子-经典变分算法,通过优化参数化量子线路(Ansatz)来近似求解 QUBO 问题,适用于最大割、最大满足等问题。 + + - **Grover 搜索算法** + 在无结构数据库中实现目标项的二次加速搜索。通过振幅放大技术,将搜索复杂度从 $O(N)$ 降低至 $O(\sqrt{N})$。 + + ### 2. **机器学习与数据挖掘算法** + + 将量子计算能力引入经典机器学习流程,提升分类、聚类、回归等任务的效率与精度。 + + - **QSVM(量子支持向量机)** + 基于量子核函数的分类模型,可在高维空间中实现更优的分类边界。 + + - **QSVR(量子支持向量回归)** + 用于拟合连续变量的回归模型,适用于时间序列预测等任务。 + + - **QKMeans(量子 K-均值聚类)** + 利用量子加速实现大规模数据聚类,适用于高维数据聚类场景。 + + - **QPCA(量子主成分分析)** + 通过量子线路提取数据主成分,实现降维加速。 + + - **QMRMR(量子最小冗余最大相关)** + 实现高效特征选择,减少冗余特征影响。 + + - **QARM(量子关联规则挖掘)** + 快速挖掘频繁项集与关联规则,适用于市场篮子分析等任务。 + + ### 3. **科学计算与数值求解算法** + + 用于求解物理建模、工程仿真中的本征值、线性方程组、矩阵分解等关键问题。 + + - **QSVD(量子变分奇异值分解)** + 在变分框架下提取矩阵的奇异值与奇异向量,用于降维与推荐系统。 + + ### 4. **通用工具与基础组件** + + 提供量子振幅估计算法、比较器、稀疏编码等底层工具。 + + - **QAE(量子振幅估计算法)** + 精确估算目标态的振幅或测量概率,具备二次加速优势,常用于金融衍生品定价、风险评估等。 + + - **Comparator(量子比较器)** + 实现数值大小比较或阈值判定,支持构建量子决策逻辑。 + + - **SparseAmp(稀疏幅度编码)** + 高效将稀疏向量编码为量子态,减少量子资源消耗,适用于数据预处理。 + + ------ + + ## 安装 + + pyqpanda_alg是基于pyqpanda3的算法扩展模块。它的安装和使用需要依赖pyqpanda3。pyqpanda3的接口用法请参考[pyqpanda3](https://qcloud.originqc.com.cn/document/qpanda-3/cn/index.html)。 + + 如果已经安装了python环境和pip工具,在终端或控制台中输入如下命令:`pip install pyqpanda_alg` + + #### 注意: + + 如果你在linux下遇到权限问题,你需要添加sudo(superuser do)。 + + ------ + + ## 环境配置 + + pyqpanda_alg采用Python作为主要语言,对系统的环境要求如下: + + ### Windows + + | software | version | + | ------------------------------------------------------------ | ------------------ | + | [Microsoft Visual C++ Redistributable x64](https://aka.ms/vs/17/release/vc_redist.x64.exe) | 2019 | + | Python | >= 3.11 && <= 3.13 | + + ### Linux + + | software | version | + | -------- | ------------------ | + | GCC | >= 7.5 | + | Python | >= 3.11 && <= 3.13 | + + ------ + + ## 开源许可 + + 使用 [Apache License 2.0](https://gitee.com/OriginQ/alg/blob/master/LICENSE),对 公司、团队、个人 等 商用、非商用 都自由免费且非常友好,请放心使用和登记。 + + + ------ + + ## 致谢 + + 感谢所有贡献者、测试者与社区支持者。特别鸣谢本源量子研究院在算法设计与性能优化方面的技术支持。 + + ------ + + ## **联系方式** + + - **官方邮箱**:[qcloud@originqc.com](mailto:qcloud@originqc.com) + + - **售前咨询链接**:https://contact.originqc.com.cn/ + + - **官方微信**:搜索“本源量子云社区”,关注开源项目动态 +

+ 本源量子云社区服务号 +

+ + - **官方小助手**:可扫描下方二维码,添加官方小助手,获取更多支持 +

+ 本源量子官方小助手 +

+ diff --git a/pyqpanda-algorithm/README.md b/pyqpanda-algorithm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8919717ea331e50c3e7973847cd3a2ea7c0fbd83 --- /dev/null +++ b/pyqpanda-algorithm/README.md @@ -0,0 +1,266 @@ +# phase_noise_toolkit — 非高斯相位退相干工程验证工具包 + +> 项目名 layered_phase_noise(分层相位噪声),Python 包名 phase_noise_toolkit。 + +## 简介 + +`phase_noise_toolkit` 是一个基于 pyqpanda3 的相位退相干实验工程化工具包, +提供分层相位噪声注入、读出校准纠错、Ramsey 衰减测量等功能, +支持在本地模拟器和本源量子云平台上进行相位退相干实验的工程化验证。 + +### 核心特性 + +- **分层相位注入**:每层相位角独立采样,分布宽度按几何级数衰减 +- **多分布支持**:均匀分布(uniform)和高斯分布(gaussian)可选 +- **多门类型支持**:U1、RZ、RPhi 三种相位门 +- **读出校准纠错**:测量校准矩阵,自动纠正读出误差 +- **Ramsey 衰减实验**:一键扫描不同层数下的相干性衰减曲线 +- **噪声模型适配**:便捷创建去极化、比特翻转、相位阻尼等噪声模型 +- **跨平台兼容**:同时支持本地 CPUQVM 模拟器和本源量子云平台后端 + +## 安装 + +### 依赖 + +- Python 3.11 ~ 3.13 +- pyqpanda3 +- numpy + +### 安装 pyqpanda3 + +```bash +pip install pyqpanda3 +``` + +### 使用本工具包 + +将 `phase_noise_toolkit` 目录复制到你的项目中,或在 Python 路径中包含其父目录: + +```python +import sys +sys.path.insert(0, "/path/to/parent_directory") +from phase_noise_toolkit import LayeredPhaseInjector, RamseyDecayExperiment +``` + +## 快速开始 + +### 1. 基本用法:生成分层相位注入线路 + +```python +from pyqpanda3.core import core +from phase_noise_toolkit import LayeredPhaseInjector + +# 创建分层相位注入器 +injector = LayeredPhaseInjector( + n_layers=10, # 相位门层数 + sigma=0.3, # 第一层分布宽度 + decay_rate=0.99, # 每层宽度衰减率 + dist_type="uniform", # 分布类型: uniform / gaussian + gate_type="U1", # 相位门类型: U1 / RZ / RPhi + seed=42, # 随机种子(可选,用于可复现实验) +) + +# 查看每层宽度 +widths = injector.get_layer_widths() +print("每层宽度:", widths) + +# 生成单个线路(不带测量) +prog = injector.generate_circuit(qubit=0, add_measure=False) + +# 生成 Ramsey 型线路(H -> 多层相位门 -> H -> 测量) +prog_ramsey = injector.generate_layered_ramsey_circuit(qubit=0) + +# 生成多个独立采样线路(用于系综平均) +circuits = injector.generate_circuits(n_samples=100, qubit=0) +``` + +### 2. Ramsey 衰减实验 + +```python +from pyqpanda3.core import core +from phase_noise_toolkit import LayeredPhaseInjector, RamseyDecayExperiment + +# 初始化本地模拟器 +machine = core.CPUQVM() +machine.init_state() + +# 创建注入器和实验 +injector = LayeredPhaseInjector(n_layers=20, sigma=0.3, decay_rate=0.99, seed=42) +experiment = RamseyDecayExperiment(machine=machine, injector=injector, qubit=0) + +# 扫描多个层数点 +results = experiment.run_scan( + layer_list=[1, 5, 10, 15, 20], + n_samples=50, # 每个层数点的独立采样线路数 + shots=2048, # 每个线路的测量次数 +) + +# 打印结果 +experiment.print_results() + +# 获取相干性衰减曲线 +layers, coherences = experiment.get_coherence_curve() +``` + +### 3. 带读出校准的实验 + +```python +from phase_noise_toolkit import ReadoutCalibrator + +# 创建校准器并在机器上执行校准 +calibrator = ReadoutCalibrator() +f0, f1 = calibrator.calibrate(machine, qubit=0, shots=8192) +print(f"读出误差: f0={f0:.6f}, f1={f1:.6f}") + +# 保存/加载校准参数 +calibrator.save("readout_cal.json") +calibrator2 = ReadoutCalibrator() +calibrator2.load("readout_cal.json") + +# 在实验中使用校准器(自动纠错) +experiment = RamseyDecayExperiment( + machine=machine, injector=injector, + calibrator=calibrator, qubit=0, +) +``` + +### 4. 对比均匀分布和高斯分布 + +```python +# 均匀分布 +inj_uniform = LayeredPhaseInjector(n_layers=20, sigma=0.3, decay_rate=0.99, + dist_type="uniform", seed=42) +exp_uniform = RamseyDecayExperiment(machine, inj_uniform) +res_uniform = exp_uniform.run_scan([1, 5, 10, 20], n_samples=50, shots=2048) + +# 高斯分布 +inj_gaussian = LayeredPhaseInjector(n_layers=20, sigma=0.3, decay_rate=0.99, + dist_type="gaussian", seed=42) +exp_gaussian = RamseyDecayExperiment(machine, inj_gaussian) +res_gaussian = exp_gaussian.run_scan([1, 5, 10, 20], n_samples=50, shots=2048) + +# 对比两种分布的衰减曲线 +for ru, rg in zip(res_uniform, res_gaussian): + print(f"层数 {ru['n_layers']:>3}: 均匀={ru['coherence']:.4f}, " + f"高斯={rg['coherence']:.4f}, 差值={ru['coherence']-rg['coherence']:.4f}") +``` + +### 5. 在云平台上使用 + +```python +from pyqpanda3.qcloud import qcloud + +# 连接云平台 +qc = qcloud.QCloudService() +qc.api_config("your_api_key", "https://qcloud.originqc.com.cn") +backend = qc.backend("chip_name") + +# 使用方式与本地模拟器完全相同 +injector = LayeredPhaseInjector(n_layers=10, sigma=0.3, decay_rate=0.99) +experiment = RamseyDecayExperiment(machine=backend, injector=injector, qubit=0) +results = experiment.run_scan([1, 5, 10], n_samples=20, shots=1024) +``` + +### 5.1 真机使用注意事项 + +在真机后端(如 WK_C180_2)上使用本工具包时,请注意以下事项: + +**原生门限制:** WK_C180_2 等真机后端的原生 RPhi 门仅支持 θ=90°。任意角度的旋转必须通过官方推荐的 decompose → transpile 标准流程分解为多个原生门组合后提交,直接提交任意角度的原生门电路会导致未定义行为。 + +**本工具包的真机适配:** 本工具包在真机模式下会自动识别后端类型,走 to_instruction + run_instruction 路径,并自动进行 decompose/transpile 门分解,确保电路按官方标准流程提交。本地模拟器路径不受影响。 + +**门集校验提示:** 初始化 RamseyDecayExperiment 时,如果检测到真机后端,会自动校验 gate_type 是否为该后端的已知原生门。如果选择了可能不支持的门类型,会输出警告提示,但不阻断运行。在真机上运行前,请参考本源量子云官方文档确认后端的门集和标准使用流程。 + +**不同后端的差异:** 不同真机后端的原生门限制、门集和标准流程可能不同。本工具包的真机适配基于 WK_C180_2 的使用经验,其他后端请以官方文档为准。 + +### 6. 噪声模型(含噪虚拟机) + +```python +from phase_noise_toolkit import NoiseModelFactory + +# 创建去极化噪声模型 +noise_model = NoiseModelFactory.depolarizing(p1=0.001, p2=0.01) + +# 创建相位阻尼噪声模型 +noise_model = NoiseModelFactory.dephasing(p1=0.005, p2=0.01) + +# 创建比特翻转噪声模型 +noise_model = NoiseModelFactory.bit_flip(p1=0.01, p2=0.02) + +# 设置到云平台后端 +backend.set_noise_model(noise_model) +``` + +## 模块说明 + +| 模块 | 类/函数 | 说明 | +|------|---------|------| +| `layered_injector` | `LayeredPhaseInjector`, `check_gate_support` | 分层相位噪声注入器 + 后端门集校验 | +| `readout_calibration` | `ReadoutCalibrator` | 读出校准与纠错工具 | +| `ramsey_decay` | `RamseyDecayExperiment` | Ramsey 衰减测量实验 | +| `noise_utils` | `NoiseModelFactory` | 噪声模型工厂(去极化/比特翻转/相位阻尼等) | +| `utils` | `coherence_from_probs`, `uniform_phase`, `gaussian_phase`, `run_and_get_probs`, `run_prog_on_backend`, `is_cloud_backend` | 通用工具函数(含跨后端真机适配) | + +## 参数说明 + +### LayeredPhaseInjector + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| `n_layers` | int | 10 | 相位门层数 | +| `sigma` | float | 0.3 | 第一层分布宽度(uniform 为半宽,gaussian 为标准差) | +| `decay_rate` | float | 0.99 | 每层宽度衰减率,范围 (0, 1] | +| `dist_type` | str | "uniform" | 分布类型:"uniform" 或 "gaussian" | +| `gate_type` | str | "U1" | 相位门类型:"U1"、"RZ" 或 "RPhi" | +| `seed` | int | None | 随机数种子(可选) | + +第 k 层(k 从 0 开始)的分布宽度 = `decay_rate^k * sigma`。 + +### RamseyDecayExperiment + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| `machine` | object | — | 量子机器对象(本地模拟器或云平台后端) | +| `injector` | LayeredPhaseInjector | — | 分层相位注入器 | +| `calibrator` | ReadoutCalibrator | None | 读出校准器(可选,提供则自动纠错) | +| `qubit` | int | 0 | 实验使用的量子比特编号 | + +## 示例 + +完整示例脚本见 `examples/demo_local_simulator.py`,演示了: +- 均匀分布和高斯分布的 Ramsey 衰减对比 +- 读出校准的使用 +- 实验结果的打印和分析 + +运行示例(在作品根目录下执行,即同时包含 `phase_noise_toolkit/`、`examples/`、`tests/` 的目录): + +```bash +python examples/demo_local_simulator.py +``` + +## 测试 + +单元测试位于 `tests/test_phase_noise.py`,使用 pytest 运行: + +```bash +pip install pytest +pytest tests/test_phase_noise.py -v +``` + +测试覆盖: +- 通用工具函数(相干性计算、分布采样) +- 分层相位注入器(初始化、参数校验、线路生成) +- 读出校准器(纠错公式、保存加载) +- Ramsey 衰减实验(本地模拟器端到端验证) + +## 许可证 + +本工具包基于 pyqpanda3 开发,遵循与 pyqpanda-algorithm 仓库相同的开源协议。 + +## 贡献 + +欢迎提交 Issue 和 Pull Request。提交代码前请确保: +1. 代码通过现有单元测试 +2. 新增功能附带相应的测试用例 +3. 代码风格与项目保持一致 +4. 文档和示例同步更新 diff --git a/pyqpanda-algorithm/examples/demo_local_simulator.py b/pyqpanda-algorithm/examples/demo_local_simulator.py new file mode 100644 index 0000000000000000000000000000000000000000..92fdbddf59303e564c9ba33184e73b787bffd7a1 --- /dev/null +++ b/pyqpanda-algorithm/examples/demo_local_simulator.py @@ -0,0 +1,138 @@ +""" +本地模拟器演示:分层相位注入的 Ramsey 衰减实验。 + +本示例演示如何使用 phase_noise_toolkit 在 pyqpanda3 本地模拟器上 +运行相位退相干实验,对比均匀分布和高斯分布噪声下的相干性衰减曲线。 + +运行方式(在作品根目录下执行): + python examples/demo_local_simulator.py + +依赖: + pyqpanda3 + numpy +""" + +import sys +import os + +# 将作品根目录(含 phase_noise_toolkit/ 包)加入路径,便于直接运行本脚本 +_examples_dir = os.path.dirname(os.path.abspath(__file__)) +_package_parent = os.path.dirname(_examples_dir) +sys.path.insert(0, _package_parent) + +from pyqpanda3.core import core +from phase_noise_toolkit import ( + LayeredPhaseInjector, + RamseyDecayExperiment, + ReadoutCalibrator, +) + + +def run_demo(): + """运行本地模拟器演示实验。""" + print("=" * 60) + print("分层相位注入 Ramsey 衰减实验(本地模拟器)") + print("=" * 60) + + # 初始化本地模拟器 + machine = core.CPUQVM() + machine.init_state() + + # 实验参数 + layer_list = [1, 3, 5, 8, 12, 16, 20] + n_samples = 50 # 每个层数点的独立采样线路数 + shots = 2048 # 每个线路的测量次数 + sigma = 0.3 # 第一层分布宽度 + decay_rate = 0.99 # 每层宽度衰减率 + + # --------------------------------------------------------- + # 实验一:均匀分布相位噪声 + # --------------------------------------------------------- + print("\n[实验一] 均匀分布 U[-sigma, sigma] 相位噪声") + print(f" 参数: sigma={sigma}, decay_rate={decay_rate}") + + injector_uniform = LayeredPhaseInjector( + n_layers=max(layer_list), + sigma=sigma, + decay_rate=decay_rate, + dist_type="uniform", + gate_type="U1", + seed=42, + ) + + experiment_uniform = RamseyDecayExperiment( + machine=machine, + injector=injector_uniform, + qubit=0, + ) + + results_uniform = experiment_uniform.run_scan( + layer_list=layer_list, + n_samples=n_samples, + shots=shots, + ) + + print(f"\n {'层数':>6} {'相干性':>10}") + print(" " + "-" * 20) + for r in results_uniform: + print(f" {r['n_layers']:>6} {r['coherence']:>10.6f}") + + # --------------------------------------------------------- + # 实验二:高斯分布相位噪声 + # --------------------------------------------------------- + print("\n[实验二] 高斯分布 N(0, sigma^2) 相位噪声") + print(f" 参数: sigma={sigma}, decay_rate={decay_rate}") + + injector_gaussian = LayeredPhaseInjector( + n_layers=max(layer_list), + sigma=sigma, + decay_rate=decay_rate, + dist_type="gaussian", + gate_type="U1", + seed=42, + ) + + experiment_gaussian = RamseyDecayExperiment( + machine=machine, + injector=injector_gaussian, + qubit=0, + ) + + results_gaussian = experiment_gaussian.run_scan( + layer_list=layer_list, + n_samples=n_samples, + shots=shots, + ) + + print(f"\n {'层数':>6} {'相干性':>10}") + print(" " + "-" * 20) + for r in results_gaussian: + print(f" {r['n_layers']:>6} {r['coherence']:>10.6f}") + + # --------------------------------------------------------- + # 对比总结 + # --------------------------------------------------------- + print("\n" + "=" * 60) + print("对比总结") + print("=" * 60) + print(f" {'层数':>6} {'均匀分布':>10} {'高斯分布':>10} {'差值':>10}") + print(" " + "-" * 42) + for ru, rg in zip(results_uniform, results_gaussian): + diff = ru["coherence"] - rg["coherence"] + print(f" {ru['n_layers']:>6} {ru['coherence']:>10.6f} " + f"{rg['coherence']:>10.6f} {diff:>10.6f}") + + # --------------------------------------------------------- + # 演示读出校准(可选) + # --------------------------------------------------------- + print("\n[附加] 读出校准演示") + calibrator = ReadoutCalibrator() + f0, f1 = calibrator.calibrate(machine, qubit=0, shots=4096) + print(f" 校准结果: f0={f0:.6f}, f1={f1:.6f}") + print(f" (本地模拟器读出误差通常接近 0)") + + print("\n演示完成。") + + +if __name__ == "__main__": + run_demo() diff --git a/pyqpanda-algorithm/phase_noise_toolkit/__init__.py b/pyqpanda-algorithm/phase_noise_toolkit/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..82cc7cd1849135b0c815ed7c0b83f2ee0883228d --- /dev/null +++ b/pyqpanda-algorithm/phase_noise_toolkit/__init__.py @@ -0,0 +1,48 @@ +""" +phase_noise_toolkit +=================== + +非高斯相位退相干工程验证工具包。 + +提供分层相位噪声注入、读出校准纠错、Ramsey 衰减测量等工具, +支持在 pyqpanda3 本地模拟器和本源量子云平台上进行相位退相干 +实验的工程化验证。 + +模块列表: + layered_injector - 分层相位噪声注入器 + readout_calibration - 读出校准与纠错 + ramsey_decay - Ramsey 衰减测量实验 + noise_utils - 噪声模型适配工具 + utils - 通用工具函数 +""" + +from .layered_injector import LayeredPhaseInjector, check_gate_support +from .readout_calibration import ReadoutCalibrator +from .ramsey_decay import RamseyDecayExperiment +from .noise_utils import NoiseModelFactory +from .utils import ( + coherence_from_probs, + uniform_phase, + gaussian_phase, + sample_phase, + run_and_get_probs, + run_prog_on_backend, + is_cloud_backend, +) + +__all__ = [ + "LayeredPhaseInjector", + "ReadoutCalibrator", + "RamseyDecayExperiment", + "NoiseModelFactory", + "check_gate_support", + "coherence_from_probs", + "uniform_phase", + "gaussian_phase", + "sample_phase", + "run_and_get_probs", + "run_prog_on_backend", + "is_cloud_backend", +] + +__version__ = "0.4.1" diff --git a/pyqpanda-algorithm/phase_noise_toolkit/layered_injector.py b/pyqpanda-algorithm/phase_noise_toolkit/layered_injector.py new file mode 100644 index 0000000000000000000000000000000000000000..b2a01983022a489bb60fedff31b8e4327651e7a1 --- /dev/null +++ b/pyqpanda-algorithm/phase_noise_toolkit/layered_injector.py @@ -0,0 +1,231 @@ +""" +分层相位噪声注入器。 + +在单个量子比特上依次施加多层相位门,每层相位角从指定分布中独立采样, +且每层的分布宽度按几何级数衰减(第 k 层半宽 = decay_rate^k * sigma)。 + +支持均匀分布和高斯分布两种相位噪声,支持 U1、RZ、RPhi 三种相位门。 + +典型用法: + injector = LayeredPhaseInjector( + n_layers=10, sigma=0.3, decay_rate=0.99, + dist_type="uniform", gate_type="U1" + ) + prog = injector.generate_circuit(qubit=0) +""" + +import warnings + +import numpy as np +from pyqpanda3.core import core + +from .utils import sample_phase + + +# 各后端支持的原生相位门集合(用于门集校验提示) +# 注意:这是经验性提示,不阻断运行;具体门集以本源量子云官方文档为准 +SUPPORTED_GATES_BY_BACKEND = { + "CPUQVM": ["U1", "RZ", "RPhi"], + "WK_C180": ["RPhi"], + "WK_C180_2": ["RPhi"], +} + + +def check_gate_support(backend_name, gate_type): + """ + 检查指定后端是否支持给定的相位门类型,给出警告提示。 + + 此函数仅做提示,不阻断运行。如果后端不在已知列表中,跳过校验。 + + 参数: + backend_name (str): 后端名称,如 "CPUQVM"、"WK_C180_2" + gate_type (str): 相位门类型,"U1"、"RZ" 或 "RPhi" + + 返回: + bool: True 表示支持(或未知后端跳过校验),False 表示可能不支持 + """ + if backend_name not in SUPPORTED_GATES_BY_BACKEND: + return True # 未知后端,跳过校验 + + supported = SUPPORTED_GATES_BY_BACKEND[backend_name] + if gate_type not in supported: + warnings.warn( + f"gate_type '{gate_type}' 可能不是后端 '{backend_name}' 的原生门。" + f"该后端已知原生相位门: {supported}。" + f"在真机上运行前请参考本源量子云官方文档确认后端门集。", + stacklevel=2, + ) + return False + return True + + +class LayeredPhaseInjector: + """ + 分层相位噪声注入器。 + + 在量子比特上依次施加 n_layers 个相位门,每层相位角独立采样, + 分布宽度按 decay_rate 几何衰减。 + + 属性: + n_layers (int): 层数 + sigma (float): 第一层分布宽度 + decay_rate (float): 每层宽度衰减率 + dist_type (str): 分布类型,'uniform' 或 'gaussian' + gate_type (str): 相位门类型,'U1'、'RZ' 或 'RPhi' + rng (numpy.random.Generator): 随机数生成器 + """ + + def __init__(self, n_layers=10, sigma=0.3, decay_rate=0.99, + dist_type="uniform", gate_type="U1", seed=None): + """ + 初始化分层相位注入器。 + + 参数: + n_layers (int): 相位门层数,默认 10 + sigma (float): 第一层分布宽度(uniform 为半宽,gaussian 为标准差),默认 0.3 + decay_rate (float): 每层宽度衰减率,范围 (0, 1],默认 0.99 + dist_type (str): 相位角分布类型,'uniform' 或 'gaussian',默认 'uniform' + gate_type (str): 相位门类型,'U1'、'RZ' 或 'RPhi',默认 'U1' + seed (int, optional): 随机数种子,用于可复现实验 + + 异常: + ValueError: 参数不合法时抛出 + """ + if n_layers <= 0: + raise ValueError(f"n_layers 必须为正整数,当前: {n_layers}") + if sigma <= 0: + raise ValueError(f"sigma 必须为正数,当前: {sigma}") + if not (0 < decay_rate <= 1): + raise ValueError(f"decay_rate 必须在 (0, 1] 范围内,当前: {decay_rate}") + if dist_type not in ("uniform", "gaussian"): + raise ValueError(f"dist_type 必须为 'uniform' 或 'gaussian',当前: {dist_type}") + if gate_type not in ("U1", "RZ", "RPhi"): + raise ValueError(f"gate_type 必须为 'U1'、'RZ' 或 'RPhi',当前: {gate_type}") + + self.n_layers = n_layers + self.sigma = sigma + self.decay_rate = decay_rate + self.dist_type = dist_type + self.gate_type = gate_type + self.rng = np.random.default_rng(seed) + + def get_layer_widths(self): + """ + 返回每层的分布宽度。 + + 第 k 层(k 从 0 开始)宽度 = decay_rate^k * sigma。 + + 返回: + list[float]: 每层宽度列表,长度为 n_layers + """ + return [self.sigma * (self.decay_rate ** k) for k in range(self.n_layers)] + + def set_seed(self, seed): + """ + 重新设置随机数种子。 + + 参数: + seed (int): 随机数种子 + """ + self.rng = np.random.default_rng(seed) + + def _apply_phase_gate(self, prog, qubit, theta): + """ + 在量子线路上施加一个相位门。 + + 参数: + prog: pyqpanda3 QProg 对象 + qubit (int): 量子比特编号 + theta (float): 相位角(弧度) + """ + if self.gate_type == "U1": + prog << core.U1(qubit, theta) + elif self.gate_type == "RZ": + prog << core.RZ(qubit, theta) + elif self.gate_type == "RPhi": + prog << core.RPhi(qubit, theta, 0.0) + + def generate_circuit(self, qubit=0, add_measure=True): + """ + 生成一个包含分层相位注入的量子线路。 + + 线路结构: (可选初态制备由调用方负责)-> 多层相位门 -> (可选测量) + + 参数: + qubit (int): 目标量子比特编号,默认 0 + add_measure (bool): 是否在末尾添加测量,默认 True + + 返回: + pyqpanda3 QProg: 生成的量子线路 + """ + prog = core.QProg() + widths = self.get_layer_widths() + + for k in range(self.n_layers): + theta = sample_phase(self.dist_type, widths[k], self.rng) + self._apply_phase_gate(prog, qubit, theta) + + if add_measure: + prog << core.measure(qubit, qubit) + + return prog + + def generate_circuits(self, n_samples, qubit=0, add_measure=True): + """ + 生成多个独立采样的量子线路(用于系综平均)。 + + 每个线路的相位角独立采样,模拟不同的噪声实现。 + + 参数: + n_samples (int): 线路数量(采样数) + qubit (int): 目标量子比特编号,默认 0 + add_measure (bool): 是否在末尾添加测量,默认 True + + 返回: + list[pyqpanda3 QProg]: 量子线路列表 + """ + if n_samples <= 0: + raise ValueError(f"n_samples 必须为正整数,当前: {n_samples}") + return [self.generate_circuit(qubit, add_measure) for _ in range(n_samples)] + + def generate_layered_ramsey_circuit(self, qubit=0): + """ + 生成一个完整的 Ramsey 型分层相位注入线路。 + + 线路结构: H -> 多层相位门 -> H -> 测量 + + 这是相位退相干实验的标准线路:初态 |+>,施加相位噪声, + 再用 H 门转回计算基测量,相干性体现在 P(0) 与 P(1) 的差值中。 + + 参数: + qubit (int): 目标量子比特编号,默认 0 + + 返回: + pyqpanda3 QProg: Ramsey 型量子线路 + """ + prog = core.QProg() + prog << core.H(qubit) + + widths = self.get_layer_widths() + for k in range(self.n_layers): + theta = sample_phase(self.dist_type, widths[k], self.rng) + self._apply_phase_gate(prog, qubit, theta) + + prog << core.H(qubit) + prog << core.measure(qubit, qubit) + return prog + + def generate_layered_ramsey_circuits(self, n_samples, qubit=0): + """ + 生成多个独立采样的 Ramsey 型线路(用于系综平均)。 + + 参数: + n_samples (int): 线路数量(采样数) + qubit (int): 目标量子比特编号,默认 0 + + 返回: + list[pyqpanda3 QProg]: Ramsey 型量子线路列表 + """ + if n_samples <= 0: + raise ValueError(f"n_samples 必须为正整数,当前: {n_samples}") + return [self.generate_layered_ramsey_circuit(qubit) for _ in range(n_samples)] diff --git a/pyqpanda-algorithm/phase_noise_toolkit/noise_utils.py b/pyqpanda-algorithm/phase_noise_toolkit/noise_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..30ac7222cf88caf57bf69960e33430f5e6d7f142 --- /dev/null +++ b/pyqpanda-algorithm/phase_noise_toolkit/noise_utils.py @@ -0,0 +1,118 @@ +""" +噪声模型适配工具。 + +封装 pyqpanda3 云平台的 QCloudNoiseModel,提供常见噪声模型的 +便捷创建方法,用于在含噪虚拟机上模拟真实硬件的噪声效应。 + +支持的噪声类型: + - 去极化噪声 (DEPOLARIZING) + - 比特翻转噪声 (BITFLIP) + - 相位阻尼噪声 (DEPHASING) + - 振幅阻尼噪声 (DAMPING) + +典型用法: + noise_model = NoiseModelFactory.depolarizing(p1=0.001, p2=0.01) + backend.set_noise_model(noise_model) +""" + +from pyqpanda3.qcloud import qcloud + + +class NoiseModelFactory: + """ + 噪声模型工厂。 + + 提供静态方法,便捷创建常见类型的 QCloudNoiseModel。 + 所有方法均返回 QCloudNoiseModel 对象,可直接设置到云平台后端。 + """ + + @staticmethod + def depolarizing(p1=0.001, p2=0.01): + """ + 创建去极化噪声模型。 + + 去极化噪声是最常见的通用噪声模型,模拟量子比特在门操作后 + 以一定概率完全混合(失去所有相位和振幅信息)。 + + 参数: + p1 (float): 单门去极化概率,默认 0.001 + p2 (float): 双门去极化概率,默认 0.01 + + 返回: + QCloudNoiseModel: 去极化噪声模型 + """ + return qcloud.QCloudNoiseModel( + qcloud.NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR, [p1], [p2] + ) + + @staticmethod + def bit_flip(p1=0.01, p2=0.02): + """ + 创建比特翻转噪声模型。 + + 比特翻转噪声模拟测量过程中的读出错误,量子比特以一定概率 + 从 |0> 翻转为 |1> 或反之。 + + 参数: + p1 (float): 单门比特翻转概率,默认 0.01 + p2 (float): 双门比特翻转概率,默认 0.02 + + 返回: + QCloudNoiseModel: 比特翻转噪声模型 + """ + return qcloud.QCloudNoiseModel( + qcloud.NOISE_MODEL.BITFLIP_KRAUS_OPERATOR, [p1], [p2] + ) + + @staticmethod + def dephasing(p1=0.005, p2=0.01): + """ + 创建相位阻尼噪声模型。 + + 相位阻尼噪声模拟纯退相干过程,量子比特的振幅信息保留, + 但相位信息以一定概率丢失。这是超导量子比特中最常见的噪声。 + + 参数: + p1 (float): 单门相位阻尼概率,默认 0.005 + p2 (float): 双门相位阻尼概率,默认 0.01 + + 返回: + QCloudNoiseModel: 相位阻尼噪声模型 + """ + return qcloud.QCloudNoiseModel( + qcloud.NOISE_MODEL.DEPHASING_KRAUS_OPERATOR, [p1], [p2] + ) + + @staticmethod + def amplitude_damping(p1=0.001, p2=0.005): + """ + 创建振幅阻尼噪声模型。 + + 振幅阻尼噪声模拟能量弛豫过程(T1 衰减),激发态 |1> 以 + 一定概率衰变为基态 |0>。 + + 参数: + p1 (float): 单门振幅阻尼概率,默认 0.001 + p2 (float): 双门振幅阻尼概率,默认 0.005 + + 返回: + QCloudNoiseModel: 振幅阻尼噪声模型 + """ + return qcloud.QCloudNoiseModel( + qcloud.NOISE_MODEL.DAMPING_KRAUS_OPERATOR, [p1], [p2] + ) + + @staticmethod + def custom(noise_type, p1_list, p2_list): + """ + 创建自定义噪声模型。 + + 参数: + noise_type (NOISE_MODEL): 噪声类型枚举值 + p1_list (list[float]): 单门噪声参数列表 + p2_list (list[float]): 双门噪声参数列表 + + 返回: + QCloudNoiseModel: 自定义噪声模型 + """ + return qcloud.QCloudNoiseModel(noise_type, p1_list, p2_list) diff --git a/pyqpanda-algorithm/phase_noise_toolkit/ramsey_decay.py b/pyqpanda-algorithm/phase_noise_toolkit/ramsey_decay.py new file mode 100644 index 0000000000000000000000000000000000000000..6cdf0dca49a6d18df5847bcc242a6af48aadbb1b --- /dev/null +++ b/pyqpanda-algorithm/phase_noise_toolkit/ramsey_decay.py @@ -0,0 +1,191 @@ +""" +Ramsey 衰减测量实验。 + +整合分层相位注入器和读出校准器,提供完整的相位退相干实验功能: +扫描不同层数下的相干性衰减曲线,支持本地模拟器和云平台, +支持系综平均和读出纠错。 + +实验线路: H -> 多层相位门 -> H -> 测量 +相干性 I = |P(0) - P(1)| + +典型用法: + injector = LayeredPhaseInjector(n_layers=10, sigma=0.3, decay_rate=0.99) + experiment = RamseyDecayExperiment(machine, injector) + results = experiment.run_scan(layer_list=[1, 5, 10, 20], n_samples=100, shots=1024) +""" + +import numpy as np + +from .layered_injector import LayeredPhaseInjector, check_gate_support +from .readout_calibration import ReadoutCalibrator +from .utils import coherence_from_probs, run_prog_on_backend, is_cloud_backend + + +class RamseyDecayExperiment: + """ + Ramsey 衰减测量实验。 + + 在指定量子机器上运行分层相位注入的 Ramsey 线路, + 测量不同层数下的相干性衰减。 + + 属性: + machine: 量子机器对象(本地模拟器或云平台后端) + injector (LayeredPhaseInjector): 分层相位注入器 + calibrator (ReadoutCalibrator, optional): 读出校准器 + qubit (int): 实验使用的量子比特编号 + results (list): 实验结果记录 + """ + + def __init__(self, machine, injector, calibrator=None, qubit=0): + """ + 初始化 Ramsey 衰减实验。 + + 参数: + machine: 量子机器对象(需有 run(prog, shots) 方法) + injector (LayeredPhaseInjector): 分层相位注入器实例 + calibrator (ReadoutCalibrator, optional): 读出校准器,若提供则自动纠错 + qubit (int): 实验使用的量子比特编号,默认 0 + """ + self.machine = machine + self.injector = injector + self.calibrator = calibrator + self.qubit = qubit + self.results = [] + + # 真机后端门集校验(仅提示,不阻断运行) + if is_cloud_backend(machine): + backend_name = self._detect_backend_name(machine) + if backend_name: + check_gate_support(backend_name, injector.gate_type) + + @staticmethod + def _detect_backend_name(machine): + """ + 尝试检测云平台后端名称。 + + 参数: + machine: 云平台后端对象 + + 返回: + str or None: 后端名称,检测不到返回 None + """ + for attr in ("backend_name", "name", "chip_name"): + try: + val = getattr(machine, attr) + if callable(val): + val = val() + if val and isinstance(val, str): + return val + except Exception: + continue + # 尝试从类名中提取 + try: + class_name = type(machine).__name__ + if "QCloud" in class_name or "Backend" in class_name: + return None # 类名太泛,不做判断 + except Exception: + pass + return None + + def _run_circuit_get_p0(self, prog, shots): + """ + 运行单个线路并返回 P(0)(可选读出纠错)。 + + 参数: + prog: pyqpanda3 QProg 对象 + shots (int): 测量次数 + + 返回: + float: P(0)(已纠错或原始) + """ + probs = run_prog_on_backend(self.machine, prog, shots) + p0 = probs.get("0", 0.0) + + if self.calibrator is not None and self.calibrator.calibrated: + p0 = self.calibrator.correct(p0) + + return p0 + + def run_single(self, n_layers, n_samples=100, shots=1024): + """ + 运行单个层数点,测量相干性。 + + 生成 n_samples 个独立采样的 Ramsey 线路,逐个运行并取系综平均。 + + 参数: + n_layers (int): 相位门层数 + n_samples (int): 独立采样线路数(系综大小),默认 100 + shots (int): 每个线路的测量次数,默认 1024 + + 返回: + dict: 包含 n_layers、coherence、p0_mean、p0_std 的结果字典 + """ + # 临时修改注入器的层数 + original_layers = self.injector.n_layers + self.injector.n_layers = n_layers + + p0_list = [] + for _ in range(n_samples): + prog = self.injector.generate_layered_ramsey_circuit(self.qubit) + p0 = self._run_circuit_get_p0(prog, shots) + p0_list.append(p0) + + # 恢复注入器的原始层数 + self.injector.n_layers = original_layers + + p0_mean = float(np.mean(p0_list)) + p0_std = float(np.std(p0_list)) + p1_mean = 1.0 - p0_mean + coherence = coherence_from_probs(p0_mean, p1_mean) + + result = { + "n_layers": n_layers, + "coherence": coherence, + "p0_mean": p0_mean, + "p0_std": p0_std, + "n_samples": n_samples, + "shots": shots, + } + self.results.append(result) + return result + + def run_scan(self, layer_list, n_samples=100, shots=1024): + """ + 扫描多个层数点,测量相干性衰减曲线。 + + 参数: + layer_list (list[int]): 要扫描的层数列表,如 [1, 5, 10, 20, 50] + n_samples (int): 每个层数点的独立采样线路数,默认 100 + shots (int): 每个线路的测量次数,默认 1024 + + 返回: + list[dict]: 每个层数点的结果列表,按 layer_list 顺序排列 + """ + scan_results = [] + for n_layers in layer_list: + result = self.run_single(n_layers, n_samples, shots) + scan_results.append(result) + return scan_results + + def get_coherence_curve(self): + """ + 从已运行的结果中提取相干性衰减曲线。 + + 返回: + tuple[list[int], list[float]]: (层数列表, 相干性列表) + """ + layers = [r["n_layers"] for r in self.results] + coherences = [r["coherence"] for r in self.results] + return layers, coherences + + def print_results(self): + """打印所有实验结果(表格形式)。""" + print(f"{'层数':>6} {'相干性':>10} {'P(0)均值':>10} {'P(0)标准差':>10}") + print("-" * 42) + for r in self.results: + print(f"{r['n_layers']:>6} {r['coherence']:>10.6f} " + f"{r['p0_mean']:>10.6f} {r['p0_std']:>10.6f}") + + def clear_results(self): + """清空已记录的实验结果。""" + self.results = [] diff --git a/pyqpanda-algorithm/phase_noise_toolkit/readout_calibration.py b/pyqpanda-algorithm/phase_noise_toolkit/readout_calibration.py new file mode 100644 index 0000000000000000000000000000000000000000..75c1997f2b0f995dfee59d811706a1af079a72a6 --- /dev/null +++ b/pyqpanda-algorithm/phase_noise_toolkit/readout_calibration.py @@ -0,0 +1,174 @@ +""" +读出校准与纠错。 + +在量子计算实验中,测量设备存在读出误差(准备 |0> 时误测为 |1>, +或准备 |1> 时误测为 |0>)。本模块提供校准矩阵测量和概率纠错功能。 + +校准模型: + f0 = 1 - P(0|准备|0>) (|0> 态的误测率) + f1 = P(0|准备|1>) (|1> 态的误测率) + +纠错公式: + P(0)_true = (P(0)_obs - f1) / (1 - f0 - f1) + +典型用法: + calibrator = ReadoutCalibrator() + calibrator.calibrate(machine, qubit=0, shots=8192) + p0_true = calibrator.correct(p0_observed) +""" + +import json +import os + +from pyqpanda3.core import core +from .utils import run_prog_on_backend + + +class ReadoutCalibrator: + """ + 读出校准与纠错工具。 + + 通过测量 |0> 态和 |1> 态的读出概率,构建校准矩阵, + 并对实验中的观测概率进行纠错。 + + 属性: + f0 (float): |0> 态的误测率(准备 |0> 时测得 |1> 的概率) + f1 (float): |1> 态的误测率(准备 |1> 时测得 |0> 的概率) + calibrated (bool): 是否已完成校准 + """ + + def __init__(self): + """初始化读出校准器(未校准状态)。""" + self.f0 = 0.0 + self.f1 = 0.0 + self.calibrated = False + + def _run_and_get_p0(self, machine, prog, shots): + """ + 在指定机器上运行线路并返回 P(0)。 + + 支持本地模拟器(CPUQVM)和云平台后端。 + + 参数: + machine: 量子机器对象(需有 run(prog, shots) 方法) + prog: pyqpanda3 QProg 对象 + shots (int): 测量次数 + + 返回: + float: 测量到 |0> 的概率 + """ + probs = run_prog_on_backend(machine, prog, shots) + return probs.get("0", 0.0) + + def calibrate(self, machine, qubit=0, shots=8192): + """ + 在指定机器上执行读出校准。 + + 分别测量 |0> 态和 |1> 态的读出概率,计算 f0 和 f1。 + + 参数: + machine: 量子机器对象(本地模拟器或云平台后端) + qubit (int): 校准的量子比特编号,默认 0 + shots (int): 每次校准的测量次数,默认 8192 + + 返回: + tuple[float, float]: (f0, f1) 校准参数 + """ + # 校准 |0> 态:不施加门,直接测量 + prog0 = core.QProg() + prog0 << core.measure(qubit, qubit) + p0_given_0 = self._run_and_get_p0(machine, prog0, shots) + + # 校准 |1> 态:施加 X 门后测量 + prog1 = core.QProg() + prog1 << core.X(qubit) + prog1 << core.measure(qubit, qubit) + p0_given_1 = self._run_and_get_p0(machine, prog1, shots) + + self.f0 = 1.0 - p0_given_0 + self.f1 = p0_given_1 + self.calibrated = True + + return self.f0, self.f1 + + def correct(self, p0_obs): + """ + 用校准矩阵纠正观测到的 P(0)。 + + 参数: + p0_obs (float): 观测到的 P(0) + + 返回: + float: 纠错后的 P(0) + + 异常: + RuntimeError: 未完成校准时抛出 + """ + if not self.calibrated: + raise RuntimeError("尚未完成读出校准,请先调用 calibrate()") + + denominator = 1.0 - self.f0 - self.f1 + if abs(denominator) < 1e-12: + return p0_obs + + p0_true = (p0_obs - self.f1) / denominator + # 裁剪到 [0, 1] 范围 + return max(0.0, min(1.0, p0_true)) + + def correct_probs(self, p0_obs, p1_obs): + """ + 同时纠正 P(0) 和 P(1)。 + + 参数: + p0_obs (float): 观测到的 P(0) + p1_obs (float): 观测到的 P(1) + + 返回: + tuple[float, float]: (p0_true, p1_true) 纠错后的概率 + """ + p0_true = self.correct(p0_obs) + p1_true = 1.0 - p0_true + return p0_true, p1_true + + def get_calibration_matrix(self): + """ + 返回校准参数。 + + 返回: + dict: 包含 f0、f1 和 calibrated 状态的字典 + + 异常: + RuntimeError: 未完成校准时抛出 + """ + if not self.calibrated: + raise RuntimeError("尚未完成读出校准,请先调用 calibrate()") + return {"f0": self.f0, "f1": self.f1, "calibrated": self.calibrated} + + def save(self, filepath): + """ + 保存校准参数到 JSON 文件。 + + 参数: + filepath (str): 保存路径 + """ + data = {"f0": self.f0, "f1": self.f1, "calibrated": self.calibrated} + with open(filepath, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + def load(self, filepath): + """ + 从 JSON 文件加载校准参数。 + + 参数: + filepath (str): 文件路径 + + 异常: + FileNotFoundError: 文件不存在时抛出 + """ + if not os.path.exists(filepath): + raise FileNotFoundError(f"校准文件不存在: {filepath}") + with open(filepath, "r", encoding="utf-8") as f: + data = json.load(f) + self.f0 = data.get("f0", 0.0) + self.f1 = data.get("f1", 0.0) + self.calibrated = data.get("calibrated", False) diff --git a/pyqpanda-algorithm/phase_noise_toolkit/utils.py b/pyqpanda-algorithm/phase_noise_toolkit/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0c8845fa7ccc9733bd490e685ea10681b464013f --- /dev/null +++ b/pyqpanda-algorithm/phase_noise_toolkit/utils.py @@ -0,0 +1,254 @@ +""" +通用工具函数。 + +提供概率分布采样、相干性计算、跨后端电路运行等基础功能。 +""" + +import time + +import numpy as np + + +def coherence_from_probs(p0, p1): + """ + 从测量概率计算相干性。 + + Ramsey 实验中,相干性 I = |P(0) - P(1)|。 + + 参数: + p0 (float): 测量到 |0> 的概率 + p1 (float): 测量到 |1> 的概率 + + 返回: + float: 相干性值,范围 [0, 1] + """ + return abs(p0 - p1) + + +def uniform_phase(sigma, rng=None): + """ + 从均匀分布 U[-sigma, sigma] 中采样一个相位角。 + + 参数: + sigma (float): 均匀分布半宽 + rng (numpy.random.Generator, optional): 随机数生成器 + + 返回: + float: 采样的相位角(弧度) + """ + if rng is None: + rng = np.random.default_rng() + return rng.uniform(-sigma, sigma) + + +def gaussian_phase(sigma, rng=None): + """ + 从高斯分布 N(0, sigma^2) 中采样一个相位角。 + + 参数: + sigma (float): 高斯分布标准差 + rng (numpy.random.Generator, optional): 随机数生成器 + + 返回: + float: 采样的相位角(弧度) + """ + if rng is None: + rng = np.random.default_rng() + return rng.normal(0, sigma) + + +def sample_phase(dist_type, sigma, rng=None): + """ + 按指定分布类型采样相位角。 + + 参数: + dist_type (str): 分布类型,'uniform' 或 'gaussian' + sigma (float): 分布宽度参数(uniform 为半宽,gaussian 为标准差) + rng (numpy.random.Generator, optional): 随机数生成器 + + 返回: + float: 采样的相位角(弧度) + + 异常: + ValueError: 不支持的分布类型 + """ + if dist_type == "uniform": + return uniform_phase(sigma, rng) + elif dist_type == "gaussian": + return gaussian_phase(sigma, rng) + else: + raise ValueError(f"不支持的分布类型: {dist_type},请使用 'uniform' 或 'gaussian'") + + +def run_and_get_probs(machine, prog, shots): + """ + 在量子机器上运行线路并返回概率字典。 + + 兼容两种机器类型: + - 本地模拟器(CPUQVM): run 返回 None,结果通过 machine.result().get_prob_dict() 获取 + - 云平台后端: run 返回 job 对象,结果通过 job.result().get_probs() 获取 + + 参数: + machine: 量子机器对象(本地模拟器或云平台后端) + prog: pyqpanda3 QProg 对象 + shots (int): 测量次数 + + 返回: + dict: 测量结果概率字典,如 {"0": 0.5, "1": 0.5} + + 异常: + RuntimeError: 无法从机器获取概率结果时抛出 + """ + ret = machine.run(prog, shots) + + # 云平台模式: run 返回 job 对象 + if ret is not None and hasattr(ret, "result"): + res = ret.result() + if hasattr(res, "get_probs"): + return res.get_probs() + if hasattr(res, "get_prob_dict"): + return res.get_prob_dict() + + # 本地模拟器模式: machine.result() 返回 QResult + if hasattr(machine, "result"): + res = machine.result() + if hasattr(res, "get_prob_dict"): + return res.get_prob_dict() + if hasattr(res, "get_probs"): + return res.get_probs() + + raise RuntimeError( + "无法从机器获取概率结果,请检查机器类型和 API 兼容性" + ) + + +def is_cloud_backend(machine): + """ + 判断机器对象是否为云平台真机后端。 + + 判定依据:具有 run_instruction 方法(云平台后端特有)。 + + 参数: + machine: 量子机器对象 + + 返回: + bool: True 表示云平台真机后端,False 表示本地模拟器 + """ + return hasattr(machine, "run_instruction") + + +def run_prog_on_backend(machine, prog, shots, timeout=120): + """ + 在本地模拟器或云平台真机上运行电路并返回概率字典。 + + 自动识别后端类型并选择对应运行路径: + - 本地 CPUQVM:使用标准 run(prog, shots) 路径 + - 云平台真机:使用 decompose → transpile → to_instruction → run_instruction 路径, + 绕过云平台编译器时序问题,并确保原生门按官方标准流程分解 + + 参数: + machine: 量子机器对象(本地模拟器或云平台后端) + prog: pyqpanda3 QProg 对象 + shots (int): 测量次数 + timeout (int): 真机作业超时时间(秒),默认 120 + + 返回: + dict: 测量结果概率字典,如 {"0": 0.5, "1": 0.5} + + 异常: + RuntimeError: 真机作业失败、超时、或 decompose/transpile 不可用时抛出 + """ + # 本地模拟器路径 + if not is_cloud_backend(machine): + return run_and_get_probs(machine, prog, shots) + + # 云平台真机路径 + try: + from pyqpanda3.transpilation import Transpiler, decompose + from pyqpanda3.qcloud import QCloudOptions + except ImportError as e: + raise RuntimeError( + f"真机路径需要 pyqpanda3 的 decompose/transpile 支持,导入失败: {e}" + ) + + # 获取芯片后端对象 + if not hasattr(machine, "chip_backend"): + raise RuntimeError( + "云平台后端缺少 chip_backend() 方法,无法获取芯片信息" + ) + chip_backend = machine.chip_backend() + + # 第一步+第二步:尝试 decompose 分解 + transpile 硬件感知编译 + # 这是官方推荐标准流程;若该后端不支持此 API,退回直接 to_instruction + compiled_prog = prog + use_standard_flow = True + try: + from pyqpanda3.transpilation import Transpiler, decompose + decomposed = decompose(prog, ["U3", "CZ"]) + compiled_prog = Transpiler().transpile( + decomposed, chip_backend, {}, 2, "fidelity" + ) + except Exception: + # decompose/transpile 不可用时退回直接 to_instruction(不分解不编译) + use_standard_flow = False + + # 第三步:to_instruction 本地时序调度(offset=1 官方标准) + try: + instruction = compiled_prog.to_instruction( + chip_backend, + offset=1, + is_scheduling=True, + compensate_pulse=False, + ) + except Exception as e: + raise RuntimeError(f"电路 to_instruction 转换失败: {e}") + + # 第四步:run_instruction 直接提交(绕过云平台编译器) + options = QCloudOptions() + options.set_mapping(False) + options.set_optimization(False) + options.set_amend(False) + + try: + job = machine.run_instruction(instruction, shots, options) + except Exception as e: + raise RuntimeError(f"真机作业提交失败: {e}") + + # 第五步:轮询作业状态 + poll_interval = 2 + max_polls = max(1, timeout // poll_interval) + for _ in range(max_polls): + try: + status = str(job.status()) + except Exception as e: + raise RuntimeError(f"获取作业状态失败: {e}") + + if "FINISHED" in status: + result = job.result() + if result is None: + raise RuntimeError("作业已完成但结果为空") + # 单个电路返回单个概率字典 + if hasattr(result, "get_probs"): + probs = result.get_probs() + if probs is None: + raise RuntimeError("作业结果概率字典为空") + return probs + if hasattr(result, "get_probs_list"): + probs_list = result.get_probs_list() + if probs_list and len(probs_list) > 0: + return probs_list[0] + raise RuntimeError("作业结果概率列表为空") + raise RuntimeError("作业结果对象缺少 get_probs/get_probs_list 方法") + + if "FAILED" in status: + result = job.result() + error_msg = "未知错误" + if result is not None and hasattr(result, "error_message"): + error_msg = result.error_message() + raise RuntimeError(f"真机作业执行失败: {error_msg}") + + time.sleep(poll_interval) + + raise RuntimeError( + f"真机作业超时({timeout}秒),请检查后端状态或增加超时时间" + ) diff --git a/pyqpanda-algorithm/tests/test_phase_noise.py b/pyqpanda-algorithm/tests/test_phase_noise.py new file mode 100644 index 0000000000000000000000000000000000000000..fc34dbe80e10673dc49c876dc218ae59763e5202 --- /dev/null +++ b/pyqpanda-algorithm/tests/test_phase_noise.py @@ -0,0 +1,408 @@ +""" +phase_noise_toolkit 单元测试。 + +使用 pytest 运行: + pytest tests/test_phase_noise.py -v + +覆盖模块: + - utils + - layered_injector + - readout_calibration + - ramsey_decay +""" + +import os +import sys +import tempfile + +import numpy as np +import pytest + +# 将上级目录加入路径 +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from pyqpanda3.core import core +from phase_noise_toolkit import ( + LayeredPhaseInjector, + ReadoutCalibrator, + RamseyDecayExperiment, + coherence_from_probs, + uniform_phase, + gaussian_phase, + sample_phase, + run_prog_on_backend, + is_cloud_backend, + check_gate_support, +) + + +# ============================================================ +# utils 模块测试 +# ============================================================ + +class TestUtils: + """测试通用工具函数。""" + + def test_coherence_from_probs(self): + """测试相干性计算。""" + assert coherence_from_probs(0.8, 0.2) == pytest.approx(0.6) + assert coherence_from_probs(0.5, 0.5) == pytest.approx(0.0) + assert coherence_from_probs(1.0, 0.0) == pytest.approx(1.0) + + def test_uniform_phase_range(self): + """测试均匀分布采样范围。""" + rng = np.random.default_rng(42) + samples = [uniform_phase(0.5, rng) for _ in range(1000)] + assert all(-0.5 <= s <= 0.5 for s in samples) + + def test_gaussian_phase_stats(self): + """测试高斯分布采样统计特性。""" + rng = np.random.default_rng(42) + samples = [gaussian_phase(0.3, rng) for _ in range(5000)] + assert abs(np.mean(samples)) < 0.05 # 均值接近 0 + assert abs(np.std(samples) - 0.3) < 0.02 # 标准差接近 0.3 + + def test_sample_phase_invalid_type(self): + """测试不支持的分布类型抛出异常。""" + with pytest.raises(ValueError, match="不支持的分布类型"): + sample_phase("invalid", 0.3) + + +# ============================================================ +# LayeredPhaseInjector 模块测试 +# ============================================================ + +class TestLayeredPhaseInjector: + """测试分层相位注入器。""" + + def test_init_default(self): + """测试默认初始化。""" + injector = LayeredPhaseInjector() + assert injector.n_layers == 10 + assert injector.sigma == 0.3 + assert injector.decay_rate == 0.99 + assert injector.dist_type == "uniform" + assert injector.gate_type == "U1" + + def test_init_custom(self): + """测试自定义参数初始化。""" + injector = LayeredPhaseInjector( + n_layers=20, sigma=0.5, decay_rate=0.95, + dist_type="gaussian", gate_type="RZ", seed=123, + ) + assert injector.n_layers == 20 + assert injector.sigma == 0.5 + assert injector.decay_rate == 0.95 + assert injector.dist_type == "gaussian" + assert injector.gate_type == "RZ" + + def test_invalid_n_layers(self): + """测试非法层数抛出异常。""" + with pytest.raises(ValueError, match="n_layers 必须为正整数"): + LayeredPhaseInjector(n_layers=0) + + def test_invalid_decay_rate(self): + """测试非法衰减率抛出异常。""" + with pytest.raises(ValueError, match="decay_rate 必须在"): + LayeredPhaseInjector(decay_rate=1.5) + + def test_get_layer_widths(self): + """测试每层宽度计算。""" + injector = LayeredPhaseInjector(n_layers=5, sigma=0.4, decay_rate=0.5) + widths = injector.get_layer_widths() + assert len(widths) == 5 + assert widths[0] == pytest.approx(0.4) + assert widths[1] == pytest.approx(0.2) + assert widths[2] == pytest.approx(0.1) + assert widths[3] == pytest.approx(0.05) + assert widths[4] == pytest.approx(0.025) + + def test_set_seed_reproducible(self): + """测试设置种子后结果可复现。""" + inj1 = LayeredPhaseInjector(n_layers=3, sigma=0.3, seed=42) + inj2 = LayeredPhaseInjector(n_layers=3, sigma=0.3, seed=42) + # 生成线路时的随机采样应该一致(通过多次运行对比) + widths1 = inj1.get_layer_widths() + widths2 = inj2.get_layer_widths() + assert widths1 == widths2 + + def test_generate_circuit_no_measure(self): + """测试生成不带测量的线路。""" + injector = LayeredPhaseInjector(n_layers=3, seed=42) + prog = injector.generate_circuit(qubit=0, add_measure=False) + assert prog is not None + + def test_generate_circuits_count(self): + """测试生成多个线路。""" + injector = LayeredPhaseInjector(n_layers=3, seed=42) + circuits = injector.generate_circuits(n_samples=5) + assert len(circuits) == 5 + + def test_generate_layered_ramsey_circuit(self): + """测试生成 Ramsey 型线路。""" + injector = LayeredPhaseInjector(n_layers=3, seed=42) + prog = injector.generate_layered_ramsey_circuit(qubit=0) + assert prog is not None + + +# ============================================================ +# ReadoutCalibrator 模块测试 +# ============================================================ + +class TestReadoutCalibrator: + """测试读出校准器。""" + + def test_init_uncalibrated(self): + """测试初始化状态为未校准。""" + cal = ReadoutCalibrator() + assert cal.calibrated is False + assert cal.f0 == 0.0 + assert cal.f1 == 0.0 + + def test_correct_without_calibration_raises(self): + """测试未校准时纠错抛出异常。""" + cal = ReadoutCalibrator() + with pytest.raises(RuntimeError, match="尚未完成读出校准"): + cal.correct(0.5) + + def test_correct_formula(self): + """测试纠错公式正确性。""" + cal = ReadoutCalibrator() + cal.f0 = 0.01 + cal.f1 = 0.02 + cal.calibrated = True + # P(0)_obs = 0.5, 纠错后应为 (0.5 - 0.02) / (1 - 0.01 - 0.02) = 0.48 / 0.97 + expected = 0.48 / 0.97 + assert cal.correct(0.5) == pytest.approx(expected) + + def test_correct_clipping(self): + """测试纠错结果裁剪到 [0, 1]。""" + cal = ReadoutCalibrator() + cal.f0 = 0.1 + cal.f1 = 0.1 + cal.calibrated = True + # 极端值应被裁剪 + assert 0.0 <= cal.correct(0.0) <= 1.0 + assert 0.0 <= cal.correct(1.0) <= 1.0 + + def test_correct_probs(self): + """测试同时纠正 P(0) 和 P(1)。""" + cal = ReadoutCalibrator() + cal.f0 = 0.01 + cal.f1 = 0.01 + cal.calibrated = True + p0_true, p1_true = cal.correct_probs(0.6, 0.4) + assert p0_true + p1_true == pytest.approx(1.0) + + def test_save_and_load(self): + """测试保存和加载校准参数。""" + cal = ReadoutCalibrator() + cal.f0 = 0.015 + cal.f1 = 0.025 + cal.calibrated = True + + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + filepath = f.name + + try: + cal.save(filepath) + cal2 = ReadoutCalibrator() + cal2.load(filepath) + assert cal2.f0 == pytest.approx(0.015) + assert cal2.f1 == pytest.approx(0.025) + assert cal2.calibrated is True + finally: + os.unlink(filepath) + + def test_load_nonexistent_raises(self): + """测试加载不存在的文件抛出异常。""" + cal = ReadoutCalibrator() + with pytest.raises(FileNotFoundError): + cal.load("/nonexistent/path/cal.json") + + +# ============================================================ +# RamseyDecayExperiment 模块测试(需要本地模拟器) +# ============================================================ + +class TestRamseyDecayExperiment: + """测试 Ramsey 衰减实验(使用本地模拟器)。""" + + @pytest.fixture + def machine(self): + """初始化本地模拟器。""" + m = core.CPUQVM() + m.init_state() + return m + + def test_run_single(self, machine): + """测试运行单个层数点。""" + injector = LayeredPhaseInjector(n_layers=5, sigma=0.3, decay_rate=0.99, seed=42) + experiment = RamseyDecayExperiment(machine, injector, qubit=0) + result = experiment.run_single(n_layers=5, n_samples=10, shots=512) + + assert "n_layers" in result + assert "coherence" in result + assert "p0_mean" in result + assert "p0_std" in result + assert result["n_layers"] == 5 + assert 0.0 <= result["coherence"] <= 1.0 + + def test_run_scan(self, machine): + """测试扫描多个层数点。""" + injector = LayeredPhaseInjector(n_layers=10, sigma=0.3, decay_rate=0.99, seed=42) + experiment = RamseyDecayExperiment(machine, injector, qubit=0) + layer_list = [1, 3, 5] + results = experiment.run_scan(layer_list=layer_list, n_samples=5, shots=256) + + assert len(results) == 3 + assert [r["n_layers"] for r in results] == layer_list + + def test_get_coherence_curve(self, machine): + """测试提取相干性曲线。""" + injector = LayeredPhaseInjector(n_layers=5, sigma=0.3, decay_rate=0.99, seed=42) + experiment = RamseyDecayExperiment(machine, injector, qubit=0) + experiment.run_single(n_layers=3, n_samples=5, shots=256) + experiment.run_single(n_layers=5, n_samples=5, shots=256) + + layers, coherences = experiment.get_coherence_curve() + assert len(layers) == 2 + assert len(coherences) == 2 + assert layers == [3, 5] + + def test_clear_results(self, machine): + """测试清空结果。""" + injector = LayeredPhaseInjector(n_layers=3, sigma=0.3, seed=42) + experiment = RamseyDecayExperiment(machine, injector, qubit=0) + experiment.run_single(n_layers=3, n_samples=3, shots=128) + assert len(experiment.results) == 1 + experiment.clear_results() + assert len(experiment.results) == 0 + + def test_with_calibrator(self, machine): + """测试带读出校准器的实验。""" + calibrator = ReadoutCalibrator() + calibrator.calibrate(machine, qubit=0, shots=512) + + injector = LayeredPhaseInjector(n_layers=3, sigma=0.3, seed=42) + experiment = RamseyDecayExperiment( + machine, injector, calibrator=calibrator, qubit=0, + ) + result = experiment.run_single(n_layers=3, n_samples=5, shots=256) + assert 0.0 <= result["coherence"] <= 1.0 + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) + + +# ============================================================ +# 真机路径适配测试(假后端触发路径分支) +# ============================================================ + +class FakeCloudBackend: + """假云平台后端,用于触发真机路径分支,验证无语法错误。""" + + def run_instruction(self, instruction, shots, options=None): + """假 run_instruction,不应被调用到(chip_backend 缺失会先报错)。""" + raise RuntimeError("假后端不应执行到此处") + + +class TestCloudBackendAdapter: + """测试跨后端运行函数和门集校验。""" + + def test_is_cloud_backend_local(self): + """本地模拟器不被识别为云平台后端。""" + m = core.CPUQVM() + m.init_state() + assert is_cloud_backend(m) is False + + def test_is_cloud_backend_fake(self): + """有 run_instruction 方法的对象被识别为云平台后端。""" + fake = FakeCloudBackend() + assert is_cloud_backend(fake) is True + + def test_run_prog_local(self): + """run_prog_on_backend 在本地模拟器上正常工作。""" + m = core.CPUQVM() + m.init_state() + prog = core.QProg() + prog << core.H(0) + prog << core.measure(0, 0) + probs = run_prog_on_backend(m, prog, shots=1024) + assert "0" in probs + assert "1" in probs + assert abs(probs["0"] - 0.5) < 0.1 + + def test_run_prog_fake_cloud_missing_chip_backend(self): + """假云后端缺少 chip_backend 时抛出清晰 RuntimeError。""" + fake = FakeCloudBackend() + prog = core.QProg() + prog << core.H(0) + prog << core.measure(0, 0) + with pytest.raises(RuntimeError, match="chip_backend"): + run_prog_on_backend(fake, prog, shots=1024, timeout=1) + + def test_check_gate_support_known_backend_ok(self): + """已知后端支持的门类型不报警。""" + import warnings + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = check_gate_support("WK_C180_2", "RPhi") + assert result is True + assert len(w) == 0 + + def test_check_gate_support_known_backend_warn(self): + """已知后端不支持的门类型发出警告。""" + import warnings + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = check_gate_support("WK_C180_2", "U1") + assert result is False + assert len(w) == 1 + assert "可能不是后端" in str(w[0].message) + + def test_check_gate_support_unknown_backend(self): + """未知后端跳过校验,返回 True。""" + import warnings + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = check_gate_support("UnknownChip", "U1") + assert result is True + assert len(w) == 0 + + +# ============================================================ +# NoiseModelFactory 模块测试(含噪虚拟机噪声模型构造) +# ============================================================ + +class TestNoiseModelFactory: + """测试噪声模型工厂(构造不报错且类型正确)。""" + + def test_depolarizing(self): + """测试去极化噪声模型构造。""" + from phase_noise_toolkit import NoiseModelFactory + from pyqpanda3.qcloud import qcloud + model = NoiseModelFactory.depolarizing(p1=0.001, p2=0.01) + assert isinstance(model, qcloud.QCloudNoiseModel) + + def test_bit_flip(self): + """测试比特翻转噪声模型构造。""" + from phase_noise_toolkit import NoiseModelFactory + from pyqpanda3.qcloud import qcloud + model = NoiseModelFactory.bit_flip(p1=0.01, p2=0.02) + assert isinstance(model, qcloud.QCloudNoiseModel) + + def test_dephasing(self): + """测试相位阻尼噪声模型构造。""" + from phase_noise_toolkit import NoiseModelFactory + from pyqpanda3.qcloud import qcloud + model = NoiseModelFactory.dephasing(p1=0.005, p2=0.01) + assert isinstance(model, qcloud.QCloudNoiseModel) + + def test_amplitude_damping(self): + """测试振幅阻尼噪声模型构造。""" + from phase_noise_toolkit import NoiseModelFactory + from pyqpanda3.qcloud import qcloud + model = NoiseModelFactory.amplitude_damping(p1=0.001, p2=0.005) + assert isinstance(model, qcloud.QCloudNoiseModel)