|
22 | 22 |
|
23 | 23 | ;; ## Single and Multivariable Calculus
|
24 | 24 | ;;
|
25 |
| -;; These functions put together the pieces laid out |
26 |
| -;; in [[emmy.dual]] and declare an interface for taking |
27 |
| -;; derivatives. |
| 25 | +;; These functions put together the pieces laid out in [[emmy.dual]] and declare |
| 26 | +;; an interface for taking derivatives. |
28 | 27 |
|
29 | 28 | ;; The result of applying the derivative `(D f)` of a multivariable function `f`
|
30 | 29 | ;; to a sequence of `args` is a structure of the same shape as `args` with all
|
|
37 | 36 | ;; To generate the result:
|
38 | 37 | ;;
|
39 | 38 | ;; - For a single non-structural argument, return `(d/derivative f)`
|
40 |
| -;; - else, bundle up all arguments into a single [[s/Structure]] instance `xs` |
| 39 | +;; - else, bundle up all arguments into a single [[emmy.structure/Structure]] |
| 40 | +;; instance `xs` |
41 | 41 | ;; - Generate `xs'` by replacing each entry in `xs` with `((d/derivative f')
|
42 | 42 | ;; entry)`, where `f'` is a function of ONLY that entry that
|
43 | 43 | ;; calls `(f (assoc-in xs path entry))`. In other words, replace each entry
|
|
49 | 49 | ;; above.
|
50 | 50 | ;;
|
51 | 51 | ;; [[jacobian]] handles this main logic. [[jacobian]] can only take a structural
|
52 |
| -;; input. [[euclidean]] and [[multivariate]] below widen handle, respectively, |
| 52 | +;; input. [[euclidean]] and [[multivariate]] below handle, respectively, |
53 | 53 | ;; optionally-structural and multivariable arguments.
|
54 | 54 |
|
55 | 55 | (defn- deep-partial
|
|
109 | 109 | (u/illegal (str "Bad selectors " selectors " for structure " input))))))
|
110 | 110 |
|
111 | 111 | (defn- euclidean
|
112 |
| - "Slightly more general version of [[jacobian]] that can handle a single |
113 |
| - non-structural input; dispatches to either [[jacobian]] or [[derivative]] |
114 |
| - depending on the input type. |
| 112 | + "Slightly more general version of [[jacobian]] that can handle a single input; |
| 113 | + dispatches to either [[jacobian]] or [[derivative]] depending on whether or |
| 114 | + not the input is structural. |
115 | 115 |
|
116 | 116 | If you pass non-empty `selectors`, the returned function will throw if it
|
117 |
| - receives a non-structural, non-numerical argument." |
| 117 | + receives a non-structural, non-scalar argument." |
118 | 118 | ([f] (euclidean f []))
|
119 | 119 | ([f selectors]
|
120 | 120 | (let [selectors (vec selectors)]
|
|
143 | 143 | (str "Selectors " selectors
|
144 | 144 | " not allowed for non-structural input " input)))))))
|
145 | 145 |
|
| 146 | +(defn- multi |
| 147 | + "Given |
| 148 | +
|
| 149 | + - some higher-order function `op` that transforms a function of a single |
| 150 | + variable into another function of a single variable |
| 151 | + - function `f` capable of taking multiple arguments |
| 152 | +
|
| 153 | + returns a new function that acts like `(op f)` but can take multiple |
| 154 | + arguments. |
| 155 | +
|
| 156 | + When passed multiple arguments, the returned functon packages them into a |
| 157 | + single `[[emmy.structure/up]]` instance. Any [[emmy.matrix/Matrix]] present in |
| 158 | + the argument list will be converted into a `down` of `up`s (a row of columns)." |
| 159 | + [op f] |
| 160 | + (-> (fn |
| 161 | + ([] 0) |
| 162 | + ([x] ((op f) x)) |
| 163 | + ([x & more] |
| 164 | + ((multi op (fn [xs] (apply f xs))) |
| 165 | + (matrix/seq-> (cons x more))))) |
| 166 | + (f/with-arity (f/arity f) {:from ::multi}))) |
| 167 | + |
146 | 168 | (defn- multivariate
|
147 | 169 | "Slightly wider version of [[euclidean]]. Accepts:
|
148 | 170 |
|
|
152 | 174 |
|
153 | 175 | And returns a new function that computes either the
|
154 | 176 | full [Jacobian](https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant)
|
155 |
| - or the entry at `selectors`. |
| 177 | + or the entry at `selectors` using [forward-mode automatic |
| 178 | + differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation#Forward_accumulation). |
156 | 179 |
|
157 | 180 | Any multivariable function will have its argument vector coerced into an `up`
|
158 |
| - structure. Any [[matrix/Matrix]] in a multiple-arg function call will be |
| 181 | + structure. Any [[emmy.matrix/Matrix]] in a multiple-arg function call will be |
159 | 182 | converted into a `down` of `up`s (a row of columns).
|
160 | 183 |
|
161 |
| - Single-argument functions don't transform their arguments." |
| 184 | + Arguments to single-variable functions are not transformed." |
162 | 185 | ([f] (multivariate f []))
|
163 | 186 | ([f selectors]
|
164 |
| - (let [d #(euclidean % selectors) |
165 |
| - df (d f) |
166 |
| - df* (d (fn [args] (apply f args)))] |
167 |
| - (-> (fn |
168 |
| - ([] 0) |
169 |
| - ([x] (df x)) |
170 |
| - ([x & more] |
171 |
| - (df* (matrix/seq-> (cons x more))))) |
172 |
| - (f/with-arity (f/arity f) {:from ::multivariate}))))) |
| 187 | + (let [d #(euclidean % selectors)] |
| 188 | + (multi d f)))) |
| 189 | + |
| 190 | +(defn gradient |
| 191 | + "Accepts: |
| 192 | +
|
| 193 | + - some function `f` of potentially many arguments |
| 194 | + - optionally, a sequence of selectors meant to index into the structural |
| 195 | + argument, or argument vector, of `f` |
| 196 | +
|
| 197 | + And returns a new function that computes either the |
| 198 | + full [Jacobian](https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant) |
| 199 | + or the entry at `selectors` using [reverse-mode automatic |
| 200 | + differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation#Reverse_accumulation). |
| 201 | +
|
| 202 | + Any multivariable function will have its argument vector coerced into an `up` |
| 203 | + structure. Any [[emmy.matrix/Matrix]] in a multiple-arg function call will be |
| 204 | + converted into a `down` of `up`s (a row of columns). |
| 205 | +
|
| 206 | + Arguments to single-variable functions are not transformed." |
| 207 | + ([f] (gradient f [])) |
| 208 | + ([f selectors] |
| 209 | + (multi #(tape/gradient % selectors) f))) |
173 | 210 |
|
174 | 211 | ;; ## Generic [[g/partial-derivative]] Installation
|
175 | 212 | ;;
|
|
192 | 229 | ;; passed to the structure of functions, instead of separately for every entry
|
193 | 230 | ;; in the structure.
|
194 | 231 | ;;
|
| 232 | +;; A dynamic variable controls whether or not this process uses forward-mode or |
| 233 | +;; reverse-mode AD. |
| 234 | +;; |
195 | 235 | ;; TODO: I think this is going to cause problems for, say, a Structure of
|
196 | 236 | ;; PowerSeries, where there is actually a cheap `g/partial-derivative`
|
197 | 237 | ;; implementation for the components. I vote to back out this `::s/structure`
|
198 | 238 | ;; installation.
|
199 | 239 |
|
| 240 | +(def ^:dynamic *mode* d/FORWARD-MODE) |
| 241 | + |
200 | 242 | (doseq [t [::v/function ::s/structure]]
|
201 | 243 | (defmethod g/partial-derivative [t v/seqtype] [f selectors]
|
202 |
| - (multivariate f selectors)) |
| 244 | + (if (= *mode* d/FORWARD-MODE) |
| 245 | + (multivariate f selectors) |
| 246 | + (gradient f selectors))) |
203 | 247 |
|
204 | 248 | (defmethod g/partial-derivative [t nil] [f _]
|
205 |
| - (multivariate f []))) |
| 249 | + (if (= *mode* d/FORWARD-MODE) |
| 250 | + (multivariate f []) |
| 251 | + (gradient f [])))) |
206 | 252 |
|
207 | 253 | ;; ## Operators
|
208 | 254 | ;;
|
209 |
| -;; This section exposes various differential operators as [[o/Operator]] |
210 |
| -;; instances. |
| 255 | +;; This section exposes various differential operators |
| 256 | +;; as [[emmy.operator/Operator]] instances. |
| 257 | + |
| 258 | +(def ^{:arglists '([f])} |
| 259 | + D-forward |
| 260 | + "Forward-mode derivative operator. Takes some function `f` and returns a |
| 261 | + function whose value at some point can multiply an increment in the arguments |
| 262 | + to produce the best linear estimate of the increment in the function value. |
211 | 263 |
|
212 |
| -(def D |
| 264 | + For univariate functions, [[D-forward]] computes a derivative. For vector-valued |
| 265 | + functions, [[D-forward]] computes |
| 266 | + the [Jacobian](https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant) |
| 267 | + of `f`." |
| 268 | + (o/make-operator |
| 269 | + (fn [x] |
| 270 | + (binding [*mode* d/FORWARD-MODE] |
| 271 | + (g/partial-derivative x []))) |
| 272 | + g/derivative-symbol)) |
| 273 | + |
| 274 | +(def ^{:arglists '([f])} |
| 275 | + D-reverse |
| 276 | + "Reverse-mode derivative operator. Takes some function `f` and returns a |
| 277 | + function whose value at some point can multiply an increment in the arguments |
| 278 | + to produce the best linear estimate of the increment in the function value. |
| 279 | +
|
| 280 | + For univariate functions, [[D-reverse]] computes a derivative. For vector-valued |
| 281 | + functions, [[D-reverse]] computes |
| 282 | + the [Jacobian](https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant) |
| 283 | + of `f`." |
| 284 | + (o/make-operator |
| 285 | + (fn [x] |
| 286 | + (binding [*mode* d/REVERSE-MODE] |
| 287 | + (g/partial-derivative x []))) |
| 288 | + g/derivative-symbol)) |
| 289 | + |
| 290 | +(def ^{:arglists '([f])} |
| 291 | + D |
213 | 292 | "Derivative operator. Takes some function `f` and returns a function whose value
|
214 | 293 | at some point can multiply an increment in the arguments to produce the best
|
215 | 294 | linear estimate of the increment in the function value.
|
|
222 | 301 | The related [[emmy.env/Grad]] returns a function that produces a structure of
|
223 | 302 | the opposite orientation as [[D]]. Both of these functions use forward-mode
|
224 | 303 | automatic differentiation."
|
225 |
| - (o/make-operator #(g/partial-derivative % []) |
226 |
| - g/derivative-symbol)) |
| 304 | + D-forward) |
227 | 305 |
|
228 | 306 | (defn D-as-matrix [F]
|
229 | 307 | (fn [s]
|
|
232 | 310 | ((D F) s)
|
233 | 311 | s)))
|
234 | 312 |
|
235 |
| -(defn partial |
| 313 | +(defn partial-forward |
| 314 | + "Returns an operator that, when applied to a function `f`, produces a function |
| 315 | + that uses forward-mode automatic differentiation to compute the partial |
| 316 | + derivative of `f` at the (zero-based) slot index provided via `selectors`." |
| 317 | + [& selectors] |
| 318 | + (o/make-operator |
| 319 | + (fn [x] |
| 320 | + (binding [*mode* d/FORWARD-MODE] |
| 321 | + (g/partial-derivative x selectors))) |
| 322 | + `(~'partial ~@selectors))) |
| 323 | + |
| 324 | +(defn partial-reverse |
236 | 325 | "Returns an operator that, when applied to a function `f`, produces a function
|
237 |
| - that computes the partial derivative of `f` at the (zero-based) slot index |
238 |
| - provided via `selectors`." |
| 326 | + that uses reverse-mode automatic differentiation to compute the partial |
| 327 | + derivative of `f` at the (zero-based) slot index provided via `selectors`." |
239 | 328 | [& selectors]
|
240 |
| - (o/make-operator #(g/partial-derivative % selectors) |
241 |
| - `(~'partial ~@selectors))) |
| 329 | + (o/make-operator |
| 330 | + (fn [x] |
| 331 | + (binding [*mode* d/REVERSE-MODE] |
| 332 | + (g/partial-derivative x selectors))) |
| 333 | + `(~'partial ~@selectors))) |
| 334 | + |
| 335 | +(def ^{:arglists '([& selectors])} |
| 336 | + partial |
| 337 | + "Returns an operator that, when applied to a function `f`, produces a function |
| 338 | + that uses forward-mode automatic differentiation to compute the partial |
| 339 | + derivative of `f` at the (zero-based) slot index provided via `selectors`." |
| 340 | + partial-forward) |
242 | 341 |
|
243 | 342 | ;; ## Derivative Utilities
|
244 | 343 | ;;
|
|
0 commit comments