I'm trying to implement Newton–Raphson root-finding algorithm using the ad package, but I can't properly match function types. I know there's a proper answer to a similar question, which was answered by the creator of ad himself, but the package have changed greatly since version 1.0.6 (current version is 4.3.4).
This first minimum example compiles and works when I iterate over it:
import Numeric.AD
import Numeric.AD.Internal.Forward
g :: Fractional a => a -> a
g x = - x + 2
g' :: Fractional a => a -> a
g' x = diff g x
newtonG :: Fractional a => a -> a
newtonG x = x - (g x) / (g' x)
But if I try to abstract the function, like this:
import Numeric.AD
import Numeric.AD.Internal.Forward
g :: Fractional a => a -> a
g x = - x + 2
newton :: Fractional a => (a -> a) -> a -> a
newton f x = x - (f x) / (diff f x)
GHC returns the following error:
fsolve.hs:8:32: error:
* Couldn't match type `a' with `AD s (Forward a)'
`a' is a rigid type variable bound by
the type signature for:
newton :: forall a. Fractional a => (a -> a) -> a -> a
at fsolve.hs:7:11
Expected type: AD s (Forward a) -> AD s (Forward a)
Actual type: a -> a
* In the first argument of `diff', namely `f'
In the second argument of `(/)', namely `(diff f x)'
In the second argument of `(-)', namely `(f x) / (diff f x)'
* Relevant bindings include
x :: a (bound at fsolve.hs:8:10)
f :: a -> a (bound at fsolve.hs:8:8)
newton :: (a -> a) -> a -> a (bound at fsolve.hs:8:1)
If I use Numeric.AD.Rank1.Forward
instead of Numeric.AD
, the compiler says it cannot match a
with Forward a
, instead of a
with AD s (Forward a)
. I also tried several ways of creating a dual number from x
to pass it to f
, e.g. snd . unbundle . f $ bundle x 1
, but it only works if I create a new g' x
using it, like in first case. Using this within newton
doesn't work either.
In Numeric.AD
, diff :: Num a => (forall s. AD s (Forward a) -> AD s (Forward a)) -> a -> a
. And in Numeric.AD.Rank1.Forward
, it's diff :: Num a => (Forward a -> Forward a) -> a -> a
. So why do they accept a function of type a -> a
in the first case, but not in the second? Besides using polymorphic functions, should I take any special care when creating functions to use with Numeric.AD
? Lastly, how should I change my code to make it work? I know the package already has a function to find roots, but I don't want to use yet (since I'm still learning Haskell), and looking at the documentation trying to sort this out, felt like running in circles.