2016-10-10 7 views
1

Так что я работаю над инструментом автоматической дифференциации в Фортране, используя перегрузку оператора. Я ранее реализовал это на 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 и следующий (в соответствии с оператором присваивания (=), определенный в классе ADType) I назначит эту переменную 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++ я мог преобразовать «двойные» значения в производный тип на месте, а затем выполнить перегруженные операции. Любые предложения относительно того, как сделать это более эффективно в Fortran? Спасибо – FRJedi

+0

Fortran не имеет неявных преобразований между объектами производного типа. Это имеет свои преимущества - это делает перегрузку очень прямой. Просто используйте явный шаблон процедуры или конструктор структуры. – IanH

0

Вот полное предложение по работе с вашей проблемой. Обратите внимание, что каждая переменная внутри модуля автоматически наследует атрибут save. Если вы в конечном итоге заинтересованы в параллелизме, вам может потребоваться заключить indexcounter внутри ADtape с соответствующей процедурой, связанной с порядком ведения бухгалтерии.

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