13関数シンボル
名前が\(f\),引数が\(x,y\)と決まっているが,中身は未定義の関数\(f(x,y)\)は,紙の上では\(f\)と引数を省略して表記されることが多い. このシンボリックな関数の引数を省略する表記をプログラミングにも導入するための機能が関数シンボル(function symbol)である. 関数シンボル自体の動機はシンプルであるが,関数シンボルはEgison特有の言語機能であるため,一つの独立した章を使ってその実装方法も含め紹介する.
13.1関数シンボルの基本的な使い方
関数シンボルが特に力を発揮するのは,微分を扱う計算のときである. 以下ではx,yを引数にとる関数シンボルfを定義している. 関数シンボルはMathValue型をもつ数式として扱われる.
declare symbol x, y, z
def f : MathValue := function (x, y)
関数シンボルをshowで表示すると,引数の値が常に表示される.
show f -- "f x y"
以下のように,関数シンボルfをxやyについて微分できる. 偏微分の結果の関数は,引数の番号を|記号で付加した表示になる. f|1は「\(f\)の第1引数に関する偏微分」,f|1|2は「第1引数で偏微分した後さらに第2引数で偏微分した結果」を表す. “|”は,“_”や“~”と同様Egison組み込みの記号である. 以下に関数シンボルの微分を標準的なEgison出力とLaTeX形式の出力の両方の表示例を示す. “\(\partial\)/\(\partial\)”はEgisonで定義されているライブラリ関数である.
show (`$\partial$`/`$\partial$` f x) -- "f|1 x y"
show (`$\partial$`/`$\partial$` (`$\partial$`/`$\partial$` f x) y) -- "f|1|2 x y"
fはxとyについての関数であるので,以下のようにzで微分すると0になる.
show (`$\partial$`/`$\partial$` f z) -- "0"
物理の計算では,\(t,x,y,z\)の4つの引数をとる関数がよく登場する. もし関数シンボルのような仕組みがないと,式が長くなり,読み難くなるため,関数シンボルの仕組みはプログラムの読み書きしやすくする.
13.2引数への値の代入
関数シンボルには,通常の関数適用と同じ構文で引数を代入できる. f 0 1と書くと,fの第1引数に0,第2引数に1を代入した数式が得られる. 引数の個数が合わない場合はエラーになる.
show (f 0 1) -- "f 0 1"
show (f x 0) -- "f x 0"
引数を代入した結果もMathValue型の数式であるため,さらに微分できる. 引数に具体的な数値が入った場合,その引数に対する微分は0になる.
`$\partial$`/`$\partial$` (f 0 1) x -- 0
V.substitute関数を使っても引数を置き換えられる.
V.substitute [|x, y|] [|0, 0|] f
-- f 0 0
13.3合成関数の微分(連鎖律)
合成関数の微分にも対応していることが,Egisonの関数シンボルの重要な特徴である. function式の仮引数が変数名ではなく式をとるのは,合成関数の微分に対応するためである. さきほどと同様に\(g\)を,\(g(x,y)\)の省略であるとする. また,\(x=r \cos \theta, y= r \sin \theta\)という関係式もあるとする. このとき,\(g\)を\(r\)で微分すると,連鎖律により\(\frac{\partial g}{\partial x_1} \cos \theta + \frac{\partial g}{\partial x_2} \sin \theta\)という結果を返している.
declare symbol r, `$\theta$`
def x : MathValue := r * cos `$\theta$`
def y : MathValue := r * sin `$\theta$`
def g : MathValue := function (x, y)
show g
-- "g (('cos θ) * r) (('sin θ) * r)"
show (`$\partial$`/`$\partial$` g r)
-- "('sin θ) * (g|2 (('cos θ) * r) (('sin θ) * r))
-- + ('cos θ) * (g|1 (('cos θ) * r) (('sin θ) * r))"
偏微分の結果に含まれるg|1,g|2は,それぞれ\(\frac{\partial g}{\partial x_1}\),\(\frac{\partial g}{\partial x_2}\)を表す記号である(引数の番号を|記号で付加する). この表示には引数の値が常に含まれるため,どの点での偏微分係数であるかが明示される.
13.4関数シンボルを成分にもつテンソル
数学や物理では,テンソルの成分として関数がでてくることがある. たとえば,ベクトル場やテンソル場を表現するときにそのような場面がある. このような場面に対応するため,テンソルの成分として関数シンボルを使うことをEgisonは許している. 以下の例のようにテンソルの成分が関数シンボルであった場合,テンソルを束縛している変数名に成分の位置の添字を付加した名前をもつ関数シンボルが生成される.
declare symbol x, y, z
def g_i_j : Tensor MathValue := generateTensor
(\match as list integer with
| [$n, #n] -> function (x, y, z)
| _ -> 0)
[3, 3]
show (withSymbols [i, j] g_i_j)
-- "[| [| g_1_1 x y z, 0, 0 |], [| 0, g_2_2 x y z, 0 |], [| 0, 0, g_3_3 x y z |] |]"
show (withSymbols [i, j] (`$\partial$`/`$\partial$` g_i_j x))
-- "[| [| g_1_1|1 x y z, 0, 0 |], [| 0, g_2_2|1 x y z, 0 |], [| 0, 0, g_3_3|1 x y z |] |]"
13.5関数シンボルの内部表現とパターンマッチ
関数シンボルは,名前と引数の値のリストをもつ因子としてEgison内部では表現されている(10.1節で紹介した数式データの因子の一種である).
funcパターンコンストラクタを使って関数シンボルにパターンマッチできる. このパターンの第一引数は関数名に,第二引数は引数の値のリストにパターンマッチする.
declare symbol x, y
def f : MathValue := function (x, y)
match f as mathValue with
| func $name $args -> (name, args)
-- (f, [x, y])
ライブラリの微分の実装では,引数の数を利用して各引数に対する偏微分係数を整数インデックスで取り出している. 以下は,ライブラリ(lib/math/analysis/derivative.egi)の関数シンボルを微分する部分の抜粋である.
def chainPartialDiffBuiltin (v : MathValue) (dx : MathValue)
: MathValue :=
match v as mathValue with
| func _ $args ->
sum (map2 (\s r -> (userRefs v [s]) * partialDiff r dx)
(between 1 (length args))
args)
...
ここでbetween 1 (length args)は[1, 2, ..., n]という整数のリストを生成する. userRefs f [1]はfの名前シンボルに整数インデックス1を付加した数式f|1 x yを返す.
13.6関数シンボルの実装方法
関数シンボルの実装で注意すべきことは,関数シンボルは自身の名前を保持することである. そのためにEgisonインタプリタは現在定義しようとしている変数の名前を管理している. また,13.4節で解説したテンソルの成分に現れる関数シンボルをサポートするためには,現在テンソルのどの位置の成分を定義しているのか管理する必要がある. そのためにEgisonインタプリタは現在定義しようとしているテンソルの成分の位置も管理している. これらはEgisonインタプリタの実装では,評価器が引き回す環境の追加要素として管理されている.