poly 设计
本页解释为什么在对偶数上对 luna-poly 多项式求值就能得到其导数、稠密和稀疏两种求值顺序分别计算什么、结果的精度如何,以及为什么桥接仅限于单变量。
设计目标
复用 luna-poly 已有的求值算法,在不引入新的多项式表示、也不进行符号运算的情况下,为 luna-poly 用户提供多项式在某点处的值和导数。
数学背景
在对偶数上求值即得导数
对于交换半环 上的 ,dual 设计 表明
其中 表示 自加 次。该恒等式是代数性的,因此对整数多项式精确成立,对浮点多项式在舍入意义下成立;它既不需要极限,也不需要除法。
对偶数上的 Horner 法则
DensePolynomial::eval 计算 ,对 计算 ,并返回 。取 、提升后的系数 以及 ,由对偶数的积与和得到
当 时,这就是同时计算多项式及其导数的经典方案。11 D. E. Knuth, The Art of Computer Programming, vol. 2, 3rd ed., section 4.6.4. 由归纳可得 以及 ,因此 ,。
通过求幂计算稀疏项
SparsePolynomial::eval 对存储的各项求 之和,并用二进制快速幂计算 。在对偶数上,每次乘法都应用乘积法则,因此 变为 (即 dual 设计中的二项式恒等式),各项之和同样给出 。
设计决策
求值而非符号求导
问题。 用户需要 在若干点处的值。
选项。 用 DensePolynomial::derivative 构建导数多项式再求值;或在 Dual[T] 上对 求值。
选择。 在 Dual[T] 上求值。它一遍就返回 和 ,只需要 Semiring(luna-poly 中的形式导数还需要 NatHomomorphism 来构成 ),不会为每一阶导数额外分配一个多项式,并且可以通过 eval_dual 与其他对偶计算组合。当需要导数多项式本身时,形式导数仍是合适的工具;测试套件会检查两者是否一致。
复用 luna-poly 的求值
桥接用 Dual::constant 提升系数,用普通构造函数构建 DensePolynomial[Dual[T]] 或 SparsePolynomial[Dual[T]],然后调用 eval。它并不重新实现 Horner 法则,因此 luna-poly 求值方式的任何改动(以及它对零系数的规范化,这也是需要 T : Eq 的原因)都会同样作用于这里。
稀疏多项式仅限单变量
问题。 SparsePolynomial 是多元的,但偏导数需要选定变量,而变量的身份存在于 luna-poly 的上下文中(VariableContext、ContextPolynomial)。
选择。 稀疏桥接用单一赋值 [x] 求值,并命名为 sparse_univariate_*。使用了其他变量的多项式会使 eval 中止。多元 API 需要接受变量上下文并返回梯度,这留待将来实现。持有上下文多项式的调用方可以先对其他变量做部分求值,正如本仓库的集成测试那样。
兼容名称
首个版本将这些函数命名为 derivative_at、value_and_derivative_at、eval_sparse_dual、sparse_derivative_at 和 sparse_value_and_derivative_at。dense_ 和 sparse_univariate_ 名称表明了所针对的表示形式和导数种类;旧名称作为普通别名保留,以便现有代码继续工作。
正确性与不变量
精确环中的精确性
对于具有精确算术的 T(Int、BigInt、精确有理数),由上述恒等式可知,结果恰好就是 和 。
稠密求值的舍入误差
使用 ,,以及乘积引理 ,。22 N. J. Higham, Accuracy and Stability of Numerical Algorithms, 2nd ed., SIAM, 2002, Lemma 3.1 and chapter 5.
值。 根据投影同态, 就是普通 Horner 法则的结果。每一步都把累加和乘以 ,并加上经过一次舍入的 ,因此当 时 带有 个因子;首项系数带有 个,因为第一步 是精确的:
导数()。 切向分量的每一步计算 ,而加上常数 只会给切向分量加上一个精确的零。导数包含 份 ,每一份对应一个步骤 ,在该步 从值链转入切向链。沿这样一条路径, 在进入时舍入一次,在值的第 步中每步舍入两次,转入切向链时舍入一次,在切向的第 步中每步舍入两次:共 个因子。因此
这与用 Horner 法则计算形式导数时的误差界相同。除非各项相互抵消,即除非 在 处是病态的,否则相对误差都很小。
代价
稠密: 步对偶乘加,即 T 上约 次乘法和 次加法,外加一个存放 个提升后系数的数组。稀疏:每一项一次二进制快速幂,即 次对偶乘法,外加一次对偶乘法和一次加法。
被否决的方案
- 先符号求导再求值。 需要两遍计算、一个额外的多项式,以及对
T更强的约束;为需要导数多项式的用户,它以luna-poly自身的DensePolynomial::derivative形式保留。 - 私有的 Horner 循环。 速度只快一个常数倍,但会重复
luna-poly的语义,并逐渐与之偏离。 - 为多元稀疏多项式猜测变量。 静默地对”第一个变量”求偏导很容易被误用;桥接会直接中止。
边界
- 仅支持一元:不支持多元多项式的偏导数或梯度,也不支持
ContextPolynomial。 - 仅支持在点处求一阶导数;不生成导数多项式(请使用
luna-poly),也不支持高阶导数。 - 没有带检查的变体:除了上面所述的稀疏中止情况外,稠密和稀疏求值都不会失败。
- 仅支持稠密和稀疏的
immut表示。