2015-04-01 46 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