2017-05-16 4 views
5

現在、Ctypesを使用してSciPyをラップして最適化するFortran関数があります。これは可能ですか?おそらく私は私の実装で何か間違ったことをしました。例えば、私が持っていると仮定します。scipy.optimize.minimizeの結果がctypesを使用するFortran関数で使用されました

cost.f90私はコンパイル

module cost_fn 
    use iso_c_binding, only: c_float 
    implicit none 

contains 

    function sin_2_cos(x,y) bind(c) 
     real(c_float) :: x, y, sin_2_cos 
     sin_2_cos = sin(x)**2 * cos(y) 
    end function sin_2_cos 

end module cost_fn 

gfortran -fPIC -shared -g -o cost.so cost.f90 

し、その後で(ローカル)最小値を検索してみてください。

コストを.py

#!/usr/bin/env python 

from ctypes import * 
import numpy as np 
import scipy.optimize as sopt 

cost = cdll.LoadLibrary('./cost.so') 

cost.sin_2_cos.argtypes = [POINTER(c_float), POINTER(c_float)] 
cost.sin_2_cos.restype = c_float 

def f2(x): 
    return cost.sin_2_cos(c_float(x[0]), c_float(x[1])) 
    # return np.sin(x[0])**2 * np.cos(x[1]) 

# print(f2([1, 1])) 
# print(f2([0.5 * np.pi, np.pi])) 

print(sopt.minimize(f2, (1.0, 1.0), options={'disp': True}, tol=1e-8)) 

I ex極小f2(π/ 2、π)= -1とする。 cost.sin_2_cosの戻り値でf2を呼び出すと、(1、1)の最初の推測で「最小」が与えられます。 f2を "Python"の戻り値で呼び出すと、optimizeは正しい最小値を見つけます。

sin_2_cosをdimension(2)配列入力に再定義しようとしましたが、同様の動作が見られました。おそらく、私はsin_2_cosを最小化で直接呼び出す必要があります(しかし、引数にc_floatをどのように指定すればよいでしょうか)。どんな考えもありがとう!

編集:以下のコメントには、2つのコメント付きprint(f2(...))行が期待値を生成することに注意してください。したがって、私はFortran関数がPythonのf2関数を通して適切に呼び出されていると考えています。

+0

引数に 'value'属性を少なくとも追加する必要があります(他の問題もあります)。詳しくはhttp://www.fortran90.org/src/best-practicesを参照してください。html#using-ctypes –

+0

どうすればいいですか? f2への呼び出しのコメントを外すと正しく動作します。あなたがリストアップしたWebページは、なぜ 'value'がそこにあるのか、それ以前の 'iso_c_binding'の例でそれを使用していないことを明確にしていません。値を追加しても問題は解決しないことに注意してください。 –

+0

タイトルをより具体的なものに変更しました。私は「はい、できます」と仮定します。あなたに受け入れられる答えではありません。 –

答えて

9

Fortranコードでは、単精度浮動小数点値(つまり32ビット浮動小数点数)が使用されます。 (Cでは、floatは単精度であり、doubleは倍精度です)。scipy.optimize.minimize()が使用するデフォルトの方法は、関数の微分を近似するために有限差分を使用します。つまり、f'(x0)派生を推定するには、(f(x0+eps) - f(x0))/epsを計算します。ここで、epsはステップサイズです。有限差分を計算するために使用するデフォルトのステップサイズは約1.49e-08です。残念ながら、この値は単精度値の値1の間隔よりも小さくなります。したがって、最小化子がepsを1に加算すると、結果は1のままです。つまり、関数は同じ点で評価され、有限差分結果はこれは最小条件であるため、ソルバーはそれが完了したと判断します。

ソルバオプションepsは、有限差分ステップサイズを設定します。 1.19e-7よりも大きな値に設定します。たとえば、options={'disp': True, 'eps': 2e-6}を使用すると、解決策が得られます。

ところで、あなたはnumpy.finfo()を使用することによって、その値、1.19e-7を見つけることができます。

In [4]: np.finfo(np.float32(1.0)).eps 
Out[4]: 1.1920929e-07 

あなたはminimize()機能にオプションmethod='nelder-mead'を使用する場合は、また、溶液を得るでしょう。その方法は有限の違いに依存しません。 ctypes.c_doubleの代わりctypes.c_floatを使用するPythonコードを変更すると

module cost_fn 
    use iso_c_binding, only: c_double 
    implicit none 

contains 

    function sin_2_cos(x,y) bind(c) 
     real(c_double) :: x, y, sin_2_cos 
     sin_2_cos = sin(x)**2 * cos(y) 
    end function sin_2_cos 

end module cost_fn 

最後に、あなたは倍精度を使用するFortranコードを変換することができます。

+0

優れています。明確な説明と2つの代替ソリューションに感謝します。両方のアプローチが機能していることを確認でき、私はあなたのソリューションを受け入れました。 興味深いことに、私は[scipy.optimize.minimize documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize)を調べましたが、 'eps'引数を見つけることができなかったので、私はそれを原因として特定しませんでした。 –

+0

私は基本的な文書を見てもメソッド固有の文書には触れていないという前のコメントを追加しておくべきです。もし私がそうしたら、私はepsに出会ったでしょう。しかし、この詳細レベルに焦点を当てると、単に「最小化」を呼び出すという抽象化の一部が取り除かれます。将来の読者が私の間違い/勤勉さから学ぶことができたらと思っています。 –

関連する問題