传递布尔数组而不使用`f2py`进行复制?

Mat*_*ock 3 python fortran numpy f2py

如果我打算将一个布尔值 NumPy 数组传递给 Fortran 变量,应该如何输入它们f2py?我已经尝试了integer*1logical*1,但是这两个都表明该数组已被复制。

例如,如果我编译文件foo.f95,包含:

subroutine foo(x, n)
    logical*1 x(n)
    !f2py intent(in) x
    !f2py intent(hide), depend(x) :: n=shape(x,0)
    ...
end subroutine
Run Code Online (Sandbox Code Playgroud)

使用f2py -c -m foo foo.f90 -DF2PY_REPORT_ON_ARRAY_COPY=1并运行以下内容:

import numpy as np
import foo
x = np.random.randn(100) < 0
foo.foo(x)
Run Code Online (Sandbox Code Playgroud)

它打印

copied an array: size=100, elsize=1
Run Code Online (Sandbox Code Playgroud)

如果我更改logical*1integer*1. Fortran 文件中布尔数组的正确类型是什么,以便不复制数组?

请注意,这不是内存连续性的问题,因为数组是一维的——foo.foo(np.asfortranarray(x))打印相同的复制消息。

roy*_*vib 5

从一些实验(*),似乎是Python的/ f2py对待np.int8与兼容logical*1,同时,np.boolnp.bool8因某些原因。插入print *, "(fort) x = ", xfoo.f90 后,我们得到:

>>> foo.foo( np.array( [ True, False, False ], dtype=np.bool ) )
copied an array: size=3, elsize=1
 (fort) x =  T F F
>>> foo.foo( np.array( [ False, True, False ], dtype=np.bool8 ) )
copied an array: size=3, elsize=1
 (fort) x =  F T F
>>> foo.foo( np.array( [ False, False, True ], dtype=np.int8 ) ) # no copy
 (fort) x =  F F T
Run Code Online (Sandbox Code Playgroud)

因为TrueFalse只是简单地映射到 1 和 0,所以int8在 Python 端使用数组可能会很方便。


(*) 一些实验

在这里,我将 f2py 意图注释更改inout为查看是否可以从 Fortran 端修改数组。

foo.f90:

subroutine foo(x, n)
    use iso_c_binding
    implicit none
    integer n
    logical*1 x( n )
    ! logical x( n )
    ! logical(c_bool) x( n )

    !f2py intent(inout) x
    !f2py intent(hide), depend(x) :: n=shape(x,0)

    print *, "(fort) x = ", x
    print *, "(fort) sizeof(x(1)) = ", sizeof(x(1))
    print *, "(fort) resetting x(:) to true"
    x(:) = .true.
end subroutine
Run Code Online (Sandbox Code Playgroud)

测试.py:

import numpy as np
import foo

for T in [ np.bool, np.bool8,
           np.int,  np.int8,  np.int32,  np.int64,
           np.uint, np.uint8, np.uint32, np.uint64,
           np.dtype('b'), np.dtype('int8'), np.dtype('int32') ]:

    print( "-------------------------" )
    print( "dtype =", T )

    x = np.array( [ True, False, True ], dtype=T )
    print( "input x =", x )

    try:
        foo.foo( x )
        print( "output x =", x )
    except:
        print( "failed" )
Run Code Online (Sandbox Code Playgroud)

结果与logical*1

-------------------------
dtype = <class 'bool'>
input x = [ True False  True]
failed
-------------------------
dtype = <class 'numpy.bool_'>
input x = [ True False  True]
failed
-------------------------
dtype = <class 'int'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.int8'>
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [1 1 1]
-------------------------
dtype = <class 'numpy.int32'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.int64'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint64'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint8'>
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [1 1 1]
-------------------------
dtype = <class 'numpy.uint32'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint64'>
input x = [1 0 1]
failed
-------------------------
dtype = int8
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [1 1 1]
-------------------------
dtype = int8
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [1 1 1]
-------------------------
dtype = int32
input x = [1 0 1]
failed
Run Code Online (Sandbox Code Playgroud)

结果logical(默认类型):

-------------------------
dtype = <class 'bool'>
input x = [ True False  True]
failed
-------------------------
dtype = <class 'numpy.bool_'>
input x = [ True False  True]
failed
-------------------------
dtype = <class 'int'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.int8'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.int32'>
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     4
 (fort) resetting x(:) to true
output x = [1 1 1]
-------------------------
dtype = <class 'numpy.int64'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint64'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint8'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint32'>
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     4
 (fort) resetting x(:) to true
output x = [1 1 1]
-------------------------
dtype = <class 'numpy.uint64'>
input x = [1 0 1]
failed
-------------------------
dtype = int8
input x = [1 0 1]
failed
-------------------------
dtype = int8
input x = [1 0 1]
failed
-------------------------
dtype = int32
input x = [1 0 1]
 (fort) x =  T F T
 (fort) sizeof(x(1)) =                     4
 (fort) resetting x(:) to true
output x = [1 1 1]
Run Code Online (Sandbox Code Playgroud)

结果logical(c_bool)(通过iso_c_binding):

-------------------------
dtype = <class 'bool'>
input x = [ True False  True]
failed
-------------------------
dtype = <class 'numpy.bool_'>
input x = [ True False  True]
failed
-------------------------
dtype = <class 'int'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.int8'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.int32'>
input x = [1 0 1]
 (fort) x =  T F F
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [65793     0     1]
-------------------------
dtype = <class 'numpy.int64'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint64'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint8'>
input x = [1 0 1]
failed
-------------------------
dtype = <class 'numpy.uint32'>
input x = [1 0 1]
 (fort) x =  T F F
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [65793     0     1]
-------------------------
dtype = <class 'numpy.uint64'>
input x = [1 0 1]
failed
-------------------------
dtype = int8
input x = [1 0 1]
failed
-------------------------
dtype = int8
input x = [1 0 1]
failed
-------------------------
dtype = int32
input x = [1 0 1]
 (fort) x =  T F F
 (fort) sizeof(x(1)) =                     1
 (fort) resetting x(:) to true
output x = [65793     0     1]
Run Code Online (Sandbox Code Playgroud)

出于某种原因,这最后一个logical(c_bool)不适用于上述用法......(f2py 似乎认为logical(c_bool)是 4 个字节,而 gfortran 将其视为 1 个字节,所以有些事情是不一致的......)