1

我正在使用 Haskell 的加速库做一个有趣的项目。我有一个需要编写的函数,在纯 Haskell 中看起来像这样:

oddfac :: Int -> Int
oddfac n = product [1,3...n]

即类似于阶乘函数,但只乘以奇数。我想在加速后端执行这个功能,所以如果我理解正确,它需要变成 type Exp Int -> Exp IntExp但是,出于性能原因,该库不允许在 中评估任意表达式。幸运的是,我只需要为小值评估这个函数,例如 n<=7。我的想法是定义一个预先计算的返回值的列表(或数组),以便简单地对其进行索引将返回适当的值,并且每次评估都将花费相同的时间,而天真的版本则不是这种情况。但是,我还没有找到一种方法来做到这一点。我现在有两个问题:

1) 有没有办法做到这一点,即定义一个硬编码数组,然后对其进行索引以检索类型函数中的适当值Exp a -> Exp b

2) 我是否以有效的方式处理事情?我对这个问题的看法有什么明显的缺陷吗?

更新

以下作品基于@ErikR 的回答和随后的评论:

module Test where

import Data.Array.Accelerate as A
import Prelude as P

oddfac :: Exp Int -> Exp Int
oddfac n = (use $ A.fromList (Z :. 6) [1, 1, 3, 3, 15, 15]) A.! (index1 n)

alloddfac :: Acc (Vector Int)
alloddfac = A.map oddfac $ use $ A.fromList (Z :. 3) [1, 3, 5]
4

2 回答 2

2

在我看来,您可以Acc (Array DIM1 Int)使用几种方法之一创建一个,然后使用 Accelerate 的(!)函数index1来索引数组。

请注意,这Vector aArray DIM1 a.

以下是如何创建一个Acc (Vector Int)奇数阶乘(7 个元素):

oddfacts :: Acc (Vector Int)
-- analogous to: take 7 $ scanl (*) 1 [3,5..]
oddfacts = A.scanl (*) (constant (1::Int)) $ A.enumFromStepN (A.index1 (constant (7::Int))) (constant (3::Int)) (constant (2::Int))

以下是如何索引它:

foo :: Exp Int -> Exp Int
foo n = oddfacts A.! (A.index1 n)

以及如何将它与条件一起使用:

bar :: Exp Int-> Exp Int
bar n = (n <=* (constant (7::Int))) ?
           ( oddfacts A.! (A.index1 n)
           , constant (0::Int)           -- return 0 if n > 7
           )

警告 - 我实际上并没有运行此代码,但它会进行类型检查。

加速示例包中的示例有很多代码使用数组生成函数(A.generate、A.scanl 等)和索引函数(A.index1、A.index2 等)

于 2015-11-06T00:28:32.923 回答
1

您可以通过实例将Haskell 中的“越过栅栏”放入Exp任意数组中 。(Shape sh, Elt e) => Lift Acc (Array sh e)所以你可以在 Haskell 中创建你的查找表,然后就lift可以了:

import Data.Array.Accelerate as A hiding (product)

oddfac :: Int -> Int
oddfac n = product [1,3..n]

oddfacs :: Vector Int
oddfacs = A.fromFunction (Z :. 7) (\(Z :. i) -> oddfac i)

lut :: Acc (Vector Int)
lut = A.lift oddfacs

其余的可以通过@ErikR 的答案通过索引到查找表中来实现lut

于 2015-11-06T06:48:26.967 回答