CSDN博客

img yuanqingfei

USING FORTRAN90 CODE IN MATLAB MEX-FILES

发表于2004/7/5 19:32:00  2052人阅读

 

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.
However, how impatient you might be, please read the
RED COMMENTS!
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 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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 LINKFLAGS=%LINKFLAGS% sstatd.lib sstats.lib

set LINKFLAGS=%LINKFLAGS% smathd.lib smaths.lib sf90mp.lib

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 LINKFLAGS=%LINKFLAGS% mkl_s.lib mkl_lapack.lib

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 set LINKFLAGS=%LINKFLAGS% mkl_def.lib

rem set LINKFLAGS=%LINKFLAGS% mkl_p3.lib

rem set LINKFLAGS=%LINKFLAGS% mkl_p4.lib

 

For CXML, add the following lines:

 

set LINKFLAGS=%LINKFLAGS% cxml.lib

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 Linker parameters

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

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

set LINKER=link

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

set LINKOPTIMFLAGS=

set LINKDEBUGFLAGS=/debug

set LINK_FILE=

set LINK_LIB=

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"

set RC_LINKER=

 

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 LINKFLAGS=%LINKFLAGS% mkl_s.lib mkl_lapack.lib

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 set LINKFLAGS=%LINKFLAGS% mkl_def.lib

set LINKFLAGS=%LINKFLAGS% mkl_p3.lib

rem set LINKFLAGS=%LINKFLAGS% mkl_p4.lib

 

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

rem Uncomment these lines for IMSL libs

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

set LINKFLAGS=%LINKFLAGS% sstatd.lib sstats.lib smathd.lib smaths.lib sf90mp.lib

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

 

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

rem Uncomment these lines for CXML libs

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

rem set LINKFLAGS=%LINKFLAGS% cxml.lib

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. 

0 0

相关博文

我的热门文章

img
取 消
img