2

I'm starting out learning Clojure, and was trying to implement some basic numerical derivative functions for practice. I'm trying to create a gradient function that accepts an n-variable function and the points at which to evaluate it. To do this in a "functional" style, I want to implement the gradient as a map of a 1-variable derivatives.

The 1-variable derivative function is simple:

(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))))

I'd like to design the gradient along these lines:

(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)))

So, gradient has variable arity (can accept any # of x's), and the order of the x's counts. The function univariate-in-i takes an index i = 0, 1, ... n-1 and returns a 1-variable function by partial-ing out all the variables except x_i. E.g., you'd get:

#(f x_0 x_1 ... x_i-1 % x_i+1 ... x_n)
                      ^
                     (x_i still variable)

map-ping this function over varity-index gets you a sequence of 1-variable functions in each x_i, and then map-ping derivative over these gets you a sequence of derivatives in each x_i which is the gradient we want.

My questions is: I'm not sure what a good way to implement univariate-in-i is. I essentially need to fill in values for x's in f except at some particular spot (i.e., place the % above), but programmatically.

I'm more interested in technique than solution (i.e., I know how to compute gradients, I'm trying to learn functional programming and idiomatic Clojure). Therefore, I'd like to stay true to the strategy of treating the gradient as a map of 1-d derivatives over partialed-out functions. But if there's a better "functional" approach to this, please let me know. I'd rather not resort to macros if possible.

Thanks in advance!

Update:

Using Ankur's answer below, the gradient function I get is:

(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)))

which does exactly what I'd hoped, and seems very concise and functional.

0

2 Answers 2

2

You can define univariate-in-i as shown below. (Assuming that all the other position values are defined in some var default which is a vector)

(fn [i] #(->>
           (assoc default i %)
           (apply f)))
Sign up to request clarification or add additional context in comments.

3 Comments

default here would just be the x rest argument above, right?
This works great: default is x, but wrapped in a vector It's a little to subtle for me to completely understand, but I'll think through it. I think this is the answer I'm looking for though. Thanks!
The trick is that vectors implement clojure.lang.Associative, which is what allows you to swap out the element at i for the unapplied argument :)
0

if you find this abit difficult to comprehend (in the context of how to implement gradient), another variant of multivariable gradient implementation using clojure: enter image description here then, given f and vector v of a1,....,aN, will differentiate while all the variables except xi are fixed:

(defn partial-diff [f v i]
  (let [h 10e-6
        w (update v i + h)]
    (/ (- (apply f w) (apply f v))
       h)))

(defn gradient [f v]
  (map #(partial-diff f v %) (range (count v))))

=>

(gradient (fn [x y] 
              (+ (* x x) (* x y y))) [3 3])
=> (15.000009999965867 18.000030000564493)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.