【问题标题】:Prevent changing variables with intent(in)防止意图改变变量(in)
【发布时间】:2016-11-13 03:23:05
【问题描述】:

所以阅读以下问题 (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

【问题讨论】:

  • 阅读 lapack 文档并注意您使用的例程何时修改其参数有这么难吗?
  • 确定这并不难,但我很好奇,想知道是否有办法避免这种行为,这样我就不必使用临时数组
  • 它实际上并不清楚你在问什么。我认为它是“如果外部例程更改了intent(in) 变量,我怎样才能让编译器提醒我”。似乎没有办法制作副本,但只需在子调用中使用括号 (AA) 即可强制制作副本。
  • 哇,谢谢你的提示。在参数中使用 (AA) 调用 lapack 函数时,它实际上在做什么?为什么要复制?
  • 通过我的测试括号强制复制。老实说,我一直在等待一些标准专家来讨论这是否可以按标准计算。

标签: fortran intel-fortran


【解决方案1】:

我家里没有 ifort,但您可能想尝试使用“-check interfaces”进行编译,也可以使用“-ipo”进行编译。您可能需要“zgetrf”的路径才能使“-check interfaces”工作,如果这不是源代码,那么它可能无济于事。 如果您将“函数确定矩阵”声明为“纯函数确定矩阵”,那么我很确定它会抱怨,因为不知道“zgetrf”是纯函数还是元素函数。先试试^这个东西^。

如果 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,那么您需要明确说明。

【讨论】:

  • 好的,复制工作正常,但检查接口的东西不起作用。它给了我以下错误:ifort: command line error: Unrecognized keyword 'interfaces' for option '-check'特此我只是更改了编译选项并没有更改任何代码
  • 对不起,它是'-警告接口'。您还需要仔细阅读手册,因为我的记忆力很差。
  • 嗯好的。将 AA 声明为 intent(in) 是不够的。该函数也必须声明为纯函数。但是,当它被声明为纯时,我不需要警告选项,它仍然会给我一条消息
  • 一旦您知道 LAPACK 不是 PURE,那么您可以将 INOUT 用于 AA,作为一种快速跟踪它的方式。如果/当您想要添加并行性时,PURE 或 ELEMENTAL(以及临时 AA_Temp)的原因通常更有用。不使用 temp 可能会稍微快一些......所以在某些情况下,您可能希望同时拥有这两种情况,这取决于您的代码是纯串行的,还是打算用于并行实现。当您在几个地方使用它时,进入图书馆通常很有用。
猜你喜欢
  • 1970-01-01
  • 2017-09-10
  • 2012-06-03
  • 1970-01-01
  • 1970-01-01
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多