elementary 教程
本教程对针对 arithmetic trait 编写的解析代码求导。你将以 Exponential、Logarithmic 和 Trigonometric 为约束编写一次函数,在 Double 上对其求值,再通过在 Dual[Double] 上运行它得到导数。背后的规则见 elementary 设计。
快速上手
moon add Luna-Flow/autodiff@0.2.0
import {
"Luna-Flow/autodiff/elementary",
}
fn[T : @elementary.Trigonometric + Mul] sin_squared(x : T) -> T {
let s = @elementary.Trigonometric::sin(x)
s * s
}
fn main {
let y = sin_squared(@elementary.Dual::variable(0.5))
println("sin^2(0.5) = \{y.value()}")
println("derivative = \{y.tangent()}")
}
sin^2(0.5) = 0.22984884706593015
derivative = 0.8414709848078965
导数为 ,此处为 。
日常任务
对数与指数
fn[T : @elementary.Exponential + @elementary.Logarithmic + Mul] f(x : T) -> T {
@elementary.Exponential::exp(x) * @elementary.Logarithmic::ln(x)
}
fn main {
let y = f(@elementary.Dual::variable(1.0))
println("f(1) = \{y.value()}, f'(1) = \{y.tangent()}")
}
f(1) = 0, f'(1) = 2.718281828459045
,在 处为 。
其他底数
exp2、log2 和 log10 包含了换底因子:
fn main {
let x = @elementary.Dual::variable(3.0)
println("d/dx 2^x at 3 = \{x.exp2().tangent()}")
println("d/dx log2 x at 3 = \{x.log2().tangent()}")
println("d/dx log10 x at 3 = \{x.log10().tangent()}")
}
d/dx 2^x at 3 = 5.545177444479562
d/dx log2 x at 3 = 0.48089834696298783
d/dx log10 x at 3 = 0.14476482730108392
泛型代码中的常数
fn[T : @elementary.Trigonometric + @elementary.Constants + Mul] wave(t : T) -> T {
let tau : T = @elementary.Constants::tau()
@elementary.Trigonometric::sin(tau * t)
}
fn main {
let y = wave(@elementary.Dual::variable(0.0))
println("slope at 0 = \{y.tangent()}")
}
slope at 0 = 6.283185307179586
常数 的切向分量为零,因此只对 求导。
正切函数及其极点
fn main {
for a in [0.0, 1.0, 1.5] {
let y = @elementary.Dual::variable(a).tan()
println("tan'(\{a}) = \{y.tangent()}")
}
}
tan'(0) = 1
tan'(1) = 3.425518820814759
tan'(1.5) = 199.8500445264925
导数 在趋近 时无界增长。
深入学习
- 将解析 trait 与根包或
core中的Ring结合,即可编写完整的模型;见 autodiff 教程。 - 对于自定义标量,实现
Exponential、Logarithmic和Trigonometric;之后它的Dual就会按相同规则对这些函数求导。 - 需要检查定义域的平方根请使用
SqrtChecked,见 checked 教程。
常见陷阱
- 超出定义域会得到 NaN 或无穷大。 非正数的
ln和零处的sqrt都不做检查。 - trait 实例需要的比方法更多。 在
Dual[T]上调用@elementary.Exponential::exp需要完整的实例约束(T上的Logarithmic和IntegralHomomorphism);方法x.exp()需要的则更少。 - 没有双曲函数或反函数。
sinh、asin、atan等函数目前还没有对偶规则;如有需要,请用现有函数组合实现。
后续阅读
- elementary API 列出了这些 trait 及其约束。
- dual API 列出了每条规则的实际计算方式。
- arithmetic 记录了这些 trait。