Mathematica:使用简化来消除常见的子表达并降低强度

时间:2010-11-17 08:56:45

标签: wolfram-mathematica compiler-optimization

最近我一直在研究Mathematica的模式匹配和术语重写如何在编译器优化中得到充分利用......试图高度优化作为循环内部部分的短代码块。减少评估表达式所需工作量的两种常用方法是识别出现多次的子表达式并存储结果,然后在后续点使用存储的结果来节省工作。另一种方法是尽可能使用更便宜的操作。例如,我的理解是,取平方根比加法和乘法需要更多的时钟周期。为了清楚起见,我感兴趣的是评估表达式所需的浮点运算成本,而不是Mathematica评估它需要多长时间。

我的第一个想法是,我将解决使用Mathematica的simplify函数开发的问题。可以指定复杂度函数来比较两个表达式的相对简单性。我打算使用权重为相关算术运算创建一个,并为表达式添加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];
  ]

从CommonSubExpressions返回的列表中选择一个公共子表达式后,替换函数如下所示。

eliminateCSE[statements_, expr_] := Module[{temp},
temp = Unique["r"];
Prepend[ReplaceAll[statements, expr -> temp], temp[expr]]
]

冒着这个问题变长的风险,我会提出一些示例代码。我认为尝试优化的一个不错的表达将是用于求解微分方程的经典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])]

最后,判断不同表达式相对成本的代码如下。权重在这一点上是概念性的,因为这仍然是我正在研究的领域。

Input:
cost[e_] := 
 Total[MapThread[
   Count[e, #1, Infinity, Heads -> True]*#2 &, {{Plus, Times, Sqrt, 
     f}, {1, 2, 5, 10}}]]
cost[transformed]

Output:
100

3 个答案:

答案 0 :(得分:5)

此作者还在此处实施了一些例程:http://stoney.sb.org/wordpress/2009/06/converting-symbolic-mathematica-expressions-to-c-code/

我把它打包成一个* .M文件并修复了一个错误(如果表达式没有重复的子表达式它就死了),我试图找到作者的联系信息,看看我是否可以上传他的修改后的代码到pastebin或任何地方。

编辑:我已获得作者的许可上传并在此处粘贴:http://pastebin.com/fjYiR0B3

答案 1 :(得分:4)

要识别重复的子表达式,您可以使用类似的东西

(*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

答案 2 :(得分:0)

我试图模仿出现在此博客上的字典压缩功能:https://writings.stephenwolfram.com/2018/11/logic-explainability-and-the-future-of-understanding/

这就是我做的:

DictionaryCompress[expr_, count_, size_, func_] := Module[
  {t, s, rule, rule1, rule2},
  t = Tally@Level[expr, Depth[expr]];
  s = Sort[
    Select[{First@#, Last@#, Depth[First@#]} & /@ 
      t, (#[[2]] > count && #[[3]] > size) &], #1[[2]]*#1[[3]] < #2[[
        2]]*#2[[2]] &];
  rule = MapIndexed[First[#1] -> func @@ #2 &, s];
  rule = (# //. Cases[rule, Except[#]]) & /@ rule;
  rule1 = Select[rule, ! FreeQ[#, Plus] &];
  rule2 = Complement[rule, rule1];
  rule = rule1 //. (Reverse /@ rule2);
  rule = rule /. MapIndexed[ Last[#1] -> func @@ #2 &, rule];
  {
   expr //. rule,
   Reverse /@ rule
   }
  ];

poly = Sum[Subscript[c, k] x^k, {k, 0, 4}];
sol = Solve[poly == 0, x];
expr = x /. sol;
Column[{Column[
     MapIndexed[
      Style[TraditionalForm[Subscript[x, First[#2]] == #], 20] &, #[[
       1]]], Spacings -> 1],
    Column[Style[#, 20] & /@ #[[2]], Spacings -> 1, Frame -> All]
    }] &@DictionaryCompress[expr, 1, 1, 
  Framed[#, Background -> LightYellow] &]

enter image description here