Передать логический массив без копирования с помощью `f2py`?

Как следует вводить переменные Fortran, если я собираюсь передать им логический массив NumPy с f2py? Я пробовал оба integer*1 а также logical*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

с 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)

это печатает

copied an array: size=100, elsize=1

Я получаю тот же результат, если я изменяю logical*1 в integer*1, Каков правильный тип для логического массива в файле Fortran, чтобы массив не копировался?

Обратите внимание, что это не проблема смежности памяти, так как массивы являются 1D - foo.foo(np.asfortranarray(x)) печатает то же самое сообщение о копировании.

1 ответ

Решение

Из некоторых экспериментов (*) кажется, что Python/f2py обрабатывает np.int8 как совместимый с logical*1, в то время как np.bool или же np.bool8 не по какой-то причине. После вставки print *, "(fort) x = ", x в foo.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

Так как True а также False просто отображаются на 1 и 0, используя int8 массив на стороне Python может быть удобным.


(*) Некоторые эксперименты

Здесь я изменил комментарий намерения f2py на inout чтобы увидеть, можем ли мы изменить массив со стороны Фортрана.

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

test.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" )

Результаты с 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

Результаты с 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]

Результаты с 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]

По какой-то причине это последнее logical(c_bool) не работает с вышеуказанным использованием... (кажется, f2py рассмотреть logical(c_bool) как 4 байта, в то время как gfortran рассматривает его как 1 байт, так что что-то противоречиво...)

Другие вопросы по тегам