core 设计

本页推导 Complex[T] 的代数结构,说明每个 luna-generic 实例在何时合法,并解释泛型类型背后的设计选择:可变字段、未缩放的除法公式,以及与浮点分析的分离。

设计目标

为生态中的每种标量提供同一个泛型复数类型,其实例所声明的代数结构恰好是该构造所具有的结构,而 IEEE 特殊值、分支切割线和超越函数则交给后端包处理。

数学背景

构造

对于交换环 RR,RR 上的复数是多项式环对由 x2+1x^2 + 1 生成的理想所作的商:

R[i]=R[x]/(x2+1),i=x+(x2+1),i2=−1.R[i] = R[x]/(x^2 + 1), \qquad i = x + (x^2 + 1), \qquad i^2 = -1 .

用首一多项式 x2+1x^2 + 1 做除法会留下唯一一个次数低于二的余式,因此每个元素都可唯一地写成 a+bia + bi(a,b∈Ra, b \in R):即二元组 (re, im)。用多项式计算并以 i2=−1i^2 = -1 化简,便得到本包的各项运算:

(a+bi)±(c+di)=(a±c)+(b±d)i,(a+bi)(c+di)=ac+(ad+bc)i+bd i2=(ac−bd)+(ad+bc)i.\begin{aligned} (a + bi) \pm (c + di) &= (a \pm c) + (b \pm d)i, \\ (a + bi)(c + di) &= ac + (ad + bc)i + bd\,i^2 = (ac - bd) + (ad + bc)i . \end{aligned}

作为交换环的商,R[i]R[i] 是交换环,零元为 0+0i0 + 0i,单位元为 1+0i1 + 0i;RR 通过 a↦a+0ia \mapsto a + 0i 嵌入其中。

共轭与范数

a+bi‾=a−bi\overline{a + bi} = a - bi 是由 x↦−xx \mapsto -x 诱导的映射,它保持理想 (x2+1)(x^2 + 1) 不变,因此是一个二阶环自同构:

z+w‾=zˉ+wˉ,zw‾=zˉ wˉ,zˉˉ=z.\overline{z + w} = \bar z + \bar w, \qquad \overline{zw} = \bar z\,\bar w, \qquad \bar{\bar z} = z .

范数 N(z)=zzˉ=(a+bi)(a−bi)=a2+b2N(z) = z\bar z = (a + bi)(a - bi) = a^2 + b^2 属于 RR,并且是乘性的:N(zw)=zw zˉwˉ=N(z)N(w)N(zw) = zw\,\bar z\bar w = N(z)N(w)。

逆元与除法

若 N(z)N(z) 在 RR 中可逆,则 z⋅zˉ N(z)−1=1z \cdot \bar z\,N(z)^{-1} = 1,因此

z−1=zˉN(z)=aa2+b2−ba2+b2 i,a+bic+di=(ac+bd)+(bc−ad)ic2+d2.z^{-1} = \frac{\bar z}{N(z)} = \frac{a}{a^2 + b^2} - \frac{b}{a^2 + b^2}\,i, \qquad \frac{a + bi}{c + di} = \frac{(ac + bd) + (bc - ad)i}{c^2 + d^2} .

反之,若 zz 可逆,则 N(z)N(z−1)=N(1)=1N(z)N(z^{-1}) = N(1) = 1,因此 N(z)N(z) 可逆。所以 zz 是单位当且仅当 a2+b2a^2 + b^2 是单位。

何时该构造是域

设 KK 为域。K[x]/(x2+1)K[x]/(x^2 + 1) 是域当且仅当 x2+1x^2 + 1 在 KK 上不可约,即 −1-1 在 KK 中不是平方数。直接来看:满足 N(z)=a2+b2=0N(z) = a^2 + b^2 = 0 的非零 zz 必有 b≠0b \ne 0,从而 (a/b)2=−1(a/b)^2 = -1。反之,若 s2=−1s^2 = -1,则 (s+i)(s−i)=s2+1=0(s + i)(s - i) = s^2 + 1 = 0 给出了零因子。

  • 对于 K=RK = \mathbb R(Float、Double),−1-1 不是平方数,R[i]=C\mathbb R[i] = \mathbb C 是域。
  • 对于 K=CK = \mathbb C(Complex[Double]),−1=i2-1 = i^2 是平方数,因此 Complex[Complex[Double]] ≅C[j]/(j2+1)≅C×C\cong \mathbb C[j]/(j^2 + 1) \cong \mathbb C \times \mathbb C 是一个含零因子的环,例如 (1+ij)(1−ij)=1−i2j2=0(1 + ij)(1 - ij) = 1 - i^2 j^2 = 0。

设计决策

实例跟随 T 的结构

问题。 Complex[T] 可以实现哪些 luna-generic trait?

选择。 Zero、One、AddMonoid 和 AddGroup 只需要 T 的相应运算,因为它们逐分量作用。MulMonoid、Semiring 和 Ring 需要 T : Ring,因为乘积要用到减法(ac−bdac - bd),而 R[i]R[i] 的环公理依赖于 RR 的环公理。Inverse、MulGroup、Field 以及 Div 运算符需要 T : Field 来计算 N(z)−1N(z)^{-1}。Conjugate 只需要 Neg。

Field 实例对每个 T : Field 都有声明。由上一节可知,当 −1-1 在 T 中不是平方数时它是合法的,这涵盖了实数标量类型;对于 T = Complex[Double] 则不然,inv 会在非零的零因子上中止。trait 约束无法表达“−1 不是平方数”,因此该实例在这里信任调用者。11 这是 Luna Flow“只实现合法实例”这一规则中的一个已知缺口。测试套件只在范数可逆的值上使用嵌套复数。

四次乘法

备选方案。 使用四次乘法的教科书式乘积 (ac−bd)+(ad+bc)i(ac - bd) + (ad + bc)i;高斯的三次乘法形式 k1=c(a+b)k_1 = c(a + b)、k2=a(d−c)k_2 = a(d - c)、k3=b(c+d)k_3 = b(c + d),乘积为 (k1−k3)+(k1+k2)i(k_1 - k_3) + (k_1 + k_2)i。

选择。 四次乘法。在浮点下,教科书形式的按范数相对误差至多为 5 u\sqrt5\,u,22 R. Brent, C. Percival and P. Zimmermann, “Error bounds on complex floating-point multiplication”, Mathematics of Computation 76 (2007). 而三次乘法形式引入了 a+ba + b、d−cd - c 这类容易相消的和,按分量的精度更差;并且对于泛型 T,乘法不一定比加法更昂贵。

按分量看,fl(ac−bd)=ac(1+θ2)−bd(1+θ2′)\mathrm{fl}(ac - bd) = ac(1 + \theta_2) - bd(1 + \theta_2'),其中 ∣θ2∣≤γ2=2u/(1−2u)|\theta_2| \le \gamma_2 = 2u/(1 - 2u),因此

∣fl(ac−bd)−(ac−bd)∣≤γ2 (∣ac∣+∣bd∣),|\mathrm{fl}(ac - bd) - (ac - bd)| \le \gamma_2\,(|ac| + |bd|),

除非 acac 与 bdbd 相消,否则它相对于结果是很小的。

泛型核心中的教科书式除法

问题。 除法需要 N(w)−1N(w)^{-1};对浮点数而言,c2+d2c^2 + d^2 远在 c+dic + di 之前就会上溢或下溢。

备选方案。 带缩放的算法(Smith 算法,或按 2 的幂重新缩放)需要比较和绝对值,而泛型域不具备这些;教科书公式只需要域运算。

选择。 泛型的 Div 和 Inverse 使用教科书公式,配合 T 的 Inverse::inv。对于精确的域它是精确的。对于 Double,只要 c2+d2c^2 + d^2 不超出范围它就是正确的,而 Double 的 Inverse 在遇到零(包括下溢后的范数)时中止。带缩放且考虑特殊值的除法位于 float_backend。

可变字段

问题。 复数值常常在循环中更新(累加器、递推),每一步都分配新值是一种浪费。

选择。 Complex[T] 是一个 pub(all) 结构体,带有 mut re 和 mut im,以及 set、set_re 和 set_im。每个运算都返回新值,从不修改其输入,因此不调用这些 setter 的代码可以把复数当作值来对待。确实要修改的调用者必须记住该结构体是按引用共享的。

核心中没有解析函数

z\sqrt z、log⁡z\log z 或 sin⁡z\sin z 需要序、绝对值、实数标量的超越函数、分支的选择以及 IEEE 特殊值。这些对泛型域都不存在。因此根包止步于代数,由 float_backend 包为 Double 提供分析功能。

文本与调试形式

Show 使用 T 的文本打印 re + imi(1−2i1 - 2i 打印为 1 + -2i),这是该类型的规范文本,也是 to_string 保留为提升方法的原因。Debug 为测试和诊断打印记录形式。MoonBit 0.10 中其他 trait 方法的提升在 src/extends.mbt 中显式完成。

正确性与不变量

  • 对于 T : Ring,由于 R[i]R[i] 是商环,环公理成立;测试套件在整数上检查结合律、单位元以及 zw‾=zˉwˉ\overline{zw} = \bar z\bar w。
  • 对于精确的 T,z * z.conjugate() 精确等于 N(z)+0iN(z) + 0i。
  • 当 N(w)N(w)、N(z)N(z) 为单位时,z * w / w == z 与 z.inv() * z == 1 对精确的域成立;对 Double 在范围内则在舍入误差意义下成立。
  • 没有任何运算会修改其参数;只有三个 setter 会。

被否决的方案

  • 仅支持 Double 的复数类型。 这会导致为 Float、整数(高斯整数)和精确有理数重复定义该类型。
  • 不可变字段。 值语义更清晰,但对原地更新 re 和 im 的现有调用者来说是破坏性变更。
  • 在核心中做带缩放的除法。 它需要泛型域所缺乏的序和量级运算。

边界

  • 不提供解析函数或超越函数,不做特殊值处理,没有分支切割线;参见 float_backend。
  • 核心中没有极坐标表示。
  • 不检查 T 是否使 R[i]R[i] 成为域;Field 实例信任这一点。
  • 不为浮点 T 提供防上溢的除法。

Footnotes

  1. 这是 Luna Flow“只实现合法实例”这一规则中的一个已知缺口。测试套件只在范数可逆的值上使用嵌套复数。 ↩

  2. R. Brent, C. Percival and P. Zimmermann, “Error bounds on complex floating-point multiplication”, Mathematics of Computation 76 (2007). ↩