| 恒's profileFrensel的日记PhotosBlogLists | Help |
|
|
December 15 MATLAB中的图加inset(插图)论文中经常要在图中再加插图,这样可以省下论文的版图,在Letters这类文章中很重要,因为一般限制你最多3-4版。 现在要在y = sin(x)这幅图中插入y = cos(x)和y = sin(x) * cos(x)这两个小图,分别位于原图的右上方和左下方: >> x = 0 : 0.01 : 2 * pi; >> y1 = sin(x); y2 = cos(x); y3 = sin(x) .* cos(x); >> plot(x, y1) >> axes('position', [0.55 0.65 0.3 0.2]) >> plot(x, y2) >> axes('position', [0.15 0.15 0.3 0.2]) >> plot(x, y3) 其中axes函数中用'position'参数就是为了在原图中加多一个坐标轴,后的参数说明该坐标轴在原图的位置和其大小。 September 02 在MATLAB调用一些Maple函数的问题有时要在MATLAB中调用Maple的函数参与数值计算,像一些MATLAB本身没有的特殊函数,如Laguerre多项式和Hermite多项式等。此时要注意,Maple是会尽可能地返回解析的表达式,而不是浮点数。比如在MATLAB中输入:
>> maple('LaguerreL', 3, 1, 2) ans = LaguerreL(3,1,2) 返回的是解析的结果,这种细果参与不了MATLAB的数值计算,我们需要的是浮点数的结果。根据Maple的基础知识可知,你输入的参数是浮点数,那么Maple就给你返回浮点数结果。比如在Maple中输入: > LaguerreL(3, 1., 2); -1.333333333 即在输入参数1后面加多一点,代表1.0,这便是个浮点数,于是LaguerreL函数给你返回浮点数结果。据此,在MATLAB中这般输入: >> a = maple('LaguerreL', 3, '1.', 2) a = -1.3333333333333333333333333333333 再将a用str2double输入双精度浮点数即可使用了。结总起来,MATLAB中调用Maple的LaguerreL函数的具体函数为: % LaguerreL.m function f = LaguerreL(n, k, x) xn = length(x); y = zeros(xn, 1); for i = 1 : xn y(i) = str2double(maple('LaguerreL', [num2str(n) '.'], k, x(i))); end f = y; end 然而,MATLAB中已提供了函数做这种事情,这就是mfun函数。例如(下面测试不计启动Maple内核时间): >> x = 0 : 0.01 : 3; >> tic, y = LaguerreL(3, 1, x); toc Elapsed time is 0.560188 seconds. >> tic, y2 = mfun('LaguerreL', 3, 1, x); toc Elapsed time is 0.053402 seconds. 即提供矢量输入时,mfun要比自己所编写的函数要快得多。但有时却不能直接用矢量形式输入,要反覆去调用该函数(LaguerreL)时,由于mfun要做许多格式判断和转换,反而令计算速度下降,如: >> tic, for i = 1 : 300, y1 = LaguerreL(3, 1, 2); end, toc Elapsed time is 0.408626 seconds. >> tic, for i = 1 : 300, y2 = mfun('LaguerreL', 3, 1, 2); end, toc Elapsed time is 0.920374 seconds. August 24 关于tensor product kron(A, B)和vector phi的相乘计算如有一An x An矩阵A和Bn x Bn矩阵B,求其Kronecker tensor product kron(A, B)与(An x Bn) x 1矢量phi的相乘,即计算
y = kron(A, B) * phi.
这个计算当An和(或)Bn很大时,会变得难以计算。例如An = Bn = 100,那么kron(A, B)就是一个10000 x 10000的矩阵。如果不使用稀疏矩阵(假设A和B够稀疏)的储存方法,在MATLAB里(32位)根本表示不出来。在这种情况下,y = kron(A, B) * phi就无法计算了。
在很多情况下,kron(A, B)会变得更大,例如在DMRG的计算中。为了解决这个问题,是有办法在不算出kron(A, B)的情况下算出y = kron(A, B) * phi的。具体的做法参考了R. M. Noack, S. R. Manmana, arxiv:cond-mat/0510321,可把y写成
y = A * (B * psi')'.
其中psi就是phi,只不过是对phi做了个reshape,使其变成psi = reshape(phi, Bn, An)'。注意到 ' 是转置的意思。利用这个技巧,可以轻易在MATLAB中编写程序,不需要算出kron(A, B),就完成了y = kron(A, B) * phi这个式子的计算。不用这个技巧,就难以提高DMRG中保留的状态数。
处理这个问题的另一个原因是大型矩阵的对角化问题。要知道,如果矩阵太大,我们是无法用一般的对角化手段去处理的(如MATLAB里那eig函数)。只有使用迭代算法,如Lanczos算法,求出该矩阵最大和最小的本征值本征矢。而这类迭代算法中,最繁重的一步计算就是对该大型矩阵H乘上一矢量phi。由于这是一个迭代的过程,H * phi要计算很多很多次。如果H能写成kron(A, B),利用上述的技巧,能大大减少H * x的计算量,具体分析可看U. Schollwöck, Rev. Mod. Phys., 77, 259 (2005)中的第二节I.2。 March 30 关于Matlab R2007a昨天又用不太正大光明的手段,搞了个Matlab R2007a,决定“试用”一下。我也不会“试用”太久,就半年左右吧……(接着“试用R2007b”……)
个人认为Matlab R2007a比起上一个版本来说改进不大,其中最重要的改进是增加了多线程的启动开关,可以让Matlab自动决定用多少个线程。Intel MKL那些能多线程化的函数,应该都能多线程运算。另外,Matlab那些element-wise,即逐个元素操作的函数,如sin,log等都可以多线程运算。另外,现在可以用键盘箭头键来移动图中的物件,这功能对我来说有用…… January 17 Matlab的Dot-Dot-DotMatlab语句如果太长,就可以用Dot-Dot-Dot,即...来换行,...后面的代码都会忽略,但昨天在解一个方程组时遇到一个奇怪的问题。该方程组很长很长,每一个方程写出来都要三四百个字符,列出方程时免不了要用...换行。因为是用fsolve做数值解,那就要它该方程组写成一个函数,需要返回代入变量值后每个方程的值,列成一个矢量的形式。例如: December 11 Matlab的Distributed Computing Toolbox初探之前讲过,实验室来了五台双核Pentium D 925计算机,这正适合用来做分布式或并行式计算。我打算只调用那些计算机中的一个核参与计算,留下一个核可以让其他人正常地使用该计算机。我们在这里将会介绍Matlab中Distributed Computing Toolbox的基本使用方法,目标是实现简单的分布式计算。 December 04 Matlab多线程运算的问题上次说到,Matlab R2006a开始使用Intel MKL,只要设好OMP_NUM_THREADS这个环境变量,即可以BLAS Level 3的运算在任意线程数下。然而,尽管设定的线程数和CPU核的数目相同,但这样也并不能保证能提升计算效率。主要的原因在于建立线程也是需要时间的。如果你的任务只要0.0001秒就能算完,但建立线程也要用0.0001秒,那就根本没有必要把该任务多线程化。 November 21 MEX文件中使用OpenMPMatlab本身是不可以做多线程计算的,即不可像Fortran/C++使用OpenMP那样,可以指明程序的那个部份应该用多少个线程去执行,但却可以使用间接地调用已经多线程化的程序,如Intel MKL中的BLAS Level 3去实现多线程运算。于是我们想到,是否可以把用Fortran/C++写的OpenMP多线程程序,编译成Matlab可以调用的动态链接库MEX文件。通过调用拥有多线程运算能力的MEX文件,提高Matlab在某些应用中的效率。这种想法是可以实现的,这里简述具体做法。 我们这里使用Fortran做例子,C++中的实现方法和Fortran是类似的。我们使用的是Widnows XP、Microsoft Visual Studio .NET 2005、Intel Visual Fortran Compiler 9.1、Matlab R2006b、CPU是Intel Core 2 Duo E6400。首先设置好MEX要用到的一些路径,如include和lib的位置,具体可参考Matlab帮助中的MATLAB -> External Interfaces -> Calling C and Fortran programs from Matlab -> Custum Building on Windows中的Compiling MEX-Files with the Microsoft Visual C++ IDE。只要按着要求的步骤做即可,要注意编写def文件时,如用IVF编译器的,要写成EXPORTS MEXFUNCTION。 现在可以做个小测试。先从Matlab帮助中,找一个最简单的Fortran MEX文件的例子,我使用的是”A First Example — Passing a Scalar”(只要搜寻”Passing a Scalar”,即可找出此例子)。这个例子是通过接口子程序mexFunction,把输入参数从Matlab里调入,同时给定Matlab的输出变量,再把该输入参数通过另一个子程序timestwo乘以2,将结果放入参数y中,最后把y返回到Matlab的输出变量中。就是说,子程序timestwo是该MEX文件的功能部份,只要将该功能部份并行化,即可提高运算效率。但子程序timestwo太简单了,我们没必要做什么。但我们却可以人为地加上一个很“艰巨”的任务,以此测试OpenMP的使用。 我们考虑做两个1000 X 1000矩阵的加法,要执行2000次。这任务看来并不轻松,足够CPU忙一阵子。既然这事情要做2000次,不如让每个线程做1000次,这可以使用OpenMP的SECTIONS Directive实现。现把子程序timestwo改动如下: 接着设定好OpenMP的编译命令,即可生成MEX文件。把该MEX文件放在Matlab的相关目录下,假设文件名为timestwo.mexw32,在Matlab里运行: 这里还有一点是必须提一下的。我们在子程序timestwo中,矩阵使用了动态分配内存,而不是静态地预先分配好。这是由于动态链接库是不会分配stack空间的,而是调用该dll(MEX)的程序Matlab分配。由于要分配1000 X 1000的double矩阵,而且每个线程都要分配一份,使得Matlab调用该MEX文件时,很可能出现stack overflow错误。要改动Matlab所分配的stack的大小看来不是个好主意,我也不清楚该如何做,那就只好动态地分配内存,使其数据放在heap中,那就可以避免stack overflow。 October 15 Matlab:多线程?我也行。Matlab从R14开始使用Intel MKL的BLAS,在Windows平台下,设定环境变量BLAS_VERSION=mkl.dll即可使用(默认还是使用ATLAS BLAS,在R2006a版本这个环境变量不用设了,都用MKL)。而且通过MKL,可实现BLAS Level 3的多线程运行。设定环境变量OMP_NUM_THREADS=2,重新启动Matlab,运行昨天的测试程序: function test(l) N = 1000; a = zeros(N, N); b = zeros(N, N); a(:) = 1.23456789; b(:) = 9.87654321; tic for i = 1 : l c = a * b; end toc end 在Matlab的Command Window运行: >> test(30) Elapsed time is 6.300522 seconds. 结果不比在Fortran直接调用MKL的要差。用双核的朋友们,别忘了设定环境变量OMP_NUM_THREADS啊! August 07 把Matlab的数值矩阵输出成Latex语句近来在写一份note,要经常写上Matlab算出来的矩阵,由本我是用Latex来编写,所以需要把Matlab的数值矩阵转成Latex语句。Matlab中看来没有把数值矩阵转成Latex语句的程序,而只有关于符号矩阵的程序(其实是Maple的),所以我就自己写了一个。我的程序可能写得很烂,但还能凑合用一用。
% 数值矩阵输出成Latex代码 % A为矩阵,precision为输出精度 % 如输入: % >> a = [1 2 3; 4 5 6; 7 8 9]; % >> latex2(a) % \left( % \begin{array}{ccc} % 1 & 2 & 3 \\ % 4 & 5 & 6 \\ % 7 & 8 & 9 \\ % \end{array} % \right) function f = latex2(A, precision) % 没输入精度参数时,默认精度为小数后4位 if nargin == 1 precision = '4'; else precision = int2str(precision); end % 定义单一元素输出格式 out_num = [' %0.' precision 'f &']; % 用作整数输出判断 z = zeros(1, str2num(precision) + 1); z(1) = '.'; z(2 : end) = '0'; z = char(z); % 求矩阵大小 [r c] = size(A); nc = zeros(1, c); nc(:) = 99; % 存放character c % 生成第一句Latex语句 out = sprintf('\\left(\n\t\\begin{array}{%s}', char(nc)); % 二重循环,用作生成整个矩阵的Latex语句 for i = 1 : r out = [out sprintf('\n\t')]; % 换行 for j = 1 : c temp = sprintf(out_num, A(i, j)); % 小数位皆为零时,把数取整。如1.0001取为1 dot_position = find(temp == '.'); if temp(dot_position : end - 2) == z temp = temp(1 : dot_position - 1); temp = [temp ' &']; % 要取整时,如有负号,则必须丢掉 if temp(2) == '-' temp = [temp(1) temp(3 : end)]; end end out = [out temp]; end % 丢掉最后的'&'号 out = out(1 : end - 1); % 行末加上'\\'号 out = [out '\\']; end % 加上最后一句结束代码 out = [out sprintf('\n\t\\end{array}\n\\right)')]; f = out; February 24 MEX文件中给一个double矩阵赋值的几种方法MEX文件中想给一个mxArray赋值,有如下几种方法: 2、使用C函数memcpy,如: void mexFunction(/*...*/) { 3、使用mxSetPr,如: 要注意的是,mxSetPr函数不会释放原本已存在的内存,所以在使用mxSetPr函数前,先使用mxFree把mxArray中内容的内存释放,否则后果严重。另外,mxSetPr函数的第二个参数必须是使用mxCalloc分配内存,注意mxCalloc函数前加上(double *),把mxCalloc返回的(void *)类型强制转换为(double *)。 February 20 Matlab调用C语言函数如果我有一个用C语言写的函数,实现了一个功能,如一个简单的函数:
double add(double x, double y) { return x + y; } 现在我想要在Matlab中使用它,比如输入: >> a = add(1.1, 2.2) 3.3000 要得出以上的结果,那应该怎样做呢? 解决方法之一是要通过使用MEX文件,MEX文件使得调用C函数和调用Matlab的内置函数一样方便。MEX文件是由原C代码加上MEX文件专用的接口函数后编译而成的。 可以这样理解,MEX文件实现了一种接口,它把在Matlab中调用函数时输入的自变量通过特定的接口调入了C函数,得出的结果再通过该接口调回Matlab。该特定接口的操作,包含在mexFunction这个函数中,由使用者具体设定。 所以现在我们要写一个包含add和mexFunction的C文件,Matlab调用函数,把函数中的自变量(如上例中的1.1和2.2)传给mexFunction的一个参数,mexFunction把该值传给add,把得出的结果传回给mexFunction的另一个参数,Matlab通过该参数来给出在Matlab语句中调用函数时的输出值(如上例中的a)。 比如该C文件已写好,名为add.c。那么在Matlab中,输入: >> mex add.c 就能把add.c编译为MEX文件(编译器的设置使用指令mex -setup),在Windows中,MEX文件类型为mexw32,即现在我们得出add.mexw32文件。现在,我们就可以像调用M函数那样调用MEX文件,如上面说到的例子。所以,通过MEX文件,使用C函数就和使用M函数是一样的了。 我们现在来说mexFunction怎样写。 mexFunction的定义为: void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { /*....................................*/ } 可以看到,mexFunction是没返回值的,它不是通过返回值把结果传回Matlab的,而是通过对参数plhs的赋值。mexFunction的四个参数皆是说明Matlab调用MEX文件时的具体信息,如这样调用函数时: >> b = 1.1; c = 2.2; >> a = add(b, c) mexFunction四个参数的意思为: nlhs = 1,说明调用语句左手面(lhs-left hand side)有一个变量,即a。 nrhs = 2,说明调用语句右手面(rhs-right hand side)有两个自变量,即b和c。 plhs是一个数组,其内容为指针,该指针指向数据类型mxArray。因为现在左手面只有一个变量,即该数组只有一个指针,plhs[0]指向的结果会赋值给a。 prhs和plhs类似,因为右手面有两个自变量,即该数组有两个指针,prhs[0]指向了b,prhs[1]指向了c。要注意prhs是const的指针数组,即不能改变其指向内容。 因为Matlab最基本的单元为array,无论是什么类型也好,如有double array、 cell array、 struct array……所以a,b,c都是array,b = 1.1便是一个1x1的double array。而在C语言中,Matlab的array使用mxArray类型来表示。所以就不难明白为什么plhs和prhs都是指向mxArray类型的指针数组。 完整的add.c如下: // add.c #include "mex.h" // 使用MEX文件必须包含的头文件 // 执行具体工作的C函数
double add(double x, double y) { return x + y; } // MEX文件接口函数
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { double *a; double b, c; plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); a = mxGetPr(plhs[0]); b = *(mxGetPr(prhs[0])); c = *(mxGetPr(prhs[1])); *a = add(b, c); } mexFunction的内容是什么意思呢?我们知道,如果这样调用函数时: >> output = add(1.1, 2.2); 在未涉及具体的计算时,output的值是未知的,是未赋值的。所以在具体的程序中,我们建立一个1x1的实double矩阵(使用mxCreateDoubleMatrix函数,其返回指向刚建立的mxArray的指针),然后令plhs[0]指向它。接着令指针a指向plhs[0]所指向的mxArray的第一个元素(使用mxGetPr函数,返回指向mxArray的首元素的指针)。同样地,我们把prhs[0]和prhs[1]所指向的元素(即1.1和2.2)取出来赋给b和c。于是我们可以把b和c作自变量传给函数add,得出给果赋给指针a所指向的mxArray中的元素。因为a是指向plhs[0]所指向的mxArray的元素,所以最后作输出时,plhs[0]所指向的mxArray赋值给output,则output便是已计算好的结果了。 上面说的一大堆指向这指向那,什么mxArray,初学者肯定都会被弄到头晕眼花了。很抱歉,要搞清楚这些乱糟糟的关系,只有多看多练。 实际上mexFunction是没有这么简单的,我们要对用户的输入自变量的个数和类型进行测试,以确保 输入正确。如在add函数的例子中,用户输入char array便是一种错误了。 从上面的讲述中我们总结出,MEX文件实现了一种接口,把C语言中的计算结果适当地返回给Matlab罢了。当我们已经有用C编写的大型程序时,大可不必在Matlab里重写,只写个接口,做成MEX文件就成了。另外,在Matlab程序中的部份计算瓶颈(如循环),可通过MEX文件用C语言实现,以提高计算速度。 February 14 Matlab中的P代码文件我们经常把语句或函数写在一个M文件里,比如: % ---------------------- % test.m a = 1; b = 2; % ---------------------- 这个M语句文件,或: % ---------------------- % test2.m function a = test2(b) a = b * 2; % ---------------------- 这个M函数。 我们在Command Window里输入: >> test 即是等于输入了 >> a = 1; >> b = 2; 而输入: >> a = test2(2) a = 4 便是执行了一个函数。 以上的内容我想大部份人都知道是怎么一回事了,以下说一说P代码文件。 如果在Command Window里输入: >> pcode test 便会在相应test.m的文件夹里产生了test.p。如果这时在Command Window里输入: >> test 其实是运行了test.p,而不是test.m。 P文件是对应M文件的一种预解析版本(preparsed version)。因为当你第一次执行M文件时,Matlab需要将其解析(parse)一次(第一次执行后的已解析内容会放入内存作第二次执行时使用,即第二次执行时无需再解析),这无形中增加了执行时间。所以我们就预先作解释,那么以后再使用该M文件时,便会直接执行对应的已解析版本,即P文件。但又因为Matlab的解析速度非常快,一般不用自己作预解析。只有当一些程序要调用到非常多的M文件时,如GUI应用程序时,才会作预解析,以增加以后的调用速度。 如Matlab的当前目录(Current Directory)有test.m文件,作预解析后,又有test.p文件。因为P文件的调用优先级比M文件要高,所以当你调用test时,会作优先选择而调用了test.p。 可以修改test.m的代码为: % ---------------------- % test.m a = 3; b = 4; % ---------------------- 再在Command Window里调用test: >> test Warning: P-file C:\Program Files\MATLAB71\work\test.p is older than M-file C:\Program Files\MATLAB71\work\test.m. C:\Program Files\MATLAB71\work\test.p may be obsolete and may need to be regenerated. Type "help pcode" for information about generating P-files from M-files. 会出现一个Warning,警告你所调用的P文件比同名的M文件要旧,即表示M文件已被修改了。尽管如此,调用的还是旧的P文件,即得出a = 1和 b = 2。 P文件可以用来作保密代码之用,如果你给别人一个M文件,别人可以打开来看到你所有的代码和算法。如果你的代码不想被别人看到,那可以给他P文件。 pcode函数也可以应用在M函数文件。 February 13 Matlab 7的编译器Matlab 7.x的编译器版本为4.x,和以前的3.x版本大不一样。主要体现在不会产生完整的C/C++代码以实现独立的可执行程序,而是通过另外一种间接的途径。
为了解决3.x版本编译器不够兼容Matlab程序的问题,在4.x版本的编译器中,编译器实际上只会产生一些“接口函数”(Wrapper),实际上M文件里的内容被加密压缩存放在一个CTF(Component Technology File)的文档里。具体计算工作由一个名为MCR(MATLAB Component Runtime)的共享库集合工具提供。
MCR就好像一个Matlab一样,只不过是集成了Matlab所有的计算方法,所以通过MCR,4.x版本的编译器几乎能编译所有的Matlab程序。尽管能编译几乎所有的Matlab程序,但这带来了一个麻烦的问题。
当你要把编译好的二进制执行文件(如Windows中的exe)发给其他机器上使用时,该机器上必须安装好MCR。然而,MCR的安装文件大小为99.9MB……所以尽管你的程序可能只完成一次加法,但你要发给其他人的机器中使用你的程序时,请一并把99.9MB的MCR附上。
而且,通过MCR的计算,实际上和在Matlab上计算没差别,速度是一样的。所以,4.x编译器的存在只是为了使你的算法保密。当你的算法程序不想被其他使用者看到时,就要用4.x编译器产生exe。 January 05 Matlab中的pretty函数上次讲到用Maple可以把式子显示得比较漂亮,Matlab中也是可以的,要用pretty函数.
pretty函数可以把一个"符号类型"式子变成漂亮的形式,如:
L=sym('6/(p-1)^4');
这个式子,只要输入:
>> pretty(L)
6 -------- 4 (p - 1) 就得到上面的效果了!
|
|
|