【问题标题】:Inconsistent result when using Fortran function on numpy array with F2PY在带有 F2PY 的 numpy 数组上使用 Fortran 函数时结果不一致
【发布时间】:2017-03-14 16:31:58
【问题描述】:

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

我写了三个不同版本的相同函数,我希望得到相同的结果:

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。

【问题讨论】:

  • 新手别学real(8)的坏习惯,人家哪来的?它没有出现在任何好的教科书中。
  • rsum1rsum3 应该完全相同。但也许 f2py 解释了其中一个错误。
  • 我可以复制它。当我在从 f2py 调用的 Fortran 子例程中调用它们时,结果是正确的。但即使在 Python 中,f.* 函数的报告签名也是正确的。
  • @VladimirF 关于符号 real(8),有人教给我。为什么这么糟糕?另外,我不明白:您在运行脚本时是否遇到同样的错误?
  • 是的,我遇到了同样的错误。 Real(8) 不可移植,它不能用某些编译器编译。见stackoverflow.com/documentation/fortran/939/data-types/4390/…stackoverflow.com/a/856243/721644

标签: python numpy fortran f2py


【解决方案1】:

简答和解决方法

问题的根本原因与在您的函数中使用假定形状的虚拟参数(即arr)有关。 Fortran 要求此类函数具有显式接口。 @VladimirF 对您的(相关?)问题here 给出了一个很好的答案,表明首选的解决方案是将函数放入模块中。假设您的函数代码列表保存在一个名为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 测试脚本中的 import 语句更新为 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

查看为 problematic 函数 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) 在原始示例中返回正确的结果,其结果具有正确的数据类型。幸运的是,rsum 有正确的接口。如果您将rsum 的名称更改为例如isum,其 F2PY 子例程包装器接口中的隐式数据类型规则将暗示它具有 integer 结果,并且您将从您的(修改以反映名称从 fsum 更改为 isum)得到以下输出python脚本:

0.0
0.0
3.0
3.0

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-13
    • 1970-01-01
    • 1970-01-01
    • 2014-09-12
    • 1970-01-01
    相关资源
    最近更新 更多