一般 QHAM 自动生成:PDE → HAM → QCL → QODE¶
English · 简体中文
QHAM 生成器将规则化 PDE、有限阶 HAM 推导和量子适配线性化连接到 QODE 输入。先阅读数学推导,再按本章提供网格、系数与初态;最小可运行示例见教程:从 PDE 表达式生成 QHAM 输入。构造依据是 QHAM 论文。
这里的“二次线性化”是 secondary linearization,即对 HAM 变形方程再做一次量子适配线性化。程序同时支持规则内的二次、三次及更高有限次数,不先把高次非线性截成二次项。
1. “一般性”的明确范围¶
输入是自治的一阶时间演化 PDE 系统。允许:
任意有限分量和有限空间维;
未知场及其空间导数的有限多项式;
已知空间系数、已知强迫及其空间导数;
对完整单项式施加外层空间导数;
任意非负 HAM 截断阶 m,受显式生成预算和当前量子接口位宽限制约束。
例如:
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 组装要求自治绑定。
外导数保留为算子作用,例如 (uu).d("x") 在离散化后表示 D_x 作用于同点乘积。程序没有偷偷把它改成 2u*D_x(u),因为有限差分一般不满足精确 Leibniz 恒等式。带外导数的非线性表达式目前不能继续作为乘积因子;这种写法需要显式改写为允许的单项式规则或提供多线性端口。
初值、边界和空间离散化由绑定提供。形式 PDE 推导本身不替用户选择边界条件,也不证明问题适定。
2. 自动推导到底生成什么¶
入口 pde.py 将表达式拆成已知线性映射:
u' = f + L u + sum_tau B_tau(u,...,u).
L 包含全部一次项,F 表示 f 的向量注入,每个非线性单项式形成一个多线性 B_tau。端口包含 PDE 系数;例如 Burgers 的 B 包含负号,KdV 的 B 包含 -6,不能只绑定一个没有这些系数的裸乘法。
linearization.py 自动生成:
各阶 Ui 的 HAM 递推,使用 -eta(1+eta)^(i-1-l) 权重;
有序张量字 Y_(a0,...,ak-1) 及其线性耦合;
物理输出块 u_sum=sum(Ui);
非零强迫需要的常量分量 one;
块的维数、偏移、索引排名/反排名和逐行算子规则。
最高非线性次数 D 对应的有限闭包取
max(1,D-1)*sum(a_j)+len(a) <= max(1,D-1)*m+1.
闭包中的张量秩有限,非线性替换使 HAM 阶数和严格下降。它精确表示截断后的 HAM 系统,没有再做一次 Carleman 高阶截断。高次 PDE 使用规范闭包超集,可能包含冗余但闭合的变量。
QHAMPlan 是惰性对象:只保存 PDE 和 m。二次无强迫、m=20 时可描述 2^21 个函数块,而无需把这些块全部写入序列化文本。row_terms、offset 和 locate 可单独查询。显式导出超过预算时,计划仍然有效,不会被伪装成已经展开完成的线路。
3. 两层可序列化表示¶
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 |
实际寄存器、 |
是,模块调用继续保留 |
格式见 PDE Schema 和 QCL Schema。PDE/QCL 没有 Python callback;可从 JSON 重建。只在显式请求时生成 rows.json 或量子模块。
“数学函数自动量子编译”和这里的 PDE 构造面有不同对象:compile_function 对基态寄存器中的数值做可逆计算;Field 表达式描述的是待求场及其幅度编码。不能把一个 out-of-place 数值函数电路直接当作对量子振幅施加非线性 PDE。QHAM 通过扩大线性状态空间处理后者。
4. 如何降低到 register-level 量子模块¶
实现见 quantum.py 和 stencils.py。
每个端口都用矩形线性映射解释:
端口 |
输入 |
输出 |
|---|---|---|
L |
V |
V |
B_tau |
V 的 r 重张量积 |
V |
F |
标量常量空间 |
V |
它们用补齐后的 BE 承载,alpha 和辅助位数由具体实现给出。QCL 将这些端口放在指定张量位置,其他坐标作恒等映射;强迫通过插入新坐标作用,非线性通过收缩多个坐标作用。
矩形块必须同时检查输入和输出窗口。全局布局不是等宽块布局,输入/输出的零填充也不能溢出到相邻块。程序使用可逆地址平移、坐标置换和两个窗口失败旗标实现嵌入。
周期网格的普通结构化实现包括:
有限差分导数由少量可逆移位及 LCU 组成;
分量选择由输入分量投影和输出分量编码实现;
同点乘积通过空间坐标 XOR、对角条件和矩形收缩实现;
外导数在收缩之后作用;
已知系数使用对角乘法 BE,当前普通门版本有显式词数预算。
这条路径不物化 N^r×N^r 的基础端口矩阵,更不物化整个提升矩阵。另有 gate_bindings,专门把很小的基础端口物化后用于对照;它不是一般路径。
例子:
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,必须保留这些相对权重:
initial weights: r, r, r^2, ..., r^K, [1 if forcing].
程序先按这些范数准备分支标签,在相应目标区间调用初始态制备 oracle,再从地址区间反算标签。调用的都是可重复、可受控的制备操作,不是复制一个只给定一次的未知量子态。初值 oracle 的相干相位也必须与所表示的经典向量一致。
每次对已知零输入的制备都复净其 work,因此同一段工作寄存器可以顺序复用。标签与其他工作位在成功制备后为零,公共 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¶
QHAMInputModel 包含生成元 BE、提升初态制备、原始状态宽度、同伦参数、初始范数和物理输出窗口。求解对象是
Y' = G Y, Y(0) = Y_in,
其中非齐次强迫已经通过常量分量齐次化。它不是直接把 G 当作一个需要求逆的 QLSS 矩阵;若 QODE solver 再构造时间离散线性系统,那个系统需要它自己的输入与谱假设。
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/CBMD 的耗散前提。
实现提供显式整体移位:
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 的惰性行规则可以配合基础差分模板查询行和元素。Discretization.qcl_row/qcl_entry 是可审阅的经典参考,不是已经完成的可逆 quantum oracle。它们没有通过扫描整张矩阵获得结果。
如果选择稀疏输入型 QODE solver,需要另外提供满足其约定的基础稀疏访问及对应可逆实现。尤其强迫注入可产生稠密列;“行规则很稀疏”并不保证满足 CKS 的双边稀疏假设。这与 QFVM 的输入模型审查 是同一条设计原则。
7. 保留未完成的实现¶
有两种开放层级:
用
QHAMBindings.declare 声明 L、F、B_tau、初始态等基础 oracle,再生成 G 和整个 QODE 调用。它们可以逐个绑定。用
open_qham_input保留整个 G 的 BE 为未完成模块,同时在属性中保存完整 QCL plan。初态仍可结构化生成。
第二种形式适合还没有高效全局 oracle 实现或超出显式块预算的情况。body=None 明确表示未完成,不会用空线路冒充实现。alpha、信号位和具体形状仍须实例化。
更换端口算法若改变 alpha 或辅助位数,应从同一数学 plan 重新生成上层 RIR。只有保持实例化签名/alpha 时,才适合在已有 IR 上晚绑定。
8. 自动推导小程序¶
# 内置案例
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。
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 也已完成输入组装。证据见 验证记录。
仍未完成:自动选择收敛的 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 节)。复现命令与完整指标见算法页数值验证与 out/verification/qham_qfvm.json。
11. QRAM 数据路径逐行讲解¶
本节针对第 4 节的已知系数项(Known 数据),逐行讲清同一条 open 程序如何在"系数烧进门里"与"系数留在 QRAM 角表"两种实现之间切换。源码:applications/qham/stencils.py;端到端示例:examples/input_models.py 的 QHAM 行。
11.1 门实现:coefficient_encoding¶
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¶
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 数据库槽,再由 diagonal_block_encoding 把它组装成对角 BE(内部:查角字 → 按位受控 RY 合成,机制与第 4 节结构化端口一致)。数据不进门:程序此时仍是 open 的,槽位名里带值表的内容哈希(_name),闭合前后 α 声明不变。代价是两条明确的约束:只接受实系数(复相位没有对应编码),以及角量化引入不超过 alpha·pi/2**angle_width 的幅值误差。
11.3 运行时角表:qram_coefficient_memory¶
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 符号残差树是同一机制(同一 qram_state_prep 电路:每层查一个内部节点角字、按位合成 RY、反查询复净,共 2·width 次查询)。差别只在权重:这里的分支权重是各提升块的相对范数(第 5 节的 r, r, r², …),同样要求非负实幅度:
qram_state_angles(profile, 8) # {树节点: 角字},编址 (1<<depth)-1+prefix
11.5 端到端:一条 open 程序、两种闭合¶
profile = [0.1, 0.2, 0.15, 0.05] # QRAM 角表只接受非负实幅度
qinitial = space.encode_fields({"u": profile})
# 换编码器:structured_fd_bindings 的 coefficient_encoder 钩子接 QRAM 版本(角字 8 位)。
encoder = partial(qram_coefficient_encoding, angle_width=8)
open_bindings = structured_fd_bindings(space, qinitial, coefficient_encoder=encoder)
# 初值也换成开放槽(宽度 + 10 位工作区):闭合时可绑门制备或 QRAM 制备。
open_bindings = QHAMBindings(
open_bindings.state_width, open_bindings.ports,
abstract_state_prep("QhamInitial", open_bindings.state_width, 10),
open_bindings.initial_norm,
)
model = qham_input_model(plan, open_bindings, eta=-0.4)
state = model.solve(schrodinger, 0.01)
# 从 open 程序中找出角库槽:除初值外唯一的未解析声明。
db_slot = [r.name for r in unresolved(state.operation.program()) if r.name != "QhamInitial"][0]
# 找到方程里含 Known 的单项式,经典侧算出它的角字表并补零到全地址域。
(forcing_monomial,) = (t.monomial for p in space.pde.ports for t in p.terms if t.monomial.known)
angle_words = qram_coefficient_memory(space, forcing_monomial, angle_width=8)
word_table = [angle_words.get(address, 0) for address in range(4)]
两种闭合(程序文本不变,只换绑定字典):
# 闭合 A(门):把同一批角字烧进真值表数据库。
{db_slot: gate_database(2, 8, word_table).operation,
"QhamInitial": gate_state_prep(profile, work_width=10).operation}
# 闭合 B(QRAM):槽位绑 QRAM 库,数据运行时给。
{db_slot: Binding(qram_database(2, 8).operation, {"table": "coeff_angles"}),
"QhamInitial": Binding(qram_state_prep(2, 8).operation, {"angles": "initial_angles"})}
# 闭合 B 的配套内存表:
{"coeff_angles": word_table, "initial_angles": qram_state_angles(profile, 8)}
三个要点:
两种闭合编码同一批数据。门真值表存的就是 QRAM 表里那些角字,所以两条路径逐位一致(第 10 节验证);相对精确系数的唯一近似是角量化。
α 随数据走。
qram_coefficient_memory的 α 与编码器声明同源;改数据表必须同步 α、谱声明与初始范数,否则闭合检查会拒绝。接口约束如实记录。非负实幅度是当前 QRAM 态制备/角表实现的接口约束,不是数学限制;带符号数据要像 QFVM 那样配独立符号库(对照其
rhs_sign的 Z 反冲),属于另一条实现路径。