# 导入 qibo 库
import qibo
# 打印当前安装的 Qibo 版本号
# 这个信息对于调试和文档参考非常重要
print(qibo.__version__)
单元格 2: 内存占用基准测试¶
目的¶
系统性地测试不同量子比特数(N)下,稀疏矩阵和密集矩阵的内存占用差异。
核心功能¶
- 构建 Ising 模型哈密顿量:横场 Ising 模型是量子计算中最常见的测试模型
- 内存计算:精确测量稀疏和密集表示的内存消耗
- 自动化测试:遍历不同系统规模(4-16 量子比特)
代码结构¶
get_memory_usage(): 通用内存计算函数,处理多种矩阵格式run_benchmark(): 主测试函数,遍历系统规模并输出结果
物理模型:横场 Ising 模型¶
$$H = -\sum_{i=0}^{N-2} Z_i Z_{i+1} - h \sum_{i=0}^{N-1} X_i$$
其中:
- $Z_i Z_{i+1}$:相邻量子比特的 ZZ 相互作用(耦合项)
- $X_i$:横场项(每个量子比特的 X 翻转)
- $h$:外场强度(这里简化为 1)
# 导入必要的标准库和第三方库
import sys # 用于获取对象大小
import numpy as np # 数值计算
from qibo import symbols, hamiltonians # Qibo 的符号和哈密顿量模块
import psutil # 系统内存监控
import os # 操作系统接口
def get_memory_usage(obj):
"""
计算对象的实际内存占用 (MB)
支持多种矩阵格式:
1. numpy.ndarray: 使用 nbytes 属性(最准确)
2. scipy.sparse.csr_matrix: 计算 data + indices + indptr 的总和
3. 其他 Python 对象: 使用 sys.getsizeof(估算值)
Args:
obj: 任意 Python 对象
Returns:
float: 内存占用大小(单位:MB)
"""
# 情况1: numpy 数组,直接读取 nbytes(最准确)
if hasattr(obj, 'nbytes'):
return obj.nbytes / (1024 ** 2)
# 情况2: scipy 稀疏矩阵 (CSR 格式)
# CSR 存储三个数组:
# - data: 非零元素的值
# - indices: 每个非零元素的列索引
# - indptr: 每行第一个非零元素在 data 中的位置
elif hasattr(obj, 'data') and hasattr(obj.data, 'nbytes'):
return (
obj.data.nbytes + # 非零值数据
obj.indices.nbytes + # 列索引数组
obj.indptr.nbytes # 行指针数组
) / (1024 ** 2)
# 情况3: 通用 Python 对象(不太准确,但提供参考)
else:
return sys.getsizeof(obj) / (1024 ** 2)
def run_benchmark(min_n=4, max_n=16):
"""
运行哈密顿量内存占用基准测试
测试流程:
1. 对于每个量子比特数 N,构建 Ising 模型符号表达式
2. 创建 SymbolicHamiltonian 对象
3. 分别计算稀疏和密集矩阵的内存占用
4. 输出对比结果
Args:
min_n: 最小量子比特数(默认 4)
max_n: 最大量子比特数(默认 16)
注意:max_n=16 时密集矩阵需要 64GB 内存,可能会 OOM!
"""
# 打印表头(对齐格式)
print(f"{'N':<4} | {'Mode':<8} | {'Memory (MB)':<12} | {'Status':<10}")
print("-" * 45)
# 遍历不同的量子比特数
for nqubits in range(min_n, max_n + 1):
# ========== 步骤1: 定义符号表达式 ==========
# 创建 Z 算符列表:[Z(0), Z(1), ..., Z(N-1)]
sym_z = [symbols.Z(i) for i in range(nqubits)]
# 创建 X 算符列表:[X(0), X(1), ..., X(N-1)]
sym_x = [symbols.X(i) for i in range(nqubits)]
# 构建 ZZ 相互作用项(最近邻耦合)
# 开边界条件:只计算 Z(i) * Z(i+1),i 从 0 到 N-2
term_zz = sum(sym_z[i] * sym_z[i+1] for i in range(nqubits - 1))
# 构建横场项(每个量子比特的 X 算符)
term_x = sum(sym_x[i] for i in range(nqubits))
# 完整哈密顿量:H = -sum(Z*Z) - sum(X)
# 负号是 Ising 模型的标准形式
ham_expr = -term_zz - term_x
# ========== 步骤2: 创建符号哈密顿量对象 ==========
# SymbolicHamiltonian 保存符号形式,不立即计算矩阵
# 矩阵只在访问 .matrix 属性时才计算(lazy evaluation)
ham = hamiltonians.SymbolicHamiltonian(ham_expr)
# ========== 步骤3: 测试稀疏矩阵模式 ==========
try:
# 3.1 获取密集矩阵
# 注意:这里会触发警告,因为计算密集矩阵内存效率低
dense_mat = ham.matrix
# 3.2 转换为稀疏格式 (CSR - Compressed Sparse Row)
# CSR 适合行优先的矩阵运算
from scipy.sparse import csr_matrix
sparse_mat = csr_matrix(dense_mat)
# 3.3 计算内存占用
mem_sparse = get_memory_usage(sparse_mat)
print(f"{nqubits:<4} | {'Sparse':<8} | {mem_sparse:<12.4f} | {'OK':<10}")
# 3.4 释放内存(准备下一轮测试)
del sparse_mat
except Exception as e:
# 捕获内存不足等错误
print(f"{nqubits:<4} | {'Sparse':<8} | {'ERROR':<12} | {str(e)}")
# ========== 步骤4: 测试密集矩阵模式 ==========
try:
# 直接获取密集矩阵(完整的 2^N x 2^N 数组)
# 注意:N=16 时需要 64GB 内存!
dense_mat = ham.matrix
# 计算内存占用
mem_dense = get_memory_usage(dense_mat)
print(f"{nqubits:<4} | {'Dense':<8} | {mem_dense:<12.4f} | {'OK':<10}")
# 释放内存
del dense_mat
except Exception as e:
print(f"{nqubits:<4} | {'Dense':<8} | {'ERROR':<12} | {str(e)}")
# 每个量子比特数测试完成后打印分隔线
print("-" * 45)
if __name__ == "__main__":
# ========== 系统环境信息 ==========
# 获取系统内存状态
mem = psutil.virtual_memory()
# 输出总内存和可用内存(单位:GB)
print(f"System Total RAM: {mem.total / (1024**3):.2f} GB")
print(f"System Available: {mem.available / (1024**3):.2f} GB")
print("Starting Qibo Matrix Construction Audit...\n")
# ========== 执行基准测试 ==========
# 测试范围:N=4 到 N=16
# 警告:N=16 的密集矩阵需要 64GB,可能 OOM
run_benchmark(4, 16)
输出结果分析¶
1. 系统环境¶
- Qibo 版本: 0.2.21
- 后端: numpy backend on /CPU:0
- 系统内存: 15.91 GB 总计,3.72 GB 可用
- 警告信息: 出现"Calculating the dense form..."警告,说明密集矩阵构造是内存密集操作
2. 内存占用趋势¶
| 量子比特数(N) | 稀疏矩阵(MB) | 密集矩阵(MB) | 密集/稀疏比率 |
|---|---|---|---|
| 4 | 0.0016 | 0.0039 | 2.4x |
| 5 | 0.0036 | 0.0156 | 4.3x |
| 6 | 0.0088 | 0.0625 | 7.1x |
| 7 | 0.0193 | 0.2500 | 13.0x |
| 8 | 0.0449 | 1.0000 | 22.3x |
| 9 | 0.0969 | 4.0000 | 41.3x |
| 10 | 0.2188 | 16.0000 | 73.2x |
| 11 | 0.4670 | 64.0000 | 137.0x |
3. 密集矩阵增长规律验证¶
理论公式: 密集矩阵内存 = $4^N \times 16$ 字节
验证:
- N=4: $4^4 \times 16B = 256 \times 256 \times 16B \approx 4KB$ ✓
- N=8: $4^8 \times 16B = 65536 \times 16B \approx 1MB$ ✓
- N=10: $4^{10} \times 16B = 1M \times 16B = 16MB$ ✓
- N=11: $4^{11} \times 16B = 4M \times 16B = 64MB$ ✓
完美符合理论预期!
4. 实际应用建议¶
内存安全阈值(基于 15.91GB 系统):
- N ≤ 10: 可以安全使用密集矩阵 (≤16MB)
- N = 11-12: 谨慎使用密集矩阵 (64MB-256MB)
- N ≥ 13: 必须使用稀疏矩阵 (≥1GB)
- N = 16: 密集矩阵需要 64GB(必定崩溃)
单元格 3: 稀疏性模式与内存跨度分析¶
目的¶
分析哈密顿量矩阵的稀疏结构和内存访问模式,评估对 CPU 缓存的影响。
核心概念¶
1. 稀疏性 (Sparsity)¶
稀疏度 = 1 - (非零元素数 / 总元素数)
- 高稀疏性意味着大部分矩阵元素为零
- 可以只存储非零元素以节省内存
2. 内存跨度 (Memory Stride)¶
定义:在矩阵-向量乘法 $H|\psi\rangle$ 中,计算结果的一个元素需要访问向量的哪些位置。
$$\text{stride} = |\text{row} - \text{col}|$$
物理意义:
- 小跨度 (≤ 16):数据在 L1 缓存中,访问极快
- 中跨度 (16-256):数据在 L2 缓存中,访问快
- 大跨度 (> 256):数据在 L3 缓存或主存中,访问慢
3. Cache Miss 影响¶
- 当跨度超过缓存大小时,会发生 Cache Miss
- CPU 必须从更慢的内存层级获取数据
- 严重拖慢计算速度
测试流程¶
- 构建 N=8 和 N=10 的 Ising 哈密顿量
- 转换为稀疏矩阵格式
- 计算所有非零元素的位置
- 统计最大跨度和平均跨度
- 评估缓存友好性
import numpy as np
from qibo import symbols, hamiltonians
import matplotlib.pyplot as plt
from scipy.sparse import csr_matrix
def analyze_sparsity_pattern(n_qubits):
"""
分析哈密顿量矩阵的稀疏性和内存跨度
Args:
n_qubits: 量子比特数
Returns:
strides: 所有非零元素的跨度数组
"""
print(f"Analyzing Sparsity for N={n_qubits}...")
# ========== 步骤1: 构建 Ising 哈密顿量 ==========
# 创建 Z 算符列表
sym_z = [symbols.Z(i) for i in range(n_qubits)]
# 创建 X 算符列表
sym_x = [symbols.X(i) for i in range(n_qubits)]
# 构建 ZZ 相互作用项(最近邻耦合)
term_zz = sum(sym_z[i] * sym_z[i+1] for i in range(n_qubits - 1))
# 构建横场项
term_x = sum(sym_x[i] for i in range(n_qubits))
# 创建符号哈密顿量(带负号,标准 Ising 形式)
ham = hamiltonians.SymbolicHamiltonian(-term_zz - term_x)
# ========== 步骤2: 获取稀疏矩阵 ==========
# 注意:SymbolicHamiltonian.matrix 返回密集矩阵
# 我们需要手动转换为稀疏格式
dense_matrix = ham.matrix
# 转换为 CSR 格式(行压缩稀疏矩阵)
# CSR 格式优势:行访问高效,适合矩阵-向量乘法
mat = csr_matrix(dense_matrix)
# 获取非零元素的行列索引
# nonzero() 返回两个数组:rows 和 cols
rows, cols = mat.nonzero()
# ========== 步骤3: 计算内存跨度 ==========
# 跨度 = |row - col|:非零元素偏离对角线的距离
# 这代表了在计算 H|psi> 时需要跨越的内存距离
strides = np.abs(rows - cols)
# ========== 步骤4: 统计关键指标 ==========
max_stride = np.max(strides) # 最大跨度(最坏情况)
avg_stride = np.mean(strides) # 平均跨度(典型情况)
# 计算稀疏度
nonzero_count = mat.nnz # 非零元素数量
total_elements = mat.shape[0] * mat.shape[1] # 总元素数量
sparsity = 1.0 - (nonzero_count / total_elements) # 稀疏度
# ========== 步骤5: 输出分析结果 ==========
print(f" - Matrix Shape: {mat.shape}")
print(f" - Non-zeros: {nonzero_count}")
print(f" - Sparsity: {sparsity:.6f}")
print(f" - Max Memory Stride (Index Distance): {max_stride}")
print(f" - Avg Memory Stride: {avg_stride:.2f}")
# ========== 步骤6: 缓存性能评估 ==========
# 计算实际数据跨度(单位:字节)
# 假设每个复数元素占 16 字节 (128-bit complex)
data_stride_bytes = max_stride * 16
# 常见 CPU 缓存大小参考(Intel i5-7400):
# - L1 Cache: 64 KB
# - L2 Cache: 256 KB
# - L3 Cache: 6 MB
print(f" - Data Stride: {data_stride_bytes / 1024:.2f} KB")
# 警告:如果数据跨度超过 256KB,会击穿 L2 缓存
if max_stride > 1024:
print(" [Warning] Large stride detected!")
print(" [Warning] Likely to cause Cache Misses on large vectors.")
return strides
# ========== 执行分析 ==========
if __name__ == "__main__":
# 测试 N=8(256x256 矩阵)
# 预期:最大跨度 = 2^(8-1) = 128
analyze_sparsity_pattern(n_qubits=8)
print("-" * 30)
# 测试 N=10(1024x1024 矩阵)
# 预期:最大跨度 = 2^(10-1) = 512
# 512 * 16字节 = 8KB,仍在 L1 缓存范围内
analyze_sparsity_pattern(n_qubits=10)
单元格 4: 原生稀疏矩阵构建与 L2 缓存击穿验证¶
目的¶
直接使用 scipy.sparse 构建大尺度哈密顿量(N=14, 16),验证内存跨度理论预测。
关键改进¶
之前的测试使用 Qibo 的 ham.matrix 先计算密集矩阵,再转换为稀疏格式。这种方法在 N>12 时会 OOM。
本节直接使用 scipy.sparse 的 Kronecker 积构建稀疏矩阵,避免生成中间的密集矩阵。
理论预期¶
内存跨度公式: $\text{Max Stride} = 2^{N-1}$
| N | Max Stride | 数据跨度 | 缓存状态 |
|---|---|---|---|
| 14 | 8192 | 128 KB | L2 命中 |
| 16 | 32768 | 512 KB | L2 击穿 |
测试流程¶
- 使用稀疏 Kronecker 积直接构建 H
- 计算内存占用和最大跨度
- 验证是否符合理论预期 $2^{N-1}$
- 评估构建时间
import numpy as np
import scipy.sparse as sp # 稀疏矩阵库
import sys
import time
def build_ising_sparse(n_qubits, h=1.0):
"""
原生构建横场 Ising 模型的稀疏矩阵
物理模型:H = -sum(Z_i Z_{i+1}) - h sum(X_i)
边界条件:开边界 (Open Boundary Condition)
Args:
n_qubits: 量子比特数
h: 横场强度(默认 1.0)
Returns:
scipy.sparse.csr_matrix: 稀疏哈密顿量矩阵
优势:
- 不生成密集矩阵,内存安全
- 即使 N=16 也只需 ~17MB 内存
- 构建速度快(< 1s)
"""
# 矩阵维度
dim = 2 ** n_qubits
# 初始化空矩阵(稀疏格式)
H = sp.csr_matrix((dim, dim), dtype=np.complex128)
# ========== 定义单量子比特泡利矩阵 ==========
# X: 泡利-X 矩阵(非对角,翻转态)
# [[0, 1],
# [1, 0]]
X = sp.csr_matrix([[0, 1], [1, 0]], dtype=np.complex128)
# Z: 泡利-Z 矩阵(对角,测量相位)
# [[1, 0],
# [0, -1]]
Z = sp.csr_matrix([[1, 0], [0, -1]], dtype=np.complex128)
# I: 单位矩阵
I = sp.eye(2, dtype=np.complex128)
print(f"Building Hamiltonian for N={n_qubits}...")
# ========== 构建 ZZ 相互作用项 ==========
# 对每个相邻量子比特对 (i, i+1),构造 I ⊗ ... ⊗ Z ⊗ Z ⊗ ... ⊗ I
for i in range(n_qubits - 1):
# 创建算符列表:初始化全为单位矩阵
ops = [I] * n_qubits
# 在位置 i 和 i+1 放置 Z 算符
ops[i] = Z
ops[i+1] = Z
# 通过 Kronecker 积构造多体算符
# 例如:I ⊗ Z ⊗ Z ⊗ I (N=4)
term = ops[0]
for op in ops[1:]:
term = sp.kron(term, op, format='csr')
# 累加到总哈密顿量(注意负号)
H -= term
# ========== 构建 X 横场项 ==========
# 对每个量子比特 i,构造 I ⊗ ... ⊗ X ⊗ ... ⊗ I
for i in range(n_qubits):
ops = [I] * n_qubits
ops[i] = X
# Kronecker 积构造
term = ops[0]
for op in ops[1:]:
term = sp.kron(term, op, format='csr')
# 累加到总哈密顿量(注意系数 -h)
H -= h * term
return H
def analyze_memory_stride(mat, n_qubits):
"""
分析稀疏矩阵的内存跨度
Args:
mat: 稀疏矩阵
n_qubits: 量子比特数
"""
# 获取非零元素位置
rows, cols = mat.nonzero()
# 计算跨度
strides = np.abs(rows - cols)
max_stride = np.max(strides)
print(f"[Analysis N={n_qubits}]")
print(f" Matrix Shape: {mat.shape}")
# 内存占用(仅数据部分,不包括索引)
print(f" Memory Usage: {mat.data.nbytes / (1024**2):.2f} MB (Data only)")
print(f" Max Stride: {max_stride}")
# ========== 验证理论预测 ==========
# 理论:Max Stride = 2^(N-1)
# 这是因为最高位的 X 算符会连接 |0...⟩ 和 |1...⟩
# 两者的索引差正好是 2^(N-1)
expected_stride = 2**(n_qubits-1)
if max_stride == expected_stride:
print(f" Verification: MATCHES Theory (2^{n_qubits-1})")
else:
print(f" Verification: MISMATCH (Expected {expected_stride})")
if __name__ == "__main__":
# ========== 测试 N=14 ==========
# 预期内存:< 4MB
# 预期跨度:8192
start = time.time()
H14 = build_ising_sparse(14)
analyze_memory_stride(H14, 14)
print(f" Build Time: {time.time() - start:.2f}s")
print("-" * 40)
# ========== 挑战 N=16 ==========
# 预期内存:~17MB
# 预期跨度:32768
# 注意:Qibo 的密集矩阵方法在 N=16 需要 64GB(必定 OOM)
# 但稀疏方法只需要 ~17MB,完全可行!
try:
start = time.time()
H16 = build_ising_sparse(16)
analyze_memory_stride(H16, 16)
print(f" Build Time: {time.time() - start:.2f}s")
except MemoryError:
print("N=16 Out of Memory (Unexpected for Sparse!)")
审计结果分析:L2 缓存击穿确认¶
1. 核心数据解读¶
N=16 测试结果:
- 内存占用:17 MB(仅数据部分)
- 相比密集矩阵的 64 GB,实现了 ~4000 倍的压缩
- 结论:在 N=16 规模下,容量不是瓶颈
内存跨度:
- Max Stride: 32,768
- 数据跨度:32,768 × 16 字节 = 512 KB
2. 硬件层面的物理意义(基于 Intel i5-7400)¶
Intel i5-7400 缓存层级:
- L1 Cache: 64 KB (最快,延迟 ~4 周期)
- L2 Cache: 256 KB (快,延迟 ~12 周期)
- L3 Cache: 6 MB (中等,延迟 ~40 周期)
推演结论:L2 Cache Miss(缓存未命中)
当计算 $H|\psi\rangle$ 中的 $X_0$(最高位翻转)项时:
# X_0 连接 |00...0⟩ 和 |10...0⟩
# 索引 0 和 2^15 = 32768
# 数据距离 = 32768 × 16字节 = 512 KB
由于 512 KB > 256 KB (L2 容量),这两个数据点无法同时存在于 L2 缓存中。
性能影响:
- 每次涉及最高位的计算必须穿透 L2,访问 L3
- 虽然未击穿 L3(仍在 6MB 范围内),但延迟显著增加
- 现象称为 Cache Thrashing(缓存颠簸)
3. 系统性能边界预警¶
基于此分析,可以预测性能断崖点:
| N | 跨度大小 | 数据跨度 | 缓存状态 | 预期性能 |
|---|---|---|---|---|
| 14 | 8192 | 128 KB | L2 命中 | 极快 |
| 16 | 32768 | 512 KB | L2 击穿 | 中等 |
| 19 | 262144 | 4 MB | L3 边缘 | 变慢 |
| 20 | 524288 | 8 MB | L3 击穿 | 断崖式下跌 |
4. 阶段总结¶
任务 1.2 已完成。我们证明了:
- 稀疏矩阵可处理 N=16(17MB vs 64GB)
- 内存跨度导致 L2 缓存在 N=16 时开始失效
- 任何基于矩阵乘法的模拟在 N≥20 时将撞上内存墙
下一步:进入任务 2 - Trotter 分解性能审计
单元格 5: Trotter 分解电路审计¶
目的¶
验证 Qibo 的 Trotter 分解功能,分析生成的量子电路结构。
核心概念:Trotter 分解¶
问题:时间演化算符 $U(t) = e^{-iHt}$ 在 H 复杂时难以直接计算。
Trotter 分解:将 $H = \sum_k H_k$ 分解为多个项的乘积:
$$e^{-iH\Delta t} \approx \prod_k e^{-iH_k \Delta t}$$
二阶 Trotter(Qibo 默认): $$e^{-iH\Delta t} \approx \prod_k e^{-iH_k \Delta t/2} \prod_k^\text{reverse} e^{-iH_k \Delta t/2}$$
审计要点¶
- 电路深度是否线性增长 $O(N)$?
- 门的总数是否符合理论预期?
- 是否正确使用了 Unitary 门封装指数算符?
import numpy as np
from qibo import symbols, hamiltonians, gates
import collections # 用于计数统计
def audit_trotter_decomposition(n_qubits, dt):
"""
审计 Qibo 的 Trotter 分解实现
Args:
n_qubits: 量子比特数
dt: 时间步长
"""
print(f"Auditing Trotter Decomposition for N={n_qubits}, dt={dt}...")
# ========== 步骤1: 定义符号哈密顿量 ==========
# 创建 Z 和 X 算符列表
sym_z = [symbols.Z(i) for i in range(n_qubits)]
sym_x = [symbols.X(i) for i in range(n_qubits)]
# 构建 Ising 哈密顿量表达式
# - sum(Z_i * Z_{i+1}): ZZ 相互作用(N-1 项)
# - sum(X_i): 横场项(N 项)
term_zz = sum(sym_z[i] * sym_z[i+1] for i in range(n_qubits - 1))
term_x = sum(sym_x[i] for i in range(n_qubits))
ham_expr = -term_zz - term_x
# 创建 SymbolicHamiltonian 对象
# 关键:这里只是定义符号,不计算矩阵!
ham = hamiltonians.SymbolicHamiltonian(ham_expr)
# ========== 步骤2: 触发 Trotter 分解 ==========
# ham.circuit(dt) 会自动执行以下转换:
# 1. 将哈密顿量展开为项列表 (terms)
# 2. 对项进行分组 (TermGroup.from_terms)
# 3. 对每组计算 exp(-i * term * dt/2)
# 4. 创建二阶 Trotter 电路(前向 + 反向)
try:
circuit = ham.circuit(dt)
except Exception as e:
print(f"Error generating circuit: {e}")
return
# ========== 步骤3: 审计电路统计信息 ==========
print(f"\n[Circuit Stats]")
print(f" Total Gates: {len(circuit.queue)}") # 门的总数
print(f" Depth: {circuit.depth}") # 电路深度(关键时间长度)
# ========== 步骤4: 统计门类型 ==========
gate_counts = collections.defaultdict(int)
print(f"\n[Gate Breakdown]")
# 遍历电路中的所有门
for i, gate in enumerate(circuit.queue):
gate_name = gate.__class__.__name__
gate_counts[gate_name] += 1
# 打印前 5 个门作为样本
if i < 5:
# 获取门参数(如果有)
# Unitary 门会有一个 4^k × 4^k 的酉矩阵参数
params = getattr(gate, 'parameters', [])
param_str = f", params={params}" if params else ""
print(f" Gate {i}: {gate_name}(qubits={gate.qubits}){param_str}")
print(" ...") # 省略剩余的门
# ========== 步骤5: 输出门类型统计 ==========
print(f"\n[Summary of Operations]")
for name, count in gate_counts.items():
print(f" {name}: {count}")
# ========== 步骤6: 验证复杂度 ==========
# 理论预期:
# - ZZ 相互作用项:(N-1) 个
# - X 横场项:N 个
# - 总交互项:2N-1 个
# - 二阶 Trotter:前后各半步,总门数 = 2 × (2N-1) = 4N-2
expected_interactions = (n_qubits - 1) + n_qubits # 2N-1
print(f"\n[Complexity Audit]")
print(f" Theoretical Interaction Terms: {expected_interactions}")
print(f" Actual Gates: {len(circuit.queue)}")
# 判断复杂度:线性 vs 指数
# 如果门数 < 10N,认为是线性增长(允许一定常数因子)
if len(circuit.queue) < n_qubits * 10:
print(" Result: PASS (Linear Scaling O(N))")
else:
print(" Result: FAIL (Exponential Scaling?)")
# ========== 执行审计 ==========
if __name__ == "__main__":
# 测试 N=6,dt=0.1
# 预期:
# - 交互项:2×6-1 = 11 个
# - 二阶 Trotter 门数:~10 个(项合并后)
audit_trotter_decomposition(n_qubits=6, dt=0.1)
import time
import numpy as np
from qibo import symbols, hamiltonians, models, set_backend
import os
# 强制使用 numpy 后端(最为稳定,便于对比基准)
set_backend("numpy")
def benchmark_evolution_with_circuit(n_qubits, steps=20, dt=1e-2):
"""
方法1:手动 Circuit 执行
流程:
1. 构建 SymbolicHamiltonian
2. 调用 ham.circuit(dt) 生成电路
3. 手动循环执行电路
Args:
n_qubits: 量子比特数
steps: 演化步数
dt: 时间步长
Returns:
float: 平均每步耗时(秒)
"""
print(f"\n[方法1] 手动 Circuit 执行 - N={n_qubits}, dt={dt}")
# ========== 阶段1: 构建哈密顿量 ==========
sym_z = [symbols.Z(i) for i in range(n_qubits)]
sym_x = [symbols.X(i) for i in range(n_qubits)]
term_zz = sum(sym_z[i] * sym_z[i+1] for i in range(n_qubits - 1))
term_x = sum(sym_x[i] for i in range(n_qubits))
ham = hamiltonians.SymbolicHamiltonian(-term_zz - term_x)
# ========== 阶段2: 编译电路 ==========
print(" [Phase 1] 编译电路...")
compile_start = time.time()
# 生成 Trotter 电路
# 这一步会:
# 1. 展开哈密顿量为项列表
# 2. 对项分组
# 3. 计算每个组的指数门
circuit = ham.circuit(dt)
compile_time = time.time() - compile_start
print(f" 编译完成: {compile_time:.4f}s")
print(f" 电路深度: {circuit.depth}, 总门数: {len(circuit.queue)}")
# 可视化电路(可选)
circuit.draw()
# ========== 阶段3: 初始化状态 ==========
# 全 0 态:|000...0⟩
initial_state = np.zeros(2**n_qubits, dtype=np.complex128)
initial_state[0] = 1.0
# ========== 阶段4: 执行演化 ==========
print(f" [Phase 2] 执行 {steps} 步演化...")
state = initial_state
t_start = time.time()
# 手动循环执行每一步
for step in range(steps):
# circuit(state) 返回 QuantumState 对象
result = circuit(state)
# ⭐ 关键:提取底层的 numpy 数组
state = result.state()
# 记录第一步的时间(预热后)
if step == 0:
first_step_time = time.time() - t_start
print(f" 第 1 步: {first_step_time:.4f}s")
total_time = time.time() - t_start
avg_tps = total_time / steps
print(f" 总时间 ({steps} 步): {total_time:.4f}s")
print(f" 平均每步: {avg_tps:.4f}s")
print(f" 推测 1000 步: {avg_tps * 1000:.2f}s")
return avg_tps
def benchmark_evolution_with_statevolution(n_qubits, steps=20, dt=1e-2):
"""
方法2:使用 StateEvolution 模型(推荐)
流程:
1. 构建 SymbolicHamiltonian
2. 创建 StateEvolution 对象
3. 调用 evolve(final_time, initial_state)
优势:
- API 更简洁
- 内部优化(如电路复用)
- 自动处理时间步进
Args:
n_qubits: 量子比特数
steps: 演化步数
dt: 时间步长
Returns:
float: 平均每步耗时(秒)
"""
print(f"\n[方法2] StateEvolution 模型 - N={n_qubits}, dt={dt}")
# ========== 阶段1: 构建哈密顿量 ==========
sym_z = [symbols.Z(i) for i in range(n_qubits)]
sym_x = [symbols.X(i) for i in range(n_qubits)]
term_zz = sum(sym_z[i] * sym_z[i+1] for i in range(n_qubits - 1))
term_x = sum(sym_x[i] for i in range(n_qubits))
ham = hamiltonians.SymbolicHamiltonian(-term_zz - term_x)
# ========== 阶段2: 初始化状态 ==========
initial_state = np.zeros(2**n_qubits, dtype=np.complex128)
initial_state[0] = 1.0
# ========== 阶段3: 创建 StateEvolution 模型 ==========
print(f" [Phase 1] 创建 StateEvolution 模型...")
evolve_start = time.time()
# StateEvolution 封装了整个演化过程
# 内部会:
# 1. 选择合适的求解器(TrotterizedExponential)
# 2. 生成 Trotter 电路
# 3. 处理时间步进
evolve = models.StateEvolution(ham, dt=dt)
print(f" 模型创建完成: {time.time() - evolve_start:.4f}s")
# ========== 阶段4: 执行演化 ==========
print(f" [Phase 2] 执行 {steps} 步演化...")
t_start = time.time()
# execute() 方法自动处理所有时间步
# internal_time = 0, dt, 2*dt, ..., steps*dt
final_state = evolve(final_time=steps*dt, initial_state=initial_state)
total_time = time.time() - t_start
avg_tps = total_time / steps
print(f" 总时间 ({steps} 步): {total_time:.4f}s")
print(f" 平均每步: {avg_tps:.4f}s")
print(f" 推测 1000 步: {avg_tps * 1000:.2f}s")
return avg_tps
# ========== 执行基准测试 ==========
if __name__ == "__main__":
print("="*60)
print("时间演化性能基准测试")
print("="*60)
# 测试参数
nqubits = 16 # 量子比特数
steps = 20 # 演化步数
# ========== 方法1:手动 Circuit 执行 ==========
t1 = benchmark_evolution_with_circuit(nqubits, steps)
print("\n" + "-"*60)
# ========== 方法2:StateEvolution 模型 ==========
t2 = benchmark_evolution_with_statevolution(nqubits, steps)
# ========== 性能对比 ==========
print("\n" + "="*60)
print("性能对比:")
print(f" 手动 Circuit: {t1:.4f}s/step")
print(f" StateEvolution: {t2:.4f}s/step")
print(f" 性能比: {t1/t2:.2f}x")
print("="*60)
import qibo
# ========== 尝试切换到 QiboJit 后端 ==========
try:
# 目标:使用 qibojit 后端 + numba 平台 (CPU JIT)
# Numba 会将 Python 代码编译为机器码,大幅提升性能
qibo.set_backend("qibojit", platform="numba")
# 验证后端是否切换成功
backend_name = qibo.get_backend()
print(f"Backend successfully set to: {backend_name}")
except Exception as e:
# 如果 qibojit 未安装或初始化失败
print(f"Failed to set qibojit: {e}")
print("Falling back to numpy backend...")
# 回退到 numpy 后端(最稳定,但较慢)
qibo.set_backend("numpy")
# 重新导入符号(确保使用新后端)
from qibo import symbols, hamiltonians
def speed_test():
"""
QiboJit 性能测试
测试流程:
1. 预热(触发 JIT 编译)
2. 真实运行(测量编译后的性能)
"""
# ========== 设置测试参数 ==========
n_qubits = 16
# 构建简化的 Ising 模型(仅 ZZ 项)
# 减少门数量,聚焦于执行速度对比
sym_z = [symbols.Z(i) for i in range(n_qubits)]
term_zz = sum(sym_z[i] * sym_z[i+1] for i in range(n_qubits - 1))
# 创建哈密顿量(只有 -sum(Z*Z))
ham = hamiltonians.SymbolicHamiltonian(-term_zz)
# ========== 编译电路 ==========
circuit = ham.circuit(dt=1e-2)
# ========== 准备初始态 ==========
# 全 1 态(均匀叠加态)
state = np.ones(2**n_qubits, dtype=np.complex128)
state /= np.linalg.norm(state) # 归一化
# ========== 阶段1: 预热 (Warm-up) ==========
# 首次执行会触发 JIT 编译
# 这一步会较慢(~0.01s),但只执行一次
print("Warm-up (Compiling)...")
start = time.time()
# 执行电路(触发 Numba 编译)
circuit(state)
warmup_time = time.time() - start
print(f"Warm-up time: {warmup_time:.4f}s")
# ========== 阶段2: 真实性能测试 ==========
# 现在代码已编译,执行速度会显著提升
print("\nRunning 50 steps...")
start = time.time()
# 执行 50 步演化
for _ in range(50):
circuit(state)
# 计算平均每步时间
avg_time = (time.time() - start) / 50
print(f"Average TPS: {avg_time:.4f}s")
# ========== 性能对比 ==========
# 与之前 numpy 后端的对比(参考之前的测试)
# numpy: ~0.0759s/step
# qibojit: ~0.0102s/step
# 加速比: ~7.4x
print(f"\nEstimated speedup: ~7x vs numpy backend")
# ========== 执行测试 ==========
if __name__ == "__main__":
speed_test()
总结¶
本 Notebook 的核心发现¶
1. 内存占用¶
- 密集矩阵:指数增长 $4^N$,N=16 需要 64GB
- 稀疏矩阵:线性增长,N=16 仅需 17MB
- 结论:N>10 必须使用稀疏表示
2. 缓存性能¶
- 内存跨度 $2^{N-1}$ 导致 L2 缓存在 N=16 时失效
- N=20 将击穿 L3 缓存,性能断崖式下跌
- 结论:矩阵-向量乘法方法有物理极限
3. Trotter 分解¶
- 电路深度线性增长 $O(N)$
- 不需要存储完整矩阵
- 结论:适合大尺度模拟
4. 后端性能¶
- Numpy:稳定但较慢 (~0.076s/step)
- QiboJit:JIT 加速 (~0.010s/step)
- 加速比:~7x
最佳实践建议¶
- N ≤ 10:可以使用密集矩阵
- 10 < N ≤ 16:使用 SymbolicHamiltonian + Trotter
- N > 16:必须使用 QiboJit + 分布式计算
- 性能关键:优先使用 StateEvolution 模型
后续工作¶
- 测试 GPU 加速 (QiboJit + CuPy)
- 分布式计算基准测试
- VQE/QAOA 算法性能审计