我正在尝试使用Repa实现累积和函数以计算积分图像。我目前的实现如下:
cumsum :: (Elt a, Num a) => Array DIM2 a -> Array DIM2 a
cumsum array = traverse array id cumsum'
where
elementSlice inner outer = slice array (inner :. (0 :: Int))
cumsum' f (inner :. outer) = Repa.sumAll $ elementSlice inner outer
问题出在elementSlice函数中。在matlab中或说numpy,这可以指定为array [inner,0:outer]。所以我正在寻找的是:
slice array (inner :. (Range 0 outer))
但是,似乎不允许在当前的Repa范围内指定切片。我考虑过在Haskell中使用高效并行模板卷积中讨论的分区,但如果每次迭代都改变它,这似乎是一种相当重要的方法。我还考虑过屏蔽切片(乘以二进制矢量) - 但这似乎再次对大型矩阵表现不佳,因为我会为矩阵中的每个点分配一个掩码矢量...
我的问题 - 有没有人知道是否有计划在一个范围内添加切片支持到Repa?或者是否有一种高效的方式我可以解决这个问题,可能采用不同的方法?
答案 0 :(得分:5)
提取子范围是一个索引空间操作,很容易用fromFunction表达,尽管我们应该为它添加一个更好的包装器。
let arr = fromList (Z :. (5 :: Int)) [1, 2, 3, 4, 5 :: Int]
in fromFunction (Z :. 3) (\(Z :. ix) -> arr ! (Z :. ix + 1))
> [2,3,4]
通过偏移提供的索引并从源查找该索引来检索结果中的元素。这种技术自然地扩展到更高级别的阵列。
关于实现并行折叠和扫描,我们可以通过向库中添加原语来实现。我们无法在地图方面定义并行缩减,但我们仍然可以使用延迟数组的整体方法。这将是一个合理的正交扩展。
答案 1 :(得分:3)
实际上,我认为主要问题是Repa没有扫描原语。 (但是,库Accelerate非常相似。)扫描有两种变体,前缀扫描和后缀扫描。给定一维数组
[a_1, ..., a_n]
前缀扫描返回
[0, a_0, a_0 + a_1, ..., a_0 + ... + a_{n-1} ]
,后缀扫描生成
[a_0, a_0 + a_1, ..., a_0 + a_1 + ... + a_n ]
我认为这是你的累积总和(cumsum
)函数的目的。
前缀和后缀扫描非常自然地概括为多维数组,并且具有基于树减少的有效实现。关于该主题的相对陈旧的论文是"Scan Primitives for Vector Computers"。此外,Conal Elliott最近撰写了several blog posts,以便在Haskell中获得高效的并行扫描。
可以通过两次扫描计算积分图像(在2D阵列上),一次扫描,一次垂直扫描。在没有扫描原语的情况下,我实现了一个,非常低效。
horizScan :: Array DIM2 Int -> Array DIM2 Int
horizScan arr = foldl addIt arr [0 .. n - 1]
where
addIt :: Array DIM2 Int -> Int -> Array DIM2 Int
addIt accum i = accum +^ vs
where
vs = toAdd (i+1) n (slice arr (Z:.All:.i))
(Z:.m:.n) = arrayExtent arr
--
-- Given an @i@ and a length @len@ and a 1D array @arr@
-- (of length n) produces a 2D array of dimensions n X len.
-- The columns are filled with zeroes up to row @i@.
-- Subsequently they are filled with columns equal to the
-- values in @arr.
--
toAdd :: Int -> Int -> Array DIM1 Int -> Array DIM2 Int
toAdd i len arr = traverse arr (\sh -> sh:.(len::Int))
(\_ (Z:.n:.m) -> if m >= i then arr ! (Z:.n) else 0)
然后可以将计算积分图像的函数定义为
vertScan :: Array DIM2 Int -> Array DIM2 Int
vertScan = transpose . horizScan . transpose
integralImage = horizScan . vertScan
鉴于已经为Accelerate实施了扫描,将它添加到Repa中应该不会太难。我不确定是否可以使用现有的Repa原语进行有效的实现。