fortran66のブログ

fortran について書きます。

【ニュース】NAG Fortran が Apple Silicon 対応 他

NAG Fortran

アップルの秘密主義のせいか噂も出ていませんでしたが、NAG が Apple silicon 用 Fortran を提供するようです。意外でした。NAG は文法に厳しい正しい言葉遣いが特徴で、実行時はそんなに速くないイメージですがどうなるでしょうか。ともかくめでたいニュースです。

llvm には Nvidia + PGI が頑張っているし、gnu は敬遠ということで、秘密のために NAG という選択肢なのでしょうか?Absoft は(少なくとも昔は)Mac に好意的な気がしていましたが。それとも ARM がイギリスだから贔屓効果でしょうか?NAG には定評ある数値計算ライブラリもあるので、その対応もあることでしょう。

www.nag.com

Intel の Steve Lionel の書き込みからは、アップルがあまり協力的でなかった感がうかがわれるような気がします。 

OpenMP コンパイラ対応一覧表

マイナーなものが列挙されているのに、日本のコンパイラが無いのが怪しからんですね。 斯様な精神風土が分断を云々かんぬんw

www.openmp.org

【寝言】LGBTQ Fortran

Loli, Gachi-muchi, BBA, TKTT, オバQ

gfortran は LBGT 止まりだが、 Intel Fortran は一歩進んでQも可。

Q Returns the number of characters remaining in an input record.

とあるので、以下の print 文では全く意味を持たない。

fortran66.hatenablog.com

    program LGBTQ
        implicit none
        print '(L, G0, B0, T1, Q)', .false.,666,0.25e32
    end program LGBTQ
 F6661110011100111011100010110101110
続行するには何かキーを押してください . . .

【ニュース】 R 界隈からの Apple silicon での Fortran 情報

gfortran

以前、R 言語中で Fortran ソースの割合がそこそこあるというメモを書きましたが、R 開発者界隈の方から Apple Silicon での fortran 最新情報が出てきました。llvm, gnu とも公式対応は以前と変わらず芳しくないようですが、gfortran に関しては個人 branch での開発が進んである程度アプリケーションが動くようです。

fortran66.hatenablog.com

BLAS/LAPACK も C で書かれたものがあるから Fortran は必要ないなどとも言われますが(そもそも FORTRAN 版の BLAS はあくまでリファレンス用途で、各ハードウェア毎にそれぞれ最適化したものをアセンブラなどで用意するのが本来の姿ですが)、リファレンス用途に独自改変をしたものがあるようで Fortran が必要なようです。この種の問題は R に限らず処々にあるのだろうと思います。

developer.r-project.org

しかし、興味深いのは R では NaN のビット配列が一意でないことを利用して、NaN 中特定の bit 列に意味を与えてデータなし NA (not available) を表すことに利用していたようなのですが、それが Apple Silicon の高速演算モードで地雷となって問題になっているらしいことです。以前 mruby が NaN の bit 列の一部を固有の意味で利用していると聞いて、将来の禍根になりそうだなと思ったら、同じようなことがこんな所で爆発していました。

これを避けるため浮動小数点演算厳密モードで計算すると多分計算が少し遅くなると思います。

グスタフソンがカーンの IEEE754 を批評して NaN がビット列を無駄にし過ぎていて、こういう冗長性がありすぎるとハック(やっつけ仕事)されて不正利用されると言っていた気がしますが、全くその通りになっていて草生えます。

縁(えにし)の糸(通常盤)

縁(えにし)の糸(通常盤)

【メモ帳】Fortran から Julia 呼び出し その4 (SymPy)

SymPy で記号演算した結果の Fortran からの利用

Fortran から Julia を呼んで、そこで PyCall + SymPy して記号処理します。出てきた結果を Julia の関数として定義して、それを Fortran から呼び出します。

実行結果

まず julia で普通に f(x) = x2 と定義した関数が Fortran から呼び出せることを確かめます。次に SymPy で log(x)/x を積分してやると log(x)2/2 と求まるので、これを Julia の関数に直します。そうして Fortran から呼び出します。念のため Fortran 側で log(x)2/2 を計算して結果を比較しています。

hp8@HP8:~/forjulia$ ifort -o test  -L$JULIA_DIR/lib -Wl,-rpath,$JULIA_DIR/lib sym.f90 -ljulia
hp8@HP8:~/forjulia$ ./test
 call SymPy from Julia from Fortran
 f(x)=x^2
 f(2.0d0)=   4.00000000000000

 integrate(log(x) / x, x)= log(x)^2/2
  1   1.0000000000000000   0.0000000000000000   0.0000000000000000
  2   2.0000000000000000   0.2402265069591007   0.2402265069591007
  3   3.0000000000000000   0.6034744804062910   0.6034744804062910
  4   4.0000000000000000   0.9609060278364028   0.9609060278364028
  5   5.0000000000000000   1.2951451969901173   1.2951451969901173
  6   6.0000000000000000   1.6052009977842006   1.6052009977842006
  7   7.0000000000000000   1.8932831540982358   1.8932831540982358
  8   8.0000000000000000   2.1620385626319059   2.1620385626319059
  9   9.0000000000000000   2.4138979216251641   2.4138979216251641
 10  10.0000000000000000   2.6509490552391997   2.6509490552391997

今回は ifort でコンパイルしてみました。gfortran より型チェックにうるさいです。

プログラム

SymPy の出力は直では普通の関数には出来ないようなので、一度文字列化してパースして eval して Fortran から呼び出せる普通の関数にしています。この辺は詳しいことがよく分かっていないので素朴にやっています。

eval(Meta.parse( "f(x)=" * string(integrate(y, x)) ))
module julia_mod
    use, intrinsic :: iso_fortran_env
    use, intrinsic :: iso_c_binding
    implicit none

!
! global variables
!
    type(c_ptr) :: jl_base_module
    bind(c, name = 'jl_base_module') :: jl_base_module

    type(c_ptr) :: jl_int64_type
    bind(c, name = 'jl_int64_type') :: jl_int64_type

    type(c_ptr) :: jl_float64_type
    bind(c, name = 'jl_float64_type') :: jl_float64_type
!
! interfaces
!
    interface
        subroutine jl_init() bind(c, name = 'jl_init__threading')
        end subroutine jl_init

        subroutine jl_atexit_hook(status) bind(c, name = 'jl_atexit_hook')
            import
            integer(c_int), value :: status
        end subroutine jl_atexit_hook

        type(c_ptr) function jl_eval_string(pstr) bind(c, name = 'jl_eval_string')
            import
            type(c_ptr), value :: pstr
        end function jl_eval_string

        real(real64) function jl_unbox_float64(ptr) bind(c, name = 'jl_unbox_float64')
            import
            type(c_ptr), value :: ptr
        end function jl_unbox_float64

        type(c_ptr) function jl_box_float64(x) bind(c, name = 'jl_box_float64')
            import
            real(real64), value :: x
        end function jl_box_float64

        type(c_funptr) function jl_get_global(modu, psymbol) bind(c, name = 'jl_get_global')
            import
            type(c_ptr), value :: modu
            type(c_ptr), value :: psymbol
        end function jl_get_global

        type(c_ptr) function jl_symbol(ptext) bind(c, name = 'jl_symbol')
            import
            type(c_ptr), value :: ptext
        end function jl_symbol

        type(c_ptr) function jl_call1(pfunc, parg) bind(c, name = 'jl_call1')
            import
            type(c_funptr), value :: pfunc
            type(c_ptr), value :: parg
        end function jl_call1

    end interface

contains

    subroutine eval(text, iret)
        character(*), intent(in) :: text
        type(c_ptr), intent(out), optional :: iret
        character(:), allocatable, target :: buff
        type(c_ptr) :: kret
        buff = text // achar(0)
        kret = jl_eval_string(c_loc(buff))
       if (present(iret)) iret = kret
    end subroutine eval

    function jl_get_function(modu, namen)
        type(c_ptr), value :: modu
        character(*), intent(in) :: namen
        type(c_funptr) :: jl_get_function
        character(:), allocatable, target :: text
        text = namen // achar(0)
        jl_get_function = jl_get_global(modu, jl_symbol(c_loc(text)))
    end function jl_get_function

end module julia_mod

program sympy
    use julia_mod
    implicit none
    type(c_ptr) :: iret, arg1, iatyp
    type(c_funptr) :: ifun
    real(real64) :: x, y
    integer :: i

    call jl_init()

    call eval('println(" call SymPy from Julia from Fortran")')

    call eval('println(" f(x)=x^2")')
    call eval('f(x)=x^2', iret)
    ifun = transfer(iret, ifun) ! c_ptr -> c_funptr
    arg1 = jl_box_float64(2.0_real64)
    iret = jl_call1(ifun, arg1)
    x = jl_unbox_float64(iret)
    print *, 'f(2.0d0)=', x
    print *

    call eval('using PyCall')
    call eval('using SymPy') ! or SymEngine
    call eval('@vars x y')
    call eval('y = log(x) / x')
    call eval('print(" integrate(log(x) / x, x)= ")')
    call eval('println(integrate(y, x))')
    call eval('eval(Meta.parse( "f(x)=" * string(integrate(y, x)) ))', iret)
    ifun = transfer(iret, ifun) ! c_ptr -> c_funptr

    do i = 1, 10
        x = real(i, real64)
        arg1 = jl_box_float64(x)
        iret = jl_call1(ifun, arg1)
        y = jl_unbox_float64(iret)
        print '(i3, 3(1x, f20.16))', i, x, y, log(x)**2 / 2
    end do

    call jl_atexit_hook(0)
end program sympy

Instant SymPy Starter (English Edition)

Instant SymPy Starter (English Edition)

【メモ帳】Fortran から Julia 呼び出し その3

Fortran から julia 呼んでそこから python 呼んでそこから Fortran 呼び出し

Python から呼び出す Fortran subroutine sub.f90

subroutine test() bind(c, name = 'test')
    print *, 'subroutine test: Fortran calls Julia calls Python calls Fortran subroutine'
end subroutine test
gfortran -shared -o sub.so sub.f90

Fortran メインプログラム test.f90

Julia を呼び出し、Julia の PyCall によって Python の numpy, matplotlib, ctypes を呼び出し、絵を描き Fortran のサブルーチンを呼び出します。

module julia_mod
    use, intrinsic :: iso_fortran_env
    use, intrinsic :: iso_c_binding
    implicit none
    interface
        subroutine jl_init() bind(c, name = 'jl_init__threading')
        end subroutine jl_init

        subroutine jl_atexit_hook(status) bind(c, name = 'jl_atexit_hook')
            import
            integer(c_int), value :: status
        end subroutine jl_atexit_hook

        function jl_eval_string(pstr) bind(c, name = 'jl_eval_string')
            import
            type(c_ptr) :: jl_eval_string
            type(c_ptr), value :: pstr
        end function jl_eval_string
    end interface

contains
    subroutine eval(text)
        character(*), intent(in) :: text
        type(c_ptr) :: iret
        character(:), allocatable, target :: buff
        buff = text // achar(0)
        iret = jl_eval_string(c_loc(buff))
    end subroutine eval
end module julia_mod

program test
    use julia_mod
    implicit none
    type(c_ptr) :: iret

    call jl_init()

    call eval('println("Julia from Fortran")')

    call eval('using PyCall')
    call eval('np = pyimport("numpy")')
    call eval('x = np.random.rand(20)')
    call eval('println(x)')

    call eval('plt = pyimport("matplotlib.pyplot")')
    call eval('plt.plot(x)')
    call eval('plt.savefig("plt.jpg")')

    call eval('pyimport("ctypes")')
    call eval('f = np.ctypeslib.load_library("sub.so", ".")')
    call eval('f.test()')

    call jl_atexit_hook(0)
end program test
hp8@HP8:~/forjulia$ gfortran -o test -fPIC -L$JULIA_DIR/lib -Wl,-rpath,$JULIA_DIR/lib test.f90 -ljulia
hp8@HP8:~/forjulia$ ./test
Julia from Fortran
[0.5654498902512056, 0.1661842542698303, 0.2505778464395546, 0.7666649031127386, 0.8496339671276011, 0.9273802000807766, 0.8927764872674302, 0.3006637895458586, 0.8680738476853896, 0.7854580868588814, 0.45450410234687666, 0.28052468257184127, 0.061124512953987575, 0.19881999156124885, 0.9443622824939664, 0.12457596083666511, 0.46569931418942245, 0.9063580131505672, 0.6581766728087582, 0.2236554710108204]
 subroutine test: Fortran calls Julia calls Python calls Fortran subroutine
hp8@HP8:~/forjulia$ eog plt.jpg

f:id:fortran66:20201108014408p:plain

Guide to Fortran 2008 Programming

Guide to Fortran 2008 Programming

1から始める Juliaプログラミング

1から始める Juliaプログラミング

Pythonによるデータ分析入門 第2版 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 第2版 ―NumPy、pandasを使ったデータ処理

  • 作者:Wes McKinney
  • 発売日: 2018/07/26
  • メディア: 単行本(ソフトカバー)

【メモ帳】Fortran から Julia 呼び出し その2

Julia from Fortran

前回の続きで、関数呼び出し、配列受け渡しなどの例題をやってみました。

一応、配列構造体を定義してみました。しかしコンパイル時の定数で構造が変わるようなので、よく分かりません。

しかし構造体などをきちんと定義していないので、ちゃんとしたものにはなっていません。 fortran66.hatenablog.com

配列は、Fortran の allocatable 型にしろ、Julia の Array にしろ、データの前に次元やサイズの情報のヘッダーがついているので、そこを何とかしなければならないのですが、めんどくさいのでちゃんとしていません。

また、C 言語の例題では Julia のガベージ・コレクション (GC) の動作のコントロールをマクロを利用してやっているのですが、そこは難しいので、気になる場合は Fortran で Julia 側で確保したメモリー領域をいじる場合には GC を停止させればいいのではないかと思います。インターフェースは作っておきました。

Fortran ソース

module julia_mod
    use, intrinsic :: iso_fortran_env
    use, intrinsic :: iso_c_binding
    implicit none
!
! types (struct)
!
    type, bind(c) :: jl_array_t
        type(c_ptr) :: dat
        ! integer(c_size_t) :: length
        type(c_ptr) :: flags
        integer(c_int16_t) :: elsize
        integer(c_int32_t) :: offset
        integer(c_size_t) :: nrows
        integer(c_size_t) :: ncols ! union maxsize
    end type jl_array_t
! !
! ! julia-1.5.2/include/julia/julia.h
! !
!
!JL_EXTENSION typedef struct {
!    JL_DATA_TYPE
!    void *data;
!#ifdef STORE_ARRAY_LEN
!    size_t length;
!#endif
!    jl_array_flags_t flags;
!    uint16_t elsize;  // element size including alignment (dim 1 memory stride)
!    uint32_t offset;  // for 1-d only. does not need to get big.
!    size_t nrows;
!    union {
!        // 1d
!        size_t maxsize;
!        // Nd
!        size_t ncols;
!    };
!    // other dim sizes go here for ndims > 2
!
!    // followed by alignment padding and inline data, or owner pointer
!} jl_array_t;
!

!
! global variables
!
    type(c_ptr) :: jl_base_module
    bind(c, name = 'jl_base_module') :: jl_base_module

    type(c_ptr) :: jl_int8_type
    bind(c, name = 'jl_int8_type') :: jl_int8_type

    type(c_ptr) :: jl_int16_type
    bind(c, name = 'jl_int16_type') :: jl_int16_type

    type(c_ptr) :: jl_int32_type
    bind(c, name = 'jl_int32_type') :: jl_int32_type

    type(c_ptr) :: jl_int64_type
    bind(c, name = 'jl_int64_type') :: jl_int64_type

    type(c_ptr) :: jl_float16_type
    bind(c, name = 'jl_float16_type') :: jl_float16_type

    type(c_ptr) :: jl_float32_type
    bind(c, name = 'jl_float32_type') :: jl_float32_type

    type(c_ptr) :: jl_float64_type
    bind(c, name = 'jl_float64_type') :: jl_float64_type
!
! interfaces
!
    interface
        subroutine jl_init() bind(c, name = 'jl_init__threading')
        end subroutine jl_init

        subroutine jl_atexit_hook(status) bind(c, name = 'jl_atexit_hook')
            import
            integer(c_int), value :: status
        end subroutine jl_atexit_hook

        type(c_ptr) function jl_eval_string(pstr) bind(c, name = 'jl_eval_string')
            import
            type(c_ptr), value :: pstr
        end function jl_eval_string

        real(real64) function jl_unbox_float64(ptr) bind(c, name = 'jl_unbox_float64')
            import
            type(c_ptr), value :: ptr
        end function jl_unbox_float64

        type(c_ptr) function jl_box_float64(x) bind(c, name = 'jl_box_float64')
            import
            real(real64), value :: x
        end function jl_box_float64

        real(real32) function jl_unbox_float32(ptr) bind(c, name = 'jl_unbox_float32')
            import
            type(c_ptr), value :: ptr
        end function jl_unbox_float32

        type(c_ptr) function jl_box_float32(x) bind(c, name = 'jl_box_float32')
            import
            real(real32), value :: x
        end function jl_box_float32

        real(int64) function jl_unbox_int64(ptr) bind(c, name = 'jl_unbox_int64')
            import
            type(c_ptr), value :: ptr
        end function jl_unbox_int64

        type(c_ptr) function jl_box_int64(x) bind(c, name = 'jl_box_int64')
            import
            real(int64), value :: x
        end function jl_box_int64

        real(int32) function jl_unbox_int32(ptr) bind(c, name = 'jl_unbox_int32')
            import
            type(c_ptr), value :: ptr
        end function jl_unbox_int32

        type(c_ptr) function jl_box_int32(x) bind(c, name = 'jl_box_int32')
            import
            real(int32), value :: x
        end function jl_box_int32

        type(c_ptr) function jl_ptr_to_array_1d(typ, pdata, sz, own_buffer) bind(c, name = 'jl_ptr_to_array_1d')
            import
            type(c_ptr), value :: typ
            type(c_ptr), value :: pdata
            integer(c_size_t), value :: sz
            integer(c_int)   , value :: own_buffer
        end function jl_ptr_to_array_1d

        type(c_ptr) function jl_ptr_to_array(typ, pdata, dims, own_buffer) bind(c, name = 'jl_ptr_to_array')
            import
            type(c_ptr), value :: typ
            type(c_ptr), value :: pdata
            type(c_ptr), value :: dims
            integer(c_int)   , value :: own_buffer
        end function jl_ptr_to_array

        type(c_ptr) function jl_apply_array_type(typ, n) bind(c, name = 'jl_apply_array_type')
            import
            type(c_ptr), value :: typ
            integer(c_size_t), value :: n
        end function jl_apply_array_type

        type(c_ptr) function jl_alloc_array_1d(typ, n) bind(c, name = 'jl_alloc_array_1d')
            import
            type(c_ptr), value :: typ
            integer(c_size_t), value :: n
        end function jl_alloc_array_1d

        type(c_ptr) function jl_alloc_array_2d(typ, nr, nc) bind(c, name = 'jl_alloc_array_2d')
            import
            type(c_ptr), value :: typ
            integer(c_size_t), value :: nr, nc
        end function jl_alloc_array_2d

        type(c_ptr) function jl_alloc_array_3d(typ, nr, nc, nz) bind(c, name = 'jl_alloc_array_3d')
            import
            type(c_ptr), value :: typ
            integer(c_size_t), value :: nr, nc, nz
        end function jl_alloc_array_3d

        type(c_ptr) function jl_arrayref(iarr, n) bind(c, name = 'jl_arrayref')
            import
            type(c_ptr), value :: iarr
            integer(c_size_t), value :: n
        end function jl_arrayref

        type(c_funptr) function jl_get_global(modu, psymbol) bind(c, name = 'jl_get_global')
            import
            type(c_ptr), value :: modu
            type(c_ptr), value :: psymbol
        end function jl_get_global

        type(c_ptr) function jl_symbol(ptext) bind(c, name = 'jl_symbol')
            import
            type(c_ptr), value :: ptext
        end function jl_symbol

        type(c_ptr) function jl_call(pfunc, pargs, nargs) bind(c, name = 'jl_call')
            import
            type(c_funptr), value :: pfunc
            type(c_ptr), value :: pargs
            integer(c_int32_t), value :: nargs
        end function jl_call

        type(c_ptr) function jl_call0(pfunc) bind(c, name = 'jl_call0')
            import
            type(c_funptr), value :: pfunc
        end function jl_call0

        type(c_ptr) function jl_call1(pfunc, parg) bind(c, name = 'jl_call1')
            import
            type(c_funptr), value :: pfunc
            type(c_ptr), value :: parg
        end function jl_call1

        type(c_ptr) function jl_call2(pfunc, parg1, parg2) bind(c, name = 'jl_call2')
            import
            type(c_funptr), value :: pfunc
            type(c_ptr), value :: parg1, parg2
        end function jl_call2

        type(c_ptr) function jl_call3(pfunc, parg1, parg2, parg3) bind(c, name = 'jl_call3')
            import
            type(c_funptr), value :: pfunc
            type(c_ptr), value :: parg1, parg2, parg3
        end function jl_call3

    end interface

contains

    subroutine eval(text, iret)
        character(*), intent(in) :: text
        type(c_ptr), intent(out), optional :: iret
        character(:), allocatable, target :: buff
        type(c_ptr) :: kret
        buff = text // achar(0)
        kret = jl_eval_string(c_loc(buff))
       if (present(iret)) iret = kret
    end subroutine eval

    function jl_get_function(modu, namen)
        type(c_ptr), value :: modu
        character(*), intent(in) :: namen
        type(c_funptr) :: jl_get_function
        character(:), allocatable, target :: text
        text = namen // achar(0)
        jl_get_function = jl_get_global(modu, jl_symbol(c_loc(text)))
    end function jl_get_function

end module julia_mod

program array_test
    use julia_mod
    implicit none
    real(real64), parameter :: pi = 4 * atan(1.0_real64)
    character(:), allocatable, target :: text
    type(c_ptr) :: iret, argument, iarr, jarr, iatyp
    real(real64) :: x
    type(c_funptr) :: ifun
    integer, allocatable, target :: m(:)
    integer, pointer :: m2(:)
    integer :: i
    integer(int64) :: n
    type(jl_array_t), pointer :: parr
    character, pointer :: chars(:)
    call jl_init()

    call eval('println("Julia from Fortran")')


    text = 'sqrt(2.0)'//achar(0)
    iret = jl_eval_string(c_loc(text))
    x = jl_unbox_float64(iret)
    print *, 'SQRT(2.0)=', x, sqrt(2.0_real64)

    ifun = jl_get_function(jl_base_module, "sinpi")
    argument = jl_box_float64(1.0_real64 / 6.0_real64)
    iret = jl_call1(ifun, argument)
    x = jl_unbox_float64(iret)
    print *, 'Julia: sinpi(1/6)', x, ' Fortran:sin(pi/6)', sin(pi / 6)

    n = 20
    m = [(i, i = 1, n)]
    iatyp = jl_apply_array_type(jl_int32_type, 1_int64)
    iarr = jl_ptr_to_array_1d(iatyp, c_loc(m), n, 0)


    print '(a, *(i3))', 'array 1:n ', m
    ifun = jl_get_function(jl_base_module, "reverse!")
    iret = jl_call1(ifun, iarr)
    print '(a, *(i3))', 'reverse!  ', m


    ifun = jl_get_function(jl_base_module, "reverse")
    jarr = jl_call1(ifun, iarr)
    call c_f_pointer(jarr, parr)
    call c_f_pointer(parr%dat, m2, [n])
    print '(a, *(i3))', 'reverse   ', m2

    call jl_atexit_hook(0)
end program array_test

実行結果

  • sqrt(2) を Julia 側と Fortran 側の両方で計算しています。
  • Julia 側では π を単位とする sinpi 関数で sinpi(1/6) を、Fortran 側では sin(π/6.0) を計算しています。π を単位とする計算は多くなされますが、初めからπを基準としておくと精度よく計算できることが知られています。Fortran202X で Fortran にも同様な関数が導入される予定です。
  • Fortran 側の要素数 n(=20) 個の整数配列を、Julia 側に渡してINTENT(IN OUT) 的に値の並びを反転させました。
  • 反転した整数配列を intent(in) 的に Julia 側に渡して、もう一度反転して元に戻した配列を関数の戻り値として Fortran 側の配列で受け取りました。
hp8@HP8:~/forjulia$ gfortran -o test -fPIC -L$JULIA_DIR/lib -Wl,-rpath,$JULIA_DIR/lib j.f90 -ljulia
hp8@HP8:~/forjulia$ ./test
Julia from Fortran
 SQRT(2.0)=   1.4142135623730951        1.4142135623730951
 Julia: sinpi(1/6)  0.50000000000000000       Fortran:sin(pi/6)  0.49999999999999994
array 1:n   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
reverse!   20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1
reverse     1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

docs.julialang.org

1から始める Juliaプログラミング

1から始める Juliaプログラミング

【メモ帳】Fortran から Julia 呼び出し

Embedding Julia in Fortran

スタバのソファーでふんぞり返って、タラタラとネットサーフィンwしていたら C から Julia を呼び出す本家のページにたどり着きましたので眺めやると、それほど面倒くさそうでもなさそうなので Fortran から呼び出すことに挑戦してみました。それにつけてもモバイルオーダーまじ楽ちん。

とりあえず、本家のここのページを参考にして、C 用のルーチンのインターフェースを最小限用意して実行してみました。Windows だと色々めんどそうなので WSL + gfortran でやってみることにします。

docs.julialang.org

Fortran ソース・プログラム

基本的にC 用のサンプルを丸写し的にやっています。

  • 文字列の内容を Julia で実行させています。
  • Julia 側の演算結果の倍精度実数のスカラー変数を Fortran 側にもってきています。
  • using LinearAlgebra で Julia で逆行列を求めています。(Fortran 側には持ってきていません)
  • using Plots でグラフ表示は出来ませんでした。display 命令で表示できることが分かりました。ただし図を表示したままにするには Fortran の puase 文(規格から廃止になりましたが)などを使う必要があります。しかし図は savefig でファイルに保存できました。
module julia_mod
    use, intrinsic :: iso_fortran_env
    use, intrinsic :: iso_c_binding
    implicit none
    interface
        subroutine jl_init() bind(c, name = 'jl_init__threading')
        end subroutine jl_init

        subroutine jl_atexit_hook(status) bind(c, name = 'jl_atexit_hook')
            import
            integer(c_int), value :: status
        end subroutine jl_atexit_hook

        function jl_eval_string(pstr) bind(c, name = 'jl_eval_string')
            import
            type(c_ptr) :: jl_eval_string
            type(c_ptr), value :: pstr
        end function jl_eval_string

        real(real64) function jl_unbox_float64(ptr) bind(c, name = 'jl_unbox_float64')
            import
            type(c_ptr), value :: ptr
        end function jl_unbox_float64
    end interface

contains
    subroutine eval(text)
        character(*), intent(in) :: text
        type(c_ptr) :: iret
        character(:), allocatable, target :: buff
        buff = text // achar(0)
        iret = jl_eval_string(c_loc(buff))
    end subroutine eval
end module julia_mod

program test
    use julia_mod
    implicit none
    character(:), allocatable, target :: text
    type(c_ptr) :: iret
    real(real64) :: x

    call jl_init()

    call eval('println("Julia from Fortran")')

    call eval('x=1/6')
    call eval('println(sinpi(x))')

    text = 'sqrt(2.0)'//achar(0)
    iret = jl_eval_string(c_loc(text))
    x = jl_unbox_float64(iret)
    print *, 'SQRT(2.0)=', x, sqrt(2.0_real64)

    call eval('using LinearAlgebra')
    call eval('A = [1 2 3; 4 1 6; 7 8 1]')
    call eval('println(A)')
    call eval('println("det(A)=", det(A))')
    call eval('println("A^-1=", inv(A))')

    call eval('using Plots')
    call eval('gui')
    call eval('p = plot(sin)')
    call eval('plot!(cos)')
    call eval('savefig("test.png")')

    call eval('display(p)')
    pause
    call eval('display(plot(rand(10), st=:pie))')
    pause

    call jl_atexit_hook(0)
end program test

実行結果

gfortran の pause 文はうるさいので ifort でやり直しました。

hp8@HP8:~$ export JULIA_DIR=~/julia-1.5.2
hp8@HP8:~/forjulia$ gfortran -o test -fPIC -L$JULIA_DIR/lib -Wl,-rpath,$JULIA_DIR/lib julia.f90 -ljulia
hp8@HP8:~/forjulia$ ifort    -o test       -L$JULIA_DIR/lib -Wl,-rpath,$JULIA_DIR/lib julia.f90 -ljulia
hp8@HP8:~/forjulia$ ./test
Julia from Fortran
0.5
 SQRT(2.0)=   1.41421356237310        1.41421356237310
[1 2 3; 4 1 6; 7 8 1]
det(A)=104.0
A^-1=[-0.4519230769230769 0.2115384615384615 0.08653846153846155; 0.3653846153846154 -0.1923076923076923 0.057692307692307675; 0.24038461538461536 0.057692307692307696 -0.0673076923076923]
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-hp8'
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-hp8'
FORTRAN PAUSE
PAUSE prompt>
FORTRAN PAUSE
PAUSE prompt>
hp8@HP8:~$ eog test.png

f:id:fortran66:20201109133136p:plain f:id:fortran66:20201107022314p:plain

Modern Fortran: Building efficient parallel applications

Modern Fortran: Building efficient parallel applications

  • 作者:Curcic, Milan
  • 発売日: 2020/11/24
  • メディア: ペーパーバック

1から始める Juliaプログラミング

1から始める Juliaプログラミング

参考ページ

docs.julialang.org

docs.juliaplots.org