Sam*_*ram 8 wolfram-mathematica compiler-optimization
最近我一直在研究Mathematica的模式匹配和术语重写如何在编译器优化中得到很好的利用......试图高度优化作为循环内部部分的短代码块.减少评估表达式所需工作量的两种常用方法是识别出现多次的子表达式并存储结果,然后在后续点使用存储的结果来节省工作.另一种方法是尽可能使用更便宜的操作.例如,我的理解是,取平方根比加法和乘法需要更多的时钟周期.为了清楚起见,我感兴趣的是评估表达式所需的浮点运算成本,而不是Mathematica评估它需要多长时间.
我的第一个想法是,我将解决使用Mathematica的简化功能开发的问题.可以指定复杂度函数来比较两个表达式的相对简单性.我打算使用权重为相关算术运算创建一个,并为表达式添加LeafCount以考虑所需的赋值操作.这解决了力量方面的减少,但它消除了我绊倒的常见子表达式.
我正在考虑将公共子表达式消除添加到简化使用的可能的转换函数中.但是对于一个大表达式,可能有许多可能被替换的子表达式,并且在看到表达式之前不可能知道它们是什么.我编写了一个函数来提供可能的替换,但是看起来你指定的转换函数需要返回一个可能的转换,至少从文档中的示例来看.关于如何解决这个限制的任何想法?有没有人更好地了解简化如何使用可能暗示前进方向的转换函数?
我想,在幕后,Simplify正在做一些动态编程,尝试对表达式的不同部分进行不同的简化,并返回具有最低复杂度得分的那个.使用常见的代数简化(例如factor和collect),我是否会更好地尝试自己动态编程?
编辑:我添加了生成可能删除的子表达式的代码
(*traverses entire expression tree storing each node*)
AllSubExpressions[x_, accum_] := Module[{result, i, len},
len = Length[x];
result = Append[accum, x];
If[LeafCount[x] > 1,
For[i = 1, i <= len, i++,
result = ToSubExpressions2[x[[i]], result];
];
];
Return[Sort[result, LeafCount[#1] > LeafCount[#2] &]]
]
CommonSubExpressions[statements_] := Module[{common, subexpressions},
subexpressions = AllSubExpressions[statements, {}];
(*get the unique set of sub expressions*)
common = DeleteDuplicates[subexpressions];
(*remove constants from the list*)
common = Select[common, LeafCount[#] > 1 &];
(*only keep subexpressions that occur more than once*)
common = Select[common, Count[subexpressions, #] > 1 &];
(*output the list of possible subexpressions to replace with the \
number of occurrences*)
Return[common];
]
Run Code Online (Sandbox Code Playgroud)
一旦从CommonSubExpressions返回的列表中选择了一个公共子表达式,则执行替换的函数如下所示.
eliminateCSE[statements_, expr_] := Module[{temp},
temp = Unique["r"];
Prepend[ReplaceAll[statements, expr -> temp], temp[expr]]
]
Run Code Online (Sandbox Code Playgroud)
冒着这个问题变长的风险,我会提出一些示例代码.我认为尝试优化的一个不错的表达将是用于求解微分方程的经典Runge-Kutta方法.
Input:
nextY=statements[y + 1/6 h (f[t, n] + 2 f[0.5 h + t, y + 0.5 h f[t, n]] +
2 f[0.5 h + t, y + 0.5 h f[0.5 h + t, y + 0.5 h f[t, n]]] +
f[h + t,
y + h f[0.5 h + t, y + 0.5 h f[0.5 h + t, y + 0.5 h f[t, n]]]])];
possibleTransformations=CommonSubExpressions[nextY]
transformed=eliminateCSE[nextY, First[possibleTransformations]]
Output:
{f[0.5 h + t, y + 0.5 h f[0.5 h + t, y + 0.5 h f[t, n]]],
y + 0.5 h f[0.5 h + t, y + 0.5 h f[t, n]],
0.5 h f[0.5 h + t, y + 0.5 h f[t, n]],
f[0.5 h + t, y + 0.5 h f[t, n]], y + 0.5 h f[t, n], 0.5 h f[t, n],
0.5 h + t, f[t, n], 0.5 h}
statements[r1[f[0.5 h + t, y + 0.5 h f[0.5 h + t, y + 0.5 h f[t, n]]]],
y + 1/6 h (2 r1 + f[t, n] + 2 f[0.5 h + t, y + 0.5 h f[t, n]] +
f[h + t, h r1 + y])]
Run Code Online (Sandbox Code Playgroud)
最后,判断不同表达式的相对成本的代码如下.权重在这一点上是概念性的,因为这仍然是我正在研究的领域.
Input:
cost[e_] :=
Total[MapThread[
Count[e, #1, Infinity, Heads -> True]*#2 &, {{Plus, Times, Sqrt,
f}, {1, 2, 5, 10}}]]
cost[transformed]
Output:
100
Run Code Online (Sandbox Code Playgroud)
要识别重复的子表达式,您可以使用类似的东西
(*helper functions to add Dictionary-like functionality*)
index[downvalue_,
dict_] := (downvalue[[1]] /. HoldPattern[dict[x_]] -> x) //
ReleaseHold;
value[downvalue_] := downvalue[[-1]];
indices[dict_] :=
Map[#[[1]] /. {HoldPattern[dict[x_]] -> x} &, DownValues[dict]] //
ReleaseHold;
values[dict_] := Map[#[[-1]] &, DownValues[dict]];
items[dict_] := Map[{index[#, dict], value[#]} &, DownValues[dict]];
indexQ[dict_, index_] :=
If[MatchQ[dict[index], HoldPattern[dict[index]]], False, True];
(*count number of times each sub-expressions occurs *)
expr = Cos[x + Cos[Cos[x] + Sin[x]]] + Cos[Cos[x] + Sin[x]];
Map[(counts[#] = If[indexQ[counts, #], counts[#] + 1, 1]; #) &, expr,
Infinity];
items[counts] // Column
Run Code Online (Sandbox Code Playgroud)
作者在这里还实现了一些例程:http : //stoney.sb.org/wordpress/2009/06/converting-symbolic-mathematica-expressions-to-c-code/
我将其打包为* .M文件,并修复了一个错误(如果该表达式没有重复的子表达式,它将终止),并且我试图查找作者的联系信息,以查看是否可以将其修改后的代码上传到pastebin或任何地方。
编辑:我已经获得作者的上传许可,并将其粘贴到此处:http : //pastebin.com/fjYiR0B3
归档时间: |
|
查看次数: |
2149 次 |
最近记录: |