MATLAB、API详解
【例12.1.4-1】有一个绘圆的M脚本文件circle.m如下。希望获得一个MEX绘圆程序。 (1)原始的绘圆脚本文件 [circle.m]
clf;r=2;t=0:pi/100:2*pi;x=r*exp(i*t); plot(x,'r*');axis('square')
(2)对这脚本文件直接编译将因错误而失败 mcc -x circle
??? Error: File \"circle\" is a Script M-file and cannot be compiled with the current Compiler.
Error in ==> H:\\MATLAB53\oolbox\\compiler\\mcc.dll
(3)把脚本文件改写成函数文件。 [circle_f.m]:
function circle_f(r)
clf;t=0:pi/100:2*pi;x=r*exp(i*t); plot(x,'r*');axis('square')
(4)再对circle_f.m进行编译,将顺利通过。
mcc -x circle_f
%mcc是编译指令,详见12.4节。
(5)运行生成的MEX文件circle_f.dll
circle_f(0.5) %调用circle_f绘制一半径为0.5的圆 which circle_f %查询所调用的circle_f的路径全称。 d:\\mywork\\circle_f.dll 0.50.40.30.20.10-0.1-0.2-0.3-0.4-0.5-0.500.5图12.1.4-1 MEX文件circle_f.dll所画的圆
1
图 12.2.1-1 【Select MATLAB Componets】对话窗的选项局部图
图 12.2.2.1-1 为产生MEX文件所产生的配置屏1
2
图 12.2.2.1-2 为产生MEX文件所产生的配置屏2
12.1.1.1 配置正确性的验证
(1)mex应用程序的验证
cd d:\\mywork %把用户目录指定为当前目录 mex my_yprime.c %由my_yprime.c文件生成my_yprime.dll文件 my_yprime(1,1:4) %运行my_yprime.dll文件 which my_yprime %获得my_yprime.dll文件的位置信息 ans =
2.0000 8.9685 4.0000 -1.0947
d:\\mywork\\my_yprime.dll
(2)在MATLAB命令窗中验证mcc应用程序
mcc -x my_yprime_m my_yprime_m(1,1:4) which my_yprime_m ans =
2.0000 8.9685 4.0000 -1.0947
d:\\mywork\\my_yprime_m.dll
%<1>
(3)在 DOS提示符后验证mex、mcc应用程序
3
图 12.2.2.2-1 在DOS状态下验证编译指令
12.1.2 为产生独立外部应用程序进行预配置
12.1.2.1 对MATLAB编译器mbuild应用程序的设置(2.0版)
图 12.2.3.1-1 为产生独立外部应用程序所产生的配置屏1
4
图 12.2.3.1-2 为产生独立外部应用程序所产生的配置屏2
12.1.2.2 对MATLAB编译器mbuild应用程序的设置(1.2版) 12.1.2.3 配置正确性的验证
(1)mbuild应用程序的验证
cd d:\\mywork mbuild my_ex1.c
%把用户目录指定为当前目录
%在d:\\mywork下生成my_ex1.exe文件
图 12.2.3.2-1 在DOS窗口运行验证程序my_ex1.exe所得的结果
%<1>
(2)mcc应用程序的验证
mcc -p my_hello.m
5
图12.2.3.2-2 在DOS窗口运行验证程序my_hello.exe所得的结果
(3)在DOS状态下验证mbuild、mcc工作正确性
图 12.2.3.2-3 在DOS状态验证编译指令
12.2 MATLAB编译器使用入门
12.2.1 由M文件创建C MEX文件的入门算例
【例12.3.1-1】先编写M文件,然后生成相应的MEX文件。该文件用以判断方阵是否奇异。
cd d:\\mywork
(1)在MATLAB的编辑器中,编写下面的函数文件exm1.m 。 [exm1.m]
function y=exm1(A) [m,n]=size(A); if m~=n;
error('An input matrix should be n-by-n.') end
r=rank(A); if r==m
disp('This matrix is nonsigular') else
disp('This matrix is sigular') end
(2)把该函数文件存放于用户目录d:\\mywork下。
(3)在MATLAB命令窗中,运行以下指令对exm1.m进行编译。
6
mcc -x exm1
%编译m文件。
(4)调用MEX文件exm1.dll 进行计算
A=[1,0,1;2,1,0;4,1,4]
exm1(A) %调用exm1来判断矩阵A是否奇异。 which exm1 %查询所调用的exm1的路径和全称。 A =
1 0 1 2 1 0 4 1 4
This matrix is nonsigular d:\\mywork\\exm1.dll
12.2.2 由M文件创建外部应用程序的入门算例
【例12.3.2-1】建立一个脱离MATLAB环境,可独立运行的外部程序。该程序的功能是:对于给定矩阵A,如果存在S使得S-1AS=Λ,则要求出一个S,否则给出信息说明所给的矩阵A不能对角化。
(1)编写两个M函数文件:exm2.m和exm2_f.m 。(第一个文件是主文件。) [exm2.m]
function exm2
A=[4,0,0;0,3,1;0,1,3]; S=exm2_f(A)
[exm2_f.m]
function S=exm2_f(A) [m,n]=size(A); if m~=n
error('输入矩阵应是方阵!'); end;
e=eig(A);
%检查输入矩阵的特征值是否各异 same=0;
for i=1:m-1
for j=(i+1):m if e(j)==e(i) same=1; end end end
% A可以对角化的条件是A具有互异特征值或者A为埃尔米特矩阵。 if any(any((A'-A)))&(same==1) error('矩阵无法对角化!'); end
[v,d]=eig(A); S=v;
(2)把这两个函数文件存放于用户自己的目录d:\\mywork,并在MATLAB中运行检验。
exm2 S =
1.0000 0 0 0 0.7071 0.7071 0 0.7071 -0.7071
7
(3)生成独立的外部可执行程序。在MATLAB指令窗中,运行如下指令
mcc -m exm2 exm2_f
(4)打开DOS窗口,在d:\\mywork目录下,运行exm2.exe,得如图12.3.2-1的结果。
图12.3.2-1 在DOS窗口运行生成程序exm2.exe所得的结果
12.3 编译指令mcc简介
12.3.1 mcc的基本调用格式 12.3.2 mcc的选项标志 12.3.2.1 编译器2.0选项简介 12.3.2.2 在命令行中指定选项标志
【例12.4.2.2-1】假设当前目录上存在一个文件exm3.m ,现要求利用M编译器将它转换为C++语言的源码文件,并要求将原M文件中那注释区的内容作为所得C++源码文件的注释。 mcc -t -L Cpp -A annotation:comments exm3 %得到exm3.cpp和exm3.hpp
12.3.2.3 设置缺省选项
【例12.4.2.3-1】假设已在d:\\mywork下创建了文本文件mccstartup (请注意:该文件不可带扩展名),该文件的内容包括若干编译指令的选项。现要求利用该文件,从exm3.m出发得到C++语言的源文件。
cd d:\\mywork type mccstartup -t -L C -A annotation:none
!copy mccstartup h:\\y9857\emp !copy exm3.m h:\\y9857\emp !del mccstartup , !del exm3* dir mccstartup dir h:\\matlab53\\bin\\mccstartup dir exm3* 1 file(s) copied 1 file(s) copied mccstartup not found.
%将d:\\mywork设为当前工作目录
%打印文本文件mccstartup的内容 %将mccstartup转移到临时目录下 %将待编译M源码转移到临时目录下 %删除mccstartup %删除原有的编译后文件
%当前目录下是否含有mccstartup % 8 h:\\matlab53\\bin\\mccstartup not found. exm3* not found. !copy h:\\y9857\emp\\exm3.m d:\\mywork mcc -L Cpp exm3 %<1> 1 file(s) copied ??? Error: The options specified will not generate any output files. Please use one of the following options to generate an executable output file: -x (generates a MEX-file executable using C) -m (generates a stand-alone executable using C) -p (generates a stand-alone executable using C++) -S (generates a Simulink MEX S-function using C) Or type mcc -? for more usage information. Error in ==> H:\\MATLAB53\oolbox\\compiler\\mcc.dll !copy h:\\y9857\emp\\mccstartup d:\\mywork %将mccstartup拷贝到当前目录 mcc -L Cpp exm3 %<2> dir exm3* %<3> !del mccstartup 1 file(s) copied exm3.cpp exm3.hpp exm3.m 12.3.2.4 编译器1.2选项简介 【例12.4.2.4-1】要求利用1.2版编辑器对文件exm4.m进行操作,目标是获得C MEX文件和C语言的独立可执行文件。 (1)在5.3版MATLAB指令窗中的执行指令 mcc -V1.2 exm4 mcc -V1.2 -em exm4 (2)为完成同样任务在5.2版指令窗中的执行指令 mcc exm4 mcc -em exm4 12.4 编译文件的性能优化 12.4.1 优化原M文件的性能 12.4.1.1 提高向量化程度 【例12.5.1.1-1】本例演示:M文件与MEX文件、向量运算与循环运算的速度比较。 (1)编写如下两类函数M文件。 function y=sa(x) x=x+(x==0)*eps; y=sin(x)./x; function y=saf(x) n=length(x); for k=1:n x(k)=x(k)+(x(k)==0)*eps; y(k)=sin(x(k))/x(k); end 9 (2)在MATLAB指令窗中,对sa_mex.m和saf_mex.m进行编译。 cd d:\\mywork mcc -x sa_mex mcc -x saf_mex (3)运行四个文件,记录运算时间 t=-2*pi:pi/500:2*pi; tic;sa(t);tt(1)=toc; tic;saf(t);tt(2)=toc; tic;sa_mex(t);tt(3)=toc; tic;y=saf_mex(t);tt(4)=toc; (4)结果显示和比较 plot(t,y,'r-');grid 10.80.60.40.20-0.2-0.4-8 %<1> %<2> -6-4-202468图12.5.1.1-1 disp('运算速度比较') disp(' sa.m saf.m sa_mex.dll saf_mex.dll') disp(tt) 运算速度比较 sa.m saf.m sa_mex.dll saf_mex.dll 0.3300 2.9100 0.3800 2.5800 12.4.1.2 对数组进行预置 【例12.5.1.2-1】以上节例12.5.1.1-1为基础。saf.m文件中数组变量x和y 的大小随循环进行而增长,这大大减慢了运算速度。本例演示:在循环前对进行变量预置的好处。 (1)编写如下文件,以saf_pro.m和saf_pro_mex.m为名,将之保存在当前目录下。 function y=saf(x) n=length(x); x=zeros(1,n);y=zeros(1,n); for k=1:n x(k)=x(k)+(x(k)==0)*eps; y(k)=sin(x(k))/x(k); end (2)产生编译文件 mcc -x saf_mex mcc -x saf_pro_mex (3)运行 10 t=-2*pi:pi/500:2*pi; tic;saf(t);tt(1)=toc; tic;saf_pro(t);tt(2)=toc; tic;saf_mex(t);tt(3)=toc; tic;y=saf_pro_mex(t);tt(4)=toc; (4)速度比较 disp('运算速度比较') disp(' saf.m saf_pro.m saf_mex.dll saf_pro_mex.dll') disp(tt) 运算速度比较 saf.m saf_pro.m saf_mex.dll saf_pro_mex.dll 2.8600 0.6600 2.6300 0.4400 12.4.1.3 避免调用复数域函数 12.4.2 利用mcc(-V1.2)的优化性能 12.4.2.1 编译时指定优化选项-r和-i 【例12.5.2.1-1】求如下非线性差分方程组在10000个点上的值。观察原M文件、无选项和有选项MEX文件运行速度的不同。 2xk11yk1.4xkyk10.3xkx00,y00 (1)利用MATLAB编辑窗口编写如下M函数文件 function [x,y]=m_henon(n) x=zeros(1,n);y=zeros(1,n); for k=1:n-1 x(k+1)=1+y(k)-1.4*x(k)^2; y(k+1)=0.3*x(k); end (2)对M文件分别选用编译器2.0和1.2进行编译如下。 cd d:\\mywork mcc -x mex20_henon mcc -V1.2 -ri ri_mex12_henon %用2.0编译器产生普通MEX文件 %用1.2编辑器产生优化的MEX文件 <1> <2> (3)计算各文件运行时间费用的相对值。 n=10000; t=zeros(3,2); for k=1:2 %运行两次,以做比较 tic;m_henon(n);t(1,k)=toc; tic;mex20_henon(n);t(2,k)=toc; tic;ri_mex12_henon(n);t(3,k)=toc; end t=t/t(1,1); (4)显示比较结果 filename=[ ' m_henon'; ' mex20_henon'; 'ri_mex12_henon';]; disp([' 各文件运行开销的相对值比较表']) disp(blanks(1)') %产生一个空行 disp([' 文件名 第一次运行 第二次运行']) disp([filename blanks(3)' blanks(3)' num2str(t)]) 11 %blanks产生两个空列 各文件运行开销的相对值比较表 文件名 第一次运行 第二次运行 m_henon 1 0.78086 mex20_henon 0.69753 0.59259 ri_mex12_henon 0.015432 0 12.4.2.2 利用变量类型申明优化性能 12.4.2.3 利用编译注记优化性能 【例12.5.2.3-1】编译注记 %#ivdep的应用举例。 (1)编写如下两个函数M文件,并存放在d:\\mywork目录上。 [ivdep0.m] function A=ivdep0( ) A=1:10; A(5:9)=A(3:7); [ivdep.m] function A=ivdep( ) A=1:10; %#ivdep A(5:9)=A(3:7); (2)分别对这两个文件进行编译。 cd d:\\mywork mcc -V1.2 ivdep0 mcc -V1.2 ivdep (3)在MATLAB指令窗中运行以下指令。 tic;A0=ivdep0;t0=toc; tic;A=ivdep;t=toc/t0; (4)显示结果。 A0,A disp(['使用编译注记%#ivdep后的运用时间仅为原来的 ' num2str(t)]) A0 = 1 2 3 4 3 4 5 6 7 10 A = 1 2 3 4 3 4 3 4 3 10 使用编译注记%#ivdep后的运用时间仅为原来的 0.15152 12.5 创建独立的外部应用程序 12.5.1 独立外部程序的工作特点和创建过程 12 12.5.1.1 独立外部程序与MEX文件的不同工作特点 12.5.1.2 独立外部程序创建过程说明 12.5.2 关于指令mbuild 12.5.3 借助编译指令mcc创建独立应用程序 12.5.3.1 创建独立应用程序时mcc的使用格式和常用选项标志 12.5.3.2 由全M源文件产生EXE应用程序 【例12.6.3.2-1】创建一个适应“超定”、“恰定”、“欠定”线性方程求解的示例性应用程序。 (1)编写以下两个M函数文件。 [LLS.m] function LLS() Ae=5; %<2> Av=2; %<3> [A,b]=LLSDATA(Ae,Av); x=A\\b; %以下都是为获得较清晰的显示而编写 S='恰定'; if Ae>Av S='超定'; elseif Ae disp([As cs es cs num2str(A) cs cs cs cs cs bs cs es cs num2str(b)]) disp('方程的解 ') nxs=fix(Av/2); cxs=blanks(Av)'; xs=cxs;xs(nxs)='x'; exs=cxs;exs(nxs)='='; disp([xs cxs exs cxs num2str(x)]) [LLSDATA.m] function [A,b]=LLSDATA(Ae,Av) n=max(Ae,Av); WA=magic(n); A=WA(:,1:Av); if n>Ae A=WA(1:Ae,:); end 13 b=ones(Ae,1); (2)对存放在d:\\mywork上的这两个文件进行编译。 图 12.6.3.2-1 独立应用程序LLS.exe解超定方程示例 (3)把LLS.m文件第2、3行改为Ae=3;Av=5,重新编译、运行,结果见图12.6.3.2-2。 图12.6.3.2-2 独立应用程序LLS.exe解欠定方程示例 12.5.3.3 由含feval指令的M文件生成EXE文件 【例12.6.3.3-1】采用编译指定法生成一个可以计算方阵各种特征量的独立外部应用程序。 (1)编写函数M文件:mat_feat.m和my_det.m 。 [mat_feat.m] function mat_feat(f_name) %<1> disp('被分析矩阵') %<2> A=magic(4) %给定的被分析矩阵 N=8; %为字符串比较而设的字符串长度 8 。 n=size(f_name,2); %测量输入字符串的长度 ff_name=[f_name blanks(N-n)]; %给输入字符串补充空字符,使长度为8。 if ff_name==['my_det' blanks(2)] disp('矩阵 A 的行列式值 = ') 14 elseif ff_name==['rank' blanks(4)] disp('矩阵 A 的秩 = ') elseif ff_name==['norm' blanks(4)] disp('矩阵 A 的2-范数 = ') elseif ff_name==['cond' blanks(4)] disp('矩阵 A 的条件数 = ') elseif ff_name==['eig' blanks(5)] disp('矩阵 A 的特征值 = ') elseif ff_name==['svd' blanks(5)] disp('矩阵 A 的奇异值 = ') else disp('您输入的指令,或者不是本函数文件所能解决的,或是错误的!') end d=feval(f_name,A); disp(d) [my_det.m] function d=my_det(A) d=det(A); (2)创建计算给定矩阵秩的EXE文件 mcc -V1.2 -pm mat_feat f_name=rank (3)所生成的mat_feat.exe可脱离MATLAB而独立运行。 !mat_feat WARNING: Reference to uninitialized variable 被分析矩阵 [ 16 2 3 13 ; 5 11 10 8 ; 9 7 6 12 ; 4 14 15 1 ] 矩阵 A 的秩 = 3 (4)求矩阵行列式值的EXE文件创建指令、验证运行指令、及运行结果。 mcc -V1.2 –p –m mat_feat f_name=my_det !mat_feat 被分析矩阵 [ 16 2 3 13 ; 5 11 10 8 ; 9 7 6 12 ; 4 14 15 1 ] 矩阵 A 的行列式值 = 0 【例12.6.3.3-2】采用编译注记法生成一个可以计算方阵行列式值的独立外部应用程序。 (1)假定想创建一个计算矩阵行列式值的独立应用程序,那么只要在上例mat_feat.m文件的第<1>和<2>行之间插入以下两行指令,而M文件的其余部分都不必改动。 %#function my_det f_name='my_det'; (2)用以下指令对M文件进行编译,便可得到正确的EXE文件。 15 mcc –p mat_feat 【例12.6.3.3-3】当feval调用的是MATLAB C++库中的函数时,可以采用更简单的feval输入宗量直接赋值法,实现EXE文件的创建。如创建一个计算方阵特征值的独立外部应用程序。 (1)只要在例12.6.3.3-1的mat_feat.m文件第<1>和<2>行中间插入如下一条指令即可。 f_name='eig'; (2)用以下指令对M文件进行编译,便可得到正确的EXE文件。 mcc –p mat_feat (3)以下是运行指令和结果 !mat_feat 被分析矩阵 A = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 矩阵 A 的特征值 = 34.0000 8.9443 -8.9443 -0.0000 12.5.3.4 由含泛函指令的M文件生成EXE程序 【例12.6.3.4-1】创建一个求一元函数y(1)为了让读者了解函数y1sin(xe0.6x)局部最小值的独立外部应用程序。 x1sin(xe0.6x),对将生成的EXE文件的运行结果正确性做x直观的判断。先运行以下指令绘制如图12.6.3.4-1所示的函数曲线。 xx=0:0.01:5; yy=SAA(xx); %SAA.m文件见本例第2步 plot(xx,yy) x0=2; x=fmins('SAA',x0); y=SAA(x); plot(xx,yy,'b-',x,y,'r.','MarkerSize', 20),grid 16 1.41.210.80.60.40.20-0.2-0.4-0.6012345图12.6.3.4-1 函数 y1sin(xe0.6x)在2附近的局部最小值 x (2)编制以下M函数文件,并存放在用户自己的工作目录上。 [fcpp.m] function fcpp(fun) %#function SAA %被泛函调用函数的编译注记是必须的。 fun='SAA'; %对泛函指令中第一输入宗量具体化也是必须的。 xs=1; x=fmins(fun,xs); y=feval(fun,x); disp(blanks(2)') disp('独立外部程序 fcpp.exe 运行结果显示') disp(blanks(2)') ss=['sin(exp(0.6x))/x 函数在 x = ' num2str(xs) ' 附近达最小值的坐标为 ']; disp(ss); disp(blanks(1)'); disp([' (' num2str(x) ',' num2str(y) ')']) [SAA.m] function y=SAA(x) x=x+(x==0)*eps; y=sin(x.*exp(0.6*x))./x; (3)在MATLAB指令窗或DOS环境中运行以下编译指令,在用户目录上产生fcpp.exe 。 mcc –p fcpp (4)在DOS中运行fcpp.exe的操作情况和结果如图12.6.3.4-2。 17 图12.6.3.4-2 生成的独立程序fcpp.exe在DOS中的运行结果 12.5.3.5 由C/C++源码和M源码文件混合生成EXE应用文件 【例12.6.3.5-1】主程序为C源码文件,被调用程序为M文件。 (1)主文件fileinc.c 和被调用的mrank.m文件如下 [fileinc.c] #include int main(int argc, char **argv[]) { int n ; mxArray *r; mxArray *N; TemplibInitialize(); n = 5; N=mxCreateDoubleMatrix(1,1,mxREAL); *mxGetPr(N)=n; r = mlfMrank(N); mlfPrintMatrix(r); mxDestroyArray(r); mxDestroyArray(N); TemplibTerminate(); return 0; } [mrank.m] function r=mrank(n) r=zeros(n,1); for k=1:n r(k)=rank(magic(k)); end (2)编译混合源码文件 mcc –t –W lib:Templib -T link:exe mrank fileinc.c mcc –t –W lib:Templib -T link:exe fileinc.c mrank (3)在DOS中分别运行mrank.exe和fileinc.exe的结果如图12.6.3.5-1。 18 图12.6.3.5-1 混合源码产生的mrank.exe和fileinc.exe运行结果 12.6 API应用程序接口 12.6.1 MEX文件 12.6.2 数据的输入输出 12.6.2.1 数据输入 12.6.2.2 数据输出 12.6.2.3 不同平台间数据传递 12.6.3 MATLAB引擎 【例17.7.3-1 】在C源程序中调用MATLAB引擎来计算三次多项式x2x5的根。 (1)编写源程序engexam.c。 #include int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { Engine *ep; mxArray *P=NULL,*r=NULL; char buffer[301]; 3 19 double poly[4]={1,0,-2,5}; /*三次多项式的系数*/ /*启动本机MATLAB引擎,如果出错则退出程序。 如想启动远程主机上的MATLAB,则用相应主机名代替\\0*/ if (!(ep=engOpen(\"\\0\"))) { fprintf(stderr,\"\\nCan't start MATLAB engine\\n\"); return EXIT_FAILURE;} /*创建变量P,将poly变量中定义的多项式系数值拷贝至该变量P*/ P=mxCreateDoubleMatrix(1,4,mxREAL); /*给变量P取名为p,将P传递到引擎空间,p将被运用于MATLAB表达式中*/ mxSetName(P,\"p\"); memcpy((char *)mxGetPr(P),(char *)poly,4*sizeof(double)); engPutArray(ep,P); /*利用MATLAB求解多项式根,并写至缓冲区*/ engOutputBuffer(ep,buffer,300); engEvalString(ep,\"r=roots(p)\"); /*将缓冲区中的内容输出屏幕窗口,关闭MATLAB引擎,释放指针P。*/ MessageBox(NULL,buffer,\"Engexam.c--多项式x^3-2x+5的根\ engClose(ep); mxDestroyArray(P); return EXIT_SUCCESS; } (2)编译连接源程序 mex -f h:\\matlab53\\bin\\bccengmatopts.bat engexam.c (3)运行engexam.exe 图 12.7.3-1 运行程序engexam.exe得到的信息窗口 20 因篇幅问题不能全部显示,请点此查看更多更全内容