2015-04-01 37 views
3

给出了基于矢量一个非常简单的矩阵的定义:如何在复杂数据类型上自动区分?

import Numeric.AD 
import qualified Data.Vector as V 

newtype Mat a = Mat { unMat :: V.Vector a } 

scale' f = Mat . V.map (*f) . unMat 
add' a b = Mat $ V.zipWith (+) (unMat a) (unMat b) 
sub' a b = Mat $ V.zipWith (-) (unMat a) (unMat b) 
mul' a b = Mat $ V.zipWith (*) (unMat a) (unMat b) 
pow' a e = Mat $ V.map (^e) (unMat a) 

sumElems' :: Num a => Mat a -> a 
sumElems' = V.sum . unMat 

(用于演示目的...我使用HMATRIX但认为这个问题在那里不知)

而且误差函数(eq3):

eq1' :: Num a => [a] -> [Mat a] -> Mat a 
eq1' as φs = foldl1 add' $ zipWith scale' as φs 

eq3' :: Num a => Mat a -> [a] -> [Mat a] -> a 
eq3' img as φs = negate $ sumElems' (errImg `pow'` (2::Int)) 
    where errImg = img `sub'` (eq1' as φs) 

为什么编译器无法推导出正确的类型呢?

diffTest :: forall a . (Fractional a, Ord a) => Mat a -> [Mat a] -> [a] -> [[a]] 
diffTest m φs as0 = gradientDescent go as0 
    where go xs = eq3' m xs φs 

确切的错误信息是这样的:

src/Stuff.hs:59:37: 
    Could not deduce (a ~ Numeric.AD.Internal.Reverse.Reverse s a) 
    from the context (Fractional a, Ord a) 
     bound by the type signature for 
       diffTest :: (Fractional a, Ord a) => 
          Mat a -> [Mat a] -> [a] -> [[a]] 
     at src/Stuff.hs:58:13-69 
    or from (reflection-1.5.1.2:Data.Reflection.Reifies 
       s Numeric.AD.Internal.Reverse.Tape) 
     bound by a type expected by the context: 
       reflection-1.5.1.2:Data.Reflection.Reifies 
        s Numeric.AD.Internal.Reverse.Tape => 
       [Numeric.AD.Internal.Reverse.Reverse s a] 
       -> Numeric.AD.Internal.Reverse.Reverse s a 
     at src/Stuff.hs:59:21-42 
     ‘a’ is a rigid type variable bound by 
      the type signature for 
      diffTest :: (Fractional a, Ord a) => 
         Mat a -> [Mat a] -> [a] -> [[a]] 
      at src//Stuff.hs:58:13 
    Expected type: [Numeric.AD.Internal.Reverse.Reverse s a] 
        -> Numeric.AD.Internal.Reverse.Reverse s a 
     Actual type: [a] -> a 
    Relevant bindings include 
     go :: [a] -> a (bound at src/Stuff.hs:60:9) 
     as0 :: [a] (bound at src/Stuff.hs:59:15) 
     φs :: [Mat a] (bound at src/Stuff.hs:59:12) 
     m :: Mat a (bound at src/Stuff.hs:59:10) 
     diffTest :: Mat a -> [Mat a] -> [a] -> [[a]] 
     (bound at src/Stuff.hs:59:1) 
    In the first argument of ‘gradientDescent’, namely ‘go’ 
    In the expression: gradientDescent go as0 
+0

我不知道昨天我是否真的已经问过这个问题了。我以为我做了...但它并没有出现在我最新的问题中... – fho 2015-04-01 12:42:02

回答

7

adgradientDescent函数具有类型

gradientDescent :: (Traversable f, Fractional a, Ord a) => 
        (forall s. Reifies s Tape => f (Reverse s a) -> Reverse s a) -> 
        f a -> [f a] 

它的第一个参数需要的类型f r -> r的函数,其中rforall s. (Reverse s a)go的类型为[a] -> a,其中a是在diffTest的签名中绑定的类型。这些a s是一样的,但Reverse s a是不一样的a

Reverse类型具有许多类型类的实例,可能允许我们将a转换为Reverse s a或返回。最明显的是Fractional a => Fractional (Reverse s a)这将允许我们将a s转换为Reverse s a s与realToFrac

为此,我们需要能够通过Mat a映射功能a -> b以获得Mat b。最简单的方法是为Mat派生Functor实例。

{-# LANGUAGE DeriveFunctor #-} 

newtype Mat a = Mat { unMat :: V.Vector a } 
    deriving Functor 

我们可以mfs转换成任何Fractional a' => Mat a'fmap realToFrac

diffTest m fs as0 = gradientDescent go as0 
    where go xs = eq3' (fmap realToFrac m) xs (fmap (fmap realToFrac) fs) 

但隐藏在广告包中有更好的方法。 Reverse s a普遍适用于所有s,但adiffTest的类型签名中绑定的那个是相同的a。我们真的只需要一个功能a -> (forall s. Reverse s a)。该函数为Mode类的auto,其中Reverse s a有一个实例。 auto有稍微奇怪的类型Mode t => Scalar t -> ttype Scalar (Reverse s a) = a。专门用于Reverseauto有型

auto :: (Reifies s Tape, Num a) => a -> Reverse s a 

这让我们对我们的Mat a秒值进行转换成Mat (Reverse s a)•不用发生转换,并从Rational乱搞。

{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE TypeFamilies #-} 

diffTest :: forall a . (Fractional a, Ord a) => Mat a -> [Mat a] -> [a] -> [[a]] 
diffTest m fs as0 = gradientDescent go as0 
    where 
    go :: forall t. (Scalar t ~ a, Mode t) => [t] -> t 
    go xs = eq3' (fmap auto m) xs (fmap (fmap auto) fs) 
+1

伟大的写作。我只是想抱怨,这应该是明显的'广告'文件...只是为了找到它在github上。现在我感到很蠢:) – fho 2015-04-01 16:40:47

+0

'〜'在'go'的签名中做了什么? – ocramz 2015-08-06 17:06:11

+1

'〜'是类型相等的。它说't'的'标量'必须与原来'Mat a'中的'a'类型相同。 – Cirdec 2015-08-06 17:47:40