Skip to content

Instantly share code, notes, and snippets.

@telekid
Last active March 28, 2020 23:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save telekid/e8fb8ef82a184c6eb7862c1f980d5bb6 to your computer and use it in GitHub Desktop.
Save telekid/e8fb8ef82a184c6eb7862c1f980d5bb6 to your computer and use it in GitHub Desktop.
(ns automatic-differentiation
"Perform simple automatic differentiation with dual numbers using the
technique described here:
https://blog.demofox.org/2014/12/30/dual-numbers-automatic-differentiation/"
(:require [clojure.walk :refer [postwalk postwalk-replace]]))
(defn dual-add [[ar ad] [br bd]]
[(+ ar br) (+ ad bd)])
(defn dual-subtract [[ar ad] [br bd]]
[(- ar br) (- ad bd)])
(defn dual-multiply [[ar ad] [br bd]]
[(* ar br) (+ (* ar bd) (* ad br))])
(defn dual-divide [[ar ad] [br bd]]
[(/ ar br) (/ (- (* ad br) (* ar bd)) (* br br))])
(def dual-fns
`{+ dual-add
- dual-subtract
* dual-multiply
/ dual-divide})
(defn constant [v] [v 0])
(defn variable [v] [v 1])
(defn dual-eq*
"Symbolically rewrite eq as a function of dual numbers. Only works for a single variable."
[eq]
(postwalk
(fn [v]
(let [dual-fn-sym (when (symbol? v) (get dual-fns (some-> v resolve symbol)))]
(cond dual-fn-sym dual-fn-sym
(number? v) (constant v)
(symbol? v) (variable (symbol (name v)))
:else v)))
eq))
(defmacro dual-eq
"Return fn, a dual version of equation 'form'. fn returns [real dual]. Only works for a single variable."
[args form]
(let [eq (dual-eq* form)]
`(fn ~args ~eq)))
;; Test it:
((dual-eq [x] (* x x)) 2)
;; [4 4]
((dual-eq [x] (* x x)) 3)
;; [9 6]
((dual-eq [x] (+ 1 (* x x))) 3)
;; [10 6]
@telekid
Copy link
Author

telekid commented Mar 28, 2020

I probably wouldn't use dual-eq in production code - the replacement of clojure.core functions is a bit too magic for my taste. I built that for fun.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment