使用 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 function
s 被 F2PY 包裹在 subroutine
s 中。为了以符合 Fortran 标准的方式支持假定形状数组,F2PY 创建的子例程包装器包含 interface
s 用于用户定义的函数,这些函数具有假定形状虚拟参数。您可以通过在使用 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 rules,rsum1
的 interface
意味着具有 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 Corporation
、NumPy 1.14.3
和 GNU Fortran (GCC) 8.2.0
。
我正在尝试了解 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 function
s 被 F2PY 包裹在 subroutine
s 中。为了以符合 Fortran 标准的方式支持假定形状数组,F2PY 创建的子例程包装器包含 interface
s 用于用户定义的函数,这些函数具有假定形状虚拟参数。您可以通过在使用 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 rules,rsum1
的 interface
意味着具有 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 Corporation
、NumPy 1.14.3
和 GNU Fortran (GCC) 8.2.0
。