科学与工程通用框架 - 第 11 部分:CodeDom 优化编译器






4.92/5 (9投票s)
科学与工程的 CodeDom 优化编译器
- 下载公式编辑器和编译器源代码 - 150 KB
- 下载航空项目源代码 - 1.79 MB
- 下载航空项目安装程序 - 1.24 MB
- 下载航空 + 控制系统项目源代码 - 1.97 MB
- 下载航空 + 控制系统项目安装程序 - 1.27 MB
- 下载天文学 + OpenGL 图形项目源代码 - 2.46 MB
- 下载天文学 + OpenGL 图形项目安装程序 - 1.78 MB
- 下载重力项目源代码 - 177.27 KB
- 下载动态大气项目源代码 - 188.15 KB
- 下载人造地球卫星多普勒速度样本 - 39.46 KB
引言
性能在IT中扮演着重要角色。然而,通常情况下,快速应用开发更为重要。本文致力于解决这两个问题。上图展示了此开发的应用。您可以在我的其他CodeProject文章中找到更深入的应用描述。您也可以访问科学与工程通用框架主页。我的文章工程对象的虚拟现实框架和机械总成动力学模拟框架中包含了额外信息。掌握整个框架并非易事。因此,它现在有许多子版本。您可以从本页下载三个版本。第一个版本致力于航空,它包含了航空中常用的数学和3D动力学。第二个版本是更高级的航空版本。除了第一个版本,它还包含控制理论的传递函数。它可用于自动驾驶系统开发。第三个版本致力于天文学。它使我们能够使用星表(第2、7部分)。存在使用OPC服务器的实时SCADA版本。其他版本将在以后上传。
背景
编译器优化理论众所周知。使用CodeDom,我们可以生成有效的代码。例如,CodeDom中常用的.NET编码模式展示了该技术非常有用的应用。本文致力于CodeDom在高性能工程应用快速开发中的应用。
主要思想
主要思想是强大的数学编辑器方程的CodeDom编译。现在正在准备该编辑器的软件文档。本文主要是这项宏大工作的公告。尽管您可以从本页下载源代码。但是源代码是科学与工程通用框架的航空版本,CodeDom优化编译器是框架的一小部分。但该编译器仅在框架中非常有效。尽管主要思想和编译器可以在没有框架的情况下使用。
1. 公式编辑器的特点
公式编辑器有许多高级数学运算。例如,编辑器部分操作伽罗瓦域、欧几里德环和函子。这里我们将考虑一些与工程问题相关的功能。公式编辑器区分大小写,操作多种类型。计算结果取决于变量的类型。
1.1 数组
令sin(a)为公式编辑器的公式。它是什么意思?如果a是实变量,则公式结果是实数值。然而,如果a是数组,则sin(a)也是sin的分量计算数组。如果a是实数数组,b是实数变量,则a + b表示a的分量与b之和的数组。
1.2 函数
公式编辑器的任何函数都可以是变量或计算结果。作为结果的函数不是函数的值,而是整个函数。对于不了解泛函分析的人来说,这个事实似乎不寻常。例如,如果a是实变量,那么f(a)表示f的计算结果。如果a是函数,那么f(a)是函数的复合。这非常方便。例如,序列组件提供了三个变量
第一个变量是函数表横坐标的数组。第二个变量是纵坐标的数组。第三个变量是表定义的函数。让我们考虑这个函数的用法。我们有以下情况
公式1组件从绘图中导入然后导出函数f。公式2从公式1导入一个函数,从绘图导入另一个函数。实际上,它们是同一个函数。然后公式2将这些函数复合。
稍后,公式3计算这些函数。结果如下
红色曲线是函数,青色曲线是复合函数。
1.3 向量和矩阵
公式编辑器支持矩阵和向量运算。下面展示了向量和矩阵运算的使用示例
这些示例包括矩阵转置、矩阵乘积、矩阵求逆和3D向量的向量积。
1.4 狄拉克δ函数
公式编辑器支持狄拉克δ函数。常微分方程右侧存在δ函数表明结果函数不连续。下图显示了公式编辑器中δ函数的存在.
2. 公式编译示例
2.1 矩阵乘法
数学编辑器中的矩阵乘法如下所示
等式位于棕色背景上。变量a和b是矩阵。a和b的类型显示在图片的右下角。类型分别为Double[4, 5]和Double[5, 6]。编辑器将这些类型对象的乘积定义为矩阵乘积。编译结果如下所示
var_2[0, 0] = var_0[0, 0] * var_1[0, 0] + var_0[0, 1] * var_1[1, 0] + var_0[0, 2] * var_1[2, 0] + var_0[0, 3] * var_1[3, 0] + var_0[0, 4] * var_1[4, 0]; var_2[0, 1] = var_0[0, 0] * var_1[0, 1] + var_0[0, 1] * var_1[1, 1] + var_0[0, 2] * var_1[2, 1] + var_0[0, 3] * var_1[3, 1] + var_0[0, 4] * var_1[4, 1]; var_2[0, 2] = var_0[0, 0] * var_1[0, 2] + var_0[0, 1] * var_1[1, 2] + var_0[0, 2] * var_1[2, 2] + var_0[0, 3] * var_1[3, 2] + var_0[0, 4] * var_1[4, 2]; var_2[0, 3] = var_0[0, 0] * var_1[0, 3] + var_0[0, 1] * var_1[1, 3] + var_0[0, 2] * var_1[2, 3] + var_0[0, 3] * var_1[3, 3] + var_0[0, 4] * var_1[4, 3]; var_2[0, 4] = var_0[0, 0] * var_1[0, 4] + var_0[0, 1] * var_1[1, 4] + var_0[0, 2] * var_1[2, 4] + var_0[0, 3] * var_1[3, 4] + var_0[0, 4] * var_1[4, 4]; var_2[0, 5] = var_0[0, 0] * var_1[0, 5] + var_0[0, 1] * var_1[1, 5] + var_0[0, 2] * var_1[2, 5] + var_0[0, 3] * var_1[3, 5] + var_0[0, 4] * var_1[4, 5]; var_2[1, 0] = var_0[1, 0] * var_1[0, 0] + var_0[1, 1] * var_1[1, 0] + var_0[1, 2] * var_1[2, 0] + var_0[1, 3] * var_1[3, 0] + var_0[1, 4] * var_1[4, 0]; var_2[1, 1] = var_0[1, 0] * var_1[0, 1] + var_0[1, 1] * var_1[1, 1] + var_0[1, 2] * var_1[2, 1] + var_0[1, 3] * var_1[3, 1] + var_0[1, 4] * var_1[4, 1]; var_2[1, 2] = var_0[1, 0] * var_1[0, 2] + var_0[1, 1] * var_1[1, 2] + var_0[1, 2] * var_1[2, 2] + var_0[1, 3] * var_1[3, 2] + var_0[1, 4] * var_1[4, 2]; var_2[1, 3] = var_0[1, 0] * var_1[0, 3] + var_0[1, 1] * var_1[1, 3] + var_0[1, 2] * var_1[2, 3] + var_0[1, 3] * var_1[3, 3] + var_0[1, 4] * var_1[4, 3]; var_2[1, 4] = var_0[1, 0] * var_1[0, 4] + var_0[1, 1] * var_1[1, 4] + var_0[1, 2] * var_1[2, 4] + var_0[1, 3] * var_1[3, 4] + var_0[1, 4] * var_1[4, 4]; var_2[1, 5] = var_0[1, 0] * var_1[0, 5] + var_0[1, 1] * var_1[1, 5] + var_0[1, 2] * var_1[2, 5] + var_0[1, 3] * var_1[3, 5] + var_0[1, 4] * var_1[4, 5]; var_2[2, 0] = var_0[2, 0] * var_1[0, 0] + var_0[2, 1] * var_1[1, 0] + var_0[2, 2] * var_1[2, 0] + var_0[2, 3] * var_1[3, 0] + var_0[2, 4] * var_1[4, 0]; var_2[2, 1] = var_0[2, 0] * var_1[0, 1] + var_0[2, 1] * var_1[1, 1] + var_0[2, 2] * var_1[2, 1] + var_0[2, 3] * var_1[3, 1] + var_0[2, 4] * var_1[4, 1]; var_2[2, 2] = var_0[2, 0] * var_1[0, 2] + var_0[2, 1] * var_1[1, 2] + var_0[2, 2] * var_1[2, 2] + var_0[2, 3] * var_1[3, 2] + var_0[2, 4] * var_1[4, 2]; var_2[2, 3] = var_0[2, 0] * var_1[0, 3] + var_0[2, 1] * var_1[1, 3] + var_0[2, 2] * var_1[2, 3] + var_0[2, 3] * var_1[3, 3] + var_0[2, 4] * var_1[4, 3]; var_2[2, 4] = var_0[2, 0] * var_1[0, 4] + var_0[2, 1] * var_1[1, 4] + var_0[2, 2] * var_1[2, 4] + var_0[2, 3] * var_1[3, 4] + var_0[2, 4] * var_1[4, 4]; var_2[2, 5] = var_0[2, 0] * var_1[0, 5] + var_0[2, 1] * var_1[1, 5] + var_0[2, 2] * var_1[2, 5] + var_0[2, 3] * var_1[3, 5] + var_0[2, 4] * var_1[4, 5]; var_2[3, 0] = var_0[3, 0] * var_1[0, 0] + var_0[3, 1] * var_1[1, 0] + var_0[3, 2] * var_1[2, 0] + var_0[3, 3] * var_1[3, 0] + var_0[3, 4] * var_1[4, 0]; var_2[3, 1] = var_0[3, 0] * var_1[0, 1] + var_0[3, 1] * var_1[1, 1] + var_0[3, 2] * var_1[2, 1] + var_0[3, 3] * var_1[3, 1] + var_0[3, 4] * var_1[4, 1]; var_2[3, 2] = var_0[3, 0] * var_1[0, 2] + var_0[3, 1] * var_1[1, 2] + var_0[3, 2] * var_1[2, 2] + var_0[3, 3] * var_1[3, 2] + var_0[3, 4] * var_1[4, 2]; var_2[3, 3] = var_0[3, 0] * var_1[0, 3] + var_0[3, 1] * var_1[1, 3] + var_0[3, 2] * var_1[2, 3] + var_0[3, 3] * var_1[3, 3] + var_0[3, 4] * var_1[4, 3]; var_2[3, 4] = var_0[3, 0] * var_1[0, 4] + var_0[3, 1] * var_1[1, 4] + var_0[3, 2] * var_1[2, 4] + var_0[3, 3] * var_1[3, 4] + var_0[3, 4] * var_1[4, 4]; var_2[3, 5] = var_0[3, 0] * var_1[0, 5] + var_0[3, 1] * var_1[1, 5] + var_0[3, 2] * var_1[2, 5] + var_0[3, 3] * var_1[3, 5] + var_0[3, 4] * var_1[4, 5];
抱歉列出如此长的代码。但我实在无法抑制对我非常简单的数学公式被编译成如此高效且无循环的代码的事实所产生的极大敬佩。
2.2 向量计算
向量计算公式如下所示
var_5[0] = Math.Sin((double)var_4[0]); var_5[1] = Math.Sin((double)var_4[1]); var_5[2] = Math.Sin((double)var_4[2]); var_5[3] = Math.Sin((double)var_4[3]); var_5[4] = Math.Sin((double)var_4[4]); var_6[0] = (var_3) * ((double)var_5[0]); var_6[1] = (var_3) * ((double)var_5[1]); var_6[2] = (var_3) * ((double)var_5[2]); var_6[3] = (var_3) * ((double)var_5[3]); var_6[4] = (var_3) * ((double)var_5[4]); var_7 = Math.Cos(var_3); var_8[0] = ((double)var_6[0]) + (var_7); var_8[1] = ((double)var_6[1]) + (var_7); var_8[2] = ((double)var_6[2]) + (var_7); var_8[3] = ((double)var_6[3]) + (var_7); var_8[4] = ((double)var_6[4]) + (var_7); var_10[0] = Math.Tan((double)var_9[0]); var_10[1] = Math.Tan((double)var_9[1]); var_10[2] = Math.Tan((double)var_9[2]); var_10[3] = Math.Tan((double)var_9[3]); var_10[4] = Math.Tan((double)var_9[4]); var_11[0] = ((double)var_8[0]) + ((double)var_10[0]); var_11[1] = ((double)var_8[1]) + ((double)var_10[1]); var_11[2] = ((double)var_8[2]) + ((double)var_10[2]); var_11[3] = ((double)var_8[3]) + ((double)var_10[3]); var_11[4] = ((double)var_8[4]) + ((double)var_10[4]); var_12[0] = (var_2) * ((double)var_11[0]); var_12[1] = (var_2) * ((double)var_11[1]); var_12[2] = (var_2) * ((double)var_11[2]); var_12[3] = (var_2) * ((double)var_11[3]); var_12[4] = (var_2) * ((double)var_11[4]); var_13[0] = Math.Exp((double)var_12[0]); var_13[1] = Math.Exp((double)var_12[1]); var_13[2] = Math.Exp((double)var_12[2]); var_13[3] = Math.Exp((double)var_12[3]); var_13[4] = Math.Exp((double)var_12[4]);
我再次无法抑制我的钦佩之情。
2.3. 循环和
循环和看起来像
编译结果如下所示
var_1 = (double)var_0[0] + /* 100000 terms */;
最后我可以避免我的赞叹了。
2.4 符号微分
符号微分是公式编辑器的一个重要特性。例如,d/dt微分如下所示
第一个箭头是函数,第二个是它的d/dt导数。天哪,这个计算不够优化,因为它包含常数乘积。我开发公式编辑器已经十多年了,但还没有一个最佳解决方案。早期的Java版本编辑器可以在http://www.mathframe.com/applets/mathonline/indexE.htm找到。但我并非孤军奋战。你可以在Mathcad中插入复杂的函数。分数的分子等于分母。Mathcad不会给出零,而是给出一个复杂的公式。实际上,你不需要插入底部公式。你可以插入以下表达式
优化结果是
currentTree = trees[1]; currentArray = treeArray_1; var_1 = (double)currentTree.Calculate(currentArray); currentTree = trees[2]; currentArray = treeArray_2; var_2 = (double)currentTree.Calculate(currentArray); var_4 = Math.Pow(var_2, var_3); var_5 = (var_1) * (var_4); var_6 = Math.Sin(var_5); var_8 = Math.Pow(var_6, var_7); var_9 = (var_0) * (var_8); currentTree = trees[10]; currentArray = treeArray_10; var_10 = (double)currentTree.Calculate(currentArray); currentTree = trees[11]; currentArray = treeArray_11; var_11 = (double)currentTree.Calculate(currentArray); var_13 = Math.Pow(var_11, var_12); var_14 = (var_10) * (var_13); var_15 = Math.Cos(var_14); currentTree = trees[16]; currentArray = treeArray_16; var_16 = (double)currentTree.Calculate(currentArray); currentTree = trees[18]; currentArray = treeArray_18; var_18 = (double)currentTree.Calculate(currentArray); var_19 = (var_17) * (var_18); currentTree = trees[20]; currentArray = treeArray_20; var_20 = (double)currentTree.Calculate(currentArray); var_21 = (var_19) * (var_20); var_22 = (var_16) * (var_21); var_23 = (var_15) * (var_22); var_24 = (var_9) * (var_23);
我不知道这个结果是否最优。此外,我不知道这个结果是否正确。但是CPOL说:“不提供任何担保”。尽管有许可证,我仍然需要知道结果是否正确。然而,即使我的思维也无法检查编译结果的正确性。你能检查吗?但是该框架可以绘制结果。绘图如下所示
蓝色曲线是函数,红色曲线是其导数。即使我的大脑也明白结果是正确的。因此,没有可视化,编译器就没有意义。3D可视化在第7部分中描述。
3 高级示例。人造地球卫星的多普勒速度
3.1 库的导出
人造地球卫星运动的模拟需要地球引力模型和地球大气模型。因此,我们需要导出包含这些库的库。这个问题在第6部分中已经讨论过。导出可以通过以下方式进行。首先,我们在框架桌面设置库元素
右键单击它会打开其属性编辑器
然后我们选择必要的库,得到以下结果
该组件变为地球引力模型。同样,我们可以上传大气模型。
3.2 完整模型的构建。
使用上传的组件和框架组件,我们可以构建完整的工程问题模型。如下所示
除了重力和大气,它还包含诸如运动方程等辅助组件,用于求解常微分方程组。最终组件是距离和多普勒。距离的属性如下所示
这是3D解析几何的标准公式。a、b和c是观察者的坐标。该编辑器将求导阶数设置为1。因此,编译器提供距离及其一阶导数。编译结果如下所示
currentTree = trees[0]; currentArray = treeArray_0; var_0 = (double)currentTree.Calculate(currentArray); currentTree = trees[1]; currentArray = treeArray_1; var_1 = (double)currentTree.Calculate(currentArray); var_2 = (var_0) - (var_1); var_4 = Math.Pow(var_2, var_3); currentTree = trees[5]; currentArray = treeArray_5; var_5 = (double)currentTree.Calculate(currentArray); currentTree = trees[6]; currentArray = treeArray_6; var_6 = (double)currentTree.Calculate(currentArray); var_7 = (var_5) - (var_6); var_9 = Math.Pow(var_7, var_8); var_10 = (var_4) + (var_9); currentTree = trees[11]; currentArray = treeArray_11; var_11 = (double)currentTree.Calculate(currentArray); currentTree = trees[12]; currentArray = treeArray_12; var_12 = (double)currentTree.Calculate(currentArray); var_13 = (var_11) - (var_12); var_15 = Math.Pow(var_13, var_14); var_16 = (var_10) + (var_15); var_17 = Math.Sqrt(var_16); var_19 = (var_3) * (var_2); currentTree = trees[20]; currentArray = treeArray_20; var_20 = (double)currentTree.Calculate(currentArray); var_21 = (var_19) * (var_20); var_22 = (var_8) * (var_7); currentTree = trees[23]; currentArray = treeArray_23; var_23 = (double)currentTree.Calculate(currentArray); var_24 = (var_22) * (var_23); var_25 = (var_21) + (var_24); var_26 = (var_14) * (var_13); currentTree = trees[27]; currentArray = treeArray_27; var_27 = (double)currentTree.Calculate(currentArray); var_28 = (var_26) * (var_27); var_29 = (var_25) + (var_28); var_30 = (var_18) * (var_29); var_31 = (var_30) / (var_17);
var_17是距离,var_31是它的时间导数。我们可以通过多普勒分量访问多普勒速度
其中d提供了距离。
3.3 结果
模拟结果显示在以下图表中
第一个图表表示距离,第二个图表表示多普勒速度。很明显,第二个图表代表第二个图表的时间导数。
4 附加功能
编译器本身并不十分重要。它在框架中要强大得多。框架的数学编辑器提供符号微分。公式编辑器还包含一些重要功能。框架的所有元素本身都不太重要。真正重要的是作为一个集成对象的框架。
关注点
关于工程软件有很多神话
- 最好的工程软件语言是Fortran;
- .NET只适用于计算机网络和万维网;
- 面向对象编程不适用于工程师,因为他们只操作实数;
- 垃圾回收技术性能缓慢;
我希望我的文章能帮助消除这些神话。
历史
此软件的历史在我的前几篇文章中有所描述。主要缺点是软件文档的不足。我现在正在准备它。但这需要大量工作。前景比历史重要得多。