На самом деле, я думаю, что главная проблема в том, что у 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
).
Сканирования с префиксами и суффиксами довольно естественно обобщаются в многомерные массивы и имеют эффективную реализациюоснованный на сокращении дерева.Относительно старая статья на эту тему - «Сканирование примитивов для векторных компьютеров» .Кроме того, Конал Эллиотт недавно написал несколько блогов сообщений о получении эффективных параллельных сканов в Haskell.
Интегральное изображение (в двумерном массиве) можно рассчитать, выполнив два сканирования, одно по горизонтали и одно по вертикали.При отсутствии сканирующего примитива я реализовал один, очень неэффективно.
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
Учитывая, что сканирование было выполнено для ускорения, его не должно быть слишком сложно добавить вРепа.Я не уверен, возможна ли эффективная реализация с использованием существующих примитивов Repa.