✈️

FortranからJuliaへの自動変換を目指して

2023/07/18に公開

FortranとJuliaはどちらも科学技術計算に強い言語ですが, Juliaのモダンな開発環境や様々な魅力*に惹かれ, Fortranで書かれた既存のコードをJuliaに移植したいと考えている人も多いのではないでしょうか. 移植にあたっては永井さんのFortranから始めるJuliaが参考になります. このようにFortranとJuliaの両方を理解できる人間からすれば手作業で移植する方が安心感がありますが, 時間がかかるので何らかのツールに頼りたくなります. Juliaで書かれた簡単なツールなどもありますが, より使いやすく, より複雑な置換操作に対応したいと考え, ブラウザ上で利用できる移植支援ツールを新たに開発しました.

作っているもの

正規表現や名前付きキャプチャグループ等を用いて, FortranからJuliaへの移植を支援するためのスクリプトを開発しています. 完成には程遠いですが, 全て手作業で書き換えるよりはマシになりつつあります. コードの変換はJavaScriptで実装しているため, 利用はブラウザ上で完結します. 使い方は簡単で, 左のボックスにFortranのコードを張り付けるだけです. ぜひこちらから試してみてください.


https://ohno.github.io/Fortran2Julia.js/

簡単な変換の例

例えばa^xを計算するとき, Fortranではa**xと書きますが, Juliaではa^xと書きます. これを変換している部分のコードは下記のようになっています.

変換
  // ** to ^
  for (const match of output.matchAll(/\*\*/mg)) {
    let before = match[0];
    let after  = "^";
    console.log(before, "to", after);
    output = output.replace(before, after);
  }
- a**x
+ a^x

キャプチャグループを用いるとより複雑な置換を行うことができます. 例えばprint *,xxxprintln(xxx)に置換するときxxxの部分を抜き出して()の中に入れることができます.

変換
  // print *,xxx to println(xxx)
  for (const match of output.matchAll(/print\s*\*\s*\,\s*(?<text>.*)/mg)) {
    let before = match[0];
    let after  = `println(${match.groups.text})`;
    console.log(before, "to", after);
    output = output.replace(before, after);
  }
- print *, "hello"
+ println("hello")

その他にも, Fotranではdo文のカウンタの範囲と刻みをi = 2,10,2とするところ, Juliaではi = 2:2:10としますが, このように順序を入れ替える場合にもキャプチャグループを用いてます.

- do i = a,b,c
-   print *, i
- end do
+ for i = a:c:b
+   println(i)
+ end

難しいところ

Juliaでは型を明示的に書く必要はありませんが, 初期値がある場合や, 初期値が無くても配列である場合などは初期化が必要です. この場合分けはなかなか骨が折れました. 配列かつ初期値がある場合はまだ未対応ですが, それ以外のケースは概ね対応できたと考えています.

-  implicit none
-  integer :: i
-  integer :: j
-  integer :: a, b, c = 5, d(2), e(2,3), f, g
-  integer, parameter :: p = 8, q = 9
-  double precision :: x = 1.0, y = 2.0
-  double precision, dimension(2,2) :: u, v
-  complex :: z = (2.0,3.0)
+  
+  
+  
+  c = 5; d = zeros(2); e = zeros(2,3)
+  p = 8; q = 9
+  x = 1.0; y = 2.0
+  u = zeros(2,2); v = zeros(2,2)
+  z = 2.0 + im * 3.0

また, 括弧の変換も難しいところです. 例えば配列Aの要素へのアクセスするとき, FortranではA(1) = 0のように丸括弧を用い, JuliaではA[1] = 0のように角括弧を用います. この丸括弧()と角括弧[]の変換を何も考えずに行うと関数がf(x)からf[x]になってしまいます. つまり, その括弧()が配列にかかっているものなのか, 関数にかかっているのか, あるいは演算の順序を指定するために使われているだけなのか認識する必要があります. このように, 型の情報を知っていないと変換ができない部分が多々あり, 実装を煩雑化させています. 現状では, 上記の型宣言の部分で場合分けを実装済みなので, どれが配列なのか記録しておくことで関数と配列を区別しています.

- integer :: A(2,2)
- integer, dimension(2,2) :: B
- 
- y = A(1,1)
- y = B(1,1)
- y = f(1,1)
- y = g(1,1)
+ a = zeros(2,2)
+ b = zeros(2,2)
+ 
+ y = a[1,1]
+ y = b[1,1]
+ y = f(1,1)
+ y = g(1,1)

printやwriteはフォーマットの指定がかなり煩雑なので, 自分で書き直してもらう前提として考えています.

Fortranのコードをお持ちの方へ

Juliaに移植したいコードの具体例を知りたいので, こちらにご提供頂ければ幸いです. 移植をお手伝いします.

リクエスト

リクエストを頂ければ優先的に対応します.

コミットする

コミットしてくれる方は/assets/js/onload.jsにテストケースを追加し, /assets/js/Fortran2Julia.jsにパターンマッチング・置換を追加してください. とはいえ, かなり脳に負荷がかかるコードとなっていますし, 置換の順番も無視できず, キャプチャグループを使いこなすにも訓練が必要です. issueを追加してくださるだけでも有難いです.

おわりに

皆様からのIssueやスターをお待ちしております🌟


*例えばFortranでは単精度と倍精度でコードを分けて書いていましたが, Juliaでは単精度と倍精度に限らず, 任意精度演算や精度保証付き演算, シンボリックなど様々な引数に対して同じコードを使用できるなど, 柔軟な型システムを備えています.

Discussion