发表 回复 带附件回复 上篇 下篇 主题上篇 主题下篇 楼主 溯源 回版面 原文发信人: helotus (blue24), 信区: MathTools
标题: MathLink混合编程(一)
发信站: 同舟共济站 (2002年04月11日16:53:00 星期四), 站内信件
一点小东西,希望大家有用
****************************************
利用MathLink同高级语言混合编程(一)
概述
同外部程序进行交互是Mathematica中令人激动的一项功能。您可能学会了如何在Mathe
matica中通过输入语句的方法进行各种数值和符号运算。然而,您可能曾用一些高级计
算机语言编写过一些针对具体应用的计算程序或者函数,现在想在Mathematica中调用这
些函数;或者您想在自己的程序中集成Mathematica的强大计算功能。利用Mathematica
提供的MathLink系统,这些都能够轻松实现,您甚至可以通过网络调用运行在远程主机
上的程序。
Mathematica采用结构化和非结构化两种方式与外部程序进行通讯。结构化通讯是指把M athematica的表达式交给事先建立起来的处理这些表达式的外部程序,其基础就是Math
Link系统。非结构化通讯是指从外部程序中接收文本数据,进行读写操作。我们将要介
绍的是结构化的通讯方式,也可以看作是Mathematica同高级语言的混合编程。使用Mat
hLink可以把Mathematica建立的表达式发送给外部程序,或者将外部程序的结果读进来
。我们将重点介绍Mathematica同C/C++语言的混合编程,并通过一个实例介绍如何利用
Visuall C++编写MathLink程序以及如何在利用Visual C++创建的应用程序中集成Mathe matica的计算功能。
x.1初识MathLink
在深入讨论混合编程的细节之前,我们有必要首先搞清楚Mathematica进行结构化通讯的
基础MathLink的相关问题。
x.1.1什么是MathLink
MathLink是一种在程序之间交换Mathematica表达式的机制,它提供了一个外部程序同M athematics通讯的通用接口。通过内置或者插件技术,现有的许多软件都是现了对Math
Link的兼容性。这些程序都可以透明地建立同Mathematica间的连接。这种连接可以是在
同一台计算机上的本地连接,也可以是跨越计算机网络的远程连接,并且连接双方主机
可能是不同类型的计算机,如Microsoft Windows和Macintosh系统间可以建立连接。
同时,Mathematica提供了MahLink开发工具进行MathLink程序的设计。利用这个工具,
你可以方便地设计自己的兼容MathLink的应用程序。
通过MathLink实现的典型应用有:
? 在Mathematica内部调用外部程序的函数;
? 在外部程序中调用Mathematica的功能;
? 构建Mathematica可选择的前端应用程序;
? 在Mathematica和外部程序间交换数据;
? 在当前的Mathematica进程间交换数据。
MathLink库提包含一些能够实现在外部程序中发送以及接收Mathematica 表达式的程序
集合。MathLink的一个重要特征是完全平台无关的。也就是说用户的外部程序完全可以
不考虑MathLink运行在什么平台上,MathLink能够透明地使用您计算机系统中的内部程
序通讯机制。
x.1.2安装MathLink
在缺省的安装情况下,安装Mathematica时就会安装MathLink开发工具(MathLink Deve loper's Kit)。一般情况下MathLink Developer's Kit的安装目录是:Mathematica\4 .0\AddOns\MathLink\DevelopersKits\Windows。
安装MathLink主要包括两部分:系统要求的部分和编译程序要求的部分。
第一部分,系统要求的部分包括一些完成MathLink功能的动态连结库。大多数的MathLi
nk程序运行所需的共享库是:"ML32I2.DLL", "ML32I1.DLL", "MLMAP32.MLP", 和 "MLT CP32.MLP"。您可以在插件子目录的SystemAdditions目录中找到他们。
它们通常被安装Mathematica时被安装在系统可以找到的默认位置:
MathLink是作为一个共享的动态连接库实现的。当运行一个MathLink程序时,操作系统
将在磁盘上找到主动态连结库ML32I2.DLL并装载进系统中,并且建立同用户程序的关联
。这样,调用的MathLink函数被映射到动态连接库中的相应代码。在Windows操作系统中
,ML32I2.DLL一般被装载在Windows的系统目录下。除了ML32I2.DLL,还有一些辅助动态
连结库(如MLTCP32.MLP 和 MLMAP32.MLP),这些文件通常放在Windows目录下。
第二部分,编译程序要求的部分主要包括支持特定高级语言(这里是C语言)编译的一些
文件:.LIB 文件、"mathlink.h"头文件、"MPREP32.EXE"代码自动生成程序等,分别在
安装Mathematica时安装在目录中的结构是:
? "MLDev32/LIB 包含库文件
? "MLDev32/INCLUDE 包含头文件
? "MLDev32/BIN 包含代码自动生成程序
当创建一个MathLink程序时,需要导入一个库文件(一个 .LIB 文件)。这个导入库文件
被加入到工程中,它并不包含代码,只是为了方便用户输出同共享库中相同的函数名。
MathLink的C语言接口在头文件"mathlink.h"被详细说明。在调用MathLink共享库的所有
C或C++源文件中都应该包含这个头文件。
"MPREP"是一个32位的控制台应用程序,它可以根据模板自动地生成MathLink程序部分代
码。将它拷贝到您的开发工具的BIN目录下会给你的开发带来方便。
除了这两部分以外,安装MathLink时还会拷贝一些文档、例程等,这些都可以在对应的
文件夹中找到。
x.1.3 MathLink支持的编译器
MathLink是通过一个共享库实现的,因此它可以被任何遵从微软规定的动态连结库接口
标准的编译器。对于C/C++语言来说,常用编译器有:
? Borland C/C++
? Metrowerks CodeWarrior C/C++
? Microsoft Visual C++ and Visual Basic
? Symantec C/C++
? Watcom C/C++
以上的每一个开发工具都遵从一种"工程文件"的机制,通过它在开发环境中统一地管理
程序源文件、编译器选项、联接器以及调试器等。除了这种集成开发环境(IDE),他们
都提供一些从命令行运行的命令来编译生成代码。
x.1.4怎样用MathLink同外部程序交互
图1 Mathematica结构图
Mathematica的结构其实比较清晰:Mathematica Kernel + External Program,包括St andard Notebook front end都可以看作是一个完善的外部程序。显然,要使用Mathema tica提供的各项计算分析功能,必须启动Mathematica Kernel并建立外部程序与其的一
个联系通道。
上图说明,MathLink实际上提供了一种访问Mathematica内核以及内核访问外部程序的途
径。
--
蓝色,忧郁的颜色
蓝色,我最喜欢的颜色
蓝色,澄静,纯洁的感觉
这个世界有了蓝色,更加精彩.....
发表 回复 带附件回复 上篇 下篇 主题上篇 主题下篇 楼主 溯源 回版面 原文
发信人: helotus (blue24), 信区: MathTools
标题: MathLink混合编程(三)
发信站: 同舟共济站 (2002年04月11日16:54:12 星期四), 站内信件
x.3从外部程序中同Mathematica交互
这一节里,我们将向您介绍如何在外部程序中集成Mathematica的强大计算功能。这同上
一节中介绍的内容是不同的,上一节中我们关注的是如何在Mathematica中调用外部程序
中的函数以扩充Mathematica处理具体问题的能力。两者思路刚好相反,但都是混合编程
方面的内容,都达到了结合C语言灵活地处理能力和Mathematica强大数值、符号处理能
力的目的。
我们首先介绍在外部程序中调用Mathematica计算模块的原理,然后介绍编写此类外部程
序的一般步骤,最后给出一个"代数多项式展开"的例子。
x.3.1建立同Mathematica连接的原理
要在外部程序中利用Mathematica内部计算功能需要使用MathLink的许多一般的特征。
我们前面提到过,通过编写MathLink模板并调用相关程序我们可以得到一些MathLink自
动生成的代码。这些代码向外部程序提供与MathLink连结通讯的手段。也就是说,这些
代码封装了实际调用MathLink库的细节,我们所需要作的就是在程序中调用MLMain(arg
c, argv)函数,这在我们前面的外部程序中的主函数中可以看出。
我们先来看一下,调用MLMain(argc, argv)函数是如何建立这个连接的,我们以x.2.3中
提到程序的为例:
int MLMain( int argc, charpp_ct argv)
{
return _MLMain( argv, argv + argc, (charp_ct)0);
}
该函数直接调用了自动生成的另一个函数_MLMain:
static int _MLMain( charpp_ct argv, charpp_ct argv_end, charp_ct commandline )
{
MLINK mlp;
long err;
if( !stdenv)
stdenv = MLInitialize( (MLParametersPointer)0);
if( stdenv == (MLEnvironment)0) goto R0;
if( !stdyielder)
stdyielder = MLCreateYieldFunction( stdenv,
NewMLYielderProc( MLDefaultYielder), 0);
if( !stdhandler)
stdhandler = MLCreateMessageHandler( stdenv,
NewMLHandlerProc( MLDefaultHandler), 0);
mlp = commandline
? MLOpenString( stdenv, commandline, &err)
: MLOpenArgv( stdenv, argv, argv_end, &err);
if( mlp == (MLINK)0){
MLAlert( stdenv, MLErrorString( stdenv, err));
goto R1;
}
if( MLIconWindow){
char textbuf[64];
int len;
len = GetWindowText(MLIconWindow, textbuf, sizeof(textbuf)-2);
strcat( textbuf + len, "(");
_fstrncpy( textbuf + len + 1, MLName(mlp), sizeof(textbuf) - len -3);
textbuf[sizeof(textbuf) - 2] = '\0';
strcat( textbuf, ")");
SetWindowText( MLIconWindow, textbuf);
}
if( MLInstance){
if( stdyielder) MLSetYieldFunction( mlp, stdyielder);
if( stdhandler) MLSetMessageHandler( mlp, stdhandler);
}
if( MLInstall( mlp))
while( MLAnswer( mlp) == RESUMEPKT){
if( ! refuse_to_be_a_frontend( mlp)) break;
}
MLClose( mlp);
R1: MLDeinitialize( stdenv);
stdenv = (MLEnvironment)0;
R0: return !MLDone;
} /* _MLMain */
我们简单分析一下这个函数:
2 MLInitialize函数执行初始化过程以初始化MathLink库函数,是整个连接过程的第
一
步。调用任何MathLink库函数之前都要调用这个函数以完成连接环境的建立,系统将返
回一个MLEnvironment变量表明库初始化成功。
2 MLCreateYieldFunction函数创建输出函数并返回MLYieldFunctionObject变量表明
创
建成功。这不是建立连接所必需调用的函数。
2 MLCreateMessageHandler函数创建了连接的消息处理句柄并返回一个MLMessageHand l
erObject变量表明创建成功。这也不是建立连接所必需调用的函数。
2 MLOpenArgv和MLOpenString是两个比较重要的函数,他们实际完成建立同Mathemati
c
a Kernel的连接。两者的区别在于:MLOpenArgv将与程序主函数相同的参数传递给Math
Link以建立一个连接,而MLOpenString函数将所有参数集中放入一个字符串中传递给Ma
thLink以建立连接。主函数_MLMain通过变量commandline来区分到底调用两者中的哪一
个来建立连接。最后,函数返回一个MLINK变量标志创建连接的结果。
2 接下来是创建窗口,并利用函数MLSetYieldFunction和MLSetMessageHandler将创建的
输出函数和消息处理句柄同已经建立的连接联系起来。
2 主函数末尾的while语句是外部程序程序的主循环。
上面,我们介绍了利用mprep生成的源代码是如何建立同Mathematica连接的。有时为了
增加外部程序的灵活性或者出于其他的一些考虑,我们需要直接在程序中调用内部的这
些MathLink库函数以建立连接。当然,更多的情况下,我们希望能在外部程序中调用Ma
thLink的库函数以实现利用内核计算的目的。下面的一节里,我们着重介绍如何编写这
类外部程序。
x.3.2怎样编写此类外部程序
编写此类MathLink外部程序的重点是如何处理同Mathematica的数据交互问题。外部程序
在成功建立连接之后,通过调用MathLink库函数向Mathematica Kernel发送调用其内部
计算模块的请求,Kernel接收请求调用相应的模块进行处理之后将计算结果发送给外部
程序,外部程序调用相应的函数接收返回的计算结果。
Mathematica表达式提供了一种处理各种数据的通用的方法。我们可能希望在外部程序中
处理这些表达式。然而,类似C语言这样的高级计算机语言并没有提供直接存储、处理这
些表达式的方法。幸运的是,MathLink提供了使用环回连接(loopback links)来处理Mathematica表达式的方法,进而达到与Mathematica交互的目的。
MLINK MLLoopbackOpen(stdenv, long *erreo) 函数打开一个环回连接;
void MLClose(MLINK link) 函数关闭一个环回连接;
MLTransferExpression(MLINK dst, MLINK src)函数将表达式从源连接src传送到目标连接dst。
我们稍后会介绍这种使用环回使用Mathematica表达式的方法。
下面,我们先来看三个问题:
第一,外部程序如何发出请求?
我们首先来看下面的这个程序段:
MLENV ep = (MLENV)0;
MLINK lp = (MLINK)0;
//初始化MathLink库
ep = MLInitialize( (MLParametersPointer)0);
……
//打开一个连接
lp = MLOpenArgv( ep, argv, argv + argc, &err);
……
//调用MLPut*函数向内核传递命令及数据
MLPutFunction( lp, "EvaluatePacket", 1L);
MLPutFunction( lp, "FactorInteger", 1L);
MLPutInteger( lp, n); //n是参数
//同步函数表明包已经传送完毕
MLEndPacket( lp);
这一段函数完成了向内核传递数据包的工作,下面等待内核处理完毕后的相应。MLPutF
unction函数向内核传递了一个调用函数名,MLPutInteger向内核传递了一个整型变量。
有关这些函数的详细用法,请参考帮助文档。
第二,外部程序同内核两者如何保持同步?也就是说,外部程序如何判断Kernel是否
处
理完毕,以作进一步的处理:例如,将结果打印出来。下面的代码段通过判断MLNextPa
cket的返回值是否为RETURNPKT来同步内核的处理过程。
while ((p = MLNextPacket(link)) && p != RETURNPKT)
MLNewPacket(link);
下表列出了MLNextPacket可能的返回值及其意义:
Mathematica 包类型返回的常量说明
ReturnPacket[expr] RETURNPKT 计算的结果
ReturnTextPacket["string"] RETURNTEXTPKT 文本形式结果
InputNamePacket["name"] INPUTNAMEPKT 输入行名称
OutputNamePacket["name"] OUTPUTNAMEPKT 输出行名称
TextPacket["string"] TEXTPKT 函数的文本形式输出
MessagePacket[symb,"tag","string" MESSAGEPKT Mathematica产生的消息DisplayPacket["string"] DISPLAYPKT 图片后文的一部分
DisplayEndPacket["string"] DISPLAYENDPKT 图片后文的结尾
InputPacket["prompt"] INPUTPKT 请求一个输入函数的相应
CallPacket[I,list] CALLPKT 请求调用一个外部函数
表三 MLNextPacket的返回值及其意义
第三,外部程序如何处理这些返回的数据?我们可以调用MLGetNext库函数依次得到ke
rnel传递给外部程序的每一个对象。
根据MLGetNext的不同返回值,我们可以编写相应的C语言语句作出处理。
下表列出了MLGetNext函数可能的返回值及其意义:
MLTKERR 出错标志
MLRKINT 整数变量
MLTKFUNC 复合函数
MLTKREAL 实数变量
MLTKSTR 字符串
MLTKSYM 符号变量
表四 MLGetNext返回值及其意义
下面我们来看一段代码,着重说明MLGetNext函数的用法:
static void read_and_print_expression( MLINK lp)
{
kcharp_ct s;
int n;
long i, len;
double r;
static int indent;
switch( MLGetNext( lp)) {
case MLTKSYM:
MLGetSymbol( lp, &s);
printf( "%s ", s);
MLDisownSymbol( lp, s);
break;
case MLTKSTR:
MLGetString( lp, &s);
printf( "\"%s\" ", s);
MLDisownString( lp, s);
break;
case MLTKINT:
MLGetInteger( lp, &n);
printf( "%d ", n);
break;
case MLTKREAL:
MLGetReal( lp, &r);
printf( "%g ", r);
break;
case MLTKFUNC:
indent += 3;
printf( "\n %*.*s", indent, indent, "");
if( MLGetArgCount( lp, &len) == 0){
error( lp);
}else{
read_and_print_expression( lp);
printf( "[");
for( i = 1; i <= len; ++i){
read_and_print_expression( lp);
if( i != len) printf( ", ");
}
printf( "]");
}
indent -= 3;
break;
case MLTKERROR:
default:
error( lp);
}
}
上面的程序使用switch语句判断MLGetNext( lp)的返回值,以分别调用处理不同类型返回对象的代码。然后将返回的数据储存在预先定义的几个内部变量中,最后将结果打印
出来。
2 MLGetSymbol得到返回的符号变量,MLDisownSymbol释放为其分配的内存;
2 MLGetString得到返回的字符串变量;MLDisownString释放为其分配的内存;
2 MLGetInteger得到返回的整型变量;
2 MLGetReal得到返回的实型变量;
2 对返回MLTKFUNC的处理相对复杂一些:输出程序先调用MLGetArgCount得到了该复合函
数的参数个数,然后递归调用read_and_print_expression本身将每个参数输出。注意,
indent是控制缩进格式的变量;
2 如果MLGetNext返回了MLTKERROR就调用错误处理函数error( lp)输出错误信息。接下来,我们给出利用上面说明的三个步骤编写的外部程序的主程序。该程序计算一个
代数方程式ax+b=0,其中x是方程的变元。
int main(int argc, char* argv[])
{
char* s="3+6";
char* q="(x-1)^2==0";
int pkt;
double result;
init and openlink( argc, argv);
printf( "Computing... \n");
MLPutFunction(lp,"Solve", 2);
MLPutFunction(lp,"Equal", 2);
MLPutFunction(lp,"Plus", 2);
MLPutFunction(lp,"Times",2);
MLPutSymbol(lp,"a");
MLPutSymbol(lp,"x");
MLPutSymbol(lp,"b");
MLPutInteger(lp,0);
MLPutSymbol(lp,"x");
MLEndPacket(lp);
while( (pkt = MLNextPacket( lp), pkt) && pkt != RETURNPKT) {
MLNewPacket( lp);
if (MLError( lp))
error( lp);
}
read_and_print_expression(lp);
MLPutFunction( lp, "Exit", 0);
return 0;
}
程序的输出如下:
List[
List[
Rule[x,
Time[-1,
Power[a,-1],b]]]]
我们可以将其转换成通常的形式:
要说明一点:外部程序同Mathematica之间的数据都是以表达式的形式传递的。例如,"
5+6"应该转化为表达式Plus[5,6]才能为内核识别,同样内核向外部程序返回Mathemati ca表达式。用户的外部程序要能够处理这些表达式。上面的程序只是简单地传送和显示
计算结果,并不复杂。如果读者想要编写更加复杂的混合程序,就要熟练掌握Mathemat
ica的表达式。
有关Mathematica 表达式的相关问题,请参考前面的相关章节及Mathematica的帮助文档。
到现在为止,我们介绍了在外部程序中同Mathematica进行交互的基本的问题,并简单分
析了一些代码。下面,我们将介绍本节开始时提到的在外部程序中利用环回处理Mathem atica表达式的方法。
在使用环回同前需要建立一个连接并打开这个环回,使用完毕后要及时关闭。假设我们
已经建立了一条连接:
MLINK lp = (MLINK)0;
lp = MLOpenArgv( ep, argv, argv + argc, &err);
下面的代码显示了利用环回处理表达式的框架:
ml = MLLoopbackOpen(stdenv, &errno); //打开一个环回
//将表达式 Power[x, 3] 放入这个环回连接上
MLPutFunction(ml, "Power", 2);
MLPutSymbol(ml, "x");
MLPutInteger(ml, 3);
……
//从环回连接上得到一个表达式
MLGetFunction(ml, &head, &n);
MLGetSymbol(ml, &sname);
MLGetInteger(ml, &k);
……
MLClose(ml); //关闭这个环回
上面的这段代码说明了如何打开一个环回,如何通过MLPut族函数和MLGet族函数操纵这
个环回连接上的表达式以及如何关闭一个环回的方法。那么,究竟如何利用已经建立起
来的连接lp同Mathematica交换表达式呢?这要用到一个重要的库函数MLTransferExpre ssion()。
通过调用MLTransferExpression(),我们可以将在环回上建立的表达式传送给Mathemat ica,同时也可以接收来自Mathematica的表达式并将其存储在环回上以便在外部程序中
作进一步的处理。
假设我们要求一个指数函数e^2的值指数函数的值,我们可以如下处理:
double result;
ml = MLLoopbackOpen(ep, &errno); //打开一个环回
//将表达式e^2放入这个环回连接上
MLPutFunction(ml,"N",1);
MLPutFunction(ml, "Exp",1);
MLPutInteger(ml, 2);
MLTransferExpression(lp,ml); //向Mathematica发送数据
MLEndPacket(lp);
while( (pkt = MLNextPacket( lp), pkt) && pkt != RETURNPKT) {
MLNewPacket( lp);
if (MLError( lp))
error( lp);
}
MLTransferExpression(ml,lp); //从Mathematica接收数据
//从环回连接上得到一个表达式
MLGetReal(ml, &result);
printf("The result is: %f \n",result);
MLClose(ml); //关闭这个环回
这个程序建立了一个环回连接,在向环回连接上输入表达式后调用库函数MLTransferEx pression将环回上的表达式传递给Mathematica,然后等待Mathematica将结果传回,又一次调用库函数MLTransferExpression将结果传入环回连接。最后调用MLGetReal函数将结果(一个实型变量)保存在一个浮点型变量result中。
在这一节里,我们重点介绍了在外部程序同Mathematica交换数据的一般方法并通过几个
实例说明如何编写这类外部程序。要编写好C语言的混合程序,最重要的就是深刻理解、
熟练掌握Mathematica的表达式。在此基础上灵活应用各类MathLink库函数,就能编写实用的混合计算程序。
x.3.3实例
我们在工程实践中往往会遇到需要求问题解析解的情况。例如,有时我们经过某种抽象
得到一个以代数式表示的求解公式。对于这个求解公式,我们希望将其表示成为多项式
的形式。如果用标准C语言编程将会比较困难,但是这在Mathematica中是容易做到的。
下面的例子演示了如何通过外部程序调用Mathematica的计算模块以实现这个功能。
/* math1.c
在命令行中运行这个程序:math1 -linkmode -launch
*/
#include
#include
#include "mathlink.h"
#if MACINTOSH_MATHLINK
extern int mlmactty_init( char*** argvp);
#endif
static void init_and_openlink( int argc, char* argv[]);
static void error( MLINK lp);
MLENV ep = (MLENV)0;
MLINK lp = (MLINK)0;
MLINK ml = (MLINK)0;
FILE* fp; //将计算的结果输出到文件中
static void read_and_print_expression_tofile( MLINK lp)
{
kcharp_ct s;
int n;
long i, len;
double r;
static int indent;
switch( MLGetNext( lp)) {
case MLTKSYM:
MLGetSymbol( lp, &s);
fprintf(fp, "%s ", s);
MLDisownSymbol( lp, s);
break;
case MLTKSTR:
MLGetString( lp, &s);
fprintf(fp, "\"%s\" ", s);
MLDisownString( lp, s);
break;
case MLTKINT:
MLGetInteger( lp, &n);
fprintf(fp, "%d ", n);
break;
case MLTKREAL:
MLGetReal( lp, &r);
fprintf(fp, "%g ", r);
break;
case MLTKFUNC:
indent += 3;
fprintf(fp, "\n %*.*s", indent, indent, "");
if( MLGetArgCount( lp, &len) == 0){
error( lp);
}else{
read_and_print_expression_tofile( lp);
fprintf(fp, "[");
for( i = 1; i <= len; ++i){
read_and_print_expression_tofile( lp);
if( i != len) fprintf(fp, ", ");
}
fprintf(fp, "]");
}
indent -= 3;
break;
case MLTKERROR:
default:
error( lp);
}
}
int main(int argc, char* argv[])
{
char* s="3+6";
char* q="(x-1)^2==0";
int pkt;
double result;
if((fp=fopen("e://math.txt","w"))==NULL)
{
printf("Open files Error!\n");
exit(1);
}
init_and_openlink( argc, argv);
printf( "Computing... \n");
//在这里将代数式转换成为MathLink表达式的形式
MLPutFunction(lp,"Collect", 2);
MLPutFunction(lp,"Expand", 1);
MLPutFunction(lp,"Power", 2);
MLPutFunction(lp,"Plus", 4);
MLPutInteger(lp,1);
MLPutSymbol(lp,"x");
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,2);
MLPutSymbol(lp,"y");
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,3);
MLPutSymbol(lp,"z");
MLPutInteger(lp,3);
MLPutFunction(lp,"List", 2);
MLPutSymbol(lp,"x");
MLPutSymbol(lp,"y");
MLEndPacket(lp);
while( (pkt = MLNextPacket( lp), pkt) && pkt != RETURNPKT) { MLNewPacket( lp);
if (MLError( lp))
error( lp);
}
read_and_print_expression_tofile(lp);
MLPutFunction( lp, "Exit", 0);
fclose(fp);
return 0;
}
static void error( MLINK lp)
{
if( MLError( lp)){
fprintf( stderr, "Error detected by MathLink: %s.\n",
MLErrorMessage(lp));
}else{
fprintf( stderr, "Error detected by this program.\n");
}
exit(3);
}
static void deinit( void)
{
if( ep) MLDeinitialize( ep);
}
static void closelink( void)
{
if( lp) MLClose( lp);
}
static void init_and_openlink( int argc, char* argv[])
{
long err;
#if MACINTOSH_MATHLINK
MLYieldFunctionObject yielder;
argc = mlmactty_init( &argv);
#endif
ep = MLInitialize( (MLParametersPointer)0);
if( ep == (MLENV)0) exit(1);
atexit( deinit);
#if MACINTOSH_MATHLINK
yielder = MLCreateYieldFunction( ep, NewMLYielderProc( MLDefaultYielder), 0
);
#endif
lp = MLOpenArgv( ep, argv, argv + argc, &err);
if(lp == (MLINK)0) exit(2);
atexit( closelink);
#if MACINTOSH_MATHLINK
MLSetYieldFunction( lp, yielder);
#endif
}
程序的输出如下:
Plus [3 ,
Times [6 , y ],
Times [9 , z ]]],
Times [
Power [y , 2 ],
Plus [12 ,
Times [36 , z ]]],
Times [y ,
Plus [6 ,
Times [36 , z ],
Times [54 ,
Power [z , 2 ]]]],
Times [x ,
Plus [3 ,
Times [12 ,
Power [y , 2 ]],
Times [18 , z ],
Times [27 ,
Power [z , 2 ]],
Times [y ,
Plus [12 ,
Times [36 , z ]]]]]]
在上面的程序中,我们需要展开的代数式是已知的。在一些情况下,根据工程应用的具
体情况,我们需要动态地生成这些代数式。这时,可以对上面的程序作出某些改动,"动
态"地调用MLPut族函数生成相应的Mahtematica表达式。
下面的例子是一个工程中经常遇到得线性规划问题。
目标函数是w=x+5y-8z,我们求其在约束条件:x+7y>10,2x-8z<=19和x+y+z<10条件下的
最小值。
为了节省篇幅,我们仅给出表达式转换的代码以及程序的输出。
MLPutFunction(lp,"N", 1);
MLPutFunction(lp,"ConstrainedMin", 3);
MLPutFunction(lp,"Plus", 3); //目标函数w=x+5y-8z
MLPutSymbol(lp,"x");
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,5);
MLPutSymbol(lp,"y");
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,-8);
MLPutSymbol(lp,"z");
MLPutFunction(lp,"List", 3);
MLPutFunction(lp,"Greater", 2); // 条件x+7y>10
MLPutFunction(lp,"Plus", 2);
MLPutSymbol(lp,"x");
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,7);
MLPutSymbol(lp,"y");
MLPutInteger(lp,10);
MLPutFunction(lp,"LessEqual", 2); // 条件2x-8z<=19
MLPutFunction(lp,"Minus", 2);
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,2);
MLPutSymbol(lp,"x");
MLPutFunction(lp,"Times", 2);
MLPutInteger(lp,8);
MLPutSymbol(lp,"z");
MLPutInteger(lp,19);
MLPutFunction(lp,"Less", 2); // 条件x+y+z<10
MLPutFunction(lp,"Plus", 3);
MLPutSymbol(lp,"x");
MLPutSymbol(lp,"y");
MLPutSymbol(lp,"z");
MLPutInteger(lp,10);
MLPutFunction(lp,"List", 3);
MLPutSymbol(lp,"x");
MLPutSymbol(lp,"y");
MLPutSymbol(lp,"z");
程序的输出结果为:
List [-61.4286 ,
List [
Rule [x , 0 ],
Rule [y , 1.42857 ],
Rule [z , 8.57143 ]]]
即:在x->0 , y-> 1.42847 , z->8.57143时,在上述条件x+7y>10,2x-8z<=19和x+y+z <10的约束下,表达式x+5y-8z趋于最小值-61.4286
--
蓝色,忧郁的颜色
蓝色,我最喜欢的颜色
蓝色,澄静,纯洁的感觉
这个世界有了蓝色,更加精彩.....
发表 回复 带附件回复 上篇 下篇 主题上篇 主题下篇 楼主 溯源 回版面 原文发信人: helotus (blue24), 信区: MathTools
标题: MathLink混合编程(四)
发信站: 同舟共济站 (2002年04月11日16:54:41 星期四), 站内信件
x.4高级主题
在本章的最后一节里,我们打算讨论几个同MathLink相关的高级主题。有些内容可能超
出了混合编程的范围,然而我们希望通过这对几个主题的讨论加深大家对MathLink结构
的认识。
x.4.1 Session间的通讯
Mathematica中,几个Session之间进行通讯是可能的。这可能出于以下两种考虑:
2 在两个Session之间简单地交换数据,取代利用中间文件交换数据的方法;
2 将一个计算任务分配到不同的Session上;
另外利用操作系统提供的网络通讯功能,我们将能在几台主机上同时进行一个计算任务
不同部分的运算。这也可以借助Session间的通讯机制来实现。其实,Mathematica的这
种Session间的通讯机制同样是由MathLink来实现的,每一个Session都相当于一个Math
Link外部程序。
下表列出了常用的进行Session间通讯的Mathematica函数:
Mathematica函数说明
LinkCreate["name"] 创建一个MathLink链接
LinkConnect["name"] 连接由其他程序创建的MathLink链接
LinkClose[link] 关闭MathLink连接
LinkWrite[link,expr] 向MathLink连接写入表达式
LinkRead[link] 从MathLink连接中读出表达式
LinkRead[link,Hold] 读表达时,并立即用Hold绑定它
LinkReadyQ[link] 查找是否有准备要从链接读出的数据
表五 Session间通讯的Mathematica函数
下面我们看几个例子:
2 利用端口8000建立一条连接
Session A:
In[1]:=link=LinkCreate["8000"]
Out[1]=LinkObject[8000,4,4]
Session B:
In[1]:=link=LinkConnect["8000"]
Out[1]=LinkObject[8000,4,4]
2 发送/接收数据
Session A:
In[1]:=LinkWrite[link , 15!]
Session B:
In[1]:=LinkRead[link]
Out[1]=1307674368000
2 发送/接收表达式
Session A:
In[1]:=LinkWrite[link , Unevaluated[2 + 2]]
Session B:
In[1]:=LinkRead[link , Hold]
Out[1]=Hold[2 +2]
2 利用未分配的端口建立一条连接
Session HostA:
In[1]:=link=LinkCreate[]
Session HostB:
In[1]:=LinkRead[link]
2 关闭连接
Session HostA:
In[1]:=LinkClose[link]
MathLink利用操作系统提供的网络通讯服务,并不依赖具体的操作系统和通讯协议,因
此可以在两种使用不同操作系统的主机间建立连接。
x.4.2同前端(front ends)的通讯
Mathematica内核用MathLink与Mathematica前端通信。如果从一个前端启动了一个Math ematica内核,则可以通过到该前端MathLink连接来控制该内核。
全局变量$ParentLink指定了特定核心用来输入输出的MathLink连接。在一个Mathemati
ca会话(Session)中间重置$ParentLink有时会很有用,可以有效地改变核心要链接的
前端。
就象Mathematica核心,标准的Mathematica NoteBook前端操作指定的MathLink包。
通常如果要从核心控制Mathematica前端,那最好使用象NotebookWrite和FrontEndExec ute这样的进程。但在某些时候,直接用LinkWrite给前端发送包还方便些。
x.4.3通过网络运行远程机上的MathLink 程序
MathLink允许调用Mathematica的外部程序,即使这个程序在远程机上运行。在一般情况
下,可以从远程机的操作系统中直接启动程序,但可以在自己的Mathematica会话中用命
令来连接它。
我们用上面创建的外部程序addtwo为例子说明建立连接的过程。我们在局域网的主机Ti
ger上运行这个外部程序:
addtwo -linkcreate 8000
这将建立一条8000端口的连接。
然后在另外一台主机Circle上运行Mathematica并打开一个会话,输入:
Install[LinkConnect["8000@Tiger"]]
这将加载这条连接对应的外部程序。接下来就可以利用前面提到的方法调用外部程序中
的函数了。需要注意的是,使用完毕要卸载这个外部程序,并中断连接。
另外,用mprep创建的外部程序通常包含设置MathLink连接的代码。如果直接从操作系统
中启动这样的程序,那么他们就要提示您指定您想要的那种类型的连接。但如果您的操
作系统支持,您还可以把这信息作为命令行给外部程序。
x.4.2MathLink的错误及中断处理
自己编写的MathLink外部程序在进行数据交换的过程中可能会出现各种各样的错误。当
发生错误以后,MathLink将会转换到非活动的状态。这时,你调用的所有MathLink库函
数将返回0。
我们通常需要在调用完一系列复杂的函数后处理这一过程中可能出现的错误。如果发现
已经产生了一个错误,就必须调用函数MLClearError()以重新激活MathLink。
例如我们在前面的程序中介绍过,在向内核发送数据后需要调用MLNextPacket检查是否
返回了计算结果。下面的代码中调用了MLError函数以捕捉可能出现的错误,并调用错误
处理函数error进行处理。
while( (pkt = MLNextPacket( lp), pkt) && pkt != RETURNPKT) {
MLNewPacket( lp);
if (MLError( lp))
error( lp);
}
下面列出三个针对错误处理的函数:
函数说明
Long MLError(MLINK link) 返回当前错误的代码,如果没有出错就返回0
char *MLErrorMessage(MLINK link) 返回描述当前错误的字符串
int MLClearError(MLINK link) 清除当前的错误,返回值表示是否能将Mathlink转换到
活动状态
表六 MathLink错误处理函数
发生错误以后,通常需要将这条连接上正在处理的剩余的包或者表达式丢弃。可以调用MLNewPacket()达到目的,正如上面的代码中所示的那样:在尚未接收到内核返回的数据
包之前用MLNewPacket( lp)将刚才接收到的包丢弃。
最后介绍一下MathLink的中断处理:
如果在Mathematica执行外部函数的过程中试图中断它的运行,Mathemaica将会将外部程
序中的全局变量MLAbort置为1。MathLink无法自动从一个外部函数调用中返回。所以,
如果你的外部函需要进行较长时间的运算或处理,最好在程序中的适当位置添加检验ML
Abort全局变量的代码以识别Mathematica的中断请求并适时返回。
--
蓝色,忧郁的颜色
蓝色,我最喜欢的颜色
蓝色,澄静,纯洁的感觉
这个世界有了蓝色,更加精彩.....
发表 回复 带附件回复 上篇 下篇 主题上篇 主题下篇 楼主 溯源 回版面 原文发信人: helotus (blue24), 信区: MathTools
标题: 应用软件Mathematica (1)
发信站: 同舟共济站 (2002年04月11日16:57:48 星期四), 站内信件
---------------------------------------------------------------------注:为了对Mathematica有一定了解的同学系统掌握Mathematica的强大
功能,我们把它的一些资料性的东西整理了一下,希望能对大家有所帮助。
---------------------------------------------------------------------一、运算符及特殊符号
Line1; 执行Line,不显示结果
Line1,line2 顺次执行Line1,2,并显示结果
?name 关于系统变量name的信息
??name 关于系统变量name的全部信息
!command 执行Dos命令
n! N的阶乘
!!filename 显示文件内容
< Expr>> filename 打开文件写 Expr>>>filename 打开文件从文件末写 () 结合率 [] 函数 {} 一个表 <*Math Fun*> 在c语言中使用math的函数 (*Note*) 程序的注释 #n 第n个参数 ## 所有参数 rule& 把rule作用于后面的式子 % 前一次的输出 %% 倒数第二次的输出 %n 第n个输出 var::note 变量var的注释 "Astring " 字符串 Context ` 上下文 a+b 加 a-b 减 a*b或a b 乘 a/b 除 a^b 乘方 base^^num 以base为进位的数 a-b 减 a*b或a b 乘 a/b 除 a^b 乘方 base^^num 以base为进位的数 lhs&&rhs 且 lhs||rhs 或 !lha 非 ++,-- 自加1,自减1 +=,-=,*=,/= 同C语言 >,<,>=,<=,==,!= 逻辑判断(同c) lhs=rhs 立即赋值 lhs:=rhs 建立动态赋值 lhs:>rhs 建立替换规则 lhs->rhs 建立替换规则 expr//funname 相当于filename[expr] expr/.rule 将规则rule应用于expr expr//.rule 将规则rule不断应用于expr知道不变为止 param_ 名为param的一个任意表达式(形式变量) param__ 名为param的任意多个任意表达式(形式变量) -- -- 蓝色,忧郁的颜色 蓝色,我最喜欢的颜色 蓝色,澄静,纯洁的感觉 这个世界有了蓝色,更加精彩.....