私はClojureの学習を始めており、練習のためにいくつかの基本的な数値微分関数を実装しようとしていました. gradient
n変数関数とそれを評価するポイントを受け入れる関数を作成しようとしています。map
これを「関数型」スタイルで行うには、勾配を1 変数導関数として実装したいと考えています。
1 変数微分関数は単純です。
(defn derivative
"Numerical derivative of a univariate function."
[f x]
(let [eps 10e-6] ; Fix epsilon, just for starters.
; Centered derivative is [f(x+e) - f(x-e)] / (2e)
(/ (- (f (+ x eps)) (f (- x eps))) (* 2 eps))))
これらの線に沿ってグラデーションを設計したいと思います:
(defn gradient
"Numerical gradient of a multivariate function."
[f & x]
(let [varity-index (range (count x))
univariate-in-i (fn [i] (_?_))] ; Creates a univariate fn
; of x_i (other x's fixed)
;; For each i = 0, ... n-1:
;; (1) Get univariate function of x_i
;; (2) Take derivative of that function
;; Gradient is sequence of those univariate derivatives.
(map derivative (map univariate-in-i varity-index) x)))
したがって、gradient
変数のアリティ (任意の数の x を受け入れることができます) と、x のカウントの順序があります。この関数univariate-in-i
はインデックスを取り、i = 0, 1, ... n-1
を除くすべての変数を部分的に取り出して、1 変数関数を返しますx_i
。たとえば、次のようになります。
#(f x_0 x_1 ... x_i-1 % x_i+1 ... x_n)
^
(x_i still variable)
map
この関数を -ping するとvarity-index
、各 の 1 変数関数のシーケンスが取得されますx_i
。次に、これらの関数をmap
-ping すると、必要な勾配であるderivative
それぞれの導関数のシーケンスが取得されます。x_i
私の質問は次のとおりです。実装する良い方法が何であるかわかりませんunivariate-in-i
。f
基本的に、特定の場所 (つまり、上記の場所) を除いて x の値を入力する必要があります%
が、プログラムで行います。
私は解決策よりも技術に興味があります (つまり、勾配の計算方法を知っています。関数型プログラミングと慣用的な Clojure を学ぼうとしています)。したがって、勾配を、部分的に取り出された関数に対する 1 次元導関数のマップとして扱うという戦略に忠実であり続けたいと思います。しかし、これに対するより良い「機能的」アプローチがあれば、私に知らせてください。できればマクロは使いたくない。
前もって感謝します!
アップデート:
以下の Ankur の回答を使用すると、取得する勾配関数は次のようになります。
(defn gradient
"Numerical gradient of a multivariate function."
[f & x]
(let [varity-index (range (count x))
x-vec (vec x)
univariate-in-i
(fn [i] #(->> (assoc x-vec i %) (apply f)))]
(map derivative (map univariate-in-i varity-index) x)))
これはまさに私が望んでいたことを行い、非常に簡潔で機能的です。