この記事はFortranからPythonを扱う方法であるforpyを解説する記事の第三弾です。
FortranからPythonを使いたい!:forpy
Fortranで機械学習がしたい:FortranからのPyTorchの利用
が以前の記事です。
今回は、Fortranで作った配列をnumpyの配列にして、その配列をnpy形式で読み書きする方法です。
npy形式とは、Pythonでnumpyの配列をそのまま保存する場合のフォーマットです。
バージョン
- gcc version 11.1.0 (Homebrew GCC 11.1.0)
- mac OS 10.14.6
で試しました。
forpyの使い方は以前の記事をみてください。
ファイル書き込み
まず、ファイルの書き込みです。こんな感じになります。
subroutine test()
use forpy_mod
use,intrinsic::iso_fortran_env
implicit none
integer(int32)::ierror
real(real64),allocatable::x(:)
integer(int32)::N
type(ndarray)::x_numpy,x2_numpy
type(module_py) :: np
type(tuple) :: args
N = 10
allocate(x(1:N))
call random_number(x)
write(*,*) "x = ",x
ierror = import_py(np, "numpy")
ierror = ndarray_create(x_numpy,x)
ierror = print_py(x_numpy)
ierror = ndarray_create_nocopy(x2_numpy,x)
x(1) = 100d0
ierror = print_py(x2_numpy)
ierror = tuple_create(args, 2)
ierror = args%setitem(0, "test")
ierror = args%setitem(1, x_numpy)
ierror = call_py_noret(np,"save",args)
call x_numpy%destroy
call x2_numpy%destroy
end subroutine
ここで、ndarray_create
は新しいnumpy配列を作り、ndarray_create_nocopy
はコピーなしでFortranの配列とnumpy配列を紐つけるものです。紐ついているために、Fortranの配列をいじるとnumpyの配列も変わっていることがわかります。このコードではtest.npyというファイルが書き出されます。
ファイル読み込み
ファイル読み込みは
subroutine test2()
use forpy_mod
use,intrinsic::iso_fortran_env
implicit none
integer(int32)::ierror
type(module_py) :: np
type(tuple) :: args
type(object) :: arr
real(real64),dimension(:), pointer ::x
type(ndarray)::x_numpy
integer(int32)::N
N = 10
ierror = import_py(np, "numpy")
ierror = tuple_create(args, 1)
ierror = args%setitem(0, "test.npy")
ierror = call_py(arr,np,"load",args)
ierror = ndarray_create_empty(x_numpy, [N], dtype="float64")
!ierror = cast(arr,x_numpy)
ierror = cast(x_numpy,arr) !arrの内容をx_numpyにcast
ierror = x_numpy%get_data(x)
write(*,*) "load"
write(*,*) x
end subroutine
です。numpy配列をFortran配列にする場合には、call_py
で返ってきたPythonオブジェクトを一旦castでnumpy配列にしてから、get_data
をしなければならないことに注意してください。また、Fortranの配列にはpointer属性をつけてください。これで、Fortranの配列が手に入りました。
全体
全体のコードは
subroutine test()
use forpy_mod
use,intrinsic::iso_fortran_env
implicit none
integer(int32)::ierror
real(real64),allocatable::x(:)
integer(int32)::N
type(ndarray)::x_numpy,x2_numpy
type(module_py) :: np
type(tuple) :: args
N = 10
allocate(x(1:N))
call random_number(x)
write(*,*) "x = ",x
ierror = import_py(np, "numpy")
ierror = ndarray_create(x_numpy,x)
ierror = print_py(x_numpy)
ierror = ndarray_create_nocopy(x2_numpy,x)
x(1) = 100d0
ierror = print_py(x2_numpy)
ierror = tuple_create(args, 2)
ierror = args%setitem(0, "test")
ierror = args%setitem(1, x_numpy)
ierror = call_py_noret(np,"save",args)
call x_numpy%destroy
call x2_numpy%destroy
end subroutine
subroutine test2()
use forpy_mod
use,intrinsic::iso_fortran_env
implicit none
integer(int32)::ierror
type(module_py) :: np
type(tuple) :: args
type(object) :: arr
real(real64),dimension(:), pointer ::x
type(ndarray)::x_numpy
integer(int32)::N
N = 10
ierror = import_py(np, "numpy")
ierror = tuple_create(args, 1)
ierror = args%setitem(0, "test.npy")
ierror = call_py(arr,np,"load",args)
ierror = ndarray_create_empty(x_numpy, [N], dtype="float64")
!ierror = cast(arr,x_numpy)
ierror = cast(x_numpy,arr) !arrの内容をx_numpyにcast
ierror = x_numpy%get_data(x)
write(*,*) "load"
write(*,*) x
end subroutine
program main
use forpy_mod
use,intrinsic::iso_fortran_env
implicit none
integer(int32)::ierror
ierror = forpy_initialize()
call test()
call test2()
end program
こんな感じになります。