LoginSignup
3
1

More than 5 years have passed since last update.

モダンfortranによる過去の資産の活用

Last updated at Posted at 2019-05-05

fortranをもっと便利に使いたい

fortranに関して、2019年時点ではf2008規格へのコンパイラ対応も充実し、オブジェクト指向も記述できるようになっている。f77の書き方はオブジェクト指向を含む処理より計算速度的には有利なので、両者をうまく使えば、ScipyやOctave並みの使い易さを持ちつつ、計算速度の非常に高速なライブラリができるのではないか。f77の資産1を活用する場面でひと工夫したので、具体的な事例を記録しておく。

具体事例

ScipyやOctaveの説明書を読んでいると、fortranのライブラリを参照する説明がしばしば登場する。fortranユーザーなら、それらのライブラリを直接使えば事足りるとはいえ、それらはf77の様式で書かれていることが多い。そこで、これを呼び出すオブジェクトを作り、不必要な引数は省略可能にしつつ、必要最小限の引数で便利な関数を使えるようにしたい。そうすれば、ScipyやOctaveと同じような感覚で数値計算ライブラリを呼び出すことができる。以下では、例として非線形方程式の解法に着目する。

Octaveの例

例えば、Octaveでは非線形方程式

\boldsymbol{f}(\boldsymbol{x})=\boldsymbol{0}

を解く時に次の関数を使うことができる2

fsolve(FCN,X0,OPTIONS)

ここに、FCNはユーザ定義関数、X0はXの初期値、OPTIONSは計算のパラメータ等を指定することができるが、デフォルト値で良い場合はOPTIONSを省略可能である。すなわち、ベクトルxを与えた時の関数ベクトルfさえ計算できればOctaveで何らかの解を得ることができる。Octaveのマニュアルによると、fsolve関数はfortranのMINPACKに含まれるhybrdに基づくらしいので、次にこれを見てみる。

f77の例

MINPACKのhybrd関数を見てみると、Octaveよりはるかに多くの引数が必要である。

   subroutine hybrd(fcn,n,x,fvec,xtol,maxfev,ml,mu,epsfcn,diag,&
                    mode,factor,nprint,info,nfev,fjac,ldfjac,r,lr,&
                    qtf,wa1,wa2,wa3,wa4)
      integer n,maxfev,ml,mu,mode,nprint,info,nfev,ldfjac,lr
      double precision xtol,epsfcn,factor
      double precision x(n),fvec(n),diag(n),fjac(ldfjac,n),r(lr),&
                       qtf(n),wa1(n),wa2(n),wa3(n),wa4(n)
      external fcn

さすがに引数が多いのか、MINPACK自体にも、引数の少ないバージョンが用意されていて、次のようなインターフェイスとなっている。

    subroutine hybrd1(fcn,n,x,fvec,tol,info,wa,lwa)
      integer n,info,lwa
      double precision tol
      double precision x(n),fvec(n),wa(lwa)
      external fcn

hybrdよりもhybrd1はかなり引数の数が少なくなっているが、それでもOctaveよりも多く、f77なので省略可能な引数はない。Octaveでは、おそらく最初の4つの引数のみをユーザに指定させ、その他はデフォルト値としてOPTIONSに含めているものと思われる。
Octaveなどの言語を知ってしまった身としては、できればtol以降の引数はユーザが細かく制御可能な状態のまま、通常はコンピュータに自動で宜しきにやってもらいたい。そこで、fortran2003以降に導入されたオブジェクト指向を使ってこれを実現する。

オブジェクト指向を活用して使いやすく

fortran2003からは、データとメソッドを持つ構造体を定義できて、さらにそれを継承する拡張型を定義できる。さらに、オブジェクト内部の公開属性を指定できるので、カプセル化も可能である。
具体的には、Modern Fortran Explainedという本がとても詳しい。日本語ではFortranからみたオブジェクト指向などが分かり易いと思う。

fortranで非線形方程式を解くオブジェクトを作る

hybrd1をf77でコールする時に必要な情報を含む、次のような構造体を定義する。

 type,public :: hybrdtype 
    integer :: info=-1 !<(out) hybrd1からの情報
    integer :: n    !<(in)関数値fと変数xの要素数
    integer :: lwa  !<hybrd1に渡されるwork arrayの要素数
    double precision :: tol=1d-10 !<(in) 誤差制御のデフォルト値
    double precision,dimension(:),allocatable :: x !<(inout)変数
      !<インプットは初期推定値、アウトプットはhybrd1による最終推定値。
    double precision,dimension(:),allocatable :: fvec !<(out)関数値
    double precision,dimension(:),allocatable :: wa !<work array
    procedure(),nopass,pointer::fcn=>null() !<fvecを計算する関数ポインタ
    contains
      final :: final_hybrd !< デストラクタ
      procedure :: setup => setup_hybr_common !<ユーザーサプライ関数や変数のセット
      procedure :: run   => run_hybrd   !<minpackのhybrd1関数を実行
  end type

contains以下のsetup手続きは、構造体内部の配列やポインタを定義するためのもので、次のインターフェイスとした。

subroutine setup_hybr_common(self,fcn,n,tol,xinit)
  class(hybrdtype),intent(inout) :: self
  integer,intent(in) :: n
  procedure() :: fcn 
  double precision,optional :: tol
  double precision,dimension(n),optional :: xinit

なお、run_hybrdは、定義したオブジェクトを用いてf77のhybrd1ルーチンをコールしているだけである。このようなオブジェクトを定義しておけば、次のような簡潔なコードで済むため、利用するときに型を宣言してセットアップして実行するだけとなり、引数の多さに悩まされない。

program test
  use hybrdmodule !hybrdtypeの定義を記述したモジュール
  implicit none
  type(hybrdtype) :: hybrd
  integer :: n=2
  !関数の定義と方程式&未知数の数、さらに初期値を設定
  call hybrd%setup(extfcn,n,xinit=[0d0,0d0]) 
  call hybrd%run() !hybrd1をcall
  print*,hybrd%x(:) !解を表示
end program

ちなみに、extfcnは例えば次のような関数を定義すれば良い。

  subroutine extfcn(n,x,fvec,iflag)
    integer :: n,iflag
    double precision::x(n),fvec(n)
    fvec(1)=-2d0*x(1)*x(1)+3d0*x(1)*x(2)+4d0*sin(x(2))-6d0
    fvec(2)=3d0*x(1)*x(1)-2d0*x(1)*x(2)**2d0+3d0*cos(x(1))+4d0
  end subroutine

おわりに

今回は、MINPACKを例にf77のコードを使いやすくする例を記した。この方法はODEPACKなどにも適用できるため、よく使うルーチンは同様の方法でモジュールにまとめている。なお、f77のルーチン毎にfortranの構造体を定義するのではなく、似たルーチンはオブジェクトの拡張やメソッドのオーバーライドを活用することもできる。
さらに、色々とモジュールを作っていると、ひとまとめにしてパソコンにインストールしたくなり、configure && make && make install でライブラリとして使えるようにしている。fortranの情報は少ないので、いつか時間があればfortranのconfigureスクリプトの作り方をメモしたい。


  1. そもそも計算アルゴリズムを近年のコンピュータ向けに書き直すという考えは脇に置いておく。 

  2. xもfも,要素が2つ以上あってよい。 

3
1
0

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
3
1