“没有实例......”,关于GHC7.8.3的Haskell'98代码

时间:2014-10-22 12:23:23

标签: haskell

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

问题 我使用GHC 7.8.3,作者明确提到这是H98代码(我在标题中提到它,因为它是我在设置和作者之间能够想到的唯一重大差异&#39 ; s,但如果错误,则plz正确)。 为什么它在成本函数的定义中扼杀? 我的理解是:函数idD和constD map浮动到Duals,g是多态的(它可以对Dual输入执行代数运算,因为Dual继承Num,Fractional和Floating),衍生图将Duals转换为Doubles。 推断出g的类型签名(与数据相关的eta减少的成本函数)。我尝试省略它,并通过将浮动类型约束替换为分数约束使其更通用。 此外,我尝试将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]:如果我们将导数视为运算符,那么Dual对象的两个字段实际上与另一个对象相互伴随

2 个答案:

答案 0 :(得分:2)

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

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

错误消息显示Integral没有Dual个实例。代码手动定义NumFractional的实例,但不是Integral

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

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

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

答案 1 :(得分:2)

首先,(Integral s, Fractional s)毫无意义; Integral适用于欧几里德域(带有divmod的域),而Fractional适用于字段(带有/的域)。如果你有真正的分裂,你所有的余数都将为零......

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

编辑:ØrjanJohansen指出你需要一个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