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 は接成分 0 の定数を生成します。

単位元

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 などには解析的トレイトが必要です。elementary またはルートパッケージをインポートしてください。
  • 独自の数値型は luna-generic のトレイトを実装することで参加できます。そうすれば直接でも、Dual[T] の T としても動作します。

よくある落とし穴

  • 除算はありません。 core には Field がありません。具体的な型では Div 演算子を使うか、checked ファサードを使ってください。
  • one() は変数ではありません。 Dual[T] 上の One::one() の接成分は 0 です。変数は Dual::variable でシードしてください。
  • 符号なし型は Semiring までです。 UInt には Ring インスタンスがないので、Ring で境界付けられた関数はこれを受け付けません。

次のステップ

  • core API には再エクスポートされるトレイトとその Dual[T] インスタンスが記載されています。
  • dual の設計 では T[ε]T[\varepsilon] の環の法則を証明しています。
  • luna-generic ではトレイト階層を文書化しています。