从Python调用时,Fortran代码会提供错误的结果

时间:2016-09-18 10:42:18

标签: fortran f2py

为了提高财务问题的执行速度,我编写了Fortran中的核心数字部分,只在Python中进行文件访问等。我使用f2py编译,并调用子例程fit(见我帖子中的下方)

vec=num.num.fit(pfmat,lqmat,20,0.01,20,0.01,pfmat.shape[0],lqmat.shape[0])

我已经测试了Fortran代码,但是没有bug,但是,在用f2py编译并从Python调用它之后,输出是不可重现的。有时,运行将返回正确的结果(与Python中编程的相同算法相比),有时调用返回带有nan的向量,有时结果大约为10 ^ 300。目前我不知道如何确定根本原因,非常感谢任何帮助!

subroutine fit(pf,m,lq,n,ns,ds,nv,dv,alloc)
integer, intent(in) :: m
integer, intent(in) :: n
double precision, intent(in) :: pf(m,14)
double precision, intent(in) :: lq(n,14)
integer, intent(in) :: ns
double precision, intent(in) :: ds
integer, intent(in) :: nv
double precision, intent(in) :: dv
double precision :: vit(1,(2*ns+1)*(2*nv+1))
double precision :: ml((2*ns+1)*(2*nv+1),n+1)
double precision, intent(out) :: alloc(1,n+1)
double precision :: matinv(n+1,n+1)
double precision :: mat_post((2*ns+1)*(2*nv+1),n+1)
integer :: i,j,k !
double precision :: f, sig, lm, strike, d1, d2, v0

do i=1,m
  strike=pf(i,1)
  f=pf(i,3)
  lm=log(f/strike)
  sig=pf(i,8)+pf(i,9)*lm+pf(i,10)*lm**2+pf(i,11)*lm**3+pf(i,12)*lm**4+ &
      pf(i,13)*lm**5+pf(i,14)*abs(lm)
  d1=(lm+0.5*sig**2*pf(i,4))/(sig*sqrt(pf(i,4)))
  d2=d1-sig*sqrt(pf(i,4))
  if (pf(i,2)==0)then
    v0=pf(i,5)*pf(i,6)*pf(i,7)*(f*cnd(d1)-strike*cnd(d2))
  else
    v0=pf(i,5)*pf(i,6)*pf(i,7)*(strike*cnd(-d2)-f*cnd(-d1))
  end if
  ! loop over all gridpoints for (u/l shock and vol shock)
  do j=-ns,ns
    do k=-nv,nv
        f=pf(i,3)*(1+j*ds)
        lm=log(f/strike)
        sig=pf(i,8)+pf(i,9)*lm+pf(i,10)*lm**2+pf(i,11)*lm**3+pf(i,12)*lm**4+ &
            pf(i,13)*lm**5+pf(i,14)*abs(lm)
        sig=sig+pf(i,8)*k*dv
        d1=(lm+0.5*sig**2*pf(i,4))/(sig*sqrt(pf(i,4)))
        d2=d1-sig*sqrt(pf(i,4))
        if (pf(i,2)==0)then
          vit(1,(j+ns)*(2*nv+1)+k+nv+1)=vit(1,(j+ns)*(2*nv+1)+k+nv+1)+ &
            pf(i,5)*pf(i,6)*pf(i,7)*(f*cnd(d1)-strike*cnd(d2))-v0
        else
          vit(1,(j+ns)*(2*nv+1)+k+nv+1)=vit(1,(j+ns)*(2*nv+1)+k+nv+1)+ &
            pf(i,5)*pf(i,6)*pf(i,7)*(strike*cnd(-d2)-f*cnd(-d1))-v0
        end if
    end do
  end do
end do

do i=1,n
  strike=lq(i,1)
  f=lq(i,3)
  lm=log(f/strike)
  sig=lq(i,8)+lq(i,9)*lm+lq(i,10)*lm**2+lq(i,11)*lm**3+lq(i,12)*lm**4+ &
      lq(i,13)*lm**5+lq(i,14)*abs(lm)
  d1=(log(f/strike)+(sig**2/2.)*lq(i,4))/(sig*sqrt(lq(i,4)))
  d2=d1-sig*sqrt(lq(i,4))
  if (lq(i,2)==0)then
    v0=lq(i,5)*lq(i,6)*lq(i,7)*(f*cnd(d1)-strike*cnd(d2))
  else
    v0=lq(i,5)*lq(i,6)*lq(i,7)*(strike*cnd(-d2)-f*cnd(-d1))
  end if
  do j=-ns,ns
    do k=-nv,nv
      f=lq(i,3)*(1+j*ds)
      lm=log(f/strike)
      sig=lq(i,8)+lq(i,9)*lm+lq(i,10)*lm**2+lq(i,11)*lm**3+lq(i,12)*lm**4+ &
          lq(i,13)*lm**5+lq(i,14)*abs(lm)
      sig=sig+lq(i,8)*k*dv
      d1=(log(f/strike)+(sig**2/2.)*lq(i,4))/(sig*sqrt(lq(i,4)))
      d2=d1-sig*sqrt(lq(i,4))
      if (lq(i,2)==0)then
        ml((j+ns)*(2*nv+1)+k+nv+1,i)=lq(i,5)*lq(i,6)*(f*cnd(d1)-strike*cnd(d2))-v0
      else
        ml((j+ns)*(2*nv+1)+k+nv+1,i)=lq(i,5)*lq(i,6)*(strike*cnd(-d2)-f*cnd(-d1))-v0
      end if
    end do
  end do
end do

f=lq(1,3)
do j=-ns,ns
  do k=-nv,nv
    ml((j+ns)*(2*nv+1)+k+nv+1,n+1)=lq(1,6)*f*j*ds
  end do
end do

call inverse(matmul(transpose(ml),ml),matinv,n+1)
mat_post=matmul(ml,matinv)
alloc=matmul(vit,mat_post)

end subroutine

double precision function cnd(x)
! standard normal distribution for computing BS formula
double precision ,parameter :: dpi=3.141592653589793238
double precision           :: x
double precision           :: l, k
double precision           :: a1,a2,a3,a4,a5
a1=0.31938153
a2=-0.356563782
a3=1.781477937
a4=-1.821255978
a5=1.330274429
l=abs(x)
k=1./(1.+0.2316419*l)
cnd=1.-1./Sqrt(2.*dpi)*Exp(-l**2./2.)*(a1*k+a2*k**2.+a3*k**3.+a4*k**4.+a5*k**5.)
If (x<=0) then
  cnd=1.-cnd
End If
end function

subroutine inverse(a,c,n)
!============================================================
! Inverse matrix
! Method: Based on Doolittle LU factorization for Ax=b
! Alex G. December 2009
!-----------------------------------------------------------
! input ...
! a(n,n) - array of coefficients for matrix A
! n      - dimension
! output ...
! c(n,n) - inverse matrix of A
! comments ...
! the original matrix a(n,n) will be destroyed
! during the calculation
!===========================================================
implicit none
integer n
double precision a(n,n), c(n,n)
double precision L(n,n), U(n,n), b(n), d(n), x(n)
double precision coeff
integer i, j, k

! step 0: initialization for matrices L and U and b
! Fortran 90/95 aloows such operations on matrices
L=0.0
U=0.0
b=0.0

! step 1: forward elimination
do k=1, n-1
  do i=k+1,n
    coeff=a(i,k)/a(k,k)
    L(i,k) = coeff
    do j=k+1,n
       a(i,j) = a(i,j)-coeff*a(k,j)
    end do
  end do
end do

! Step 2: prepare L and U matrices
! L matrix is a matrix of the elimination coefficient
! + the diagonal elements are 1.0
do i=1,n
  L(i,i) = 1.0
end do
! U matrix is the upper triangular part of A
do j=1,n
  do i=1,j
    U(i,j) = a(i,j)
  end do
end do

! Step 3: compute columns of the inverse matrix C
do k=1,n
  b(k)=1.0
  d(1) = b(1)
  ! Step 3a: Solve Ld=b using the forward substitution
  do i=2,n
    d(i)=b(i)
    do j=1,i-1
      d(i) = d(i) - L(i,j)*d(j)
    end do
  end do
  ! Step 3b: Solve Ux=d using the back substitution
  x(n)=d(n)/U(n,n)
  do i = n-1,1,-1
    x(i) = d(i)
    do j=n,i+1,-1
      x(i)=x(i)-U(i,j)*x(j)
    end do
    x(i) = x(i)/u(i,i)
  end do
  ! Step 3c: fill the solutions x(n) into column k of C
  do i=1,n
    c(i,k) = x(i)
  end do
  b(k)=0.0
end do
end subroutine inverse

1 个答案:

答案 0 :(得分:2)

这绝不是一个解决方案,但是当你在FORTRAN中看到10e300的顺序(或者那些不可重复的结果)的值时,它通常意味着变量(或数组)的值等)未初始化。根据编译器设置等,FORTRAN中的声明变量接收默认随机值(在gfortran10e300的顺序中为10e-300。如果您除以较小的(10e-300或其左右),这可能会给您NaN(或错误,具体取决于编译器选项),因为10e-300将被视为0工作精度。

我的建议:在FORTRAN代码中添加一些打印语句,然后通过Python调用它。确保FORTRAN子例程中的所有声明变量都已初始化。 (从涉及任何类型划分的那些开始。)