This is an interesting approach. Haskell is not a symbolic language, but you take advantage of the abstractness of type parameters in function definitions to thread your implementation of "D x" through, and pattern match on that. It's a neat design pattern. I bet it'd work in Julia too.
Yes, but Julia has both forward and backward differention implemented (backwards it's harder).
Automatic Differentiation in 38 lines of Haskell
11–20 of 60 posts
Re: Automatic Differentiation in 38 lines of Haskell
#12Re: Automatic Differentiation in 38 lines of Haskell
#13Re: Automatic Differentiation in 38 lines of Haskell
#14This is an interesting approach. Haskell is not a symbolic language, but you take advantage of the abstractness of type parameters in function definitions to thread your implementation of "D x" through, and pattern match on that. It's a neat design pattern. I bet it'd work in Julia too.
I'm only a beginner in Julia and not and AD expert, but I went through the exercise of porting this to python and found it very enlightening
Re: Automatic Differentiation in 38 lines of Haskell
#15I'm neither a math expert nor a haskell expert, but I happen to enjoy both. It's been a while since I've watched a SPJ lecture, and I'd forgotten how much I want him to explain everything.
Re: Automatic Differentiation in 38 lines of Haskell
#16How do you come up with code looking this good? I've been working in Haskell for 6 months professionally: currently - I wouldn't be able to come up with something like this in a day (or more).
can't speak to the OPs process though, maybe they shat it out on a whim :)
Re: Automatic Differentiation in 38 lines of Haskell
#17 {-# LANGUAGE ImportQualifiedPost #-}
module Module_1663406024_9206 where
import Numeric.AD qualified as Ad
import Data.Number.Symbolic qualified as Sym
-- >>> f x = x^2 + 3 * x
-- >>> Ad.diff f 1
-- >>> Ad.diff f (Sym.var "a")
-- 5
-- 3+a+a
-- >>> Ad.diff sin pi
-- >>> Ad.diff sin (Sym.var "a")
-- -1.0
-- cos a
The package authors did not need to coordinate to make this possible which is pretty wild.Saw it first here: https://twitter.com/GabriellaG439/status/647601518871359489 and https://www.reddit.com/r/haskell/comments/3r75hq/comment/cwm...
Re: Automatic Differentiation in 38 lines of Haskell
#18Curious: I don't see the `^` op defined, or is it translated inti `exp` im guessing?
`Num`, then, is a Haskell class (Java interface).
The `Num` class will have a bunch of what Java would call "default methods".
Now, the "instance" of `Num` defined here has only a few methods defined, but the other default methods of `Num` will use those. So if `Num` has a `^` defined in terms of ``, and you define an instance of `Num` that defines ``, then you get `^` for free if you don't implement it.
Re: Automatic Differentiation in 38 lines of Haskell
#19Being purely functional makes this quite easy, still a beauty to see
it's not just the functionalness. you couldn't do it this way in Lisp/Scheme (I think?) because of the lack of multiple dispatch. If you did (f 'x) for instance, you'd end up with things like (* 2 'x) which would blow up, since Lisp would try to compute the answer instead giving you '(* 2 x) back.
Re: Automatic Differentiation in 38 lines of Haskell
#20One of my favourite Haskell "one-liners" is combining the AD package with Number.Symbolic: {-# LANGUAGE ImportQualifiedPost #-} module Module_1663406024_9206 where import Numeric.AD qualified as Ad import Data.Number.Symbolic qualified as Sym -- >>> f x = x^2 + 3 * x -- >>> Ad.diff f 1 -- >>> Ad.diff f (Sym.var "a") -- 5 -- 3+a+a -- >>> Ad.diff sin pi -- >>> Ad.diff sin (Sym.var "a") -- -1.0 -- cos a The package auth…
It works because `f` is polymorphic. The type of its `x` argument is not constrained in `f`'s definition, so you can plug in any `x` of any type you want provided that `x`'s type implements the methods used in `f`'s definition. With the `Dual` scheme you get to use as `x` a "dual" of `y` (`f x`, for some `f`) and `y'`, and then you get an `f` applied to that `x` where the actual `f` is parameterized by the actual `x`'s type, and so the methods called by `f` are those that apply to `x`'s type. So instead of the traditional numeric addition and multiplication, you'd get the "dual" addition and multiplication, and then everything "chains" through and you end up with `diff f x` being the `y'` in the dual of `y` and `y'` (you don't care about the `y`, just the `y'` because you want the `diff` -- the differential or derivative).
It's brilliant.