问题描述
我想将一些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)