# USING FORTRAN90 CODE IN MATLAB MEX-FILES

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

As the standard for Fortran programming slowly advances in the direction of Fortran90, I have had the problem of creating mex-files using the fortran90 coding standard. Herefore I have developed the following method of creating and compiling the mex-files. As a help I will give an example. The program will compute the inverse of a 2x2 matrix, together with the determinant. Furthermore an optional verbose flag can be defined. The code itself is not very useful, but it contains all the possible difficulties one can expect to have during developing of mex-files.

STEP 1.

First write the matlab source file: a .m file  I will not elaborate on that, beautiful coding examples are available on the Internet, on your local machine and in the matlab manual

EXAMPLE

function [InvMat,Determ]=inverse(Mat,verbose);

%INVERSE compute inverse of 2x2 matrix
%
%SYNTAX: [InvMat,Determ]=inverse(Mat,verbose);
%
%INPUT:  Mat     =   2x2 matrix
%        verbose =   verbose=1: show information, verbose=0: no information,  default=0
%
%OUTPUT: InvMat  =   inverse of matrix, if no inverse exists, return conjugate of matrix
%        Determ  =   determinant of matrix
%
%AUTHOR: Jeroen Goudswaard,  J.C.M.Goudswaard@CTG.TUDelft.NL

%how many variables
if (nargin == 1);
verbose = 0;
end;
if (verbose ~= 0);
verbose = 1;
end;

%compute determinant

Determ = Mat(1,1)*Mat(2,2) - Mat(2,1)*Mat(1,2);

%fill Invmat
if (abs(Determ) > 2*eps);
InvMat(1,1) =   Mat(2,2);
InvMat(2,2) =   Mat(1,1);
InvMat(1,2) = - Mat(1,2);
InvMat(2,1) = - Mat(2,1);
InvMat      =   InvMat ./ Determ;
else;
%return conjugate
if(verbose == 1);
disp(['No Inverse possible: Determ almost ' int2str(Determ)' ==> transpose returned']);
% sure, I could return 0 instead of int2str(Determ),
% but I will use this also in the f90 source, and
% it is not trivial overthere.
end;
InvMat = Mat';
end;

STEP 2

Now rewrite the matlab code into f90 subroutine coding. And now we are at the beautiful part of it: it won't cost you too much effort,
as their coding standards are very much alike.
It is also advisable only to ALLOCATE arrays only in the gateway subroutine, USE ASSUMED SIZE ARRAYS PREFERABLY!.
Many of my own INEXPLICABLE BUGS have been solved by removing all allocatable arrays from the actual subroutines.

EXAMPLE

SUBROUTINE inverse(Mat,verbose,InvMat,Determ)

!INVERSE compute inverse of 2x2 matrix
!
!AUTHOR: Jeroen Goudswaard,  J.C.M.Goudswaard@CTG.TUDelft.NL

IMPLICIT NONE

INTEGER :: i,j,verbose
DOUBLE PRECISION :: Determ,Mat(2,2),InvMat(2,2)
CHARACTER*80 :: string

! compute determinant
Determ = Mat(1,1)*Mat(2,2) - Mat(2,1)*Mat(1,2)

!fill Invmat
IF (ABS(Determ) > 2*eps) THEN
InvMat(1,1) =   Mat(2,2)
InvMat(2,2) =   Mat(1,1)
InvMat(1,2) = - Mat(1,2)
InvMat(2,1) = - Mat(2,1)
InvMat      =   InvMat / Determ
ELSE
!return conjugate-transpose
IF(verbose == 1)THEN
WRITE(string,'(f8.3)')Determ
CALL mexprintf('No Inverse possible: Determ = '// string //'&
& conjugate-transpose returned'//CHAR(10))
!CHAR(10) is a <CR>
! first pitfall: you CANNOT use write or print statements, because matlab has control
!     over your display so you HAVE TO USE mexprintf, which is in the f77-library of matlab
END IF
InvMat = TRANSPOSE(Mat);
ENDIF
END SUBROUTINE inverse

STEP 3

Now the most important task comes: writing the gateway-subroutine `mexfunction', much of it is standard, but I will document this one very carefully

EXAMPLE

SUBROUTINE mexfunction(nlhs,plhs,nrhs,prhs)
IMPLICIT NONE

INTEGER*8 :: plhs(*), prhs(*)
!pointers to input data: prhs and output: plhs, always take INTEGER*8, to let it work on 64-bit
!machines (SGI e.g.) 32-bit compilers will correct this to INTEGER*4, so don't worry about the
!warning(s) on this during compilation. (a #ifdef can also be used in a include file)
INTEGER :: nlhs, nrhs
!number of inputs on right and left hand side
INTEGER*8 :: mxcreatefull, mxgetpr
!pointers to intrinsic functions of the matlab library
INTEGER :: mxgetm, mxgetn
!declare the name of these intrinsics
!up till here all statements are obliged
INTEGER*8 :: pr_Mat, pr_verbose, pr_InvMat, pr_Determ
!define pointer to specific data in input prhs, one pointer per variable/array
INTEGER :: mMat, nMat
!integers that will contain the 'size' of Mat
INTEGER :: nverbose
!integer to copy the value of verbose to
DOUBLE PRECISION :: verbose, Determ
!everything that comes out of matlab is double precision!!
DOUBLE PRECISION, ALLOCATABLE :: Mat(:,:), InvMat(:,:)
!let's suppose we do not know the size of Mat in advance
!so we will use ALLOCATABLE arrays

IF (nrhs>2) THEN
CALL mexerrmsgtxt('more than two arguments')
END IF
IF (nrhs<1) THEN
CALL mexerrmsgtxt('no input matrix specified')
END IF
!some checks on input, we do not want matlab to give the error, but
!we check it ourselves
mMat  = mxgetm(prhs(1))
!get the first dimension of the first input, i.e. Mat
nMat  = mxgetn(prhs(1))
!get the second dimension of the first input, i.e. Mat

ALLOCATE(Mat(mMat,nMat))
!allocate memmory for the input matrix

pr_Mat  = mxgetpr(prhs(1))
!let the pointer point to the data
CALL mxcopyptrtoreal8(pr_Mat , Mat , mMat*nMat)
!copy the values from matlab to the local variable, size is mMat*nMat

IF (nrhs==2)THEN    !check if there is a second input: verbose
pr_verbose = mxgetpr(prhs(2))
CALL mxcopyptrtoreal8(pr_verbose , verbose , 1)
!verbose is of size 1, naturally
ELSE
verbose = 0D0
END IF

nverbose = NINT(verbose)
!nverbose is the integer representation of double precision verbose

ALLOCATE(InvMat(mMat,nMat))
!ALLOCATE the outgoing array InvMat

plhs(1) = mxcreatefull(mMat,nMat, 0)
!create matrix for output
plhs(2) = mxcreatefull(   1,   1, 0)
!create variable for output

pr_InvMat = mxgetpr(plhs(1))
pr_Determ = mxgetpr(plhs(2))
!create the output pointers

CALL inverse(Mat,nverbose,InvMat,Determ)
!call the Fortran90 subroutine

CALL mxcopyreal8toptr(InvMat, pr_InvMat, mMat*nMat)
CALL mxcopyreal8toptr(Determ, pr_Determ, 1)
!copy the data back to matlab

DEALLOCATE(Mat,InvMat)
!NEVER EVER forget to DEALLOCATE dynamically allocated arrays, without this, either
!your mex-file or matlab is very likely to CRASH!

END SUBROUTINE mexfunction

STEP 4

Now you are ready to compile your mex-file either from matlab or from the commandline.
First put all subroutines together in one file,
and let it end on .f
matlab does not compile sourcecode ending on .f90,

In this case copy the routines from STEP2 and STEP3 together in a file inverse.f
then go to your mexopts.sh file,
for your own platform you have to specify some information, like your preferred compiler, linker and some options
in ANY case you need the option that says that it should compile .f -files as f90 source code!
Other necessary options are HIGHLY DEPENDENT on your particular platform and compiler.

。。。。。so if any of you has working mexopts.sh configurations, I would be glad to publish them on this page

And then the last part of it:

mex -O inverse.f
and now you can use inverse.mex just as inverse.m

COMMENTS, SUGGESTIONS, JUST SAYING HELLO: Jeroen@Goudswaard.org <?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

8. Can I use CXML, IMSL or the Intel MKL libraries in FMEX files?

Yes, I do it quite frequently. The following is written using Matlab 5.3, Compaq Visual Fortran 6.6, and Windows 2000. The example here uses Fortran95 source, though this can be done in FORTRAN77 just as easily. There are examples of MEX files that use MKL: vvmult_mkl.f90, IMSL: vvmult_imsl.f90, and CXML: vvmult_cxml.f90.

First, you will need to modify your mexopts.bat file. On R13 (v6.5) modify the file c:/matlab6p5/bin/win32/mexopts/df66opts.bat. On R11 and R12, you will need to issue the command prefdir(1) to tell you where your mexopts.bat is located. You can see my mexopts.bat file for R11/R12 and R13 at these links.

In order to add IMSL, you will need to add the following lines to your mexopts file:

set LIB=%LIB%;%DFDir%/IMSL/LIB;

For MKL, you will need to add these lines, you must also uncomment one of the last three lines to specify the processor type. Note that MEX files linked against mkl_p4.lib will only run on a P4. MEX files linked against mkl_p3.lib will only run on a P3 or P4.

set LIB=%LIB%;C:/Program Files/Intel/MKL/ia32/lib

rem ********************************************************************

rem Choose One (p4=Pentium 4, p3=Pentium 3, def=all others)

rem ********************************************************************

For CXML, add the following lines:

set LIB=%LIB%;%DFDir%/CXML/LIB

set INCLUDE=%INCLUDE%;%DFDir%/CXML/INCLUDE

Note that many of the routines in these libraries conflict. Many conflict across all 3 libraries, in fact. Therefore, if you think that you might be using different libs on different projects, you can also leave all of the LINKFLAG variables commented, and specify the libraries on the command line. So if you want to use MKL, you build the file with the command

? mex vvmult_mkl.f90 mkl_s.lib mkl_def.lib mkl_lapack.lib

Likewise, the syntax for IMSL is:

? mex vvmult_imsl.f90 sstatd.lib sstats.lib smathd.lib smaths.lib

And finally, CXML:

? mex vvmult_cxml.f90 cxml.lib

And there you go!

10. Can I optimize my MEX files?

Sure, I do it, so can you. The optimization happens in the mexopts.bat file. If you have R11/R12, issue the command prefdir(1), which will reveal its location. For R13, I hack on the mexopts file located at c:/matlab6p5/bin/win32/mexopts/df66opts.bat. If you do this, be sure to save a backup of the original! And if you use R13, you will need to run mex -setup after each change.

You can see my R11/R12 mexopts file here, and here is my R13 file.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

r13_df66opts.bat.txt

@echo off

rem DF66OPTS.BAT

rem

rem    Compile and link options used for building MEX-files

rem    using the Compaq Visual Fortran compiler version 6.6

rem

rem    $Revision: 1.1$  $Date: 2002/06/03 12:17:13$

rem

rem ********************************************************************

rem General parameters

rem ********************************************************************

set MATLAB=%MATLAB%

set DF_ROOT=%DF_ROOT%

set VCDir=%DF_ROOT%/VC98

set MSDevDir=%DF_ROOT%/Common/msdev98

set DFDir=%DF_ROOT%/DF98

set PATH=%MSDevDir%/bin;%DFDir%/BIN;%VCDir%/BIN;%PATH%

set INCLUDE=%DFDir%/INCLUDE;%INCLUDE%

set LIB=%DFDir%/LIB;%VCDir%/LIB;%LIB%

rem ********************************************************************

rem Compiler parameters

rem ********************************************************************

set COMPILER=df

set COMPFLAGS=/fpp:"/m /S%MATLAB%/extern/include" /compile_only  /tune:host /nologo /DMATLAB_MEX_FILE /keep

set OPTIMFLAGS = /threads /optimize:4 /fast /arch:generic /assume:nodummy_aliases /inline:speed

rem set OPTIMFLAGS=/libs:dll /threads /optimize:4 /fast /arch:generic /assume:nodummy_aliases /inline:speed /DNDEBUG

set DEBUGFLAGS=/libs:dll /threads /dbglibs /debug:full /pdbfile

set NAME_OBJECT=/Fo

rem ********************************************************************

rem ********************************************************************

set LIBLOC=%MATLAB%/extern/lib/win32/digital/df60

set LINKFLAGS=/DLL /EXPORT:_MEXFUNCTION@16 /LIBPATH:"%LIBLOC%" libmx.lib libmex.lib libmat.lib /implib:%LIB_NAME%.lib /NOLOGO

set NAME_OUTPUT="/out:%OUTDIR%%MEX_NAME%.dll"

set RSP_FILE_INDICATOR=@

rem ********************************************************************

rem Resource compiler parameters

rem ********************************************************************

set RC_COMPILER=rc /fo "%OUTDIR%mexversion.res"

rem ********************************************************************

rem There is little conflict between IMSL & CXML or MKL, but

rem CXML & MKL conflict. I typically will leave all of the LIB variables

rem uncommented, and I uncomment the IMSL LINK_FILE, but leave the

rem other LINK_FILE variables commented. Then if I need MKL or CXML I

rem specify the libs on the mex command line.

rem ********************************************************************

rem ********************************************************************

rem Uncomment these lines for MKL 5.2 libs

rem One of the architecture-specific lines below must also be uncommented

rem ********************************************************************

set LIB=%LIB%;C:/Program Files/Intel/MKL/ia32/lib

rem ********************************************************************

rem Choose One (p4=Pentium 4, p3=Pentium 3, def=all others)

rem ********************************************************************

rem ********************************************************************

rem Uncomment these lines for IMSL libs

rem ********************************************************************

set LIB=%LIB%;%DFDir%/IMSL/LIB;

rem ********************************************************************

rem Uncomment these lines for CXML libs

rem ********************************************************************

set LIB=%LIB%;%DFDir%/CXML/LIB

set INCLUDE=%INCLUDE%;%DFDir%/CXML/INCLUDE

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

3. Why can't I compile FORTRAN90/95 source code using the MEX command?

(This information has only been tested on Win32 using DVF6.x. On other platforms, mileage may vary.)

R13 (v6.5)

The Mathworks have finally updated mex.bat to handle .f90 files properly, however, they have specified compiler options that cause many Fortran9x files that worked perfectly with R11 and R12 to fail. You will need to alter your df66opts.bat file located at c:/matlab6p5/bin/win32/mexopts/ and then run >>mex -setup again

If you examine your mexopts.bat file, you will find variables called OPTIMFLAGS and COMPFLAGS that look like

set OPTIMFLAGS=/MD -Ox -DNDEBUG

set COMPFLAGS=/fpp:"/m /S%MATLAB%/extern/include" -c -nokeep -G5 -nologo -DMATLAB_MEX_FILE /fixed

set DEBUGFLAGS=/MDd -Zi

Replace these lines with:

set OPTIMFLAGS=/MT -Ox -DNDEBUG

set COMPFLAGS=/fpp:"/m /S%MATLAB%/extern/include" -c -nokeep -G5 -nologo -DMATLAB_MEX_FILE

set DEBUGFLAGS=/MTd -Zi

(delete /fixed and change /MD -> /MT and /MDd -> /MTd) You can see my df66opts.bat for more details.

R12 (v6)

(This was submitted to me by Rob Brendel, I haven't tried it yet, as I don't have R12)

Make changes, as above, to lines 1216, 1283 and 1618 of c:/matlab/bin/mex.bat .

That should work just fine.

R11 (version 5.x)

Basically the problem is that mex.bat doesn't recognize files with extension .f90 as being source files, it thinks that they are object files and passes them straight to the linker. This is obviously problematic. This change will make your F90 code work fine:

(1) open c:/matlab/bin/mex.bat using notepad (or pico or emacs, for your *NIXers)

(2) line 2338 is:

if ($EXTENSION =~ /(c|f|cc|cxx|cpp|for)$/i ) {

change it to

if ($EXTENSION =~ /(c|f|cc|cxx|cpp|for|f90)$/i ) {

(notice the "|f90"?)

And that is all you will need! You should be able to run just fine.

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120