2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Fortranの抽象型の配列のそれぞれに異なる具象型を入れる

Last updated at Posted at 2025-01-07

Fortranでオブジェクト指向プログラミングをしたい時には、やはり抽象型を使いたくなるかと思います。Fortranの抽象型に関しては、以前オブジェクト指向Fortranで抽象型を使うで解説しました。今回は、

  • 抽象型の配列に異なる具象型を入れる

ことを行いました。これ、gfortran (gcc version 14.2.0)で苦戦しましたので、記録を残しておきます。

抽象型と具象型

今回は、以下のような抽象型と具象型を考えます。

module module_abstractlayer
    implicit none

    type, abstract,public :: Layer
    contains
        procedure(display_layer), deferred :: display
    end type Layer

    abstract interface
       subroutine display_layer(this) 
            import :: Layer
            class(Layer), intent(in) :: this
       end subroutine display_layer
    end interface

end module module_abstractlayer

module module_denselayer
    use iso_fortran_env
    use module_abstractlayer,only:Layer
    implicit none

    type, public,extends(Layer) :: Denselayer
        integer::indim,outdim
        real(real64),allocatable::W(:,:)
        real(real64),allocatable::b(:)
    contains
        procedure :: display => Denselayer_display
    end type Denselayer

    interface Denselayer
        procedure::init_Denselayer
    end interface 

    contains

    type(Denselayer) function init_Denselayer(indim,outdim) result(layer)
        implicit none
        integer,intent(in)::indim,outdim
        allocate(layer%W(outdim,indim))
        allocate(layer%b(outdim))

        call random_number(layer%W)
        call random_number(layer%b)
        layer%indim = indim
        layer%outdim = outdim
    end function

    subroutine Denselayer_display(this) 
        class(Denselayer), intent(in) :: this
        write(*,*) "Dense layer."
        write(*,*) "Input and output dimensions: ",this%indim,this%outdim
    end subroutine Denselayer_display


end module module_denselayer

module module_unitlayer
    use iso_fortran_env
    use module_abstractlayer,only:Layer
    implicit none

    type, public,extends(Layer) :: Unitlayer
    contains
        procedure :: display => Unitlayer_display
    end type Unitlayer

    contains

    subroutine Unitlayer_display(this) 
        class(Unitlayer), intent(in) :: this
        write(*,*) "Unitlayer layer."
    end subroutine Unitlayer_display
end module

を考えます。ニューラルネットワークのレイヤーみたいなものを念頭に置いたものです。レイヤーごとに異なる演算がしたいので、配列としてレイヤーごとに異なる処理ほ保持しておきたい、というところです。

素朴なやり方

まず、一番素朴なやり方は

class(Layer),allocatable::layers(:)
allocate(layers(2))

です。これで、配列の長さが確保できる、気がします。しかし、gfortranではこれは

   64 |     allocate(larers(2))
      |             1
Error: Allocate-object at (1) is neither a data pointer nor an allocatable variable

というエラーが出ます。webで調べるとこのやり方でできるような雰囲気があったのですが、できませんでした。allocatableをpointerにしても結果は変わりません。

解決方法

解決するには、ラッパー型を用意します。こちらを参考にしました。

つまり、ラッパーとして、

module module_abstractlayer
    implicit none

    type, abstract,public :: Layer
    contains
        procedure(display_layer), deferred :: display
    end type Layer


    type :: LayerWrapper
        class(Layer), allocatable :: layer
        contains
        procedure :: display 
    end type

    abstract interface
       subroutine display_layer(this) 
            import :: Layer
            class(Layer), intent(in) :: this
       end subroutine display_layer
    end interface

    contains

    subroutine display(self)
        class(LayerWrapper),intent(in)::self
        call self%layer%display()
    end


end module module_abstractlayer

のように、LayerWrapperという型を用意して、

program main
    use module_denselayer,only:Denselayer
    use module_abstractlayer,only:Layer,LayerWrapper
    use module_unitlayer,only:Unitlayer
    implicit none
    type(LayerWrapper),allocatable::layers(:)
    integer::numlayer,ilayer
    integer,allocatable::numnodes(:)


    numlayer = 2
    allocate(numnodes(numlayer+1))
    numnodes(1) = 44
    numnodes(2) = 10
    numnodes(3) = 1
    allocate(layers(numlayer))

    ilayer= 1
    allocate(layers(ilayer) % layer,source=Denselayer(numnodes(ilayer),numnodes(ilayer+1)))
 
    ilayer= 2
    allocate(Unitlayer:: layers(ilayer) % layer)
    layers(ilayer) % layer = Unitlayer()

    do ilayer = 1,numlayer
        call layers(ilayer)%display()
    end do

end program

としますと、出力結果は、

 Dense layer.
 Input and output dimensions:           44          10
 Unitlayer layer.

となり、配列のそれぞれに異なる具象型を入れることができます。

なぜ、素朴なやり方ができなかったのか、謎です。

2
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?