【问题标题】:How to properly match types when using Numeric.AD in Haskell?在 Haskell 中使用 Numeric.AD 时如何正确匹配类型?
【发布时间】:2017-08-04 05:28:20
【问题描述】:

我正在尝试使用 ad 包实现 Newton–Raphson 寻根算法,但我无法正确匹配函数类型。我知道有a proper answer to a similar question,这个是广告主自己回答的,但是从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,编译器会说它不能匹配aForward a,而不是aAD s (Forward a)。我还尝试了几种从x 创建双号码的方法,将其传递给f,例如snd . unbundle . f $ bundle x 1,但它只有在我使用它创建一个新的g' x 时才有效,就像在第一种情况下一样。在newton 中使用它也不起作用。

Numeric.ADdiff :: 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 一起使用的函数时,我是否应该特别注意?最后,我应该如何更改我的代码以使其工作?我知道包already has a function to find roots,但我还不想使用(因为我还在学习Haskell),并且查看文档试图解决这个问题,感觉就像在兜圈子。

【问题讨论】:

  • 我不记得这在我的脑海中是否有任何意义,我也无法验证它,我还看到您在帖子中简要讨论了它,但尝试添加(Num a => a -> a) 而不是 (a -> a) 作为牛顿的论据之一。编辑——我刚刚意识到它会推断 a 的类型已经是 Fractional aka Num,这个评论可能没有效果。我会继续关注这个,挑战接受了。
  • 听起来第一个示例在两种不同的类型中使用了g。为了能够使用函数参数做到这一点,您需要 rank-2 类型:newton :: Fractional a => (forall b. Fractional b => b -> b) -> a -> a

标签: haskell forall automatic-differentiation rank-n-types


【解决方案1】:

观察你的函数:

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)

【讨论】:

  • 如果我只启用RankNTypes,编译器是否能够推断出正确的签名?
  • @FyodorSoikin GHC 永远不会推断出更高级别的类型。我想我听说使用此类类型进行类型检查是一个无法确定的问题,因此用户必须提供一些帮助。 (不过,我没有这方面的参考资料)
  • 谢谢。那行得通。我想我的下一个主题是了解RankNTypes
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多