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

问题描述

我想将一些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,incx,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

我如何实现自己想要的?

解决方法

感谢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,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/

现在的诀窍是用wrap_sswap替换每个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,y_rows) ! in :sswap_module:sswap_ext.f95
            fortranname wrap_sswap
            integer intent(in) :: n
            real dimension(x_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_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,y)
>>> x,y
(array([-0.,-1.,2.,3.],dtype=float32),array([ 0.,1.,-2.,-3.],dtype=float32))
,

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

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

!! mylib.f90

module mymod
contains

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

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

    call sswap_extern(n,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,optional
    Default: len(y)

>>> x,dtype=np.float32)
>>> mylib.mymod.sswap( 2,y )
>>> x
array([-0.,dtype=float32)
>>> y
array([ 0.,dtype=float32)