checked 设计
本页解释对偶数的哪些运算具有带检查的形式、它们的错误如何从求导规则中产生,以及为什么门面包复用 arithmetic 的错误模型。
设计目标
让被求导的代码以值的形式报告非法运算,使用与 Luna Flow 标量代码相同的错误类型,并使带检查的运算不可能返回一个有效的值却附带无效的导数。
数学背景
导数在何处失效
当 的任一分量无定义时,带检查的运算必须失败。对于这两个带检查的运算:
商恰好在值失效的地方失效,因为在精确算术中 。平方根则不同: 在 处存在,但 在那里不可导(其差商 无界),因此对偶运算的定义域是开半直线 ,比标量平方根的定义域 更小。
浮点数的定义域
在 Double 中,非零的 也可能使 为零:当 时(采用就近舍入并支持渐进下溢), 会下溢为 ,即使 可能是有限值。这时带检查的商会因切向分量而报告除以零。这类输入最好先重新缩放;dual 设计 假定不发生下溢。
设计决策
检查两个分量
问题。 带检查的标量运算可能成功,而导数却不存在。
选择。 每个带检查的对偶运算都对值调用带检查的标量运算,对切向分量的除法调用 div_checked,并返回遇到的第一个错误。这使得对偶运算的定义域成为 与 定义域的交集,正如上面推导的那样;特别地,sqrt_checked 在 处失败。
不对零切向分量做特殊处理
当 且 时,切向分量是不定式 。可以返回 (常数输入没有需要报告的导数),但那样会让结果取决于常数是如何产生的。实现交由 T 决定,对于 Double,这是一个定义域错误。
复用 arithmetic
Dual[T] 实现了 arithmetic 中的 DivChecked 和 SqrtChecked,本门面包恰好重新导出这两个 trait,以及 ArithmeticContext、ArithmeticError、ArithmeticErrorKind 和 RoundingMode。因此泛型的带检查代码无需改动即可在 Double 和 Dual[Double] 上运行,两个层次的错误也是同一类型。
只有除法和平方根
arithmetic 为除法、平方根、比较、解析和整数幂定义了带检查的 trait。其中只有除法和平方根是本仓库中具有求导规则的 运算,因此只有它们具有带检查的对偶形式。对数和三角函数沿用 T 不带检查的语义。
正确性与不变量
div_checked(x, y)恰好在T的div_checked对 和 都成功时成功;此时它等于用相同运算计算出的x / y。sqrt_checked(x)恰好在T的sqrt_checked(a)和div_checked(b, 2√a)都成功时成功;对于Double,即 或 为 NaN。- 上下文被原样传给
T,且从不被修改。
被否决的方案
- 专门的 autodiff 错误类型。 它会与
ArithmeticError重复,并迫使每个边界处都进行转换。 Option结果。 它会丢失失败的原因。- 只检查值。 这会让”带检查的”运算返回无穷大或 NaN 的导数。
边界
- 没有带检查的对数、指数或三角运算。
- 没有上下文相关(
ArithmeticOutcome)的运算,也没有舍入诊断。 - 不会自动重新缩放极小的除数。