4

我试图在加速之上定义一个类型安全的矩阵计算库,部分是出于教育目的,部分是为了看看这是否是一种实用的方法。

但是在正确定义矩阵的乘积时,我完全陷入了困境——即以 GHC 接受/编译我的代码的方式。

我已经尝试了几次,它们是这个的变体:

Linear.hs

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}

import qualified Data.Array.Accelerate as A

import GHC.TypeLits
import Data.Array.Accelerate ( (:.)(..), Array
                             , Exp, Shape, FullShape, Slice
                             , DIM0, DIM1, DIM2, Z(Z)
                             , IsFloating, IsNum, Elt, Acc
                             , Any(Any), All(All))
import           Data.Proxy

newtype Matrix (rows :: Nat) (cols :: Nat) a = AccMatrix {unMatrix :: Acc (Array DIM2 a)}
(#*#) :: forall k m n a. (KnownNat k, KnownNat m, KnownNat n, IsNum a, Elt a) =>
    Matrix k m a -> Matrix m n a -> Matrix k n a
 v #*# w = let v' = unMatrix v
               w' = unMatrix w
           in AccMatrix $ A.generate (A.index2 k' n') undefined
          where k' = fromInteger $ natVal (Proxy :: Proxy k)
                n' = fromInteger $ natVal (Proxy :: Proxy n)
                aux :: Acc (Array (FullShape (Z :. Int) :. Int) e) -> Acc (Array (FullShape (Z :. All) :. Int) e) -> Exp ((Z :. Int) :. Int) -> Exp e
                aux v w sh = let (Z:.i:.j) = A.unlift sh
                                 v' = A.slice v (A.lift $ Z:.i:.All)
                                 w' = A.slice w (A.lift $ Z:.All:.j)
                              in A.the $ A.sum $ A.zipWith (*) v' w'

stack build给我的错误是

.../src/Linear.hs:196:55:
    Couldn't match type ‘A.Plain ((Z :. head0) :. head1)’
                   with ‘(Z :. Int) :. Int’
    The type variables ‘head0’, ‘head1’ are ambiguous
    Expected type: Exp (A.Plain ((Z :. head0) :. head1))
      Actual type: Exp ((Z :. Int) :. Int)
    Relevant bindings include
      i :: head0 (bound at src/Linear.hs:196:38)
      j :: head1 (bound at src/Linear.hs:196:41)
    In the first argument of ‘A.unlift’, namely ‘sh’
    In the expression: A.unlift sh

.../src/Linear.hs:197:47:
    Couldn't match type ‘FullShape (A.Plain (Z :. head0))’
                   with ‘Z :. Int’
    The type variable ‘head0’ is ambiguous
    Expected type: Acc
                     (Array (FullShape (A.Plain (Z :. head0) :. All)) e)
      Actual type: Acc (Array (FullShape (Z :. Int) :. Int) e)
    Relevant bindings include
      v' :: Acc (Array (A.SliceShape (A.Plain (Z :. head0)) :. Int) e)
        (bound at src/Linear.hs:197:34)
      i :: head0 (bound at src/Linear.hs:196:38)
    In the first argument of ‘A.slice’, namely ‘v’
    In the expression: A.slice v (A.lift $ Z :. i :. All)

.../src/Linear.hs:198:39:
    Couldn't match type ‘A.SliceShape (A.Plain ((Z :. All) :. head1))’
                   with ‘A.SliceShape (A.Plain (Z :. head0)) :. Int’
    The type variables ‘head0’, ‘head1’ are ambiguous
    Expected type: Acc
                     (Array (A.SliceShape (A.Plain (Z :. head0)) :. Int) e)
      Actual type: Acc
                     (Array (A.SliceShape (A.Plain ((Z :. All) :. head1))) e)
    Relevant bindings include
      w' :: Acc (Array (A.SliceShape (A.Plain (Z :. head0)) :. Int) e)
        (bound at src/Linear.hs:198:34)
      v' :: Acc (Array (A.SliceShape (A.Plain (Z :. head0)) :. Int) e)
        (bound at src/Linear.hs:197:34)
      i :: head0 (bound at src/Linear.hs:196:38)
      j :: head1 (bound at src/Linear.hs:196:41)
    In the expression: A.slice w (A.lift $ Z :. All :. j)
    In an equation for ‘w'’: w' = A.slice w (A.lift $ Z :. All :. j)

.../src/Linear.hs:198:47:
    Couldn't match type ‘FullShape (A.Plain ((Z :. All) :. head1))’
                   with ‘(Z :. Int) :. Int’
    The type variable ‘head1’ is ambiguous
    Expected type: Acc
                     (Array (FullShape (A.Plain ((Z :. All) :. head1))) e)
      Actual type: Acc (Array (FullShape (Z :. All) :. Int) e)
    Relevant bindings include
      j :: head1 (bound at src/Linear.hs:196:41)
    In the first argument of ‘A.slice’, namely ‘w’
    In the expression: A.slice w (A.lift $ Z :. All :. j)

我已经查阅了Accelerate的文档,并且我还在阅读具有类似目的但不用于断言数组/向量维度的加速算术。TypeLits

我还尝试制作一个香草版本(即没有我自己的矩阵类型),以防我的类型错误,我认为这与使用slice. 为了完整起见,我将其包括在内,我可以添加错误消息,但我选择省略它们,因为我相信它们与上述问题无关。

(#*#) :: forall a. (IsNum a, Elt a) =>
    Acc (Array DIM2 a) -> Acc (Array DIM2 a) -> Maybe (Acc (Array DIM2 a))   
v #*# w = let Z:.k :.m = A.unlift $ A.arrayShape $ I.run v
              Z:.m':.n = A.unlift $ A.arrayShape $ I.run w
           in if m /= m'
                 then Nothing
                 else Just $ AccMatrix $ A.generate (A.index2 k n) (aux v w)
          where aux :: Acc (Array DIM2 a) -> Acc (Array DIM2 a) -> Exp DIM2 -> Exp a
                aux v w sh = let (Z:.i:.j) = A.unlift sh
                                 v' = A.slice v (A.lift $ Z:.i:.All)
                                 w' = A.slice w (A.lift $ Z:.All:.j)
                              in A.the $ A.sum $ A.zipWith (*) v' w'
4

1 回答 1

2

您的代码实际上是正确的。不幸的是,类型检查器不够聪明,无法弄清楚,所以你必须帮助它:

let (Z:.i:.j) = A.unlift sh

变成

let (Z:.i:.j) = A.unlift sh :: (Z :. Exp Int) :. Exp Int

这里的关键是A.unlift :: A.Unlift c e => c (A.Plain e) -> ebutA.Plain是一个关联的类型族(因此是非内射的),因此e没有类型签名就无法确定类型,并且e需要选择一个实例来用于Unlift c e. 这就是“模棱两可的类型”错误的来源——这确实e是模棱两可的。


您还有一个不相关的错误。aux应该有类型

aux :: (IsNum e, Elt e) => ...

或者

aux :: (e ~ a) => ... 

在后一种情况下,a它是类型签名中的一个,(#*#)因此它已经具有约束IsNum, Elt

于 2016-04-20T16:54:33.767 回答