1

I'm trying to initialize a sparse matrix in a Mex file written in Fortran. I saw the example code fulltosparse. There, the number of nonzero elements and the matrix dimensions are set/guessed before entering the computational subroutine that modifies the matrix arrays of indices jc, ir, and pr. In my case, I don't know beforehand how many zeros there will be in the matrix. This question follows directly this one, but here the question revolves strictly around how Mex interface works. I tried coding something like this example that deals with handling numeric arrays of unknown length. After compiling the file in Matlab (Intel compiler), when I try to run it as S = mex_function_name(ii,jj,kk), Matlab crashes. This is what I coded. Thank you

  #include "fintrf.h"

  subroutine mexFunction(nlhs, plhs, nrhs, prhs)
  implicit none
  mwPointer plhs(*), prhs(*)
  integer (kind = 4) nlhs, nrhs
  mwPointer mxGetN, mxCreateSparse, mxGetPr, mxGetIr, mxGetJc
  mwPointer  :: ir, jc, pr
  mwSize     :: n, m=4, zero=0, nnew
  integer(4) :: mxREAL = 0
  
  n = mxGetN(prhs(1)) 
  
  plhs(1) = mxCreateSparse(zero,zero,zero,mxREAL) 
  pr = mxGetPr(plhs(1))
  ir = mxGetIr(plhs(1))
  jc = mxGetJc(plhs(1))
  
  call new_sparse(%val(mxGetPr(prhs(1))), %val(mxGetPr(prhs(2))), %val(mxGetPr(prhs(3))), pr, ir, jc, n, m, nnew)
  
  call mxSetM(plhs(1),m)
  call mxSetN(plhs(1),m)
  
  call mxSetNzmax(plhs(1),nnew)

  ! call mxSetPr(plhs(1), mxRealloc(pr, nnew*8));
  ! call mxSetIr(plhs(1), mxRealloc(ir, nnew*1));
  ! call mxSetJc(plhs(1), mxRealloc(jc, (m+1)*1));

  call mxSetPr(plhs(1),pr) 
  call mxSetIr(plhs(1),ir)
  call mxSetJc(plhs(1),jc)
  
  return
  end

!============================================================
subroutine new_sparse(MatI, MatJ, MatK, pr, ir, jc, n, m, nnew)

implicit none
real(kind = 8),  intent(in) :: MatK(n),     MatI(n),     MatJ(n)
mwSize    :: nnew, n,m
mwPointer :: pr, ir, jc
mwPointer :: mxMalloc
real   (kind = 8), allocatable :: pr_all(:)
integer(kind = 4), allocatable :: ir_all(:), jc_all(:)
integer(kind = 4) ::  i, k, col, row, c, r 
integer(kind = 4) ::  hcol(m+1), jcS(m+1), jrS(m+1)
integer(kind = 4) ::  ixijs, irank(n), rankk(n) 

hcol = 0
jcS  = 0
jrS  = 0    

do i = 1,n
    jrS(MatI(i)+1) = jrS(MatI(i)+1)+1
end do
do r = 2,m+1
    jrS(r) = jrS(r) + jrS(r-1)
end do

do  i = 1,n 
     rankk(jrS(MatI(i))+1) = i
     jrS(MatI(i)) = jrS(MatI(i)) + 1
end do

k = 1
do row = 1,m
    do i = k , jrS(row) 
        ixijs = rankk(i)
        col = MatJ(ixijs)
        if (hcol(col) < row) then
            hcol(col) = row
            jcS(col+1) = jcS(col+1)+1
        end if
        irank(ixijs) = jcS(col+1)
        k = k+1
    end do
end do

do c = 2,m+1  
    jcS(c) = jcS(c) + jcS(c-1)
end do

do i = 1,n
    irank(i) = irank(i) + jcS(MatJ(i))
end do    

nnew = jcS(m+1)

allocate(pr_all(nnew))
allocate(ir_all(nnew))
allocate(jc_all(m+1))

jc_all = jcS
ir_all(irank) = MatI - 1
pr_all = 0
do i = 1,n
    pr_all(irank(i)) = pr_all(irank(i)) +  MatK(i)
end do

pr = mxMalloc( 8 * nnew )
ir = mxMalloc( 8 * nnew )
jc = mxMalloc( 8 * (m+1) )
call mxCopyReal8ToPtr   (pr_all, pr, nnew)
call mxCopyInteger4ToPtr(ir_all, ir, nnew)
call mxCopyInteger4ToPtr(jc_all, jc, (m+1))

deallocate(pr_all)
deallocate(ir_all)
deallocate(jc_all)

return    
end

Edit #1:
I practiced at the beginning writing a function that receives a triplet of indices (ii and jj) and values (kk) of length n and produces another triplet of length nnew as an output, with their values rearranged and with the repetitions eliminated. The function [iinew, jjnew, kknew]=mex_function_name(ii,jj,kk) works fine. For example, if my original triplet is

ii = [3, 4, 1, 3, 2, 1, 4, 4, 4, 3, 2, 3, 1]
jj = [3, 3, 1, 4, 1, 1, 4, 3, 1, 3, 2, 2, 4]
kk = [4, 4, 5, 7, 3, 5, 5, 4, 3, 4, 9, 7, -2]

The output is

iinew = [1, 2, 4, 2, 3, 3, 4, 1, 3, 4]
jjnew = [1, 1, 1, 2, 2, 3, 3, 4, 4, 4]
kknew = [10, 3, 3, 9, 7, 8, 8, -2, 7, 5]

And this is the code:

  #include "fintrf.h"

  subroutine mexFunction(nlhs, plhs, nrhs, prhs)

  implicit none

  mwPointer plhs(*), prhs(*)
  integer (kind = 8) nlhs, nrhs

  mwPointer mxGetN, mxCreateDoubleMatrix, mxGetPr     

  mwPointer MatI_out, MatJ_out, MatK_out
  mwSize     :: n, m, nnew, zero=0
  integer(4) :: mxREAL = 0
  
  n = mxGetN(prhs(1)) 
  m = 4   

  plhs(1) = mxCreateDoubleMatrix(zero,zero,mxREAL)     
  plhs(2) = mxCreateDoubleMatrix(zero,zero,mxREAL)         
  plhs(3) = mxCreateDoubleMatrix(zero,zero,mxREAL) 

  MatI_out = mxGetPr(plhs(1))
  MatJ_out = mxGetPr(plhs(2))  
  MatK_out = mxGetPr(plhs(3))

  call new_sparse(%val(mxGetPr(prhs(1))), %val(mxGetPr(prhs(2))), %val(mxGetPr(prhs(3))), MatI_out, MatJ_out, MatK_out, n, m, nnew)
  
  call mxSetPr(plhs(1),MatI_out) 
  call mxSetM(plhs(1),1)
  call mxSetN(plhs(1),nnew)
  call mxSetPr(plhs(2),MatJ_out) 
  call mxSetM(plhs(2),1)
  call mxSetN(plhs(2),nnew)
  call mxSetPr(plhs(3),MatK_out) 
  call mxSetM(plhs(3),1)
  call mxSetN(plhs(3),nnew)

  return
  end

 !======================================================================
 !    Computational subroutine

subroutine new_sparse(MatI, MatJ, MatK, MatI_out, MatJ_out, MatK_out, n, m, nnew)

implicit none
real(kind = 8),  intent(in) :: MatK(n),     MatI(n),     MatJ(n)
mwPointer :: MatK_out, MatI_out, MatJ_out
mwPointer :: mxMalloc
real(kind = 8), allocatable :: MatK_all(:), MatI_all(:), MatJ_all(:)
mwSize  n, m, nnew
integer ::  i, k, col, row, c, r 
integer ::  hcol(m+1), jcS(m+1), jrS(m+1)
integer ::  ixijs, irank(n), rankk(n)   

... ! same computational subroutine as before

MatI_out = mxMalloc( 8 * nnew )
MatJ_out = mxMalloc( 8 * nnew )
MatK_out = mxMalloc( 8 * nnew )
call mxCopyReal8ToPtr(MatI_all, MatI_out, nnew)
call mxCopyReal8ToPtr(MatJ_all, MatJ_out, nnew)
call mxCopyReal8ToPtr(MatK_all, MatK_out, nnew)
deallocate(MatI_all)
deallocate(MatJ_all)
deallocate(MatK_all)

return
  
end
    
Kadiya
  • 63
  • 1
  • 6
  • I'm slightly confused what the actual question is here. I think I get what you're trying to do, but what went wrong when you tried it? – veryreverie Jun 21 '21 at 07:55
  • @veryreverie Matlab crashes every time I try to use the function. – Kadiya Jun 21 '21 at 08:02
  • 2
    @Kadiya I also had a few problems working out what you were asking, to be honest "I can't make it work" is not the most useful thing you can say. Could you edit the question to make it so that it is clearer what is not working and what you are asking, please? Including examples of running it and the error messages generated could be very useful. – Ian Bush Jun 21 '21 at 08:57
  • @IanBush Yes, sorry, I added more elements and I hope I didn't make it even more confusing that before. Thank you – Kadiya Jun 21 '21 at 09:20
  • What is the ultimate goal? To be able to have a Fortran version of fulltosparse that will work with arbitrary I,J,V inputs? I.e., be able to sort/combine the data same as MATLAB would do it? Or do you really have an underlying algorithm in mind for producing the I,J,V inputs and on-the-fly want to stuff them into a sparse matrix? Or ...? Maybe we can fix up your current code (i.e., maybe those integer indexes should be integer(8) etc.), but I am not sure that is what you ultimately want or need. – James Tursa Jun 22 '21 at 17:06
  • @JamesTursa I have a Mex function that generates the triplets and I use Matlab's `sparse` after that. I'm not trying to create a general all-purpose `sparse` function, as you correctly guessed. I'm just trying to stuff this triplet into a sparse matrix. I don't know if this process (i.e. triplet creation and sparse filling in the same Mex function) could be faster than the one I'm currently using (i.e. triplet creation in Mex + sparse creation in Matlab). Thank you – Kadiya Jun 22 '21 at 17:31
  • @Kadiya If you can generate the triplets in column sorted order, then you might be able to avoid that processing time in your mex code by stuffing the I,J,V into the sparse matrix on the fly. This would also avoid a potentially large data copy operation. But if you can't generate your data in sorted order and don't know the max number of non-zeros needed ahead of time, then you are probably stuck with the sorting and data copying operations, which means I doubt you could beat the MATLAB sparse( ) function with your own mex code. – James Tursa Jun 22 '21 at 19:52
  • @JamesTursa Yes, don't know the number of nonzeros in advance, so I will stick to Matlab's `sparse`. Thank you, best – Kadiya Jun 23 '21 at 16:35

0 Answers0