0

我正在尝试使用ad包实现 Newton–Raphson 寻根算法,但我无法正确匹配函数类型。我知道有一个类似问题的正确答案,这是由广告的创建者自己回答的,但自 1.0.6 版(当前版本为 4.3.4)以来,该软件包发生了很大变化。

当我迭代它时,第一个最小示例编译并工作:

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)

但是,如果我尝试抽象函数,如下所示:

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 返回以下错误:

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)

如果我使用Numeric.AD.Rank1.Forward而不是Numeric.AD,编译器会说它不能匹配awith Forward a,而不是awith AD s (Forward a)。我还尝试了几种创建双数的方法,x将其传递给f,例如snd . unbundle . f $ bundle x 1,但它只有在我g' x使用它创建一个新数字时才有效,就像在第一种情况下一样。在内部使用它newton也不起作用。

Numeric.AD, diff :: Num a => (forall s. AD s (Forward a) -> AD s (Forward a)) -> a -> a. 在 中Numeric.AD.Rank1.Forward,它是diff :: Num a => (Forward a -> Forward a) -> a -> a。那么为什么他们a -> a在第一种情况下接受类型函数,而在第二种情况下不接受呢?除了使用多态函数之外,在创建要使用的函数时我是否应该特别小心Numeric.AD?最后,我应该如何更改我的代码以使其工作?我知道这个包已经有一个 find root 的功能,但我还不想使用(因为我还在学习 Haskell),看着文档试图解决这个问题,感觉就像在绕圈子。

4

1 回答 1

2

观察你的功能:

newton :: Fractional a => (a -> a) -> a -> a
newton f x = x - (f x) / (diff f x)

在两个地方使用函数参数f。首先,子表达式与类型一起f x使用f

f :: Fractional a => a -> a

其次,由于diff子表达式的diff f x使用f与类型一起使用:

f :: forall s a. Fractional a => AD s (Forward a) -> AD s (Forward a)

您收到的错误消息是类型系统观察到这些是无法统一的不同类型。

解决方案是显式量化函数参数,newton 使其适用于满足适当数字类型类约束的所有类型。这需要RankNTypes语言扩展:

{-# LANGUAGE RankNTypes #-}

newton
  :: Fractional a
  => (forall b. Fractional b => b -> b)
  -> a
  -> a
newton f x = x - (f x) / (diff f x)
于 2017-08-04T07:49:07.500 回答