【问题标题】:"No instance for .. ", Haskell'98 code on GHC7.8.3“没有 .. 的实例”,GHC7.8.3 上的 Haskell'98 代码
【发布时间】:2014-12-17 21:32:09
【问题描述】:

我正在试用linear regression with automatic differentiation 的附加代码。 它指定由两个浮点数组成的数据类型 [Dual][2],并声明它是 Num、Fractional 和 Floating 的实例。 与所有拟合/回归任务一样,有一个标量成本函数,由拟合参数 c 和 m 进行参数化,还有一个优化器通过梯度下降改进这两个参数的估计。

问题 我正在使用 GHC 7.8.3,作者明确提到这是 H98 代码(我在标题中提到它是因为这是我能想到的我的设置和作者的设置之间的唯一实质性区别,但是如果错误请纠正)。 为什么它会在成本函数的定义中窒息? 我的理解是:函数 idD 和 constD 将 Floats 映射到 Duals,g 是多态的(它可以对 Dual 输入执行代数运算,因为 Dual 继承自 Num、Fractional 和 Floating),并且 deriv 将 Duals 映射到 Doubles。 推断出 g 的类型签名(数据的 eta 减少成本函数)。我尝试省略它,并通过将 Floating 类型约束替换为 Fractional 来使其更通用。 此外,我尝试将 c 和 m 的数字类型与 (fromIntegral c :: Double) 内联,但无济于事。

特别是这段代码给出了这个错误:

No instance for (Integral Dual) arising from a use of ‘g’
In the first argument of ‘flip’, namely ‘g’
In the expression: flip g (constD c)
In the second argument of ‘($)’, namely ‘flip g (constD c) $ idD m’

有什么提示吗?我敢肯定这是一个非常菜鸟的问题,但我就是不明白。

完整代码如下:

{-# LANGUAGE NoMonomorphismRestriction #-}

module ADfw (Dual(..), f, idD, cost) where

data Dual = Dual Double Double deriving (Eq, Show)

constD :: Double -> Dual
constD x = Dual x 0

idD :: Double -> Dual
idD x = Dual x 1.0

instance Num Dual where
  fromInteger n             = constD $ fromInteger n
  (Dual x x') + (Dual y y') = Dual (x+y) (x' + y')
  (Dual x x') * (Dual y y') = Dual (x*y) (x*y' + y*x')
  negate (Dual x x')        = Dual (negate x) (negate x')
  signum _                  = undefined
  abs _                     = undefined

instance Fractional Dual where
  fromRational p = constD $ fromRational p
  recip (Dual x x') = Dual (1.0 / x) (- x' / (x*x))

instance Floating Dual where
   pi = constD pi
   exp   (Dual x x') = Dual (exp x)   (x' * exp x)
   log   (Dual x x') = Dual (log x)   (x' / x)
   sqrt  (Dual x x') = Dual (sqrt x)  (x' / (2 * sqrt x))
   sin   (Dual x x') = Dual (sin x)   (x' * cos x)
   cos   (Dual x x') = Dual (cos x)   (x' * (- sin x))
   sinh  (Dual x x') = Dual (sinh x)  (x' * cosh x)
   cosh  (Dual x x') = Dual (cosh x)  (x' * sinh x)
   asin  (Dual x x') = Dual (asin x)  (x' / sqrt (1 - x*x))
   acos  (Dual x x') = Dual (acos x)  (x' / (-sqrt (1 - x*x)))
   atan  (Dual x x') = Dual (atan x)  (x' / (1 + x*x))
   asinh (Dual x x') = Dual (asinh x) (x' / sqrt (1 + x*x))
   acosh (Dual x x') = Dual (acosh x) (x' / (sqrt (x*x - 1)))
   atanh (Dual x x') = Dual (atanh x) (x' / (1 - x*x))

-- example
-- f    = sqrt . (* 3) . sin
-- f' x = 3 * cos x / (2 * sqrt (3 * sin x)) 

-- linear fit sum-of-squares cost
-- cost :: Fractional s => s -> s -> [s] -> [s] -> s
cost m c x y = (/ (2 * (fromIntegral $ length x))) $
               sum $ zipWith errSq x y
  where
    errSq xi yi = zi * zi
      where
        zi = yi - (m * xi + c)

-- test data
x_ = [1..10]
y_ = [a | a <- [1..20], a `mod` 2 /= 0]

-- learning rate
gamma = 0.04

g :: (Integral s, Fractional s) => s -> s -> s
g m c = cost m c x_ y_

deriv (Dual _ x') = x'

 z_ = (0.1, 0.1) : map h z_

 h (c, m) = (c - gamma * cd, m - gamma * md) where
   cd = deriv $ g (constD m) $ idD c
   md = deriv $ flip g (constD c) $ idD m

 -- check for convergence
 main = do
   take 2 $ drop 1000 $ map (\(c, m) -> cost m c x_ y_) z_
   take 2 $ drop 1000 $ z_

其中测试数据 x_ 和 y_ 是数组,学习率 gamma 是一个标量。

[2]:如果我们将导数视为运算符,则对偶对象的两个字段实际上是彼此相邻的

【问题讨论】:

  • 如果有人偶然发现这个例子,错误在于使用列表推导生成数据 x_ 和 y_。如果您明确声明他们的条目,例如x_=[1,2,4,10,2] 等,它只是工作

标签: haskell


【解决方案1】:

首先,(Integral s, Fractional s) 没有意义; Integral 用于欧几里得域(带有div 和mod 的域),而Fractional 用于域(带有/ 的域)。如果你有真正的除法,你所有的余数都将为零...... .

我认为问题在于y_ 试图过滤到奇数。 Haskell 98 为数字定义了一个“阶梯式”范围形式,因此您可以将y_ 写为[1,3..19]。这应该允许y_ 用于[Dual] 类型,这应该允许g 在不需要Integral 约束的情况下使用它。

编辑:Ørjan Johansen 指出您还需要一个 Enum 实例以用于 Dual,这实际上很容易实现(这对于数字类型来说是相当标准的;我基本上复制了 GHC 的 Double 实例(这与 Float 的实例相同,例如)):

instance Enum Dual where
    succ x              = x + 1
    pred x              = x - 1
    toEnum              = fromIntegral
    fromEnum (Dual x _) = fromEnum x
    enumFrom            = numericEnumFrom
    enumFromTo          = numericEnumFromTo
    enumFromThen        = numericEnumFromThen
    enumFromThenTo      = numericEnumFromThenTo

【讨论】:

  • 类型约束是推断出来的,别看我..我尝试省略它但同样的错误。与此同时,我想通了! g 的当前定义使其依赖于输入数据 x_, y_ 的类型。特别是 [1..4] :: (Num t, Enum t) =&gt; [t] ,而 [1,2,3,4] :: Num t =&gt; [t] 我采用了将数据声明为列表推导的捷径,但它不必要地过度约束了代码。去搞清楚。感谢您的提示!
  • @ocramz,不,正如 jcast 所说,限制代码的是 mod。这就是您的 Integral 约束的来源。
  • [1,3..19] 不起作用,因为 Dual 也没有 Enum 实例。
  • @ØrjanJohansen,我从不喜欢 Haskell 处理数值范围的方式,但是在数值类型上缺少 Enum 实例比 (Integral s, Fractional s) 约束更容易解决。
【解决方案2】:

在原始代码中,我没有看到 g 的类型签名。在您的代码中,您专门编写了

g :: (Integral s, Fractional s) => s -> s -> s

错误消息显示Dual 没有Integral 实例。代码手动为Num 和Fractional 定义实例,但不是Integral。

我实际上不确定为什么 g 必须是 Integral。如果您删除该约束,代码甚至可以工作...

编辑:似乎Integral 实例是必要的,因为您使用mod 来生成测试数据。我不太确定这个巨大的代码块是做什么的,但我怀疑如果你应用 fromIntegral 将所有内容转换为(比如)Double,那么它可能会起作用。

(我怀疑让Dual 成为Integral 的实例可能不是原作者的本意。再说一次,我不太了解代码,所以......)

【讨论】:

  • 不幸的是,删除约束不会改变问题。我将尝试将Dual 设为Integral 的实例。是否也可能是编译指示 {-# LANGUAGE NoMonomorphismRestriction #-} 以某种方式干扰?我只是从原版中复制的,但没有查看它的定义..
  • 您不能只转换为Double,它必须是Dual。但是fromIntegral 应该仍然可以工作,因为Dual 确实有一个Num 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
  • 1970-01-01
相关资源
最近更新 更多