2016-11-13 2 views
1

so die folgende Frage (Correct use of FORTRAN INTENT() for large arrays) Ich lernte, dass das Definieren einer Variablen mit Absicht (in) ist nicht genug, da wenn die Variable an ein anderes Unterprogramm/Funktion übergeben wird, kann wieder geändert. Wie kann ich das vermeiden? Im ursprünglichen Thread haben sie darüber gesprochen, das Unterprogramm in ein Modul zu setzen, aber das hilft mir nicht. Zum Beispiel möchte ich die Determinante einer Matrix mit einer LU-Faktorisierung berechnen. Daher benutze ich die Lapack-Funktion zgetrf, aber diese Funktion verändert meine Eingabematrix und der Compiler zeigt keine Warnungen an. Was kann ich also tun?Verhindern Ändern von Variablen mit Absicht (in)

module matHelper 
    implicit none 
    contains 

    subroutine initMat(AA) 
     real*8      :: u 
     double complex, dimension(:,:), intent(inout) :: AA 
     integer      :: row, col, counter 

     counter = 1 
     do row=1,size(AA,1) 
      do col=1,size(AA,2) 
       AA(row,col)=cmplx(counter ,0) 
       counter=counter+1 
      end do 
     end do 

    end subroutine initMat 

    !subroutine to write a Matrix to file 
    !Input: AA  - double complex matrix 
    !  fid  - integer file id 
    !  fname - file name 
    !  stat  - integer status =replace[0] or old[1] 
    subroutine writeMat(AA,fid, fname, stat) 
     integer      :: fid, stat 
     character(len=*)    :: fname 
     double complex, dimension(:,:), intent(in) :: AA 
     integer      :: row, col 
     character (len=64)    :: fmtString 

     !opening file with given options 
     if(fid /= 0) then 
      if(stat == 0) then 
       open(unit=fid, file=fname, status='replace', & 
        action='write') 
      else if(stat ==1) then 
       open(unit=fid, file=fname, status='old', & 
        action='write') 
      else 
       print*, 'Error while trying to open file with Id', fid 
       return 
      end if 
     end if 

     !initializing matrix print format 
     write(fmtString,'(I0)') size(aa,2) 
     fmtString = '('// trim(fmtString) //'("{",ES10.3, ",", 1X, ES10.3,"}",:,1X))' 
     !write(*,*) fmtString 

     !writing matrix to file by iterating through each row 
     do row=1,size(aa,1) 
      write(fid,fmt = fmtString) AA(row,:) 
     enddo 
     write(fid,*) '' 
    end subroutine writeMat 



    !function to calculate the determinant of the input 
    !Input: AA    - double complex matrix 
    !Output determinantMat - double complex, 
    !       0 if AA not a square matrix 
    function determinantMat(AA) 
     double complex, dimension(:,:), intent(in) :: AA 
     double complex    :: determinantMat 
     integer, dimension(min(size(AA,1),size(AA,2)))& 
            :: ipiv 
     integer      :: ii, info 

     !check if not square matrix, then set determinant to 0 
     if(size(AA,1)/= size(AA,2)) then 
      determinantMat = 0 
      return 
     end if 

     !compute LU facotirzation with LAPACK function 
     call zgetrf(size(AA,1),size(AA,2), AA,size(AA,1), ipiv,info) 

     if(info /= 0) then 
      determinantMat = cmplx(0.D0, 0.D0) 
      return 
     end if 
     determinantMat = cmplx(1.D0, 0.D0) 
     !determinant of triangular matrix is product of diagonal elements 
     do ii=1,size(AA,1) 
      if(ipiv(ii) /= ii) then 
       !a permutation was done, so a factor of -1 
       determinantMat = -determinantMat *AA(ii,ii) 
      else 
       !no permutation, so no -1 
       determinantMat = determinantMat*AA(ii,ii) 
      end if  
     end do 

    end function determinantMat 

end module matHelper 
!*********************************************************************** 


!module which stores matrix elements, dimension, trace, determinant 

program test 
    use matHelper 
    implicit none 
    double complex, dimension(:,:), allocatable :: AA, BB 
    integer         :: n, fid 

    fid = 0; 

    allocate(AA(3,3)) 
    call initMat(AA) 
    call writeMat(AA,0,' ', 0) 
    print*, 'Determinante: ',determinantMat(AA) !changes AA 
    call writeMat(AA,0, ' ', 0) 
end program test 

PS: Ich bin mit dem ifort Compiler v15.0.3 20150407

+3

ist es so schwer, die lapack docs zu lesen und sich bewusst zu sein, wenn die Routinen, die Sie verwenden, ihre Argumente ändern? – agentp

+0

sicher ist es nicht so schwer, aber ich war neugierig und wollte wissen, ob es eine Möglichkeit gab, dieses Verhalten zu vermeiden, so dass ich nicht ein temporäres Array verwenden müsste –

+1

ist eigentlich nicht klar, was Sie fragen. Ich habe es als "Wie kann ich den Compiler dazu bringen, mich zu warnen, wenn eine externe Routine eine' intent (in) 'Variable" ändert. Es scheint keinen Weg zu geben, eine Kopie zu erstellen, aber die Verwendung von Klammern "(AA)" im Unteraufruf sollte eine Kopie erzwingen. – agentp

Antwort

2

Ich habe keinen ifort zu Hause, aber Sie können mit ‚-Check-Schnittstellen‘ und vielleicht mit ‚-ipo versuchen wollen Kompilieren ". Möglicherweise müssen Sie den Pfad zu "zgetrf" für die "check interfaces" verwenden, und wenn dies keine Quelle ist, kann es nicht helfen. Wenn Sie 'function determinantMat' als 'PURE FUNCTION determinantMat' deklarieren, bin ich mir ziemlich sicher, dass es sich beschweren würde, weil 'zgetrf' nicht als PURE oder ELEMENTAL bekannt ist. Versuchen Sie^dieses Zeug^zuerst.

Wenn LAPACK ein Modul hat, dann könnte zgetrf als PURE/ELEMENTAL bekannt sein oder nicht. https://software.intel.com/en-us/articles/blas-and-lapack-fortran95-mod-files

Ich würde vorschlagen, dass Sie auf Ihre Kompilierung Zeile hinzu:

-check interfaces -ipo 

Während der ersten Build Ich mag (es für Geschwindigkeit herausnehmen, wenn es funktioniert):

-check all -warn all 

ein temporäres Array zu machen ist ein Weg um es herum. (Ich habe dies nicht kompiliert, so ist es nur ein konzeptionelles Vorbild.)

PURE FUNCTION determinantMat(AA) 
USE LAPACK95      !--New Line--! 
IMPLICIT NONE      !--New Line--! 
double complex, dimension(:,:) , intent(IN ) :: AA 
double complex         :: determinantMat !<- output 
!--internals-- 
integer, dimension(min(size(AA,1),size(AA,2))) :: ipiv 
!!--Next line is new-- 
double complex, dimension(size(AA,1),size(AA,2)) :: AA_Temp !!<- I have no idea if this will work, you may need an allocatable?? 
integer           :: ii, info 

!check if not square matrix, then set determinant to 0 
if(size(AA,1)/= size(AA,2)) then 
    determinantMat = 0 
    return 
end if 

!compute LU factorization with LAPACK function 
!!--Next line is new-- 
AA_Temp = AA !--Initialise AA_Temp to be the same as AA--! 
call zgetrf(size(AA_temp,1),size(AA_Temp,2), AA_Temp,size(AA_Temp,1), ipiv,info) 

if(info /= 0) then 
    determinantMat = cmplx(0.D0, 0.D0) 
    return 
end if 

determinantMat = cmplx(1.D0, 0.D0) 
!determinant of triangular matrix is product of diagonal elements 
do ii=1,size(AA_Temp,1) 
    if(ipiv(ii) /= ii) then 
     !a permutation was done, so a factor of -1 
     determinantMat = -determinantMat *AA_Temp(ii,ii) 
    else 
     !no permutation, so no -1 
     determinantMat = determinantMat*AA_Temp(ii,ii) 
    end if  
end do 

end function determinantMat 

Mit dem ‚USE LAPACK95‘ Sie wahrscheinlich PURE nicht brauchen, aber wenn man es wollte PURE werden dann möchten Sie ausdrücklich sagen, damit.

+0

Ok, eine Kopie funktioniert gut, aber die Check-Schnittstellen-Sache wird nicht funktionieren. Es gibt mir den folgenden Fehler: 'ifort: Befehlszeilenfehler: Unbekanntes Schlüsselwort 'Schnittstellen' für Option '-check'' Hiermit habe ich nur die Kompilierungsoption geändert und keinen Code geändert –

+0

Sorry es ist' -warn Schnittstellen ". Sie müssen auch das Handbuch durchlesen, da meine Erinnerung zweifelhaft ist. – Holmz

+0

Hm ok. Zu erklären, dass "AA" als "Absicht (in)" ist nicht genug. Die Funktion muss ebenfalls als rein deklariert werden. Wenn es jedoch als rein erklärt wird, brauche ich die Warnoptionen nicht und es wird mir immer noch eine Nachricht geben –