割り当て可能な形状配列を使用したF2Py


18

f2py最新のFortranで使用したいと思います。特に、次の基本的な例を動作させようとしています。これは、私が生成できる最小の有用な例です。

! alloc_test.f90
subroutine f(x, z)
  implicit none

! Argument Declarations !
  real*8, intent(in) ::  x(:)
  real*8, intent(out) :: z(:)

! Variable Declarations !
  real*8, allocatable :: y(:)
  integer :: n

! Variable Initializations !
  n = size(x)
  allocate(y(n))

! Statements !
  y(:) = 1.0
  z = x + y

  deallocate(y)
  return
end subroutine f

nは、入力パラメータの形状から推測されることに注意してくださいxyは、サブルーチンの本体内で割り当ておよび割り当て解除されることに注意してください。

これをコンパイルすると f2py

f2py -c alloc_test.f90 -m alloc

そして、Pythonで実行します

from alloc import f
from numpy import ones
x = ones(5)
print f(x)

次のエラーが表示されます

ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (-1,)

そこで、pyf手動でファイルを作成および編集します

f2py -h alloc_test.pyf -m alloc alloc_test.f90

元の

python module alloc ! in 
    interface  ! in :alloc
        subroutine f(x,z) ! in :alloc:alloc_test.f90
            real*8 dimension(:),intent(in) :: x
            real*8 dimension(:),intent(out) :: z
        end subroutine f
    end interface 
end python module alloc

修正済み

python module alloc ! in 
    interface  ! in :alloc
        subroutine f(x,z,n) ! in :alloc:alloc_test.f90
            integer, intent(in) :: n
            real*8 dimension(n),intent(in) :: x
            real*8 dimension(n),intent(out) :: z
        end subroutine f
    end interface 
end python module alloc

現在は実行されていますが、出力の値zは常に0です。一部のデバッグ印刷では、サブルーチン内にn値があることがわかります。この状況を適切に管理するためのヘッダーマジックが欠けていると思います。 0ff2py

より一般的には、上記のサブルーチンをPythonにリンクする最良の方法は何ですか?サブルーチン自体を変更する必要がないことを強く望みます。


マット、Ondrej Certikのベストプラクティスガイド、具体的にはPythonとのインターフェイスセクションに精通していますか?PyClawについても同様のインターフェイスの問題について議論してきましたが、現時点ではまだ解決していません:)
Aron Ahmadia

回答:


23

私はf2pyの内部構造にはあまり精通していませんが、Fortranのラッピングには精通しています。F2pyは、以下の一部またはすべてを自動化します。

  1. 以下の例のように、最初にiso_c_bindingモジュールを使用してCにエクスポートする必要があります。

    http://fortran90.org/src/best-practices.html#interfacing-with-c

    免責事項:私はfortran90.orgページの主な著者です。これは、CからFortranを呼び出すプラットフォームおよびコンパイラに依存しない唯一の方法です。これはF2003であるため、最近では他の方法を使用する理由はありません。

  2. 指定された全長(明示的な形状)の配列のみをエクスポート/呼び出しできます。

    integer(c_int), intent(in) :: N
    real(c_double), intent(out) :: mesh(N)

    形状を想定していません:

    real(c_double), intent(out) :: mesh(:)

    これは、C言語がそのような配列自体をサポートしていないためです。そのようなサポートをF2008以降(私にはわかりません)のいずれかに含めるという話がありますが、それが機能する方法は、配列に関する形状情報を伝える必要があるため、いくつかのサポートCデータ構造を介しています。

    Fortranでは、主に仮定形状を使用する必要があります。特殊な場合にのみ、ここで説明するように明示的な形状を使用する必要があります。

    http://fortran90.org/src/best-practices.html#arrays

    つまり、上記の最初のリンクごとに、形状引き継ぎサブルーチンの周りに単純なラッパーを作成し、明示的な形状配列にラップする必要があることを意味します。

  3. Cシグネチャを取得したら、好きな方法でPythonから呼び出すだけです。Cythonを使用しますが、ctypeまたはC / APIを手動で使用できます。

  4. deallocate(y)必要とされていない文は、Fortranは自動的に割り当てを解除します。

    http://fortran90.org/src/best-practices.html#allocatable-arrays

  5. real*8使用すべきではなく、むしろreal(dp)

    http://fortran90.org/src/best-practices.html#floating-point-numbers

  6. ステートメントy(:) = 1.0は1.0を単精度で割り当てているため、残りの数字はランダムになります!これはよくある落とし穴です:

    http://fortran90.org/src/gotchas.html#floating-point-numbers

    を使用する必要がありますy(:) = 1.0_dp

  7. 書く代わりにy(:) = 1.0_dp、ただ書くことができますy = 1、それだけです。精度を損なうことなく整数を浮動小数点数に割り当てることができ(:)、そこに冗長性を置く必要はありません。はるかに簡単です。

  8. の代わりに

    y = 1
    z = x + y

    ただ使う

    z = x + 1

    そして、y配列をまったく気にしません。

  9. サブルーチンの最後に「return」ステートメントは必要ありません。

  10. 最後に、おそらくモジュールを使用implicit noneする必要があり、モジュールレベルに置くだけで、各サブルーチンで繰り返す必要はありません。

    そうでなければ私には良さそうです。上記の提案1〜10に続くコードを次に示します。

    module test
    use iso_c_binding, only: c_double, c_int
    implicit none
    integer, parameter :: dp=kind(0.d0)
    
    contains
    
    subroutine f(x, z)
    real(dp), intent(in) ::  x(:)
    real(dp), intent(out) :: z(:)
    z = x + 1
    end subroutine
    
    subroutine c_f(n, x, z) bind(c)
    integer(c_int), intent(in) :: n
    real(c_double), intent(in) ::  x(n)
    real(c_double), intent(out) :: z(n)
    call f(x, z)
    end subroutine
    
    end module

    単純化されたサブルーチンとCラッパーを示しています。

    f2pyに関しては、おそらくこのラッパーを作成しようとして失敗します。iso_c_bindingモジュールを使用しているかどうかもわかりません。これらすべての理由から、私は物を手で包むことを好みます。その後、何が起こっているのかが明確になります。


私の知る限り、f2pyはISO Cバインディングに依存していません(その主なターゲットはFortran 77およびFortran 90コードです)。
アロンアフマディア

私は少し愚かなことを知っていましたが、y何かを割り当てたいと思っていました(実際のコードには重要な割り当てがあります)。しかし、他の多くの点については知りませんでした。Fortran90のベストプラクティスガイドを調べる必要があるようです。徹底的な回答をありがとうございます。
MRocklin

今日のFortranコンパイラを使用すると、単純なiso_c_bindingラッパーを記述し、そこからレガシーf77サブルーチンを呼び出すことで、まったく同じ方法でF77をラップすることに注意してください。
オンドレジ・セティク

6

あなたがしなければならないのは以下です:

!alloc_test.f90
subroutine f(x, z, n)
  implicit none

! Argument Declarations !
  integer :: n
  real*8, intent(in) ::  x(n)
  real*8, intent(out) :: z(n)

! Variable Declarations !
  real*8, allocatable :: y(:)

! Variable Initializations !
  allocate(y(n))

! Statements !
  y(:) = 1.0
  z = x + y

  deallocate(y)
  return
end subroutine f

配列xとzのサイズは明示的な引数として渡されるようになりましたが、f2pyは引数nをオプションにします。以下は、Pythonに表示される関数のdocstringです。

Type:       fortran
String Form:<fortran object>
Docstring:
f - Function signature:
  z = f(x,[n])
Required arguments:
  x : input rank-1 array('d') with bounds (n)
Optional arguments:
  n := len(x) input int
Return objects:
  z : rank-1 array('d') with bounds (n)

Pythonからインポートして呼び出す:

from alloc import f
from numpy import ones
x = ones(5)
print f(x)

次の出力が得られます。

[ 2.  2.  2.  2.  2.]

サイズとして重要な表現を使用する方法はありますか?たとえば、私はパスnし、サイズの配列を取得したい2 ** n。これまでのところ、2 ** nも別の引数として渡す必要があります。
アレオ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.