如何在Haskell中实现维度不可知矩阵 - 向量乘法?

时间:2018-06-13 21:09:36

标签: haskell matrix-multiplication typechecking existential-type

我定义了一个具有存在隐藏维度的矩阵类型,因此我可以形成任意大小的矩阵列表:

import qualified Data.Vector.Sized as VS
import           Data.Vector.Sized (Vector)

-- | A matrix with existentially hidden dimensions.
data Mat :: * -> * where
  Mat :: (Vector m :.: Vector n) a -> Mat a

但是,现在,我无法弄清楚如何以类型检查的方式定义矩阵向量乘法函数。 试试这个:

| Matrix-vector multiplication for existentially typed matrices.
(^*^)
  :: (KnownNat m, KnownNat n, Num a)
  => Mat a       -- ^ transform matrix
  -> Vector n a  -- ^ input vector
  -> Vector m a
Mat (Comp1 mat) ^*^ v = VS.map (dot v) mat

dot :: (KnownNat n, Num a) => Vector n a -> Vector n a -> a
u `dot` v = VS.sum $ VS.zipWith (*) u v

结果:

• Couldn't match type ‘m1’ with ‘m’
  ‘m1’ is a rigid type variable bound by
    a pattern with constructor:
      Mat :: forall a (m :: Nat) (n :: Nat).
             (:.:) (Vector m) (Vector n) a -> Mat a,
    in an equation for ‘^*^’
    at src/RL/MulES.hs:89:1-15
  ‘m’ is a rigid type variable bound by
    the type signature for:
      (^*^) :: forall (m :: Nat) (n :: Nat) a.
               (KnownNat m, KnownNat n) =>
               Mat a -> Vector n a -> Vector m a
    at src/RL/MulES.hs:(84,1)-(88,15)
  Expected type: Vector m a
    Actual type: Vector m1 a
• In the expression: VS.map (dot v) mat
  In an equation for ‘^*^’:
      Mat (Comp1 mat) ^*^ v = VS.map (dot v) mat
• Relevant bindings include
    mat :: Vector m1 (Vector n8 a) (bound at src/RL/MulES.hs:89:12)
    (^*^) :: Mat a -> Vector n a -> Vector m a
      (bound at src/RL/MulES.hs:89:17)
   |
89 | Mat (Comp1 mat) ^*^ v = VS.map (dot v) mat
   |                         ^^^^^^^^^^^^^^^^^^

1 个答案:

答案 0 :(得分:2)

首先让我说一下,我发现这个想法非常可疑:如果你再用一个存在的包装器把它扔掉,为什么还要通过类型系统来修复尺寸呢?

由于您不知道该线性映射的域维度,因此您可以应用它的唯一向量是可以按需提供任何维度的向量。这很疯狂,但可以使用-XRank2Types在Haskell中表达。此外,您还不知道codomain维度,因此结果必须再次存在。存在包装器必须包含KnownNat约束,否则你根本无法对向量做任何事情。像

{-# LANGUAGE GADTs, Rank2Types, UnicodeSyntax #-}

-- | This is really just a pretentious replacement for Data.Vector.Vector
data Array :: * -> * where
  Array :: KnownNat n => Vector n a -> Mat a

data Mat :: * -> * where
  Mat :: (KnownNat n, KnownNat m) => (Vector m :.: Vector n) a -> Mat a

lapply :: Num a => Mat a -> (∀ n . KnownNat n => Vector n a) -> Array a
lapply (Mat m) v = Array $ VS.map (dot v) mat

这对任何事都有用吗?不确定。你真的可以创建这样一个长度不可知的向量,如果你真正想要表达的是一个连续函数,它是在统一网格上进行PCM采样的(Matlab人们整天都在使用它,而不是有适当的功能类型...)。采样将自动调整到矩阵要求的任何分辨率。

但是,在运行时这只意味着你的输入“vector”实际上是一个采用resolution-argument的函数。整个类型级数foo在这里买不到多少。

IMO,当你发现自己处理这种“可变长度向量”时,它通常表明你真正在无限维向量空间中工作,就像通常一样这样的功能空间。 (最广泛使用的这样的空间是L2 Hilbert space。)这不能用任何基于数组的方法表达,但是如果你放弃维度/元素,它就可以表达分解,而是使用向量空间作为类型类Conal Elliott's package因为这已经存在很长时间了;它可能看起来有点过时但实际上仍然非常实用。我花了一些力气在它上面a proper category of linear maps,但是它还没有达到使它完全可用于无限维空间的程度。