Mathematica 表达式的编译
在定义 f[x_]:=x Sin[x] 函数时,Mathematica 将表达式 x Sin[x] 按能对所有的
求值的方式保存. 然后,当你给出了
的一个值时,Mathematica 就将这个值代入表达式 x Sin[x] 中计算出结果. 不管
的指定值是一个数、一个列表、一个代数元素,或者是其它形式的表达式,用来进行计算的 Mathematica 内部代码使其工作过程一样有效.
要考虑到所有这些可能性时计算过程就会变慢. 但是,如果 Mathematica 能将
看作一个机器数时,这将省掉许多步骤,从而使 x Sin[x] 的计算相当快.
使用 Compile 可以在 Mathematica 中产生编译后的函数,这个函数在计算 Mathematica 表达式时假设所有出现的参数都是数(或逻辑变量). Compile[{x1, x2, ...}, expr] 使用表达式 expr 并产生一个"编译过的函数",当给出了变量
的值后,所编译的函数就计算表达式的值.
一般来说,Compile 产生一个 CompiledFunction 目标,该目标包含一系列计算所编译函数的一系列简单指令. 这些指令与典型计算机中的机器代码很接近,所以执行得很快.
| Compile[{x1,x2,...},expr] | 产生编译后的函数,对 的值计算 expr |
产生编译后的函数.
定义

是计算
x Sin[x] 关于

的纯函数.
| Out[1]= |  |
| Out[2]= |  |
| Out[3]= |  |
在计算数量逻辑表达式许多次时,Compile 是非常有效的. 花一点时间调用 Compile 后,所得到的编译函数比任何普通的 Mathematica 函数执行得更快.
对形如 x Sin[x] 这样的简单表达式,普通函数和编译函数的执行速度几乎没有什么差别. 当所涉及的表达式的数量增加时,编译函数就显示出了其优越性. 对大型的表达式编译可以提高速度20倍.
当表达式含有大量简单的算术运算和函数时,编译就有很大的差异. 对 BesselK 和 Eigenvalues 等复杂函数,大部分时间花在执行 Mathematica 的内部程序上,此时编译没有效果.
这里产生一个计算10阶勒让德多项式.
Evaluate 令
Mathematica 在编译之前构造多项式.
| Out[4]= |  |
当自变量为

时,计算勒让德多项式的值.
| Out[5]= |  |
| Out[6]= |  |
即使用编译可以加快数值函数的计算速度,但应该尽可能地使用 Mathematica 的内部函数. 通常内部函数比用户能产生的任何编译过的 Mathematica 程序执行得更快,另外它们使用许多算法,容易控制且计算精度高.
应该知道 Mathematica 的内部函数经常使用 Compile. 例如,NIntegrate 自动对要积分的表达式使用 Compile. 函数如 Plot 和 Plot3D 对要绘图的表达式也自动使用 Compile. 使用 Compile 的内部函数一般都有可选项Compiled. 设置 Compiled->False 让函数不使用 Compile.
| Compile[{{x1,t1},{x2,t2},...},expr] | 编译 expr,假设 的类型为  |
| Compile[{{x1,t1,n1},{x2,t2,n2},...},expr] |
| 编译 expr,假设 是秩为 、元素都是 类型的阵列 |
| Compile[vars,expr,{{p1,pt1},...}] | 编译 expr,假设与 匹配的子表达式的类型为  |
| _Integer | 机器长度的整型数 |
| _Real | 近似实数的机器精度 |
| _Complex | 近似复数的机器精度 |
| True|False | 逻辑变量 |
指定编译类型.
Compile 工作时要假设在所给表达式计算过程中出现的对象的类型. 默认值是假设表达式的所有变量是近似实数.
Compile 使用整数、复数(True 或 False)及阵列. 可以通过与某一类型的值匹配的方法来指定某一个变量. 例如,用模式
指定整型数,用 True|False 指定逻辑变量是 True 或者 False.
| Out[7]= |  |
| Out[8]= |  |
| Out[9]= |  |
按照编译后的方式进行一系列运算,其结果是一个整数.
| Out[10]= |  |
Compile 处理的类型基本上与机器代码水平上计算机处理的类型相对应. 例如,Compile 能处理机器精度内的近似实数,但不能处理任意精度的实数. 另外,如果指定某变量是整数,Compile 产生的代码仅能处理机器长度之内的证书,该长度通常在
之间.
当编译的表达式仅涉及标准算术运算和逻辑运算时,Compile 能够简单地从输入变量的类型推断每一步产生的对象的类型. 但如果,调用其它函数时,Compile 一般就无法推断其返回值的类型. 当用户不指定时,Compile 假定其它函数产生一个近似实数值. 当然可以直接给出一个模式列去确定与一个模式匹配的表达式的类型.
假设

总是整数,编译

.
| Out[12]= |  |
| Out[13]= |  |
Compile 的思路是对自变量的某一类型产生一个优化后的函数. 但 Compile 的构造使得它所产生的函数对任何类型的自变量都能使用. 当无法优化时,就通过计算一个标准的 Mathematica 表达式来得到函数的值.
| Out[14]= |  |
| Out[15]= |  |
Mathematica 给出一个警告,然后计算原来的符号表达式.
| Out[16]= |  |
由 Compile 产生的编译代码不仅要对所使用的自变量类型进行假设,而且也要对执行过程中产生目标的类型进行假设. 有时候,这些类型依赖于所给自变量的实际值. 因此,例如,Sqrt[x] 当 x 为非负数时的值为实数,而当 x 不是负数时,x 为复数.
Compile 总是对一个函数的返回值类型进行确定的假设. 当这假设在 Compile 产生的代码执行过程中不成立时,Mathematica 就放弃编译过的代码,并计算普通的 Mathematica 表达式去得到结果.
编译的代码不能得到复数结果,所以
Mathematica 必须直接计算原来的符号表达式.
| Out[17]= |  |
Compile 的一个重要特点是它不但可以处理数学表达式,还可以处理各种简单的 Mathematica 程序. 例如,Compile 可以处理条件和控制流结构.
在所有情况下,Compile[vars, expr] 保持其变量不计算,这意味着可以通过对表达式的编译给出一个程序.
这里产生了一个牛顿法求平方根的
Mathematica 程序的编译形式.
| Out[18]= |  |
| Out[19]= |  |