防止意图更改变量(in)

时间:2016-11-13 03:23:06

标签: fortran intel-fortran

所以阅读以下问题(Correct use of FORTRAN INTENT() for large arrays)我了解到使用intent(in)定义变量是不够的,因为当变量传递给另一个子例程/函数时,它可以再次更改。那我怎么能避免这个呢?在原始线程中,他们讨论了将子例程放入模块中,但这对我没有帮助。例如,我想用LU分解来计算矩阵的行列式。因此我使用Lapack函数zgetrf,但是这个函数改变了我的输入矩阵,编译器不显示任何警告。那我该怎么办?

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:我使用的是ifort编译器v15.0.3 20150407

1 个答案:

答案 0 :(得分:2)

我家里没有ifort,但您可能想尝试使用'-check interfaces'进行编译,也可以使用'-ipo'进行编译。您可能需要“-geck接口”的'zgetrf'路径才能工作,如果不是源,那么它可能无济于事。 如果你将'function determinantMat'声明为'PURE FUNCTION determinantMat',那么我很确定它会抱怨因为'zgetrf'不知道是PURE还是ELEMENTAL。首先尝试^这个东西^。

如果LAPACK有一个模块,则可以知道zgetrf是否为PURE / ELEMENTAL。 https://software.intel.com/en-us/articles/blas-and-lapack-fortran95-mod-files

我建议你添加到编译行:

-check interfaces -ipo

在初始构建期间我喜欢(一旦它工作就把它拿出去):

-check all -warn all

制作临时数组是解决它的一种方法。 (我没有编译过这个,所以它只是一个概念性的例子。)

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

使用'USE LAPACK95'你可能不需要PURE,但是如果你想让它成为PURE,那么你想要明确说出来。