I've tried to use BLAS functions in my Fortran mex file, but it didn't work. Here's an example of one of my code that uses DGEMM:
#include "fintrf.h"
C Gateway subroutine
subroutine mexfunction(nlhs, plhs, nrhs, prhs)
C Declarations
implicit none
C mexFunction arguments:
mwPointer plhs(*), prhs(*)
integer nlhs, nrhs
C Function declarations:
mwPointer mxGetPr
mwPointer mxCreateDoubleMatrix
mwsize mxGetM,mxGetN
C Pointers to input/output mxArrays:
mwPointer pr_A, pr_B, pr_C
mwSize :: sizea,sizeb
C Array information:
real*8, allocatable :: A(:,:),B(:,:),C(:,:)
C Get the size of the input array.
sizea = mxGetM(prhs(1))
sizeb = mxGetN(prhs(2))
allocate(A(sizea,sizea),B(sizea,sizeb),C(sizea,sizeb))
C Create Fortran array from the input argument.
pr_A = mxGetPr(prhs(1))
pr_B = mxGetPr(prhs(2))
call mxCopyPtrToReal8( pr_A, A, sizea**2 )
call mxCopyPtrToReal8( pr_B, B, sizea*sizeb )
call MUL(A,B,C,sizea,sizeb)
C Create matrix for the return argument.
plhs(1) = mxCreateDoubleMatrix(sizea, sizeb, 0)
pr_C = mxGetPr(plhs(1))
call mxCopyReal8ToPtr(C , pr_C, sizea*sizeb )
return
end
subroutine MUL(A,B,C,sizea,sizeb)
implicit none
mwSize :: sizea,sizeb
real*8 :: A(sizea,sizea),B(sizea,sizeb),C(sizea,sizeb),alpha,beta
integer*4 :: M,N,K
character ch1, ch2
ch1 = 'N'
ch2 = 'N'
M=size(A,1)
N=size(B,2)
K=size(A,2)
Alpha=1.
Beta=0.
CALL DGEMM(ch1,ch2,M,N,K,ALPHA,A,M,B,K,BETA,C,M)
return
end subroutine MUL
I use the following line to create a mex file:
mex -lmwblas Test.F
The mex file builds without any error, but when I try to use the function, matlab closes without any error message. I'm using Matlab R2016a with Intel Visual Fortran Composer XE 2013 with Microsoft Visual Studio 2012.
You do not call DGEMM correctly. All of your arguments are real*8 but many of them are supposed to be integers.
Check thoroughly the documentation of DGEMM and check your call argument after argument to follow the interface exactly. I am pretty convinced passing the value of 1 instead of the sizea for K is wrong as well. But really, you have to check each and every argument.
I suggest to test your subroutines in pure Fortran and only after they work correctly use them from Matlab.
here's the solution:
#include "fintrf.h"
C Gateway subroutine
subroutine mexfunction(nlhs, plhs, nrhs, prhs)
C Declarations
implicit none
C mexFunction arguments:
mwPointer plhs(*), prhs(*)
integer nlhs, nrhs
C Function declarations:
mwPointer mxGetPr
mwPointer mxCreateDoubleMatrix
mwsize mxGetM,mxGetN
C Pointers to input/output mxArrays:
mwPointer pr_A, pr_B, pr_C
mwSignedIndex :: sizea,sizeb
C Array information:
real*8, allocatable :: A(:,:),B(:,:),C(:,:)
C Get the size of the input array.
sizea = mxGetM(prhs(1))
sizeb = mxGetN(prhs(2))
allocate(A(sizea,sizea),B(sizea,sizeb),C(sizea,sizeb))
C Create Fortran array from the input argument.
pr_A = mxGetPr(prhs(1))
pr_B = mxGetPr(prhs(2))
call mxCopyPtrToReal8( pr_A, A, sizea**2 )
call mxCopyPtrToReal8( pr_B, B, sizea*sizeb )
call MUL(A,B,C,sizea,sizeb)
C Create matrix for the return argument.
plhs(1) = mxCreateDoubleMatrix(sizea, sizeb, 0)
pr_C = mxGetPr(plhs(1))
call mxCopyReal8ToPtr(C , pr_C, sizea*sizeb )
return
end
subroutine MUL(A,B,C,sizea,sizeb)
implicit none
mwSignedIndex :: sizea,sizeb
real*8 :: A(sizea,sizea),B(sizea,sizeb),C(sizea,sizeb),alpha,beta
mwSignedIndex :: M,N,K
character ch1, ch2
ch1 = 'N'
ch2 = 'N'
M=size(A,1)
N=size(B,2)
K=size(A,2)
Alpha=1.0
Beta=0.0
CALL DGEMM(ch1,ch2,M,N,K,ALPHA,A,M,B,K,BETA,C,M)
return
end subroutine MUL
and to build the mex file use
mex -largeArrayDims -lmwblas Test.F
Related
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.
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.
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.
I have to use mxCalloc function instead of regular allocation in mex file to avoid matlab from crashing while using dgesv.
I've tried many ways but none of them worked.
Here's one of the samples
#include "fintrf.h"
C Gateway subroutine
subroutine mexfunction(nlhs, plhs, nrhs, prhs)
C Declarations
implicit none
C mexFunction arguments:
mwPointer plhs(*), prhs(*)
integer nlhs, nrhs
C Function declarations:
mwPointer mxGetPr
mwPointer mxCreateDoubleMatrix
mwPointer mxGetM
C Pointers to input/output mxArrays:
mwPointer pr_A, pr_B
C Array information:
mwPointer sizea,mxCalloc
real*8 :: A,B
character*120 :: line
C Get the size of the input array.
sizea = mxGetM(prhs(1))
A=mxCalloc(sizea*sizea,8)
B=mxCalloc(sizea*sizea,8)
C Create Fortran array from the input argument.
pr_A = mxGetPr(prhs(1))
call mxCopyPtrToReal8(pr_A,A,sizea**2)
C Create matrix for the return argument.
plhs(1) = mxCreateDoubleMatrix(sizea, sizea, 0)
pr_B = mxGetPr(plhs(1))
write(line,*), sizea
call mexPrintf(line)
B=A
call mxCopyReal8ToPtr(B,pr_B,sizea*sizea)
return
end
when i run this code, i get the following result
A = [0.9575 , 0.1576 ; 0.9649 , 0.9706]
test(A) = [0.9575 , 0 ; 0.9649 , 0]
but if i change the line
call mxCopyReal8ToPtr(B,pr_B,sizea*sizea)
to
call mxCopyReal8ToPtr(A,pr_B,sizea*sizea),
the results are correct
the variable sizea is equal to 2 which is correct, but i cant access any member of A, say A(1,1), and heres the error i encounter:
error #6410: This name has not been declared as an array or a
function. [A]
(Disclaimer: I have no experience with Mex, so please take the following code as such...)
This is some continuation of the answers by #ftiaronsem and a previous one by Dave. By looking at this tutorial, it seems OK to use Fortran allocatable arrays in the mexfunction(). But, the purpose of the OP is to use mxCalloc() rather than allocatable arrays for memory management.
According to the online manual of Matlab, mxCalloc() seems to return the address of the allocated memory as mwpointer (which is just an alias of integer*8 on 64-bit machines, according to the header file fintrf.h). So, we first get the address as
mwpointer iA
iA = mxCalloc( sizea * sizea, 8 )
(and similarly for B). Next, we want to access the memory starting from iA as 2-d Fortran array. This can be done by using c_f_pointer() (in F2003). But, since the first argument of this function receives type(c_ptr), we proceed as follows:
use iso_c_binding
type(c_ptr) cA
real*8, pointer :: A(:,:)
cA = transfer( iA, cA ) !! cast integer*8 to c_ptr
call c_f_pointer( cA, A, [ sizea, sizea ] ) !! init an array pointer with c_ptr
After this statement, we can use A as an ordinary 2-d Fortran array (e.g., we can pass it to LAPACK routines). Including these modifications into the OP's code gives the following. So could you try it and see whether it works or not...?
Updated code (ver1):
#include "fintrf.h"
C Gateway subroutine
subroutine mexfunction(nlhs, plhs, nrhs, prhs)
use iso_c_binding !<---
C Declarations
implicit none
C mexFunction arguments:
mwPointer plhs(*), prhs(*)
integer nlhs, nrhs
C Function declarations:
mwPointer mxGetPr
mwPointer mxCreateDoubleMatrix
mwsize mxGetM, sizea !<--- changed to mwsize (may not be necessary, though)
C Pointers to input/output mxArrays:
mwPointer pr_A, pr_B
C Array information:
mwPointer mxCalloc
mwPointer :: iA, iB !<---
type(c_ptr) :: cA, cB !<---
real*8, pointer :: A(:,:), B(:,:) !<---
character*120 :: line
C Get the size of the input array.
sizea = mxGetM(prhs(1))
iA = mxCalloc( sizea**2, 8 ) !<---
iB = mxCalloc( sizea**2, 8 ) !<---
cA = transfer( iA, cA ) !<---
cB = transfer( iB, cB ) !<---
call c_f_pointer( cA, A, [ nsizea, nsizea ] ) !<---
call c_f_pointer( cB, B, [ nsizea, nsizea ] ) !<---
C Create Fortran array from the input argument.
pr_A = mxGetPr(prhs(1))
call mxCopyPtrToReal8( pr_A, A, sizea**2 )
C Create matrix for the return argument.
plhs(1) = mxCreateDoubleMatrix(sizea, sizea, 0)
pr_B = mxGetPr(plhs(1))
write(line,*), sizea
call mexPrintf(line)
B = A
call mxCopyReal8ToPtr( B, pr_B, sizea**2 )
!! may need to call mxFree( iA ) and mxFree( iB ) manually?
!! (or Matlab may automatically do it upon exit)
return
end
A in the code is declared as a single variable, but size(A,1) is trying to access it as if it was an array. You will have to tell Fortran explicitly that A has the shape of an array. After a quick glance on the rest of the code I assume that A and B are supposed to be 2 dimensional
real*8 :: A(:,:),B(:,:)
Now I don't know too much about how mxCalloc works and how it handles the memory allocation and interfacing to Fortran, but it could be that you also need to declare A and B as pointers.
real*8, pointer :: A(:,:),B(:,:)
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!