# 一般 QHAM 自动生成:PDE → HAM → QCL → QODE
English · **简体中文**
QHAM 生成器将规则化 PDE、有限阶 HAM 推导和量子适配线性化连接到 QODE 输入。先阅读[数学推导](../reference/qham-derivation.md),再按本章提供网格、系数与初态;最小可运行示例见[教程:从 PDE 表达式生成 QHAM 输入](../tutorials/qham.md)。构造依据是 [QHAM 论文](https://arxiv.org/html/2411.06759v2)。
这里的“二次线性化”是 secondary linearization,即对 HAM 变形方程再做一次量子适配线性化。程序同时支持规则内的二次、三次及更高有限次数,不先把高次非线性截成二次项。
## 1. “一般性”的明确范围
输入是自治的一阶时间演化 PDE 系统。允许:
- 任意有限分量和有限空间维;
- 未知场及其空间导数的有限多项式;
- 已知空间系数、已知强迫及其空间导数;
- 对完整单项式施加外层空间导数;
- 任意非负 HAM 截断阶 m,受显式生成预算和当前量子接口位宽限制约束。
例如:
```python
from oracq.applications.qham import Field, Known, PolynomialPDE, QHAMPlan
# 声明两个未知场 u、v:各自是一个单项式原子,参与多项式运算。
u, v = Field("u"), Field("v")
# 用普通 Python 运算符书写方程右端:u.d("x", 2) 是二阶空间导数,
# u*u.d("x") 是二次对流项,Known("f") 是按名字引用的已知强迫数据。
# from_equations 会校验并冻结成不可变项集合,同时拆出 L/F/B 端口。
pde = PolynomialPDE.from_equations({
"u": 0.1*u.d("x", 2) - u*u.d("x") + Known("f"),
"v": -0.3*v + 0.2*u*v - 0.1*v**3,
}, axes=("x",), label="coupled_flow")
# HAM 截断阶 m=3:plan 只保存 PDE 和阶数,块闭包惰性生成,不物化矩阵。
plan = QHAMPlan(pde, order=3)
```
未知场的除法、超越函数、未消去的压力约束、一般隐式时间方程和时间依赖绑定不在首批规则内。它们不能仅改一个标签就被当成同一种已实现算法。时变 eta/L/f/B 的数学位置在推导中保留,但当前 QODE 组装要求自治绑定。
外导数保留为算子作用,例如 (u*u).d("x") 在离散化后表示 D_x 作用于同点乘积。程序没有偷偷把它改成 2*u*D_x(u),因为有限差分一般不满足精确 Leibniz 恒等式。带外导数的非线性表达式目前不能继续作为乘积因子;这种写法需要显式改写为允许的单项式规则或提供多线性端口。
初值、边界和空间离散化由绑定提供。形式 PDE 推导本身不替用户选择边界条件,也不证明问题适定。
## 2. 自动推导到底生成什么
入口 [pde.py](../api/applications/qham/pde.rst) 将表达式拆成已知线性映射:
```text
u' = f + L u + sum_tau B_tau(u,...,u).
```
L 包含全部一次项,F 表示 f 的向量注入,每个非线性单项式形成一个多线性 B_tau。端口包含 PDE 系数;例如 Burgers 的 B 包含负号,KdV 的 B 包含 -6,不能只绑定一个没有这些系数的裸乘法。
[linearization.py](../api/applications/qham/linearization.rst) 自动生成:
1. 各阶 Ui 的 HAM 递推,使用 -eta(1+eta)^(i-1-l) 权重;
2. 有序张量字 Y_(a0,...,ak-1) 及其线性耦合;
3. 物理输出块 u_sum=sum(Ui);
4. 非零强迫需要的常量分量 one;
5. 块的维数、偏移、索引排名/反排名和逐行算子规则。
最高非线性次数 D 对应的有限闭包取
```text
max(1,D-1)*sum(a_j)+len(a) <= max(1,D-1)*m+1.
```
闭包中的张量秩有限,非线性替换使 HAM 阶数和严格下降。它精确表示**截断后的 HAM 系统**,没有再做一次 Carleman 高阶截断。高次 PDE 使用规范闭包超集,可能包含冗余但闭合的变量。
{obj}`QHAMPlan ` 是惰性对象:只保存 PDE 和 m。二次无强迫、m=20 时可描述 2^21 个函数块,而无需把这些块全部写入序列化文本。row_terms、offset 和 locate 可单独查询。显式导出超过预算时,计划仍然有效,不会被伪装成已经展开完成的线路。
## 3. 两层可序列化表示
```mermaid
flowchart LR
P[PDE 规则] --> H[HAM 递推]
H --> C[有序张量闭包]
C --> G[模块化生成元 G]
B[L / B / F 算子] --> G
C --> Y[提升初态]
I[初值制备与范数] --> Y
G --> Q[QODE solver]
Y --> Q
Q --> O[u_sum 物理通道]
```
| 表示 | 作用 | 是否含量子门 |
|---|---|---|
| PDE 0.1 | 字段、空间轴、单项式、已知系数和内外导数 | 否 |
| QCL plan 0.1 | PDE+HAM 阶数,以及确定的有限闭包规则 | 否 |
| RIR 0.1 | 实际寄存器、{obj}`Call `、控制、矩形窗口、工作位和 BE 组合 | 是,模块调用继续保留 |
格式见 [PDE Schema](../reference/schemas/pde.schema.json) 和 [QCL Schema](../reference/schemas/qcl-plan.schema.json)。PDE/QCL 没有 Python callback;可从 JSON 重建。只在显式请求时生成 rows.json 或量子模块。
“数学函数自动量子编译”和这里的 PDE 构造面有不同对象:compile_function 对基态寄存器中的数值做可逆计算;{obj}`Field ` 表达式描述的是待求场及其幅度编码。不能把一个 out-of-place 数值函数电路直接当作对量子振幅施加非线性 PDE。QHAM 通过扩大线性状态空间处理后者。
## 4. 如何降低到 register-level 量子模块
实现见 [quantum.py](../api/algorithms/input_model/qham.rst) 和 [stencils.py](../api/applications/qham/stencils.rst)。
每个端口都用矩形线性映射解释:
| 端口 | 输入 | 输出 |
|---|---|---|
| L | V | V |
| B_tau | V 的 r 重张量积 | V |
| F | 标量常量空间 | V |
它们用补齐后的 BE 承载,alpha 和辅助位数由具体实现给出。QCL 将这些端口放在指定张量位置,其他坐标作恒等映射;强迫通过插入新坐标作用,非线性通过收缩多个坐标作用。
矩形块必须同时检查输入和输出窗口。全局布局不是等宽块布局,输入/输出的零填充也不能溢出到相邻块。程序使用可逆地址平移、坐标置换和两个窗口失败旗标实现嵌入。
周期网格的普通结构化实现包括:
- 有限差分导数由少量可逆移位及 LCU 组成;
- 分量选择由输入分量投影和输出分量编码实现;
- 同点乘积通过空间坐标 XOR、对角条件和矩形收缩实现;
- 外导数在收缩之后作用;
- 已知系数使用对角乘法 BE,当前普通门版本有显式词数预算。
这条路径不物化 N^r×N^r 的基础端口矩阵,更不物化整个提升矩阵。另有 {obj}`gate_bindings `,专门把很小的基础端口物化后用于对照;它不是一般路径。
例子:
```python
from oracq.applications.qham import Grid, Discretization, structured_fd_bindings, qham_input_model
# 一维周期网格:4 个格点、间距 1.0;地址宽 2 位,导数用中心差分加周期回绕。
grid = Grid(("x",), (4,), (1.0,), boundary="periodic")
# 把 PDE 和网格接起来;第二个参数为每个 Known 名字提供覆盖全网格的数据,
# 缺表或长度不符会在这里报错(数据所有权在此刻由宿主决定)。
space = Discretization(pde, grid, {"f": [0.05, 0.0, -0.05, 0.0]})
# 把各分量初值嵌入完整 2^width 维振幅向量;必须覆盖全部未知场。
u_in = space.encode_fields({
"u": [0.1, 0.2, 0.0, -0.1],
"v": [0.0, 0.1, 0.0, -0.1],
})
# 唯一编译电路的一步:每个端口变成移位 LCU + 对角系数 + 矩形收缩的 BE,
# 初值变成门级制备,范数被记录;不物化 N^r×N^r 端口矩阵。
bindings = structured_fd_bindings(space, u_in)
# 按 plan 闭包装配提升后的线性生成元 BE 与提升初态,返回带 solve /
# dissipative_shift 适配器的输入模型;eta 在此代入同伦权重。
model = qham_input_model(plan, bindings, eta=-0.4)
```
参考差分支持周期和零延拓 Dirichlet;结构化移位实现当前要求各轴长度为二次幂的周期网格。其他离散化、边界或已知系数实现可通过同样的端口绑定提供。物理坐标的解释属于离散化层,QODE 只消费 V 上的线性数据。
## 5. 初态不能逐块随便归一化
设 r=||u_in||。提升初态只有 u_sum、全零字和可选 one 分量非零。全零 k 重张量字的范数为 r^k,必须保留这些相对权重:
```text
initial weights: r, r, r^2, ..., r^K, [1 if forcing].
```
程序先按这些范数准备分支标签,在相应目标区间调用初始态制备 oracle,再从地址区间反算标签。调用的都是可重复、可受控的制备操作,不是复制一个只给定一次的未知量子态。初值 oracle 的相干相位也必须与所表示的经典向量一致。
每次对已知零输入的制备都复净其 work,因此同一段工作寄存器可以顺序复用。标签与其他工作位在成功制备后为零,公共 {obj}`StatePreparation ` 接口仍是 target/work。初始整体范数以 log_initial_norm 记录,生成时使用缩放后的权重避免直接对大幂求和。
若 u_in=0 且有强迫,初态只需 one 分量。若没有强迫,零初态对应零解,不存在需要制备的非零归一化向量。
这里的范数是所选离散向量的 Euclidean 范数。若采用带网格权重的 L2 表示,必须相应调整端口和初值编码,不能只修改 initial_norm。若用 u=s*v 缩放物理变量,则 r 次项系数变为 s^(r-1) 倍、强迫变为 f/s,最终物理结果乘回 s;程序不会仅为获得较好成功率而偷偷归一化原 PDE。
## 6. 进入 QODE 的 input model
{obj}`QHAMInputModel ` 包含生成元 BE、提升初态制备、原始状态宽度、同伦参数、初始范数和物理输出窗口。求解对象是
```text
Y' = G Y, Y(0) = Y_in,
```
其中非齐次强迫已经通过常量分量齐次化。它不是直接把 G 当作一个需要求逆的 QLSS 矩阵;若 QODE solver 再构造时间离散线性系统,那个系统需要它自己的输入与谱假设。
```python
from functools import partial
from oracq.algorithms.qode.ode import linear_qode
from oracq.algorithms.common.hamiltonian import taylor_hamiltonian
# 按名字选择求解器族(schrodingerization),并把 Hermitian 分支模拟核
# 作为普通参数注入:partial 固定 degree=1 的截断 Taylor 核。
# 换族或换核只改这一行,上面的输入模型不动。
qode = linear_qode(
"schrodingerization",
hamiltonian_function=partial(taylor_hamiltonian, degree=1),
)
# 在 t=0.01 求解:消耗生成元 BE 与提升初态,后选物理输出块,
# 返回带 plan/范数/待办项属性的 state oracle。
solution = model.solve(qode, 0.01)
# 取出其中的 RIR Program,用于序列化、导出或后端执行。
program = solution.operation.program()
```
返回结果选择第一个 N 维物理块,因此是归一化的截断 HAM 和。物理幅值仍需要所选 QODE solver 的范数恢复信息。当前 finite Taylor QODE 提供另一条不要求 Hermitian/耗散的普通候选,用来打通门级执行与对照;它不代表高效最优 QODE 算法。
### LCHS / CBMD 的额外前提
提升后的 G 通常非 Hermitian、非正规,强迫增广还会产生零模。不能直接假设它满足 [LCHS](algorithms/lchs.md)/[CBMD](algorithms/cbmd.md) 的耗散前提。
实现提供显式整体移位:
```text
G_shift = G - mu*I, mu >= alpha_G >= ||G||.
Y_shift(t) = exp(-mu*t) Y(t).
```
于是 -G_shift 的 Hermitian 部分非负。可调用 model.dissipative_shift(),再接入 LCHS/CBMD。结果保存 growth_shift*t 作为整体幅值恢复的对数因子。该操作保持归一化方向,但可能显著影响成功概率和成本,不是免费的性能优化。
### 稀疏输入不是从 BE 自动恢复的
QCL 的惰性行规则可以配合基础差分模板查询行和元素。{obj}`Discretization `.qcl_row/qcl_entry 是可审阅的经典参考,不是已经完成的可逆 quantum oracle。它们没有通过扫描整张矩阵获得结果。
如果选择稀疏输入型 QODE solver,需要另外提供满足其约定的基础稀疏访问及对应可逆实现。尤其强迫注入可产生稠密列;“行规则很稀疏”并不保证满足 CKS 的双边稀疏假设。这与 [QFVM 的输入模型审查](qfvm.md) 是同一条设计原则。
## 7. 保留未完成的实现
有两种开放层级:
1. 用 {obj}`QHAMBindings `.declare 声明 L、F、B_tau、初始态等基础 oracle,再生成 G 和整个 QODE 调用。它们可以逐个绑定。
2. 用 {obj}`open_qham_input ` 保留整个 G 的 BE 为未完成模块,同时在属性中保存完整 QCL plan。初态仍可结构化生成。
第二种形式适合还没有高效全局 oracle 实现或超出显式块预算的情况。body=None 明确表示未完成,不会用空线路冒充实现。alpha、信号位和具体形状仍须实例化。
更换端口算法若改变 alpha 或辅助位数,应从同一数学 plan 重新生成上层 RIR。只有保持实例化签名/alpha 时,才适合在已有 IR 上晚绑定。
## 8. 自动推导小程序
```bash
# 内置案例
python -m oracq.applications.qham --example burgers --order 3 \
--eta=-0.4 --state-width 2 -o out/qham-general/my-derivation
# 任意符合 PDE 0.1 Schema 的输入
python -m oracq.applications.qham my-pde.json --order 4 \
--eta=-0.6 --state-width 3 -o out/my-qham
# 大阶数只查询一个块行,不枚举整个系统
python -m oracq.applications.qham my-pde.json --order 20 \
--row 0,1 --max-blocks 0 -o out/my-qham-row
# 重建本文五组完整案例
PYTHONPATH=src .venv/bin/python tools/build_general_qham.py
```
输出包括 pde.json、qcl-plan.json、rows.json、derivation.md 和 qode-input.json。量子案例还包含开放/闭合 RIR、模块化 OriginIR、Toffoli/U3/CZ 描述、端口绑定规格和数学验证记录。完整 Python 示例见 [general_qham.py](../../../examples/general_qham.py)。
## 9. 验证与实现边界
| 案例 | HAM 阶数 | 非线性次数 | 提升维数 | 函数块数 |
|---|---:|---:|---:|---:|
| 强迫 Burgers | 2 | 2 | 129 | 9 |
| KdV | 3 | 2 | 628 | 16 |
| 强迫三次反应 | 2 | 3 | 101 | 14 |
| 双分量耦合系统 | 2 | 2 | 129 | 9 |
| 二维向量 Burgers | 2 | 2 | 35,968 | 8 |
这些是对应 PDE 家族的构造案例,不声称逐项复现论文图中的参数和边界条件。五组都使用结构化端口,不物化全局矩阵。独立的 HAM 递推、张量链式法则与生成的线性作用之间,见证残差约为 1e-16 或更小。它们检验的是代数生成,不是原始 PDE 的收敛误差。m=1 无强迫还与此前特例的实际 BE 角块做了回归。
真实 PySparQ 已执行多分量非线性端口、含强迫的二阶生成元和完整的小型 QHAM→有限 Taylor QODE→物理输出链;实际 OriginIR 解析器消费了组合描述。Schrödingerization 和经过显式耗散移位的 CBMD 也已完成输入组装。证据见 [验证记录](https://github.com/IAI-USTC-Quantum/oracq/blob/main/docs/archive/qham-general-validation.json)。
仍未完成:自动选择收敛的 h/m、HAM/PDE 收敛认证、时间依赖 QODE 适配、IQHAM 外层重启、完整范数/幅度估计和大规模性能认证。显式 BE lowering 会枚举块耦合,成本可随 m 组合增长;惰性 plan 和逐行算法为更高效 oracle 留出边界,但不能据此声称已经达到论文的查询复杂度。当前 BE 的单个 target/work 包装还受 64 位限制,超出时需保持开放或扩展该接口。
这套实现展示的是语言的表达与组装能力:PDE、阶数、分量或非线性次数改变时,同一套规则自动推导线性系统并生成可组合量子模块;无需为每个案例重新手写 QHAM 线路。
## 10. 数值验证
2026-09-16 的论文级数值验证(`tests/verification/verify_qham_qfvm.py`,真实后端无替身)从数值上确认了本章各构造环节:
- **线性化代数**:生成的 QCL 提升生成元经真实后端执行,其 signal-0 矩阵块与独立经典参考(`applications/qham/reference.py`)逐元素一致,结构化端口 1.46e-17、谱嵌入端口 7.96e-18;四种后端路径(内置参考、PySparQ RIR、PySparQ 适配器、UniQC 态向量)两两偏差 8.67e-18。
- **同伦步**:修正权重 −η(1+η)ᵖ 与物理权重 1−(1+η)ᵖ 逐点精确;行规则作用与独立链式法则残差 1.39e-17(含强迫 Burgers 实例);HAM 部分和在 Riccati 实例上以平均收缩因子 0.604(η=−0.4)与 0.208(η=−0.8)收敛到解析解,与收缩映射理论值 |1+η|(0.6 与 0.2)吻合。
- **初态**:提升初态保持张量字相对范数 r, r, r², …,逐振幅误差 1.11e-16,`log_initial_norm` 与直接构造一致,工作位复净。
- **输入模型可替换性**:同一问题在结构化端口、谱嵌入端口、QRAM 角表系数三种输入模型下生成元矩阵一致——前两者 1e-17 量级,QRAM 角表路径 5.99e-05,在声明的角量化界 α·π/2^angle_width(9.20e-03)之内;QRAM 系数编码的逐地址振幅误差 2.15e-03(界 6.14e-03)。
- **求解链**:有限 Taylor QODE 端到端(BE 生成元 + 提升初态 + 物理块选取)对照经典 (I+tG)Y_in,两种输入模型各 1.11e-16;显式耗散移位 G−μI 在完整 2ʷ 空间的全矩阵误差 5.70e-17。
仍属求解器一侧的收敛认证、η/m 自动选择与大规模性能不在本次验证范围(见第 9 节)。复现命令与完整指标见[算法页数值验证](algorithms/qham.md#数值验证)与 `out/verification/qham_qfvm.json`。
## 11. QRAM 数据路径逐行讲解
本节针对第 4 节的已知系数项({obj}`Known ` 数据),逐行讲清同一条 open 程序如何在"系数烧进门里"与"系数留在 QRAM 角表"两种实现之间切换。源码:`applications/qham/stencils.py`;端到端示例:`examples/input_models.py` 的 QHAM 行。
### 11.1 门实现:`coefficient_encoding`
```python
values = [discretization.known_product(monomial, row) if row < grid.size else 0j
for row in range(1 << width)] # 经典侧算出每个地址的对角值
alpha = max((abs(v) for v in values), default=0)
for address, value in enumerate(values):
with b.control(b["target"], address): # 按地址受控的分支
b.ry(b["signal"], 2 * math.acos(min(1, abs(value) / alpha)))
if value:
b.global_phase(cmath.phase(value)) # 复系数走全局相位
```
逐行:对角块编码的角块约定是 `D_jj = alpha·cos(theta_j/2)`——每个地址一个受控 RY,旋转角由 `|value|/alpha` 反余弦给出;复相位用 `gphase` 分支写入。注意 `values` 表被**编译进了控制字**:换一批系数就是换一段电路。超过 `max_words`(默认 4096)时此实现直接报错,要求改绑 QRAM 或自定义系数 BE。
### 11.2 QRAM 实现:`qram_coefficient_encoding`
```python
values = [...] # 同一张经典值表
if any(v.imag for v in values):
raise ValidationError("QRAM 角编码的系数数据必须为实数")
alpha = max((abs(v.real) for v in values), default=0)
db = abstract_database(_name("coefficient_angles", values), width, angle_width)
return diagonal_block_encoding(db, alpha=alpha)
```
与门版本共用同一合同(target 为空间位、alpha 相同),差别只有两步:**开放**一个"地址宽 = 空间位宽、数据宽 = angle_width"的 XOR 数据库槽,再由 {obj}`diagonal_block_encoding ` 把它组装成对角 BE(内部:查角字 → 按位受控 RY 合成,机制与第 4 节结构化端口一致)。数据不进门:程序此时仍是 open 的,槽位名里带值表的内容哈希(`_name`),闭合前后 α 声明不变。代价是两条明确的约束:只接受**实系数**(复相位没有对应编码),以及角量化引入不超过 `alpha·pi/2**angle_width` 的幅值误差。
### 11.3 运行时角表:`qram_coefficient_memory`
```python
alpha = max((abs(v.real) for v in values), default=0)
step = 2 * math.pi / (1 << angle_width)
return {
address: round(2 * math.acos(min(1, max(-1, v.real / alpha))) / step) % (1 << angle_width)
for address, v in enumerate(values)
}
```
逐行:α 与 11.2 声明的同源(换数据表必须同步 α 与谱声明);每个地址的角字 = θ 除以步进 `2π/2^aw` 后四舍五入取模。这个量化界就是第 10 节"QRAM 角表路径 5.99e-05,界 9.20e-03"那条验证的来源。
### 11.4 提升初态:`qram_state_angles`
初态角树与 QFVM 的 RHS 符号残差树是**同一机制**(同一 {obj}`qram_state_prep ` 电路:每层查一个内部节点角字、按位合成 RY、反查询复净,共 `2·width` 次查询)。差别只在权重:这里的分支权重是各提升块的相对范数(第 5 节的 r, r, r², …),同样要求非负实幅度:
```python
qram_state_angles(profile, 8) # {树节点: 角字},编址 (1<` 的 α 与编码器声明同源;改数据表必须同步 α、谱声明与初始范数,否则闭合检查会拒绝。
3. **接口约束如实记录**。非负实幅度是当前 QRAM 态制备/角表实现的接口约束,不是数学限制;带符号数据要像 [QFVM](qfvm.md) 那样配独立符号库(对照其 `rhs_sign` 的 Z 反冲),属于另一条实现路径。