I could successfully use Mathlink to call a simple fortran subroutine. However, when I went to a realistic problem, I failed. To be specific, I want to fit a set of data with some function using the cernlib package MINUIT (for function minimization and error analysis). Then within FORTRAN, I can get the best parameter sets and their errors, and many more.
Now, I want to plot some figures with the resulting best parameters in Mathematica. Of course, I can read the parameters by eyes... But it would be nice to do this with Mathlink. However, I failed. I created an executable, but it cannot be recoganized by Mathlink. Install simply hang until I aborted it. This is a bit strange, since the fortran code was successfully called in C when Mathlink was not using.
Here is the shortened FORTRAN code, to be called myprog.f
, (still somewhat lengthy) for a MINUIT example here:
! n is not used, just to give the Mathlink function an argument
subroutine myprog(n, fmin)
!program myprog
implicit none
integer SYSIN/1/, SYSRD/5/, SYSWR/6/, SYSPU/7/
integer n, ndat, npari, nparx, istat
real(8) fmin, fedm, errdef
external myfcn
! initialize unit numbers
call mintio(SYSRD, SYSWR, SYSPU)
!driven card running the fit
open(unit=SYSRD, file='myprog.mincards', status='OLD')
!output, if commented out, the output will be written to terminal
open(unit=SYSWR, file='myprog.out', status='UNKNOWN')
! invoke minuit
call minuit(myfcn, 0)
! current status of minimization
CALL mnstat(fmin, fedm, errdef, npari, nparx, istat)
end
subroutine myfcn(npar, g, f, x, iflag, futil)
implicit none
integer npar, iflag, i, ndat, n
integer SYSIN/1/, SYSRD/5/, SYSWR/6/, SYSPU/7/
parameter (ndat=5)
real*8 f, g(*), x(*), xdat(ndat), ydat(ndat), edat(ndat), func,
& answer/15.7/, rms, avg, diff, futil
! Save all data that must be preserved between calls!
save xdat, ydat, edat ! MUST
if (iflag .eq. 1) then
! Initialization mode
do i=1,ndat
xdat(i) = i
ydat(i) = answer + i - float(ndat + 1) / 2.
edat(i) = 1.
end do
end if
! compute LSQ
f = 0
do i = 1, ndat
f = f + (ydat(i) - func(xdat(i), x(1)))**2 / edat(i)
end do
end
function func(x, par)
implicit none
real*8 x, par(*), func
func = par(1)
end
The driven card myprog.mincards, where the initial values of the parameters are set, contains
set title
Minuit data cards for myprog example program
parameters
1 'average' 0.0 1.0
set print 1
mig
mino
return
stop
The subroutine myprog(n,fmin)
(fmin gives the minimized value of chi-square) can be successfully called in C with the following code
#include "stdio.h"
#ifdef __cplusplus
extern "C"{
#endif
double myprog(int n)
{
double fmin;
myprog_(&n, &fmin);
return fmin;
}
#ifdef __cplusplus
}
#endif
int main(void)
{
printf("%f\n", myprog());
return 0;
}
The result is 10. I thought as long as the FORTRAN subroutine can be called in C, one should be able to call it in Mathematica with Mathlink. Here is the C wrapper (I paste the complete code since it might be helpful to others):
#include "mathlink.h"
#ifdef __cplusplus
extern "C"{
#endif
double myprog(int n)
{
double fmin;
myprog_(&n, &fmin);
MLPutDouble(stdlink, fmin);
return fmin;
}
#ifdef __cplusplus
}
#endif
#if WINDOWS_MATHLINK
#if __BORLANDC__
#pragma argsused
#endif
int PASCAL WinMain( HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, LPSTR lpszCmdLine, int nCmdShow)
{
char buff[512];
char FAR * buff_start = buff;
char FAR * argv[32];
char FAR * FAR * argv_end = argv + 32;
hinstPrevious = hinstPrevious; /* suppress warning */
if( !MLInitializeIcon( hinstCurrent, nCmdShow)) return 1;
MLScanString( argv, &argv_end, &lpszCmdLine, &buff_start);
return MLMain( (int)(argv_end - argv), argv);
}
#else
int main(int argc, char* argv[])
{
return MLMain(argc, argv);
}
#endif
The Mathlink template file myprog.tm is
:Begin:
:Function: myprog
:Pattern: Minuit[n_Integer]
:Arguments: { n }
:ArgumentTypes: { Integer }
:ReturnType: Manual
:End:
:Evaluate: Minuit::usage = "Minuit[n] does fit using MINUIT. n can be an arbitary integer. This function returns the value of the least chi-square."
I use Windows 7 with MinGW. I wrote math.bat
to create an executable for Mathlink
gfortran -c myprog.f
gcc -c callfmath.c -o myprogc.o
mprep myprog.tm -o myprogtm.c
gcc -c myprogtm.c -o myprogtm.o
gcc *.o libminuit.a libML32i3.a -lm -lpthread -mwindows -lstdc++ -lgfortran -o myprog_math
rm *.o
where libminuit.a
is the library for MINUIT.
But the generated myprog_math.exe
does not do what I want. It does not create a link to Mathematica, and is not Mathlink-aware...
If anybody can tell me what was wrong with the procedure, I would be appreciate it very much!!!
EDIT: The MINUIT source files can be downloaded here, and libminuit.a made by gfortran.
myprog
without an argument (printf("%f\n", myprog());
), does C really work like that? I thoughtmyprog
required an integer as an input so that it could return a double. $\endgroup$libminuit.a
I get an error from the fileminuit.F
. This file "Can't open module file 'd506cm.mod' for reading". Do you know where I can get this? $\endgroup$