2017-05-16 1 views
5

현재 Ctypes를 사용하여 SciPy를 사용하여 최적화하려는 Fortran 함수가 있습니다. 이것이 가능한가? 아마도 구현에서 잘못된 점을 발견했을 것입니다. 예를 들어, 가정 내가 가진 :ctypes를 사용하는 Fortran 함수에서 scipy.optimize.minimize의 결과가 잘못되었습니다.

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)) 

1 ex 국부 최소값 f2 (pi/2, pi) = -1을 구한다. cost.sin_2_cos 반환 값을 사용하여 f2를 호출하면 "minimimum"이 (1,1)의 초기 추측에서 제공됩니다. "파이썬"반환 값을 사용하여 f2를 호출하면 optimize가 올바른 최소값을 찾습니다.

차원 (2) 배열 입력을 가져 오기 위해 sin_2_cos를 재정의하려고 시도했지만 비슷한 동작이 나타납니다. 아마 내가 minim_2로 직접 sin_2_cos를 호출 할 필요가있다. (그렇다면 인수에 대해 어떻게 c_float를 지정 하겠는가?) 어떤 생각이라도 감사드립니다!

편집 : 아래 주석에 두 개의 주석이 달린 print(f2(...)) 줄이 예상 값을 생성합니다. 따라서 Fortran 함수가 Python f2 함수를 통해 제대로 호출되고 있다고 생각합니다.

+0

인수에 'value'속성을 추가해야합니다 (다른 문제가있을 수 있음). http://www.fortran90.org/src/best-practices를 참조하십시오.html # using-ctypes –

+0

왜해야합니까? f2에 대한 호출의 주석을 제거하면 제대로 작동합니다. 여러분이 열거 한 웹 페이지는 'value'가 왜 존재하는지 명확하게하지 않으며 이전의'iso_c_binding' 예제에서 사용하지도 않습니다. 값을 추가한다고해서 문제가 해결되지는 않습니다. –

+0

제목을 좀 더 구체적으로 변경했습니다. 나는 "그렇습니다."라고 가정합니다. 너에게 받아 들일 수있는 대답은 아니 겠지. –

답변

9

포트란 코드는 단 정밀도 부동 소수점 값 (즉, 32 비트 부동 소수점 숫자)을 사용합니다. (C에서는 float은 단 정밀도이며 double은 배정 밀도입니다.) scipy.optimize.minimize()이 사용하는 기본 방법은 유한 차분을 사용하여 함수의 미분을 근사합니다. 즉, 도함수 f'(x0)을 계산하려면 (f(x0+eps) - f(x0))/eps을 계산합니다. 여기서 eps은 단계 크기입니다. 유한 차이를 계산하는 데 사용하는 기본 단계 크기는 약 1.49e-08입니다. 불행히도,이 값은 값 1 주위의 단 정밀도 간격보다 작습니다. 따라서 최소화 기가 eps을 1에 추가하면 결과는 여전히 1입니다. 즉, 함수가 같은 지점에서 평가되고 유한 차이 결과가 0입니다. 최소 조건이므로 해석자가 결정합니다.

솔버 옵션 eps은 유한 차이 단계 크기를 설정합니다. 1.19e-7보다 큰 값으로 설정하십시오. 예를 들어 options={'disp': True, 'eps': 2e-6}을 사용하면 해결책을 얻습니다.

그런데

, 당신은 찾을 수 값 1.19e-7, numpy.finfo()를 사용하여 : 당신이 minimize() 기능의 옵션 method='nelder-mead'을 사용하는 경우

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

또한 해결책을 얻을 것이다. 이 방법은 유한 차이점에 의존하지 않습니다.

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 

그런 다음 ctypes.c_double 대신 ctypes.c_float 사용하는 파이썬 코드를 변경 :

마지막으로, 당신은 포트란 코드를 배정 밀도를 사용하여 변환 할 수 있습니다.

+0

우수, 명확한 설명과 두 가지 대안 솔루션을 제공해 주셔서 감사합니다. 두 가지 접근법이 모두 작동하며 귀하의 솔루션을 수락했음을 확인할 수 있습니다. 흥미롭게도 나는 [scipy.optimize.minimize 문서] (https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize)를 보았지만 'eps' 논쟁을 찾을 수 없었기 때문에 그 사실을 원인으로 파악하지 못했습니다. –

+0

기본 문서를 보았지만 메서드 관련 설명서는 다루지 않았다는 이전 설명을 추가해야합니다. 내가 그렇게했다면, 나는'eps'를 보았을 것이다. 그러나이 세부적인 수준에 초점을두면 단지 '최소화'라고하는 추상화의 일부가 제거됩니다. 미래의 독자들이 내 실수/부지런함에서 배울 수 있기를 바랍니다. –

관련 문제