core 教程

本教程用 core 门面包编写环层面的泛型代码并对其求导。你将为任意环一次性定义函数,在整数、浮点数和对偶数上运行它们,并在泛型代码中构造整数常量。背景知识见 core 设计。

快速上手

moon add Luna-Flow/autodiff@0.2.0
import {
  "Luna-Flow/autodiff/core" @ad_core,
}
fn[T : @ad_core.Ring] square_minus(x : T, y : T) -> T {
  x * x - y
}

fn main {
  println(square_minus(3, 1))
  println(square_minus(3.0, 1.0))
  let d = square_minus(@ad_core.Dual::variable(3.0), @ad_core.Dual::constant(1.0))
  println("value \{d.value()}, derivative \{d.tangent()}")
}
8
8
value 8, derivative 6

日常任务

泛型代码中的整数常量

字面量具有固定的类型;在泛型函数中,请用 IntegralHomomorphism::from_integral 构造常量:

fn[T : @ad_core.Ring + @ad_core.IntegralHomomorphism] cubic(x : T) -> T {
  let two : T = @ad_core.IntegralHomomorphism::from_integral(2)
  let seven : T = @ad_core.IntegralHomomorphism::from_integral(7)
  two * x * x * x - seven
}

fn main {
  let y = cubic(@ad_core.Dual::variable(1.5))
  println("value \{y.value()}, derivative \{y.tangent()}")
}
value -0.25, derivative 13.5

在 Dual[T] 上,from_integral 产生切向分量为零的常数。

单位元

Zero::zero() 和 One::one() 是任意环(包括 Dual[T])中的常数:

fn[T : @ad_core.Ring] power(x : T, n : Int) -> T {
  let mut acc : T = @ad_core.One::one()
  for _ in 0..<n {
    acc = acc * x
  }
  acc
}

fn main {
  let y = power(@ad_core.Dual::variable(2.0), 10)
  println("2^10 = \{y.value()}, d/dx x^10 at 2 = \{y.tangent()}")
}
2^10 = 1024, d/dx x^10 at 2 = 5120

基于半环的泛型代码

不需要减法的代码可以只要求 Semiring,这样它也能接受无符号整数:

fn[T : @ad_core.Semiring] sum_of_squares(xs : Array[T]) -> T {
  let mut acc : T = @ad_core.Zero::zero()
  for x in xs {
    acc = acc + x * x
  }
  acc
}

fn main {
  println(sum_of_squares([1U, 2U, 3U]))
  let d = sum_of_squares([@ad_core.Dual::variable(3.0), @ad_core.Dual::constant(4.0)])
  println("value \{d.value()}, derivative \{d.tangent()}")
}
14
value 25, derivative 6

深入学习

  • 同样的泛型函数也可以传给 @autodiff.diff,见 forward 教程。
  • sqrt、exp、sin 等函数需要解析 trait;请导入 elementary 或根包。
  • 你自己的数值类型只需实现 luna-generic 的 trait 即可加入;之后它既能直接使用,也能作为 Dual[T] 中的 T。

常见陷阱

  • 没有除法。 core 不提供 Field;请在具体类型上使用 Div 运算符,或使用 checked 门面包。
  • one() 不是变量。 Dual[T] 上的 One::one() 切向分量为零;请用 Dual::variable 设定变量的种子。
  • 无符号类型止步于 Semiring。 UInt 没有 Ring 实例,因此以 Ring 为约束的函数不接受它。

后续阅读

  • core API 列出了重新导出的 trait 及其 Dual[T] 实例。
  • dual 设计 证明了 T[ε]T[\varepsilon] 满足环的定律。
  • luna-generic 记录了 trait 层次结构。