Calling a Matlab function in Fortran - matlab

Using the MATLAB Engine API for Fortran, I am trying to call a simple MATLABfunction from a Fortran code.
I followed the fengdemo example found here.
It worked, so I want to adapt my Fortran code to call a specific Matlab script I wrote.
My MATLAB script call_fortran.m is very simple: it takes x as an entry and multiplies it by 2:
function multiply = call_fortran(x)
multiply = 2*x;
end
I want my FORTRAN code to generate a variable my_x, open a MATLAB session, send the variable to the workspace, apply the function call_fortran and display the result. Using the fengdemo.f code, I wrote :
program main
C Declarations
implicit none
mwPointer engOpen, engGetVariable, mxCreateDoubleMatrix
mwPointer mxGetPr
mwPointer ep, my_x ! ep: variable linked to engOpen, starting a Matlab session, my_x: variable que je veux donner a Matlab
double precision my_x
integer engPutVariable, engEvalString, engClose
integer temp, status
mwSize i
my_x = 6
ep = engOpen('matlab ')
if (ep .eq. 0) then
write(6,*) 'Can''t start MATLAB engine'
stop
endif
C Place the variable my_x into the MATLAB workspace
status = engPutVariable(ep, 'my_x', my_x)
C
if (status .ne. 0) then
write(6,*) 'engPutVariable failed'
stop
endif
! My issue now is to call the correct Matlab script
! nlhs = 1
! plhs = 1
! nrhs = 1
! prhs = 1
! integer*4 mexCallMATLAB(nlhs, plhs, nrhs, prhs, functionName)
So I have my my_x, I send it to MATLAB, but how do I apply the call_fortran.m function and get the new value of my_x?

You have two fundamental errors with this code. You do not use the correct variable type for my_x, and you cannot call mexCallMATLAB( ) from an Engine application (that can only be used in mex routines). Let's fix these.
First, the my_x variable needs to be an mxArray, not a double precision variable. There are various ways to do this, but for a scalar, the easiest way to create this array is as follows:
mwPointer, external :: mxCreateDoubleScalar
mwPointer my_x
my_x = mxCreateDoubleScalar(6.d0)
Then you can pass this to the MATLAB Engine per your current code. To call your function in the MATLAB Engine workspace, you need to evaluate a string there:
integer*4, external :: engEvalString
integer*4 status
status = engEvalString( ep, 'result = call_fortran(my_x)' )
The result should display in the Engine workspace since we did not terminate the string with a semi-colon. If you want to get the result back into your Fortran code, you would need to do something like this:
mwPointer, external :: engGetVariable
mwPointer result
result = engGetVariable( ep, 'result' )
The result inside your Fortran code will be an mxArray. To extract the number there are various ways, but for a scalar it would be easiest to just do as follows (the real*8 is used instead of double precision to match the MATLAB API signature in the doc exactly):
real*8, external :: mxGetScalar
real*8 myresult
myresult = mxGetScalar(result)
To avoid memory leaks, once you are done with the mxArray variables you should destroy them. E.g.,
call mxDestroyArray(my_x)
call mxDestroyArray(result)
Having written all this, are you sure you want to create MATLAB Engine applications, and not mex routines? Mex routines are generally easier to work with and don't involve extra data copies to pass variables back & forth.

Related

How to solve the error undefined the function or variables in matlab

This is the code I write in Matlab. As you can see, I have defined the symbolic variable tao. However, it still shows that undefined variable or functions. I didn't know where is wrong in the code.
The function is like this:
Ch = 0.8; H=0.6088;
syms t u s k tao
fsb= limit( symsum( (int( Ch*s^(1/2)*int((u-s)^(H-3/2)*u^(H-1/2),u,floor(t/tao)*tao,floor((t+tao)/tao)*tao),s,(k-1)*tao,k*tao ))^2,k,1,floor(t/tao)),tao,0) + ...
+limit( (int( Ch*s^(1/2)*int((u-s)^(H-3/2)*u^(H-1/2),u,s,floor((t+tao)/tao)*tao ),s,floor(t/tao)*tao,floor((t+tao)/tao)*tao) )^2,tao , 0);
fsb = matlabFunction(fsb);
sss=fsb(1);
I have tried change the path into the Matlab installed path, but it didn't work.
The fsb is a function defined by my self. It's a complicated function including integral and limit.
This is the information about the error:
undefined functions or variables 'tao'
symengine>#(t)limit(integral(#(s)sqrt(s).*integral(#(u)u.^(6.8e1./6.25e2).*1.0./(-s+u).^(5.57e2./6.25e2),s,tao.*floor((t+tao)./tao)).*(4.0./5.0),tao.*floor(t./tao),tao.*floor((t+tao)./tao)).^2,tao==0.0)+limit(symsum(integral(#(s)sqrt(s).*integral(#(u)u.^(6.8e1./6.25e2).*1.0./(-s+u).^(5.57e2./6.25e2),tao.*floor(t./tao),tao.*floor((t+tao)./tao)).*(4.0./5.0),tao.*(k-1.0),k.*tao).^2,k,1.0,floor(t./tao)),tao==0.0,Real)

Increase file size for Mex on Matlab

I'm writing a FORTRAN mex file in Matlab r2019a. I've tested the code with small arrays and everything works fine. However, when I try and increase the size of the arrays I get the following error.
fatal error LNK1248: image size (9B993000) exceeds maximum allowable size(80000000)
I'm using Visual Studio 2017 and intel FORTRAN compiler on windows 10 and I've got 16GB of RAM.
This link seems to have the solution, but I can't seem to find the linker option in VS 2017 to pass -heap-array.
https://software.intel.com/content/www/us/en/develop/articles/intel-fortran-compiler-increased-stack-usage-of-80-or-higher-compilers-causes-segmentation-fault.html
Could someone please walk me through how to increase the size?
Thank you
Kinan
#include "fintrf.h"
C Gateway routine
subroutine mexFunction(nlhs, plhs, nrhs, prhs)
C Declarations
implicit none
C mexFunction arguments:
mwPointer plhs(*), prhs(*)
mwSize nlhs, nrhs
C Function declarations:
mwPointer mxCreateDoubleMatrix
mwPointer mxDuplicateArray
mwPointer mxGetPr
mwPointer mxGetM, mxGetN
mwSize mxIsNumeric
C Pointers to input/output mxArrays:
mwPointer coord_pr,dualnumfam_pr,dualfail_pr,dualpointfam_pr
mwPointer dualnodefam_pr,totnode_pr,width_pr,scr0_pr,vol_pr,bc_pr
mwPointer disp_pr,numfam_pr,nodefam_pr,pointfam_pr,fail_pr
mwPointer dmgpar1_pr,dmgpar2_pr, pforce_pr, dualpforce_pr
mwPointer fails_pr,dualfails_pr
C Array information:
mwPointer m, n, x, y, u, l,o, p
mwSize mm, nn
mwSize size, row, sizes
C Arguments for computational routine:
real*8 coord(450000), dualnumfam(300000)
real*8 dualfail(30000000), dualpointfam(150000)
real*8 dualnodefam(30000000),totnode(1) ,width(1)
real*8 scr0(150000), vol(150000), bc(150000)
real*8 disp(450000), numfam(150000),nodefam(30000000)
real*8 pointfam(150000),fail(30000000)
real*8 dmgpar1(150000),dmgpar2(150000)
real*8 pforce(450000),dualpforce(450000)
real*8 fails(30000000),dualfails(30000000)
character*200 msg
character*20 fmt
character*10 sm, sn, sx, sy, so, sp
C-----------------------------------------------------------------------
C Check for proper number of arguments.
if (nrhs .ne. 15) then
call mexErrMsgIdAndTxt ('MATLAB:test:nInput',
+ 'One inputs required.')
endif
C Validate inputs
C Check to see both inputs are numeric.
if (mxIsNumeric(prhs(1)) .ne. 1) then
call mexErrMsgIdAndTxt ('MATLAB:test:NonNumeric1',
+ 'Input # 1 is not a numeric.')
endif
C Check that input #1 is a scalar.
m = mxGetM(prhs(1))
n = mxGetN(prhs(1))
size = m*n
x = mxGetM(prhs(3))
y = mxGetN(prhs(3))
sizes=x*y
u = mxGetM(prhs(5))
fmt = '(I8)'
write (sm,fmt) m
write (sn,fmt) n
write (sx,fmt) x
write (sy,fmt) y
msg = 'm=' // trim(sm) // ',\t n=' // trim(sn) // '\n'
call mexPrintf(trim(msg))
msg = 'x=' // trim(sx) // ',\t y=' // trim(sy) // '\n'
call mexPrintf(trim(msg))
C Create matrix for the return argument.
call mexPrintf("one")
plhs(1) = mxCreateDoubleMatrix(m,1,0)
plhs(2) = mxCreateDoubleMatrix(m,1,0)
plhs(3) = mxCreateDoubleMatrix(m,3,0)
plhs(4) = mxCreateDoubleMatrix(m,3,0)
plhs(5) = mxCreateDoubleMatrix(x,y,0)
plhs(6) = mxCreateDoubleMatrix(x,y,0)
call mexPrintf("two")
coord_pr = mxGetPr(prhs(1))
dualnumfam_pr = mxGetPr(prhs(2))
dualfail_pr = mxGetPr(prhs(3))
dualpointfam_pr = mxGetPr(prhs(4))
dualnodefam_pr = mxGetPr(prhs(5))
totnode_pr = mxGetPr(prhs(6))
width_pr = mxGetPr(prhs(7))
scr0_pr = mxGetPr(prhs(8))
vol_pr = mxGetPr(prhs(9))
bc_pr = mxGetPr(prhs(10))
disp_pr = mxGetPr(prhs(11))
numfam_pr = mxGetPr(prhs(12))
nodefam_pr = mxGetPr(prhs(13))
pointfam_pr = mxGetPr(prhs(14))
fail_pr = mxGetPr(prhs(15))
dmgpar1_pr = mxGetPr(plhs(1))
dmgpar2_pr = mxGetPr(plhs(2))
pforce_pr = mxGetPr(plhs(3))
dualpforce_pr = mxGetPr(plhs(4))
fails_pr = mxGetPr(plhs(5))
dualfails_pr = mxGetPr(plhs(6))
call mexPrintf("three")
C Load the data into Fortran arrays.
call mxCopyPtrToReal8(coord_pr,coord,size)
call mxCopyPtrToReal8(dualnumfam_pr,dualnumfam,m)
call mxCopyPtrToReal8(dualfail_pr,dualfail,sizes)
call mxCopyPtrToReal8(dualpointfam_pr,dualpointfam,m)
call mxCopyPtrToReal8(dualnodefam_pr,dualnodefam,u)
call mxCopyPtrToReal8(totnode_pr,totnode,1)
call mxCopyPtrToReal8(width_pr,width,1)
call mxCopyPtrToReal8(scr0_pr,scr0,m)
call mxCopyPtrToReal8(vol_pr,vol,m)
call mxCopyPtrToReal8(bc_pr,bc,m)
call mxCopyPtrToReal8(disp_pr,disp,size)
call mxCopyPtrToReal8(numfam_pr,numfam,m)
call mxCopyPtrToReal8(nodefam_pr,nodefam,u)
call mxCopyPtrToReal8(pointfam_pr,pointfam,m)
call mxCopyPtrToReal8(fail_pr,fail,sizes)
call mxCopyPtrToReal8(dmgpar1_pr,dmgpar1,m)
call mxCopyPtrToReal8(dmgpar2_pr,dmgpar2,m)
call mxCopyPtrToReal8(pforce_pr,pforce,size)
call mxCopyPtrToReal8(dualpforce_pr,dualpforce,size)
call mexPrintf("four")
call mxCopyPtrToReal8(fails_pr,fails,sizes)
call mxCopyPtrToReal8(dualfails_pr,dualfails,sizes)
C Call the computational subroutine.
call body(coord,dualnumfam,dualfail,dualpointfam,dualnodefam,
+ totnode,width,scr0,vol,bc,disp,numfam,nodefam,pointfam,fail
+ ,m,n,dmgpar1,dmgpar2,pforce,dualpforce,u,fails,dualfails)
C Load the output into a MATLAB array.
call mxCopyReal8ToPtr(dmgpar1,dmgpar1_pr,m)
call mxCopyReal8ToPtr(dmgpar2,dmgpar2_pr,m)
call mxCopyReal8ToPtr(pforce,pforce_pr,size)
call mxCopyReal8ToPtr(dualpforce,dualpforce_pr,size)
call mxCopyReal8ToPtr(fails,fails_pr,sizes)
call mxCopyReal8ToPtr(dualfails,dualfails_pr,sizes)
return
end
The stack is a relatively small amount of memory compared to the heap. The stack is a fixed amount of memory that is essentially part of your program. It is used for such things as passing arguments among routines, local variable memory, etc. Because of its relatively small size, you should not create large local variables that can overflow the stack. This is true in any language, not just Fortran. So creating a local variable like this:
real*8 dualfail(30000000)
causes dualfail memory to come from the stack.
A better method for large variables is to allocate memory for them from the heap, which is essentially your entire main computer memory. E.g.,
real*8, allocatable :: dualfail(:)
integer alloc_stat
allocate(dualfail(30000000),stat=alloc_stat)
if( alloc_stat /= 0 ) then
! allocation failed, so take action here
endif
! code to use dualfail here
deallocate(dualfail)
All of your large variables should use this technique.
Having said that, it appears that the only reason you have these large variables in your mex routine is to make copies of the MATLAB inputs & outputs for the body( ) routine. This is a very inefficient way of managing this. You are also deep copying your output variables which are just 0's even though I am guessing they get overwritten by body( ) anyway. Rather than taking this approach, especially when working with large variables, it would be best to simply pass "pointers" to the memory to your routines. One way to do this is using the %VAL( ) construct. This will eliminate the need to create these large local variables in the first place. E.g.,
call body(%VAL(coord_pr),%VAL(dualnumfam_pr),%VAL(dualfail_pr), etc.
So everywhere you were passing copies of the MATLAB variable data, you would instead pass the "pointers" (actually passing the address contained in the integer by value) to the original data areas of the MATLAB variables. As long as your body( ) routine treats the MATLAB prhs( ) inputs as read-only then this will work without the need to make deep copies as you are doing. This eliminates the need for all of those mxCopyReal8ToPtr( ) and mxCopyPtrToReal8( ) calls.
Note that my use of the word "pointers" above is used generically ... these are not Fortran pointer variables. But using actual Fortran pointers would be another method for avoiding deep data copies ... i.e., turn the integer returned by mxGetPr( ) into a regular Fortran pointer and then use that downstream in your code.
I would also point out that your mex routine is severely lacking in input argument checks. You only check for the number of inputs and that the first input is numeric. What you should be doing is checking that each and every one of your inputs is double, non-complex, non-sparse, and is of the correct size. As it is the routine is not robust and you are at risk of getting erroneous results or crashing MATLAB if the inputs are not exactly as expected.
Finally, be advised that for some reason The Mathworks has chosen to hard code the /fixed option into their Fortran mex build files. I have asked them to remove this but as of R2020a it is still there. This forces the compiler to treat all files as fixed format, even those ending in .f90. My advice is to find those xml build files on your system and remove the /fixed option from those files. That way you can naturally compile .f90 files as free format.

Portable declaration of REAL variables in mex gateway for Fortran

I am writing a mex gateway for a piece of Fortran code.
In the Fortran code, for portability, the floating-point variables are declared as
REAL(kind(0.0D0)) :: x, y, etc
(BTW, I am aware that there are better ways to do it, as discussed at
Fortran: integer*4 vs integer(4) vs integer(kind=4),
What does "real*8" mean?, and
https://software.intel.com/en-us/blogs/2017/03/27/doctor-fortran-in-it-takes-all-kinds )
However, it seems to me that mex supports only REAL*8 and REAL*4, the former being Double, the latter being Single. I got this impression from the following functions/subroutines:
mxIsDouble, mxIsSingle, mxCopyPtrToReal8, mxCopyReal8ToPtr, mxCopyPtrToReal4, mxCopyReal4ToPtr
My questions are as follows.
Is it true that mex supports only REAL*8 and REAL*4?
Does it improve the portability of the mex gateway if I declare double-precision floating-point variables as
REAL(kind(0.0D0)) :: x, y, etc
or even
integer, parameter :: dp = selected_real_kind(15, 307)
real(kind=dp) :: x, y, etc
Or should I simply declare
REAL*8 :: x, y, etc
Are REAL*8 and/or REAL*4 supported on all platforms? If no, does this mean that MATLAB mex is intrinsically unportable?
What is the best way to specify the kind of floating-point variables in mex gateways for Fortran code?
The following code is an example. See the declaration of x, y, and xs.
#include "fintrf.h"
subroutine mexFunction(nlhs, plhs, nrhs, prhs)
C y = square (x)
C x: a floating point scalar
C y: x^2
implicit none
C mexFunction arguments
integer, intent(in) :: nlhs, nrhs
mwPointer, intent(in) :: prhs(nrhs)
mwPointer, intent(inout) :: plhs(nlhs)
C function declarations:
mwPointer, external :: mxCreateDoubleScalar, mxGetPr
mwSize, external :: mxGetM, mxGetN
integer*4, external :: mxIsDouble, mxIsSingle
C variables
mwSize, parameter :: mwOne = 1
integer, parameter :: dKind = kind(0.0D0)
integer, parameter :: sKind = kind(0.0)
real(kind=dKind) :: x, y ! Does this improve the portablity?
real(kind=sKind) :: xs ! Does this improve the portablity?
C validate number of arguments
if (nrhs .ne. 1) then
call mexErrMsgIdAndTxt ('mex:nInput', '1 input required.')
endif
if (nlhs .gt. 1) then
call mexErrMsgIdAndTxt ('mex:nOutput', 'At most 1 output.')
endif
C validate input
if (mxIsDouble(prhs(1)) .ne. 1 .and. mxIsSingle(prhs(1)) .ne. 1)
! What if the input is a floating point number but neither Double nor Single?
+ then
call mexErrMsgIdAndTxt ('mex:Input', 'Input a real number.')
endif
if (mxGetM(prhs(1)) .ne. 1 .or. mxGetN(prhs(1)) .ne. 1) then
call mexErrMsgIdAndTxt ('mex:Input', 'Input a scalar.')
endif
C read input
if (mxIsDouble(prhs(1)) .eq. 1) then
call mxCopyPtrToReal8(mxGetPr(prhs(1)), x, mwOne)
else
call mxCopyPtrToReal4(mxGetPr(prhs(1)), xs, mwOne)
x = real(xs, dKind)
! What if the input is a floating point number but neither REAL*8 nor REAL*4
endif
C do the calculation
y = x**2
C write output
plhs(1) = mxCreateDoubleScalar(y)
return
end subroutine mexFunction
The code runs correctly. Yet I am not sure whether it is portable.
REAL*4 and REAL*8 are non-standard and non-portable. REAL(KIND(0.0D0) gets you DOUBLE PRECISION on every platform, as this is required by the Fortran standard.
I can't speak to MEX gateways, but you should avoid obvious non-standard features.
A popular choice is to define a module that declares named (PARAMETER) constants for the kinds in use. For example:
module kinds
integer, parameter :: SP = KIND(0.0)
integer, parameter :: DP = KIND(0.0D0)
end module kinds
Then you can use SP and DP as kind values. If you ever need to change these, just edit the module.
Currently, it makes no difference whether you define variables as REAL*8/REAL*4 or REAL(REAL64)/REAL(REAL32). In the future MathWorks may come around and rewrite their functions to use portable variable declarations, but in my opinion this is unlikely for many reasons.
If you look in the fintrf.h file (included in every Fortran MEX gateway source file), you'll see that all of the MEX-specific procedures are defined with "asterisk notation," e.g. # define MWPOINTER INTEGER*8. So even if you define all of your variables with kinds from iso_fortran_env or selected_real_kind, any time you use a MathWorks variable type you're still using "asterisk notation" types, unless you go through that header file and redefine every symbol using your chosen kind specification.

Deploy an matlab file to executable

I want to deploy an m file into an executable. I am using mcc command: mcc -m epidemic.m. Epidemic is my function which takes no arguments and returns a vector and write that vector to txt. Mcc creates epidemic.exe and when I am running that exe it creates the txt file however it seems that it doesnt return values (the return value of .exe). I am trying to run the exe from matlab using:
cmd = ['epidemic.exe '];
system(cmd);
It return cmdout " and status 0. How can I take the returned values of the .exe?
When you compile matlab code like:
function [out1, out2] = epidemic(in1, in2, in3)
%[
...
%]
to standalone (mcc -m epidemeic.m), Matlab produces somehow the following pseudo c-code and compiles it to .exe:
int main(int argc, char** argv)
{
// Load compiled code produced by mcc
HMCRInstance* hInst = loadByteCodeProducedByMccFromResources();
// Similar to have wrote in matlab "epidemic(argv[0], argv[1], ...)"
// 1) Without asking for any argument output
// 2) Argument inputs are passed as strings
int errorCode = mclFevalFromExeArg(hInst, "epidemic", argc, argv);
return errorCode; // only indicates if call to 'mclFEvalFromExeArg'
// succeded, it does not relate to out1, out2 at all.
}
NB: If you want to see the exact code produced by mcc, use mcc -W main -T codegen epidemic.m
So, directly compiling to standalone, you cannot work with outputs of your Matlab function. If you need to play around with output arguments of epidemic, either
[Simple solution] Consider saving outputs to files or display them to shell console using disp (NB: you can use isdeployed in your .m file to check if you're running from matlab or from compiled code).
[Advanced solution] Consider compiling your code to shared library (mcc -l epidemic.m) instead of standalone (mcc -m epidemeic.m)
NB: When you compile your code to shared library, mcc will produce a dll that exports the following function:
extern LIB_epidemeic_C_API
bool MW_CALL_CONV mlxEpidemic(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]);
nrhs/prhs are the number of input arguments and their values (as mxArray type). And nlhs/plhs are the ouput arguments you want to have when calling epidemic. Up to you to do the marshaling between mxArray and equivalent C native type.
EDIT
As you indicate that epidemic returns a vector of values, you can display them from standalone like this:
function [output] = epidemic(v1, v2, v3)
%[
% When called from system cmd line, v1, v2, v3 are passed
% as string. Here is how to convert them to expected type if required
if (ischar(v1)), v1 = str2double(v1); end
if (ischar(v2), v2 = str2double(v2); end
if (ischar(v3)), v3 = str2double(v3); end
...
output = ...;
...
if (isdeployed())
disp(output);
end
%]
An exe does not have a return value, you need to find another way to transport the data back, for example via console outputs or text files. What you get is the error code and error message.

MATLAB, mex files, Fortran, warnings and errors

I'm not quite seeing the issue I'm having, and I've tried suggestions I've seen in other forum posts. I am trying to write a (fortran) mex file, per request of my boss. However, I was getting warnings when passing a matrix to my computational routine. If I ignored the warning, my MATLAB shut down. So now I'm trying a simpler program, an inner product. However, I am still getting the warning: "Expected a procedure at (1)" where (1) is at 'call innerProd(x,y,c)' underneath the x. I'm not sure what that means... I've included my code.
#include "fintrf.h"
C======================================================================
#if 0
C
C innerProd.F
C .F file needs to be preprocessed to generate .for equivalent
C
#endif
C
C innerProd.F
C calculates the inner product
C This is a MEX file for MATLAB.
C Copyright 1984-2011 The MathWorks, Inc.
C $Revision: 1.12.2.9 $
C======================================================================
C Gateway routine
subroutine mexFunction(nlhs, plhs, nrhs, prhs)
C Declarations
implicit none
C mexFunction arguments:
mwPointer:: plhs(*), prhs(*)
integer:: nlhs, nrhs
C Function declarations:
mwPointer:: mxCreateDoubleMatrix, mxGetPr,mxGetM, mxGetData
integer:: mxIsNumeric
C Pointers to input/output mxArrays:
mwPointer:: x_ptr, y_ptr, c_ptr
C Array information:
mwSize:: m
C Arguments for computational routine:
real*8:: x,y,c
C----------------------------------------------------------------------
C Check for proper number of arguments.
if (nrhs .ne. 2) then
call mexErrMsgTxt('Error.')
elseif (nlhs .ne. 1) then
call mexErrMsgTxt('One output required.')
endif
C Check to see if inputs are numeric.
if (mxIsNumeric(prhs(1)) .ne. 1 ) then
call mexErrMsgTxt('Input # 1 is not a numeric array.')
elseif (mxIsNumeric(prhs(2)) .ne. 1) then
call mexErrMsgTxt('Input #2 is not a numeric array.')
endif
C Find dimensions of mxArrays
m=mxGetM(prhs(1))
C create Fortran arrays from the input arguments
x_ptr=mxGetData(prhs(1))
call mxCopyPtrToReal8(x_ptr,x,m)
y_ptr= mxGetData(prhs(2))
call mxCopyPtrToReal8(y_ptr,y,m)
C create matrix for the return argument
plhs(1) =mxCreateDoubleMatrix(1,1,0)
c_ptr= mxGetPr(plhs(1))
C Call the computational subroutine.
call innerProd(x,y,c)
C Load the output into a MATLAB array.
call mxCopyReal8ToPtr(c, c_ptr, 1)
return
end subroutine mexFunction
C----------------------------------------------------------------------
C Computational routine
subroutine innerProd(x,y,c)
implicit none
real*8:: x,y,temp,c
integer:: i,m
do i=1,m
temp=temp+x(i)*y(i)
end do
c = temp
return
end subroutine innerProd
I'm just learning this for the first time, and I would appreciate any suggestions. Even if it is where to look for solutions. I've looked through MATLAB mex Fortran aids online. There isn't any help there. I can't get the function to run, so I can't use print statements which is a good way to debug. I think mex has a print function, I will try to get that to work.
Thank you!
The main problem is that you haven't anywhere declared the arguments of innerProd as arrays. That holds for the actual arguments x and y in the subroutine mexFunction and the dummy arguments x and y in innerProd itself.
So, in innerProd the expression x(i) isn't referencing the i-th element of the real*8 array x, but the real*8 result of the function x with argument i. As the x you've passed isn't a function (procedure), this is an error.
There are ways to solve this, but all involve declaring the dummy arguments as arrays. Which brings up another point.
You have in innerProd
integer:: i,m
do i=1,m
temp=temp+x(i)*y(i)
end do
where m is not defined. Crucially, from mexfunction you're expecting the compiler to know that m is the size of the arrays x and y: this isn't true. m is a variable local to innerProd. Instead you may want to pass it as an argument to the subroutine and use that to dimension the arrays:
subroutine innerProd(x,y,c,m)
implicit none
integer :: m
real*8:: x(m),y(m),temp,c
...
end subroutine
[You could, of course, use assumed-shape arrays (and the SIZE intrinsic), but that's an additional complication requiring more substantial changes.] You also need to think about how to declare arrays appropriately in mexfunction, noting that the call to mxCopyPtrToReal8 also requires an array argument.
I couldn't get the Fortran code to work for innerProd, but I did get C code to work. I recommend, if you are having issues with Fortran, to use C. It seems that Matlab is more flexible when it comes to mex files and C. Here is the code:
#include "mex.h"
/*
* innerProd.c
*
*Computational function that takes the inner product of two vectors.
*
*Mex-file for MATLAB.
*/
void innerProd(double *x, double *y,double *c,int m){
double temp;
int i;
for(i=0; i < m; i++){
temp = temp + x[i]*y[i];
}
*c=temp;
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *x, *y, *c;
size_t m;
/*check for proper number of arguments.*/
if(nrhs != 2){
mexErrMsgIdAndTxt("MATLAB:innerProd:invalidNumInputs","Two input required.");
}
/*The input must be a noncomplex double vector.*/
m = mxGetM(prhs[0]);
if(!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) || m==1){
mexErrMsgIdAndTxt("MATLAB:innerProd:inputNotRealDouble","Input must be noncomplex double vector");
}
/*create return argument */
plhs[0] = mxCreateDoubleMatrix(1,1,0);
c=mxGetPr(plhs[0]);
/*Assign pointers to each input and output. */
x = mxGetPr(prhs[0]);
y=mxGetPr(prhs[1]);
/*call subroutine*/
innerProd(x,y,c,m);
}
I will still take suggestions on the Fortran code above, though. I'd like to know how to get it to work. Thanks!