如何定义与外部子例程同名的Fortran子例程?

2024-10-03 23:18:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我想将一些BLAS函数包装到Python模块中。例如,为了包装函数sswap,我创建了一个包含内容的.f95文件

subroutine wrap_sswap(n, x, y, x_rows, y_rows)
    implicit none

    integer, intent(in) :: n
    real, intent(inout) :: x(x_rows), y(y_rows)

    integer :: x_rows, y_rows, incx = 1, incy = 1

    external :: sswap

    call sswap(n, x, incx, y, incy)
end subroutine wrap_sswap

然后我使用numpy.f2py创建一个python模块my_module,以便在python中调用my_module.wrap_sswap()。这很有效。但是,我希望能够调用my_module.sswap(),也就是说,我希望将子例程sswap命名为与外部BLAS函数sswap相同的名称。这是不可能的,并导致错误EXTERNAL attribute conflicts with SUBROUTINE attribute

我怎样才能实现我想要的


Tags: 模块函数内容myattributeintegerblasrows
2条回答

(仅用于记录可能的第二种方法:-)

虽然不是严格可移植的,但另一种方法可能是使用bind(C,name="sswap_"),假定C导出的名称为sswap_(在libblas.a等中)。例如

!! mylib.f90

module mymod
contains

subroutine sswap(n, x, y, x_rows, y_rows)
    implicit none
    integer, intent(in) :: n, x_rows, y_rows
    real, intent(inout) :: x(x_rows), y(y_rows)

    integer, save :: incx = 1, incy = 1
    interface
        subroutine sswap_extern(n, x, incx, y, incy) bind(C,name="sswap_")
            integer :: n, incx, incy
            real    :: x(n), y(n)
        end
    end interface

    call sswap_extern(n, x, incx, y, incy)
end

end module

我们像往常一样使用f2py:

$ python3.8 -m numpy.f2py -c mylib.f90 -m mylib
or
$ python3.8 -m numpy.f2py -c mylib.f90 -m mylib -L/usr/local/Cellar/openblas/0.3.10_1/lib -lopenblas   # on Mac + Homebrew

那么

$ python3.8
>>> import mylib
>>> print( mylib.mymod.sswap.__doc__ )
sswap(n,x,y,[x_rows,y_rows])

Wrapper for ``sswap``.

Parameters
     
n : input int
x : in/output rank-1 array('f') with bounds (x_rows)
y : in/output rank-1 array('f') with bounds (y_rows)

Other Parameters
        
x_rows : input int, optional
    Default: len(x)
y_rows : input int, optional
    Default: len(y)

>>> x, y = np.arange(4, dtype=np.float32), -np.arange(4, dtype=np.float32)
>>> mylib.mymod.sswap( 2, x, y )
>>> x
array([-0., -1.,  2.,  3.], dtype=float32)
>>> y
array([ 0.,  1., -2., -3.], dtype=float32)

多亏了roygvib,我们现在可以提供一个工作示例。它基于更改f2py签名文件的建议

假设上述问题中的代码保存在sswap_ext.f95中。第一步是生成签名文件:

f2py sswap_ext.f95 -m sswap_module -h sswap_ext.pyf

在我的例子中,生成的签名文件sswap_ext.pyf具有以下内容:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module sswap_module ! in 
    interface  ! in :sswap_module
        subroutine wrap_sswap(n,x,y,x_rows,y_rows) ! in :sswap_module:sswap_ext.f95
            integer intent(in) :: n
            real dimension(x_rows),intent(inout) :: x
            real dimension(y_rows),intent(inout) :: y
            integer, optional,check(len(x)>=x_rows),depend(x) :: x_rows=len(x)
            integer, optional,check(len(y)>=y_rows),depend(y) :: y_rows=len(y)
        end subroutine wrap_sswap
    end interface 
end python module sswap_module

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

现在的诀窍是用sswap替换每个wrap_sswap,并添加fortranname wrap_sswap,如下所示:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module sswap_module ! in 
    interface  ! in :sswap_module
        subroutine sswap(n,x,y,x_rows,y_rows) ! in :sswap_module:sswap_ext.f95
            fortranname wrap_sswap
            integer intent(in) :: n
            real dimension(x_rows),intent(inout) :: x
            real dimension(y_rows),intent(inout) :: y
            integer, optional,check(len(x)>=x_rows),depend(x) :: x_rows=len(x)
            integer, optional,check(len(y)>=y_rows),depend(y) :: y_rows=len(y)
        end subroutine sswap
    end interface 
end python module sswap_module

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

现在我们可以编译:

f2py -c sswap_ext.pyf sswap_ext.f95 /path/to/blas.a

然后,我们可以在Python中使用sswap_module.sswap

>>> import sswap_module
>>> print(sswap_module.sswap.__doc__)
sswap(n,x,y,[x_rows,y_rows])

Wrapper for ``sswap``.

Parameters
     
n : input int
x : in/output rank-1 array('f') with bounds (x_rows)
y : in/output rank-1 array('f') with bounds (y_rows)

Other Parameters
        
x_rows : input int, optional
    Default: len(x)
y_rows : input int, optional
    Default: len(y)

>>> import numpy as np
>>> x, y = np.arange(4, dtype=np.float32), -np.arange(4, dtype=np.float32)
>>> sswap_module.sswap(2, x, y)
>>> x, y
(array([-0., -1.,  2.,  3.], dtype=float32), array([ 0.,  1., -2., -3.], dtype=float32))

相关问题 更多 >