2016-10-10 77 views
1

因此,我正在使用運算符重載在Fortran中使用自動差分工具箱。我以前在C++中實現過這個功能,但真的需要讓它在Fortran中運行。爲用戶定義類型分配被動值或常量

Fortran中我有以下模塊定義:

 module adopov 
     integer  :: indexcount 
     integer, parameter :: tape_size = 1000 
! 
!....... ADtype 
     public   :: ADtype 
     type ADtype 
      integer  :: index  = -1 
      real  :: v   = 0.0 
! 
     contains 
      procedure  :: oo_asg 
      generic, public :: assignment(=) => oo_asg 
     end type ADtype 
! 
!....... class tape 
     public   :: ADtape 
     type ADtape 
      real  :: v   = 0.0 
     end type ADtape 
! 
!....... interface(s) 
     interface assignment(=) 
      module procedure oo_asg 
     end interface 
! 
     type (ADtape), dimension(tape_size) :: tape 
! 
!....... definitions 
     contains 
! 
!....... assignment 
     subroutine oo_asg (x,y) 
      implicit none 
      class(ADtype), intent(out) :: x 
      class(ADtype), intent(in) :: y 
! 
      tape(indexcount)%v   = y%v 
      indexcount = indexcount + 1 
      x%v  = y%v 
      x%index = indexcount 
     end subroutine oo_asg 
! 
end module adopov 

在C++中,我有一個類似的用戶定義類型爲

class ADType { 
    public: 
     int index; 
     double v; 
     ADType() : index(-1), v(0) {}; 
     ADType(const double&); 
     ADType& operator=(const ADType&); 
    }; 

在構造函數設置的初始值的索引和價值的部分。接下來,我有一個被動值或常量(double類型)的構造函數,以便我可以在有雙變量時定義一個新的類變量(ADType)。例如,當我有:

ADType x; 
x = 2.0; 

最初ADType類型的新變量被設置爲2.0的值創建的,讓我們說var1 = 2.0和next(根據在類ADType定義的賦值運算符(=))我將該變量分配給x,即x = var1。這整個過程被記錄在一個記錄操作並記錄值和索引的磁帶中。

現在,你可以說「爲什麼你必須這樣做?」。那麼,在使用運算符重載進行自動微分的伴隨方法中,這是一個必要的步驟。

我做C語言中的方式++是,我只是有以下兩個構造函數:

ADType:: ADType(const double& x): v(x) { 
    tape[indexcounter].v = x; 
    indexcounter++; 
}; 

ADType& ADType::operator=(const ADType& x) { 
    if (this==&x) return *this; 
    tape[indexcounter].v = v = x.v; 
    indexcounter++; 
    return *this; 
} 

,但我不知道如何實現用Fortran被動值和常量構造函數。

+1

這是你的計劃,但如果你用小字母C++爲什麼Fortran語言不使用它們呢?他們更可讀。而且你不需要'= 0.D0',即使你的變量是雙打,'= 0'也是非常好的。而且因爲你的變量是默認實數,所以根本就不需要D。 –

+0

@VladimirF感謝您的提示。我想這只是一個老習慣 – FRJedi

+0

@VladimirF只是編輯了這個問題,使它更具可讀性 – FRJedi

回答

0

你有兩種選擇,你可以組合使用。

  1. 使用與右側具有REAL對象相對應的過程來重載定義的分配。

    TYPE ADTYPE 
        ... 
    CONTAINS 
        PROCEDURE  :: OO_ASG 
        PROCEDURE  :: ASSIGN_FROM_REAL 
        GENERIC, PUBLIC :: ASSIGNMENT(=) => OO_ASG, ASSIGN_FROM_REAL 
    END TYPE AREAL 
    
    ! Users of the module shoudn't be calling this procedure 
    ! (or the specific binding, really) directly. 
    PRIVATE :: ASSIGN_FROM_REAL 
    
    ... 
    
    SUBROUTINE ASSIGN_FROM_REAL(x,y) 
        CLASS(ADTYPE), INTENT(OUT) :: x 
        REAL, INTENT(IN) :: y 
    
        ! Do what you have to do... 
        ... 
        x%V = y 
    END SUBROUTINE ASSIGN_FROM_REAL 
    
    ! Example of use... 
    TYPE(ADTYPE) :: z 
    z = 2.0 
    
  2. 使用結構構造函數或重載的過程等效項。

    INTERFACE ADTYPE 
        MODULE PROCEDURE ADTYPE_construct_from_REAL 
    END INTERFACE ADTYPE 
    PRIVATE :: ADTYPE_construct_from_REAL 
    ... 
    
    FUNCTION ADTYPE_construct_from_REAL(y) RESULT(x) 
        REAL, INTENT(IN) :: y 
        TYPE(ADTYPE) :: x 
    
        ! Do what you have to do. 
        ... 
        x%V = y 
    END FUNCTION ADTYPE_construct_from_REAL 
    
    ! Example of use: 
    TYPE(ADTYPE) :: z 
    z = ADTYPE(3.0) 
    

如果您做出需要在源代碼中的雙重明確在你的C++例子構造函數的調用(即ADType x; x = ADType(2.0);,那麼你就相當於這兩種方法的第二個 - Fortran語言做在派生類型的對象之間沒有隱式轉換

(您的示例代碼顯示了類型綁定賦值和Fortran 90風格的獨立接口,這兩個都沒有意義 - 該模塊甚至不應該編譯。 )

+0

@lanH感謝您的回答和建議。我現在使用第一種方法來避免**顯式**轉換。希望進行隱式轉換的另一個原因是爲了避免用特殊的程序來重載所有的二進制操作,這些程序可以處理實際類型與派生類型(以及派生類型與實際類型)的情況。它需要更多的編碼,但我已將它實現到模塊中。在C++中,我可以將'double'值轉換爲就地派生類型,然後執行重載操作。有關如何在Fortran中更高效地執行此操作的任何建議?謝謝 – FRJedi

+0

Fortran在派生類型的對象之間沒有隱式轉換。這有它的好處 - 它使超負荷解決非常簡單。只需使用顯式的過程引用或結構構造函數即可。 – IanH

0

以下是對您的問題的完整工作建議。請注意,模塊中的每個變量都會自動繼承save屬性。如果您最終對併發感興趣,則可能需要在ADtape的內部附帶indexcounter以及適當的類型綁定的簿記過程。

module adopov 

    use, intrinsic :: ISO_C_binding, only: & 
     ip => C_INT, & 
     wp => C_DOUBLE 

    ! Explicit typing only 
    implicit none 

    ! Everything is private unless stated otherwise 
    private 
    public :: Adtype, wp 

    ! Declare derived data types 
    type ADtape 
    real(wp) :: v = 0.0_wp 
    end type ADtape 

    type, public :: ADtype 
    integer(ip) :: index = -1 
    real(wp) :: v = 0.0_wp 
    contains 
    procedure, private :: asgn_from_type, asgn_from_real, asgn_from_int 
    generic, public :: assignment(=) => asgn_from_type, asgn_from_real, asgn_from_int 
    end type ADtype 

    ! Set user-defined constructor 
    interface ADtype 
    module procedure :: ADtype_constructor 
    end interface ADtype 

    ! Variables confined to the module. 
    ! Please note that every variable 
    ! in a module implicitly inherits the save attribute. 
    integer   :: indexcount = 0 ! Your original code left this uninitialized 
    integer, parameter :: TAPE_SIZE = 1000 
    type (ADtape)  :: tape(TAPE_SIZE) 

contains 

    pure function ADtype_constructor(x, indx) result (return_value) 
    real (wp), intent(in)   :: x 
    integer (ip), intent (in), optional :: indx 
    type (ADtype)      :: return_value 

    return_value%v = x 
    if (present(indx)) return_value%index = indx 

    end function ADtype_constructor 

    subroutine update_tape(float) 
    real (wp), intent (in) :: float 

    tape(indexcount)%v = float 
    indexcount = indexcount + 1 

    end subroutine update_tape 

    subroutine asgn_from_type(this, y_type) 
    class(ADtype), intent(out) :: this 
    class(ADtype), intent(in) :: y_type 

    associate(& 
     v => this%v, & 
     indx => this%index & 
     ) 

     call update_tape(y_type%v) 
     v = y_type%v 
     indx = indexcount 
    end associate 

    end subroutine asgn_from_type 

    subroutine asgn_from_real(this, y_real) 
    class(ADtype), intent(out) :: this 
    real(wp),  intent(in) :: y_real 

    associate(& 
     v => this%v, & 
     indx => this%index & 
     ) 
     call update_tape(y_real) 
     v = y_real 
     indx = indexcount 
    end associate 

    end subroutine asgn_from_real 

    subroutine asgn_from_int(this, y_int) 
    class(ADtype), intent(out) :: this 
    integer(ip), intent(in) :: y_int 

    associate(& 
     v => this%v, & 
     indx => this%index, & 
     float => real(y_int, kind=wp) & 
     ) 
     call update_tape(float) 
     v = float 
     indx = indexcount 
    end associate 

    end subroutine asgn_from_int 

end module adopov 

program main 

    use, intrinsic :: ISO_Fortran_env, only: & 
     stdout => OUTPUT_UNIT, & 
     compiler_version, & 
     compiler_options 

    use adopov, only: & 
     ADtype, wp 

    ! Explicit typing only 
    implicit none 

    type(ADtype) :: foo, bar, woo 

    ! Invoke the user-defined constructor 
    foo = ADtype(42.0_wp) 
    bar = ADtype(42.0_wp, -6) 
    woo = foo 

    print *, foo 
    print *, bar 
    print *, woo 

    write(stdout, '(/4a/)') & 
     ' This file was compiled using ', compiler_version(), & 
     ' using the options ', compiler_options() 

end program main 

這就產生

gfortran -Wall -o main.exe adopov.f90 main.f90 
./main.exe 
      1 42.000000000000000  
      2 42.000000000000000  
      3 42.000000000000000  

This file was compiled using GCC version 6.1.1 20160802 using the options -mtune=generic -march=x86-64 -Wall 
相關問題