可以使用具有数据并行运算符的全部功能语言来表示哪些算法?


11

想象一下一种函数式编程语言,其唯一的数据类型是数字标量和数组的任意嵌套。该语言缺乏任何无限迭代的方式,因此不允许以下内容:

  • 显式循环(无论如何,没有副作用的使用很少)
  • 递归
  • 任意一等函数(无y组合器)

但是,该语言具有:

  • 顶级功能
  • 词法范围的let绑定
  • 分支控制流程
  • 常见的标量数学和逻辑函数
  • 一些简单的数组构造函数,例如fill(n,x),它创建一个具有x相同值的n元素数组
  • 最重要的是:执行并行结构化迭代(例如映射,归约,扫描,所有对)的一组受限的高阶运算符。

更具体地讲数据并行运算符:

  • y = map(f,x)=> y [i] = f [i]
  • y = reduce(f,a,x)=> y = f(a,f(y [p [0]],f(y [p [1 []],...))))
  • y = scan(f,a,x)=> y [i] = reduce(f,a,y [0 ... i-1])
  • y = allpairs(f,x,y)=> y [i,j] = f(x [i],y [j])

我们也可以有其他运算符,但是要使它们具有多项式运行时间,可以在某种合理的数据并行计算模型下实现,并且可以在大多数多项式空间使用。

显然,有些构造无法用这种语言表达,例如:

while f(x) > tol:
    x <- update(x)   

我们可以在这个系统中表达什么?我们是否仅限于FP中的搜索问题?我们可以捕获所有多项式时间算法吗?此外,此类是否有一些最小的运算符集?

Answers:


7

您可能想看一下认真对待这些想法的旧编程语言NESL。该语言基于对集合的操作,本质上是列表和列表列表等,但是我认为通过棘手的编码也考虑了树和图。在该项目中(1990年代中期)所做的一些研究可以帮助回答您的问题。博士论文(可作为书)Guy E. Blelloch。数据并行计算的矢量模型。麻省理工学院出版社,1990年可能会提供一些答案。自从我看了一段时间之前。

慈善形式Bird-Meertens Formalism)(BMF)上所做的工作也属于此类。从Charity Wikipedia页面上,它说该语言不是图灵完整的,但是可以表达Ackermann的功能,这意味着它不仅仅是原始的递归。BMF和Charity都涉及诸如折叠和扫描(变形,变形等)之类的运算符,它们起源于类别理论。

简而言之,不准确的答案是您可以表达很多内容。


1
NESL并不是一种全面的语言。
Per Vognsen

我表面上已经了解NESL一段时间了,但是第一次才详细阅读Blelloch的论文。谢谢你的提示。NESL与我上面描述的语言非常相似,除了Per Vognsen注意到,它允许递归。
亚历克斯·鲁宾斯坦

我也对Blelloch选择的基本运算符感兴趣:map,dist(我相信与我所说的“ fill”相同),length,array-read,array-write,scan,partition,flatten。NESL的原语是“完整的”,还是使用数据并行实现进行了其他一些操作,而这些操作无法有效地表达出来?
亚历克斯·鲁宾斯坦

2
除去递归,您将拥有一种颇具表现力的语言,尤其是考虑折叠等的情况。那么,对BMF及其后续工作可能会更感兴趣。抱歉,我在这方面不是最新的。
戴夫·克拉克

7

我的其他回答指出了该语言的缺陷。在这个答案中,我将在整体语言中更加详细地讨论折叠和展开并存的问题。然后,我将提出一个解决方案,并证明可以用这种扩展语言解决P中的所有问题(甚至更多)。

折合全部语言会消耗列表:

fold :: (a -> b -> b) -> b -> List a -> b

以全部语言展开会生成流,这些流可能是无限的:

unfold :: (b -> Maybe (a, b)) -> b -> Stream a

不幸的是,列表和流生活在不同的世界中,因此这些运算符无法组成。我们需要部分对应:

stream :: List a -> Stream a
list :: Int -> Stream a -> List a

流运算符将列表嵌入到有界流中。list函数将前n个元素(如果流更早终止,则提取更少的n个元素)到列表中。因此,我们有以下等式:

for all xs :: List a, xs == list (length xs) (stream xs)

作为效率优化,我们可以将流完全切出作为中间数据结构:

unfoldList :: Int -> (b -> Maybe (a, b)) -> b -> List a

现在,我将草拟一个证明,该证明(与原始问题中已经暗示的其他运算符)使我们能够模拟任何多项式时间算法。

根据定义,当存在图灵机M和多项式p时,语言L在P中,这样可以通过最多运行M个p(n)迭代(其中n = | x |)来确定x在L中的隶属关系。按照标准参数,即使机器的磁带是无限的,也可以使用最多2k + 1的长度列表来编码迭代k中机器磁带的状态。

现在的想法是将M的转换表示为从列表到列表的函数。机器的执行将通过使用转移功能展开初始状态来完成。这将生成列表流。L在P中的假设意味着我们只需要看p(n)个元素即可。因此,我们可以对展开进行组合list p(n)以获得有限列表。最后,我们将其折叠以检查决策问题的答案是否是。

更一般而言,这表明只要我们有一个算法的终止时间可以由该语言中的可计算函数来限制,就可以对其进行仿真。这也说明了为什么无法模拟像阿克曼函数这样的东西:它是自己的约束,所以存在鸡肉和鸡蛋问题。


4

这是一种非常粗俗的语言。尝试对阶乘函数进行编程:

fact 0 = 1
fact n = n * fact (n-1)

问题是您的语言只有折叠而没有展开。表达阶乘的自然方法是将n的展开组成列表[1、2,...,n],将其折叠在乘法时分解。

您是否真的对此特定语言或总体功能编程感兴趣?很明显,您的语言最多只能表达多项式时间算法。系统F(多态Lambda演算)可以轻松表达像Ackermann函数这样的怪物。即使您对多项式时间算法感兴趣,您也经常需要超多项式肘部空间来自然地表达它们。

编辑:正如Dave所指出的那样,NESL是开创性的功能性数据并行编程语言之一,但距离总数还很远(它甚至没有尝试过)。APL系列具有平行的演变轨迹,并具有总的代数子集(避免使用定点运算符)。如果您的问题重点是总体性,David Turner在该领域已经写了一些不错的论文,尽管不是专门针对数据并行编程的。


抱歉,缺少可扩展的运算符是我的疏忽……我的意思是添加一个,但忘了张贴在帖子中。我不一定对这种特定的语言感兴趣,而是对数据并行计算的表达性和局限性感兴趣。
亚历克斯·鲁宾斯坦yn'9

展开自然是流值的,而不是数组值的,这是所有严格语言中corecursion的基本问题。
Per Vognsen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.