使用 F2PY 在 numpy 数组上使用 Fortran 函数时结果不一致

Inconsistent result when using Fortran function on numpy array with F2PY

我正在尝试了解 F2PY 的工作原理。为此,我编写了一个简单的 Fortran 函数,它将一个数组作为输入,returns 数组元素的总和。

我写了相同函数的三个不同版本,我希望它们具有相同的结果:

function rsum(arr)
real, dimension(:), intent(in) :: arr
real :: rsum
rsum=sum(arr)
end function rsum

function rsum1(arr)
real(8), dimension(:), intent(in) :: arr
real(8) :: rsum1
rsum1=sum(arr)
end function rsum1

function rsum2(arr) result(s)
real, dimension(:), intent(in) :: arr
real :: s
s=sum(arr)
end function rsum2

function rsum3(arr) result(s)
real(8), dimension(:), intent(in) :: arr
real(8) :: s
s=sum(arr)
end function rsum3

我的 python 测试这些功能的脚本如下:

from numpy import *
import ftest as f

a=array(range(3))

print(f.rsum(a))
print(f.rsum1(a))
print(f.rsum2(a))
print(f.rsum3(a))

但结果是这样的:

3.0
0.0
3.0
3.0

所有结果都是正确的,除了 rsum1 中的一个 0.0。我发现更奇怪的是 rsum3,其中我只是更改函数结果的名称(或者,至少,我认为我正在这样做),效果很好!

我知道这与Fortran和numpy之间的类型转换有关,但我不明白是什么问题。

PS: 最近才学Fortran

简答和解决方法

问题的根本原因与在您的函数中使用假定形状伪参数(即 arr)有关。 Fortran 要求此类函数具有显式接口。 @VladimirF 对您的(相关的?)问题 给出了一个很好的答案,表明首选的解决方案是将函数放在一个模块中。假设您的带有函数的代码清单保存在一个名为 funcs.f90 的文件中,您可以简单地将它们放入一个模块中,例如叫做 mod_funcs.f90,像这样:

module mod_funcs
    implicit none
    contains
        include "funcs.f90"
end module mod_funcs

用 F2PY python -m numpy.f2py -m ftest -c mod_funcs.f90 包装它,将 python 测试脚本中的导入语句更新为 from ftest import mod_funcs as f 然后 运行 以获得预期结果:

3.0
3.0
3.0
3.0

更长的答案和解释

Fortran functions 被 F2PY 包裹在 subroutines 中。为了以符合 Fortran 标准的方式支持假定形状数组,F2PY 创建的子例程包装器包含 interfaces 用于用户定义的函数,这些函数具有假定形状虚拟参数。您可以通过在使用 F2PY 包装时指定带有 --build-dir 标志的构建目录来查看这些包装器,例如像这样:

python -m numpy.f2py --build-dir .\build -m ftest -c funcs.f90

查看为 有问题的 函数 rsum1 创建的包装器正在揭示(我正在逐字复制 ftest-f2pywrappers.f 并保持 F2PY 的缩进):

subroutine f2pywraprsum1 (rsum1f2pywrap, arr, f2py_arr_d0)
integer f2py_arr_d0
real(8) arr(f2py_arr_d0)
real(8) rsum1f2pywrap
interface
function rsum1(arr) 
    real(8), dimension(:),intent(in) :: arr
end function rsum1
end interface
rsum1f2pywrap = rsum1(arr)
end

请注意,由于 implicit data typing rulesrsum1interface 意味着具有 real 数据类型的函数, 不是 real(8) 符合预期 - 因此接口中的数据类型不匹配!这解释了为什么在您的原始示例中,带有显式 result 语句 (rsum3) return 的看似相同的函数是正确的结果,其结果具有正确的数据类型。幸运的是你的函数命名,rsum 有正确的接口。如果您将 rsum 的名称更改为例如isum,其 F2PY 子例程包装器接口中的隐式数据类型化规则将暗示它具有 integer 结果,您将从您的(已修改以反映 [=36= 的名称更改)中获得以下输出] 到 isum) python 脚本:

0.0
0.0
3.0
3.0

所以在我看来,F2PY 如何为具有假定形状虚拟参数的函数创建接口(可以通过将这些函数直接放入模块或通过显式声明 return 函数的值使用 result).

为了完整起见,我使用了 Python 3.6.3 :: Intel CorporationNumPy 1.14.3GNU Fortran (GCC) 8.2.0