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.
となり、配列のそれぞれに異なる具象型を入れることができます。
なぜ、素朴なやり方ができなかったのか、謎です。