dual API
dual パッケージは Dual[T]、すなわち を満たす双対数 を、その算術、Luna Flow のトレイトインスタンス、初等関数の微分規則とともに所有しています。このリポジトリの他のすべてのパッケージは、この型を再エクスポートするか利用します。規則の背後にある数学は dual の設計 にあります。
ソース:src/dual/dual.mbt と src/dual/extends.mbt。
インポート
import {
"Luna-Flow/autodiff/dual",
}
ほとんどのプログラムは代わりにルートパッケージ Luna-Flow/autodiff をインポートします。これは Dual を @autodiff.Dual として再エクスポートしています(autodiff API を参照)。このページの例はルートのエイリアスを使います。
型
Dual
Dual[T] はプライマル値と 1 階の接成分を格納します。
pub struct Dual[T] {
value : T
tangent : T
} derive(Eq, @debug.Debug)
組 (value, tangent) は、 = value、 = tangent として を表します。計算を Dual::variable(x) から始めると、すべての中間結果の接成分は、その中間値の x に関する導関数になります。フィールドはパッケージ外からは読み取り専用です。値は以下のコンストラクタで構築してください。
derive(Eq) は両成分を比較するため、値が同じでも接成分が異なる 2 つの双対数は等しくありません。derive(Debug) はレコード形式 { value: …, tangent: … } を出力し、debug_inspect と assert_eq がこれを使います。
Dual[T] は意図的に Compare、Field、MulGroup、Inverse、Show のインスタンスを持ちません。設計上の判断 を参照してください。
構築とアクセス
Dual::new
明示的な値と接成分から を構築します。
pub fn[T] Dual::new(T, T) -> Dual[T]
以外の方向をシードするときに使います。たとえば方向微分 では、各入力座標に接成分 を与えます。
Dual::constant
接成分 0 で値を埋め込みます:。
pub fn[T : @luna-generic.Zero] Dual::constant(T) -> Dual[T]
定数は微分変数に依存しないので、その導関数は です。微分される計算に入るすべてのリテラルやキャプチャされた値に使ってください。
Dual::variable
接成分 1 で微分変数を埋め込みます:。
pub fn[T : @luna-generic.One] Dual::variable(T) -> Dual[T]
Dual::value
プライマル成分 を返します。
pub fn[T] Dual::value(Dual[T]) -> T
Dual::tangent
接成分 を返します。
pub fn[T] Dual::tangent(Dual[T]) -> T
test "construct and read dual numbers" {
let x = @autodiff.Dual::new(2.0, 3.0)
assert_eq(x.value(), 2.0)
assert_eq(x.tangent(), 3.0)
let c : @autodiff.Dual[Double] = @autodiff.Dual::constant(5.0)
assert_eq(c.tangent(), 0.0)
let v : @autodiff.Dual[Double] = @autodiff.Dual::variable(5.0)
assert_eq(v.tangent(), 1.0)
}
Dual::zero
加法単位元 を返します。
pub fn[T : @luna-generic.Zero] Dual::zero() -> Dual[T]
これは Zero インスタンスの昇格されたメソッドです。
Dual::one
乗法単位元 を返します。
pub fn[T : @luna-generic.One + @luna-generic.Zero] Dual::one() -> Dual[T]
one は定数なので、その接成分は 0 です。Dual::variable(1) ではありません。これは One インスタンスの昇格されたメソッドです。
算術
4 つの演算子は和・差・積・商の規則を実装します。演算子としても、昇格されたメソッドとしても利用できます。
| 項目 | 演算子 | 規則 | T の境界 |
|---|---|---|---|
Dual::add | x + y | Add | |
Dual::sub | x - y | Sub | |
Dual::neg | -x | Neg | |
Dual::mul | x * y | Add + Mul | |
Dual::div | x / y | Div + Sub + Mul |
Dual::add
成分ごとに加算します。
pub fn[T : Add] Dual::add(Dual[T], Dual[T]) -> Dual[T]
pub impl[T : Add] Add for Dual[T]
Dual::sub
成分ごとに減算します。
pub fn[T : Sub] Dual::sub(Dual[T], Dual[T]) -> Dual[T]
pub impl[T : Sub] Sub for Dual[T]
Dual::neg
両成分の符号を反転します。
pub fn[T : Neg] Dual::neg(Dual[T]) -> Dual[T]
pub impl[T : Neg] Neg for Dual[T]
Dual::mul
積の規則で乗算します。
pub fn[T : Add + Mul] Dual::mul(Dual[T], Dual[T]) -> Dual[T]
pub impl[T : Add + Mul] Mul for Dual[T]
接成分は self.value * other.tangent + other.value * self.tangent、すなわち として計算されます。数値型 T の乗算はいずれも可換なので、これは積の規則 に等しくなります。
Dual::div
定義域チェックなしで、商の規則により除算します。
pub fn[T : Div + Sub + Mul] Dual::div(Dual[T], Dual[T]) -> Dual[T]
pub impl[T : Div + Sub + Mul] Div for Dual[T]
接成分は (self.tangent * other.value - self.value * other.tangent) / (other.value * other.value) です。除数の値が 0 の場合の結果は T の除算の結果に従います(Double では無限大または NaN)。代わりにエラーを得たい場合は Dual::div_checked を使ってください。
test "dual arithmetic" {
let x = @autodiff.Dual::new(2.0, 3.0)
let y = @autodiff.Dual::new(5.0, 7.0)
assert_eq(x + y, @autodiff.Dual::new(7.0, 10.0))
assert_eq(y - x, @autodiff.Dual::new(3.0, 4.0))
assert_eq(-x, @autodiff.Dual::new(-2.0, -3.0))
assert_eq(x * y, @autodiff.Dual::new(10.0, 29.0)) // 2*7 + 5*3
assert_eq(y / x, @autodiff.Dual::new(2.5, -0.25)) // (7*2 - 5*3) / 4
}
Dual::equal
両成分が等しいかを判定します。
pub fn[T : Eq] Dual::equal(Dual[T], Dual[T]) -> Bool
これは導出された Eq インスタンスの昇格されたメソッドです。== を使うことを推奨します。
チェック付き演算
チェック付きの形式は、無限大や NaN を生成する代わりに Luna-Flow/arithmetic の Result[Dual[T], ArithmeticError] を返します。使用する型は checked API が再エクスポートしています。
Dual::div_checked
商の規則で除算し、不正な除算をエラーとして報告します。
pub fn[T : @arithmetic.DivChecked + Sub + Mul] Dual::div_checked(Dual[T], Dual[T], @arithmetic.ArithmeticContext) -> Result[Dual[T], @arithmetic.ArithmeticError]
pub impl[T : @arithmetic.DivChecked + Sub + Mul] @arithmetic.DivChecked for Dual[T]
まず T の div_checked で値 を計算し、次に 2 回目の div_checked 呼び出しで接成分 を計算します。最初のエラーはそのまま返されます。Double でのエラーは次のとおりです:
| 条件 | エラー |
|---|---|
| かつ | is_division_by_zero() |
| かつ | is_domain_error()(ゼロをゼロで割る) |
| と がともに無限大 | is_domain_error() |
| だが がアンダーフローして になる | 接成分の除算が失敗します。下記の落とし穴を参照してください |
コンテキスト引数はそのまま T に渡されます。arithmetic の Double と Float のインスタンスはこれを無視します。
test "checked dual division" {
let ctx = @autodiff.ArithmeticContext::new(53)
let x = @autodiff.Dual::new(6.0, 2.0)
let y = @autodiff.Dual::new(3.0, 1.0)
match x.div_checked(y, ctx) {
Ok(q) => assert_eq(q, @autodiff.Dual::new(2.0, 0.0))
Err(_) => fail("unexpected error")
}
match x.div_checked(@autodiff.Dual::new(0.0, 1.0), ctx) {
Ok(_) => fail("expected an error")
Err(e) => assert_true(e.is_division_by_zero())
}
}
Dual::sqrt_checked
規則 で平方根を取り、定義域エラーを報告します。
pub fn[T : @arithmetic.SqrtChecked + @arithmetic.DivChecked + @luna-generic.IntegralHomomorphism + Mul] Dual::sqrt_checked(Dual[T], @arithmetic.ArithmeticContext) -> Result[Dual[T], @arithmetic.ArithmeticError]
pub impl[T : @arithmetic.SqrtChecked + @arithmetic.DivChecked + @luna-generic.IntegralHomomorphism + Mul] @arithmetic.SqrtChecked for Dual[T]
値は T の sqrt_checked(a)、接成分は div_checked(b, 2 * root) です。ここで 2 は IntegralHomomorphism::from_integral(2) から得られます。Double では:
| 条件 | エラー |
|---|---|
平方根からの is_domain_error() | |
| , | 接成分からの is_division_by_zero() |
| , | 接成分からの is_domain_error()(ゼロをゼロで割る) |
したがって sqrt_checked は、定数入力であっても で失敗します。 は で微分可能ではなく、チェック付きの形式は接成分が 0 の場合を特別扱いしません。
test "checked dual square root" {
let ctx = @autodiff.ArithmeticContext::new(53)
match @autodiff.Dual::new(9.0, 6.0).sqrt_checked(ctx) {
Ok(r) => assert_eq(r, @autodiff.Dual::new(3.0, 1.0))
Err(_) => fail("unexpected error")
}
match @autodiff.Dual::new(-1.0, 1.0).sqrt_checked(ctx) {
Ok(_) => fail("expected an error")
Err(e) => assert_true(e.is_domain_error())
}
}
初等関数
各初等関数は固有メソッドであると同時に、対応する arithmetic トレイトインスタンスのメソッドでもあります。そのため Sqrt、Exponential、Logarithmic、Trigonometric に対して書かれたジェネリックなコードは、変更なしで Dual[T] を通して微分できます。いずれも、下記の導関数を用いて規則 を適用します。いずれも定義域をチェックしません。定義域の外では結果は T が返すものになります(Double では NaN または無限大)。
| 項目 | 値 | 実際に計算される接成分 | 導関数 |
|---|---|---|---|
Dual::sqrt | b / (2 * sqrt(a)) | ||
Dual::exp | b * exp(a) | ||
Dual::exp2 | b * exp2(a) * ln(2) | ||
Dual::ln | b / a | ||
Dual::log2 | b / (a * ln(2)) | ||
Dual::log10 | b / (a * ln(10)) | ||
Dual::sin | b * cos(a) | ||
Dual::cos | -(b * sin(a)) | ||
Dual::tan | b / (cos(a) * cos(a)) |
定数 と は IntegralHomomorphism::from_integral から得られます。
Dual::sqrt
接成分 を持つ平方根です。
pub fn[T : @arithmetic.Sqrt + @luna-generic.IntegralHomomorphism + Mul + Div] Dual::sqrt(Dual[T]) -> Dual[T]
pub impl[T : @arithmetic.Sqrt + @luna-generic.IntegralHomomorphism + Mul + Div] @arithmetic.Sqrt for Dual[T]
Dual::exp
接成分 を持つ指数関数です。値は 1 回だけ計算して再利用します。
pub fn[T : @arithmetic.Exponential + Mul] Dual::exp(Dual[T]) -> Dual[T]
Dual::exp2
接成分 を持つ底 2 の指数関数です。
pub fn[T : @arithmetic.Exponential + @arithmetic.Logarithmic + @luna-generic.IntegralHomomorphism + Mul] Dual::exp2(Dual[T]) -> Dual[T]
pub impl[T : @arithmetic.Exponential + @arithmetic.Logarithmic + @luna-generic.IntegralHomomorphism + Mul] @arithmetic.Exponential for Dual[T]
Exponential インスタンスは exp と exp2 の両方を提供するため、exp2 の境界が必要です。
Dual::ln
接成分 を持つ自然対数です。
pub fn[T : @arithmetic.Logarithmic + Div] Dual::ln(Dual[T]) -> Dual[T]
Dual::log2
接成分 を持つ底 2 の対数です。
pub fn[T : @arithmetic.Logarithmic + @luna-generic.IntegralHomomorphism + Mul + Div] Dual::log2(Dual[T]) -> Dual[T]
Dual::log10
接成分 を持つ底 10 の対数です。
pub fn[T : @arithmetic.Logarithmic + @luna-generic.IntegralHomomorphism + Mul + Div] Dual::log10(Dual[T]) -> Dual[T]
pub impl[T : @arithmetic.Logarithmic + @luna-generic.IntegralHomomorphism + Mul + Div] @arithmetic.Logarithmic for Dual[T]
Logarithmic インスタンスは ln、log2、log10 を提供します。
Dual::sin
接成分 を持つ正弦関数です。
pub fn[T : @arithmetic.Trigonometric + Mul] Dual::sin(Dual[T]) -> Dual[T]
Dual::cos
接成分 を持つ余弦関数です。
pub fn[T : @arithmetic.Trigonometric + Mul + Neg] Dual::cos(Dual[T]) -> Dual[T]
Dual::tan
導関数 を持つ正接関数です。 として計算します。
pub fn[T : @arithmetic.Trigonometric + Mul + Div] Dual::tan(Dual[T]) -> Dual[T]
pub impl[T : @arithmetic.Trigonometric + Mul + Neg + Div] @arithmetic.Trigonometric for Dual[T]
Trigonometric インスタンスは sin、cos、tan を提供します。
test "elementary derivative rules" {
let x = @autodiff.Dual::variable(0.5)
assert_eq(x.sin().tangent(), @math.cos(0.5))
assert_eq(x.exp().tangent(), @math.exp(0.5))
assert_eq(x.ln().tangent(), 2.0)
assert_eq(@autodiff.Dual::variable(4.0).sqrt().tangent(), 0.25)
}
トレイトインスタンス
Dual[T] は、代数 が満たす Luna Flow の構造トレイトを、それぞれ T に対する対応する境界のもとで実装します。法則は dual の設計 で導出しています。
| インスタンス | T の境界 | 意味 |
|---|---|---|
Zero | Zero | zero() は |
One | One + Zero | one() は |
AddMonoid | AddMonoid | 成分ごとの加算 |
AddGroup | AddGroup | 成分ごとの符号反転 |
MulMonoid | Semiring | 積の規則による乗算 |
Semiring | Semiring | T が半環なら も半環 |
Ring | Ring | T が環なら も環 |
NatHomomorphism | NatHomomorphism + Zero | from_nat(n) は Dual::constant(from_nat(n)) |
IntegralHomomorphism | IntegralHomomorphism + Zero | from_integral(n) は Dual::constant(from_integral(n)) |
@arithmetic.Constants | Constants + Zero | pi()、tau()、e() は定数 |
@arithmetic.DivChecked | DivChecked + Sub + Mul | Dual::div_checked を参照 |
@arithmetic.SqrtChecked | Dual::sqrt_checked を参照 | Dual::sqrt_checked を参照 |
@arithmetic.Sqrt, Exponential, Logarithmic, Trigonometric | 各メソッドを参照 | 初等関数 を参照 |
整数と自然数の埋め込み、および定数は、微分変数に依存しないため接成分 0 を生成します。
fn[T : @autodiff.Ring + @autodiff.IntegralHomomorphism] three_x_squared(x : T) -> T {
let three : T = @autodiff.IntegralHomomorphism::from_integral(3)
three * x * x
}
test "generic code differentiates through the trait instances" {
let y = three_x_squared(@autodiff.Dual::variable(2.0))
assert_eq(y, @autodiff.Dual::new(12.0, 12.0))
}
非推奨
Dual は、かつて MoonBit がトレイトインスタンスから暗黙的に生成していたメソッド形式をいくつか残しています。これらはインターフェースファイルから隠されており、このパッケージの外で使うと警告が出ます。
| 非推奨のメソッド | 代替 |
|---|---|
x.not_equal(y) | x != y |
x.to_repr() | Repr(x) または @debug.Debug::to_repr(x) |
Dual::from_nat(n) | Luna-Flow/luna-generic の NatHomomorphism::from_nat(n) |
Dual::from_integral(n) | @autodiff.IntegralHomomorphism::from_integral(n) |
Dual::pi(), Dual::e(), Dual::tau() | @autodiff.Constants::pi(), e(), tau() |