0

Before someone closes the question, yes, there are many questions that seem similar, but so far I haven't found one with this exact weird problem that seems to go away only sometimes.

I had an odd Fortran error while trying to make a module for linear regression.

The module is named "LSQregression" and the main program "LSQAdvertising". Compiling it using the following gfortran command works:

gfortran ../LSQregression.f90 LinearAdvertising.f90 -llapack -o LinAd

However, I'd like to be able to turn my module into a .o file and link it with whatever program I may need instead of compiling again every time. So I tried to do that:

gfortran -c ../LSQregression.f90

And then link it with my program file like that:

gfortran ../LSQregression.o LinearAdvertising.f90 -llapack -o LinAd

I also tried also turning the program into a .o file. Neither works, they both return the following error:

/usr/bin/ld: /tmp/ccf7T1aj.o: in function `MAIN__':
LinearAdvertising.f90:(.text+0x1f8b): undefined reference to `__lsqregression_MOD_lsqestimate_simple'
collect2: error: ld returned 1 exit status

It refers to the following function in the module that is being called here inside the program:

print *, LSQestimate(test,LSQbeta(inputs,sales,(/1,2/)) )

The function itself is defined as part of an interface:

interface LSQestimate
     procedure LSQestimate_simple, LSQestimate_using
  end interface LSQestimate

 real(8) function LSQestimate_simple(X, beta)
    implicit none
    real(8), dimension(:,:) :: X, beta
    real(8)                 :: boundary, Y
    integer                 :: i, Xlen, betalen

    Xlen    = size(X, 1)
    betalen = size(beta, 1)

    if (Xlen + 1 .ne. betalen) stop "incompatible beta and X"

    Y = beta(1,1)

    do i = 1, Xlen
       Y = Y + beta(i+1, 1)*X(i,1)
    end do

    LSQestimate_simple = Y
    
  end function LSQestimate_simple

It's very odd because I've done the same thing with another function and it seems to work fine, and the problem only happens when I turn the module into a .o file first, and goes away if I try to directly compile the .f90 file... I can't figure out why one works and the other doesn't.

EDIT: Someone told me to do nm ../LSQregression.o | grep -i regression . I have no idea what that does but I did it so here are the results if it helps at all:

0000000000002768 T __lsqregression_MOD_inv
0000000000000b85 T __lsqregression_MOD_lsqbeta_simple
000000000000035c T __lsqregression_MOD_lsqbeta_using
0000000000000000 T __lsqregression_MOD_lsqdecision

Oddly enough the name of the function in question doesn't even appear here.

EDIT 2: Decided to post the entire code of the module:

module LSQregression
  implicit none

  public  :: LSQbeta, LSQestimate

  interface LSQbeta
     procedure LSQbeta_simple, LSQbeta_using
  end interface LSQbeta

  interface LSQestimate
     procedure LSQestimate_simple, LSQestimate_using
  end interface LSQestimate
  
  
  
  
contains
  
  function inv(A) result(Ainv)
    implicit none
    real(8), dimension(:,:), intent(in)     :: A
    real(8), dimension(size(A,1),size(A,2)) :: Ainv

    real(8), dimension(size(A,1)) :: work  ! work array for LAPACK
    integer, dimension(size(A,1)) :: ipiv   ! pivot indices
    integer :: n, info

    ! External procedures defined in LAPACK
    external DGETRF
    external DGETRI

    ! Store A in Ainv to prevent it from being overwritten by LAPACK
    Ainv = A
    n = size(A,1)

    ! DGETRF computes an LU factorization of a general M-by-N matrix A
    ! using partial pivoting with row interchanges.
    call DGETRF(n, n, Ainv, n, ipiv, info)

    if (info /= 0) then
       stop 'Matrix is numerically singular!'
    end if

    ! DGETRI computes the inverse of a matrix using the LU factorization
    ! computed by DGETRF.
    call DGETRI(n, Ainv, n, ipiv, work, n, info)
    
    if (info /= 0) then
       stop 'Matrix inversion failed!'
    end if
  end function inv
  
  !----------------------------------------------------------------------
  
  function LSQbeta_simple(Xold, Y) result(beta)
    real(8), dimension(:,:)                          :: Xold, Y
    real(8), dimension(size(Xold,1), size(Xold,2)+1) :: X
    real(8), dimension(:,:), allocatable             :: beta, XTX, IXTX, pinv
    integer                                          :: rowsX, colsX, rowsY, colsY,i


    rowsX = size(X(:,1))
    colsX = size(X(1,:))
    rowsY = size(Y(:,1))
    colsY = size(Y(1,:))

    if (colsY .ne. 1)     stop 'Inappropriate y'
    if (rowsY .ne. rowsX) stop "Y and X rows don't match"
    
    !---X-ify-----------
    X(:,1) = 1.0     !includes 1 in the first column
    
    do i = 2, colsX
       X(:,i) = Xold(:,i-1)
    end do
    !--------------------

   
    allocate(XTX(colsX,colsX))
    allocate(IXTX(colsX,colsX))
    allocate(pinv(colsX,rowsY))
    allocate(beta(colsX,1))

    XTX   = matmul(transpose(X),X)
    IXTX  = inv(XTX)
    pinv  = matmul(IXTX, transpose(X))
    beta  = matmul(pinv, Y)

    deallocate(pinv)
    deallocate(XTX)
    deallocate(IXTX)

    return
    
    deallocate(beta)
    
  end function LSQbeta_simple
  !--------------------------------------------
  
  function LSQbeta_using(Xold, Y, indexes) result(beta)
    implicit none
    real(8), dimension(:,:)                        :: Xold, Y
    integer, dimension(:)                          :: indexes
    real(8), dimension(size(indexes)+1,1)          :: beta
    real(8), dimension(size(Xold,1),size(indexes)) :: X
    integer                                        :: indLen,i

    indLen = size(indexes) 
    
    do i = 1, indLen
       X(:,i) = Xold(:,indexes(i))
    end do

    beta = LSQbeta_simple(X,Y)
    
  end function LSQbeta_using

  real(8) function LSQestimate_simple(X, beta)
    implicit none
    real(8), dimension(:,:) :: X, beta
    real(8)                 :: boundary, Y
    integer                 :: i, Xlen, betalen

    Xlen    = size(X, 1)
    betalen = size(beta, 1)

    if (Xlen + 1 .ne. betalen) stop "incompatible beta and X"

    Y = beta(1,1)

    do i = 1, Xlen
       Y = Y + beta(i+1, 1)*X(i,1)
    end do

    LSQestimate_simple = Y
    
  end function LSQestimate_simple

  real(8) function LSQestimate_using()
    LSQestimate_using = 1.0D0
  end function LSQestimate_using
  
  

  logical function LSQdecision(X, beta, boundary)
    implicit none
    real(8), dimension(:,:) :: X, beta
    real(8)                 :: boundary
    
    LSQdecision = LSQestimate(X, beta) > boundary
    
  end function LSQdecision
  


  
end module LSQregression
Andreas C
  • 45
  • 1
  • 5
  • Well without a complete code it's impossible to test, but does something like `gfortran -I .. LinearAdvertising.f90 ../LSQregression.o -llapack -o LinAd` work - note the order! – Ian Bush Mar 19 '22 at 12:57
  • 1
    Also note `real( 8 )` is poor practice, not portable, might not be supported, and might not do what you want ti to do. Learn about Fortran kind values - https://stackoverflow.com/questions/838310/fortran-90-kind-parameter might be a starting place. – Ian Bush Mar 19 '22 at 12:58
  • https://stackoverflow.com/questions/45135/why-does-the-order-in-which-libraries-are-linked-sometimes-cause-errors-in-gcc might help – Ian Bush Mar 19 '22 at 13:04
  • Ian Bush, I just tried exactly what you posted, but it didn't work... Still says undefined reference. – Andreas C Mar 19 '22 at 13:59
  • To parameter: thanks, I was only using real(8) because my professor kept using it. – Andreas C Mar 19 '22 at 14:00
  • Well without a complete example it is not easy to say more – Ian Bush Mar 19 '22 at 14:01
  • try a `nm ../LSQregression.o | grep -i regression` and see how the function might be defined in the `LSQregression.o`. – albert Mar 19 '22 at 14:04
  • albert, I did what you said. TBH I have no idea what the thing you told me to do does lol but I posted the results in an edit if it helps any of you. Oddly enough the function in question does not appear. – Andreas C Mar 19 '22 at 14:20
  • Might it be that the function is defined as private in the module? – albert Mar 19 '22 at 14:33
  • Hmm I think I had messed something up... I cleaned up some stuff (code for deleted everything and recompiled everything again) and now it does show the function in question in the grep thing, whatever that is... Not sure what I messed up before. Now, everything works fine if I do gfortran -c (program.f90) (module.f90) and then put together the .o files, I'm just wondering now if there is a way to not require the .f90 file at all for the module... – Andreas C Mar 19 '22 at 15:03
  • FWIW: If I try something similar, with a module called `foo_m.f90` in the parent directory and the file `main.f90` in the current directory, the command `gfortran -c ../foo_m.f90` creates `foo_m.o` and `foo_m.mod` in the *current* directory. I can then compile and link `main.f90` with `gfortran main.f90 foo_m.o -o main` (i.e. I refer to `foo_m.o`, not `../foo_m.o`, because the `.o` and `.mod` files are in the current directory, not the parent directory). – Warren Weckesser Mar 19 '22 at 15:32
  • @WarrenWeckesser The directory is not the important thing. I think that Andreas had the problem that he added something to the LSQregression.f90 but forgot to recompile it / copy the recompiled .0 file to the right directory. – albert Mar 19 '22 at 17:29
  • the nm command shows the symbols that are visible in the file (like the man page says: nm - list symbols from object files) and greop just filters the relevant information. – albert Mar 19 '22 at 17:31
  • Yes, I think you are right, the issue was that I recompiled into the wrong directory or something like that... I'm still very much a fortran noob. – Andreas C Mar 19 '22 at 21:56

0 Answers0