Spec-Zone.ru › NumPy 2.0

Примеры использования F2PY

Ниже приведены некоторые примеры использования F2PY. Этот список не является исчерпывающим, но может служить отправной точкой при упаковке собственного кода.

Примечание

Лучшее место для поиска примеров — трекер задач NumPy на сайте GitHub, или тесты для f2py. Некоторые другие примеры использования находятся в Раздел по сокращению boilerplate и шаблонам.

Руководство по F2PY: базовый модуль расширения

Создание исходного кода для базового модуля расширения

Рассмотрим следующую подпрограмму, содержащуюся в файле с именем add.f

C
      SUBROUTINE ZADD(A,B,C,N)
C
      DOUBLE COMPLEX A(*)
      DOUBLE COMPLEX B(*)
      DOUBLE COMPLEX C(*)
      INTEGER N
      DO 20 J = 1, N
         C(J) = A(J)+B(J)
 20   CONTINUE
      END

Эта процедура просто складывает элементы двух смежных массивов и помещает результат в третий. Память для всех трёх массивов должна быть предоставлена вызывающей процедурой. Очень базовый интерфейс к этой процедуре может быть автоматически сгенерирован с помощью f2py:

python -m numpy.f2py -m add add.f

Эта команда создаст модуль расширения с именем addmodule.c в текущем каталоге. Этот модуль расширения теперь можно скомпилировать и использовать из Python, как любой другой модуль расширения.

Создание скомпилированного модуля расширения

Вы также можете использовать f2py для компиляции add.f вместе с сгенерированным модулем расширения, оставив только файл расширения с разделяемой библиотекой, который можно импортировать в Python:

python -m numpy.f2py -c -m add add.f

Эта команда создаёт модуль расширения Python, совместимый с вашей платформой. Этот модуль затем можно импортировать из Python. Он будет содержать метод для каждой подпрограммы в add. Строка документации каждого метода содержит информацию о том, как можно вызвать метод модуля:

>>> import add
>>> print(add.zadd.__doc__)
zadd(a,b,c,n)

Wrapper for ``zadd``.

Parameters
----------
a : input rank-1 array('D') with bounds (*)
b : input rank-1 array('D') with bounds (*)
c : input rank-1 array('D') with bounds (*)
n : input int

Улучшение базового интерфейса

По умолчанию интерфейс представляет собой очень буквальный перевод Fortran-кода в Python. Аргументы Fortran-массивов преобразуются в NumPy-массивы, а целочисленный аргумент должен быть сопоставлен с C целым числом. Интерфейс попытается преобразовать все аргументы в их требуемые типы (и формы) и выдаст ошибку, если это не удастся. Однако, поскольку f2py ничего не знает о семантике аргументов (таким образом, C является выходным, а n должны соответствовать размерам массивов), злоупотребление этой функцией может привести к сбою Python. Например:

>>> add.zadd([1, 2, 3], [1, 2], [3, 4], 1000)

приведёт к сбою программы на большинстве систем. Под капотом списки преобразуются в массивы, но затем функция add получает указание циклически выходить за пределы выделенной памяти.

Для улучшения интерфейса f2py поддерживает директивы. Это достигается путём построения файла сигнатур. Обычно лучше начинать с интерфейсов, которые f2py создаёт в этом файле, что соответствует поведению по умолчанию. Чтобы f2py сгенерировал файл интерфейса, используйте опцию -h.

python -m numpy.f2py -h add.pyf -m add add.f

Эта команда создаёт файл add.pyf в текущем каталоге. Часть этого файла, соответствующая zadd, выглядит так:

subroutine zadd(a,b,c,n) ! in :add:add.f
   double complex dimension(*) :: a
   double complex dimension(*) :: b
   double complex dimension(*) :: c
   integer :: n
end subroutine zadd

Путем размещения директив intent и проверки кода интерфейс можно значительно улучшить, сделав метод Python-модуля более удобным в использовании и устойчивым к некорректным входным данным.

subroutine zadd(a,b,c,n) ! in :add:add.f
   double complex dimension(n) :: a
   double complex dimension(n) :: b
   double complex intent(out),dimension(n) :: c
   integer intent(hide),depend(a) :: n=len(a)
end subroutine zadd

Директива intent(out) используется для указания f2py, что c является переменной вывода и должна быть создана интерфейсом перед передачей в основной код. Директива intent(hide) указывает f2py не разрешать пользователю задавать переменную n, а вместо этого брать её из размера a. Директива depend( a ) необходима для того, чтобы указать f2py, что значение n зависит от входного a (чтобы он не пытался создать переменную n до создания переменной a).

После изменения add.pyf, новый файл Python-модуля можно сгенерировать, скомпилировав add.f и add.pyf:

python -m numpy.f2py -c add.pyf add.f

Строка документации нового интерфейса:

>>> import add
>>> print(add.zadd.__doc__)
c = zadd(a,b)

Wrapper for ``zadd``.

Parameters
----------
a : input rank-1 array('D') with bounds (n)
b : input rank-1 array('D') with bounds (n)

Returns
-------
c : rank-1 array('D') with bounds (n)

Теперь функцию можно вызывать гораздо более надёжным способом:

>>> add.zadd([1, 2, 3], [4, 5, 6])
array([5.+0.j, 7.+0.j, 9.+0.j])

Обратите внимание на автоматическое преобразование в правильный формат.

Вставка директив в исходный код Fortran

Надежный интерфейс из предыдущего раздела также можно автоматически сгенерировать, разместив директивы переменных в виде специальных комментариев в исходном коде Fortran.

Примечание

Для проектов, где Fortran-код активно развивается, это может быть предпочтительнее.

Таким образом, если исходный код изменён на:

C
      SUBROUTINE ZADD(A,B,C,N)
C
CF2PY INTENT(OUT) :: C
CF2PY INTENT(HIDE) :: N
CF2PY DOUBLE COMPLEX :: A(N)
CF2PY DOUBLE COMPLEX :: B(N)
CF2PY DOUBLE COMPLEX :: C(N)
      DOUBLE COMPLEX A(*)
      DOUBLE COMPLEX B(*)
      DOUBLE COMPLEX C(*)
      INTEGER N
      DO 20 J = 1, N
         C(J) = A(J) + B(J)
 20   CONTINUE
      END

то можно скомпилировать модуль расширения с помощью:

python -m numpy.f2py -c -m add add.f

Полученная сигнатура для функции add.zadd будет точно такой же, что и созданная ранее. Если исходный код содержал A(N) вместо A(*) и так далее с B и C, то почти тот же интерфейс можно получить, разместив комментарий INTENT(OUT) :: C в исходном коде. Единственное отличие заключается в том, что N будет необязательным входным значением, которое будет по умолчанию равно длине A.

Пример фильтрации

Этот пример демонстрирует функцию, которая фильтрует двумерный массив чисел с двойной точностью с помощью фиксированного фильтра усреднения. Преимущество использования Fortran для индексации многомерных массивов должно быть очевидно из этого примера.

C
      SUBROUTINE DFILTER2D(A,B,M,N)
C
      DOUBLE PRECISION A(M,N)
      DOUBLE PRECISION B(M,N)
      INTEGER N, M
CF2PY INTENT(OUT) :: B
CF2PY INTENT(HIDE) :: N
CF2PY INTENT(HIDE) :: M
      DO 20 I = 2,M-1
         DO 40 J = 2,N-1
            B(I,J) = A(I,J) +
     &           (A(I-1,J)+A(I+1,J) +
     &           A(I,J-1)+A(I,J+1) )*0.5D0 +
     &           (A(I-1,J-1) + A(I-1,J+1) +
     &           A(I+1,J-1) + A(I+1,J+1))*0.25D0
 40      CONTINUE
 20   CONTINUE
      END

Этот код можно скомпилировать и связать в модуль расширения с именем filter с помощью:

python -m numpy.f2py -c -m filter filter.f

Это создаст модуль расширения в текущем каталоге с методом dfilter2d, который возвращает отфильтрованную версию ввода.

Пример использования ключевого слова depends

Рассмотрим следующий код, сохранённый в файле myroutine.f90:

subroutine s(n, m, c, x)
	implicit none
  	integer, intent(in) :: n, m
  	real(kind=8), intent(out), dimension(n,m) :: x
  	real(kind=8), intent(in) :: c(:)

	x = 0.0d0
	x(1, 1) = c(1)

end subroutine s

Упаковывая это с помощью python -m numpy.f2py -c myroutine.f90 -m myroutine, мы можем сделать следующее в Python:

>>> import numpy as np
>>> import myroutine
>>> x = myroutine.s(2, 3, np.array([5, 6, 7]))
>>> x
array([[5., 0., 0.],
   [0., 0., 0.]])

Теперь, вместо прямого создания модуля расширения, мы сначала создадим файл сигнатур для этой подпрограммы. Это распространённый шаблон для многоступенчатой генерации модулей расширения. В данном случае, после выполнения

python -m numpy.f2py myroutine.f90 -m myroutine -h myroutine.pyf

генерируется следующий файл сигнатур:

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

python module myroutine ! in 
    interface  ! in :myroutine
        subroutine s(n,m,c,x) ! in :myroutine:myroutine.f90
            integer intent(in) :: n
            integer intent(in) :: m
            real(kind=8) dimension(:),intent(in) :: c
            real(kind=8) dimension(n,m),intent(out),depend(m,n) :: x
        end subroutine s
    end interface 
end python module myroutine

! This file was auto-generated with f2py (version:1.23.0.dev0+120.g4da01f42d).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e

Теперь, если мы выполним python -m numpy.f2py -c myroutine.pyf myroutine.f90 мы увидим ошибку; обратите внимание, что в файле сигнатур была директива depend(m,n) для x, которая не нужна. Действительно, отредактировав файл выше до

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

python module myroutine ! in 
    interface  ! in :myroutine
        subroutine s(n,m,c,x) ! in :myroutine:myroutine.f90
            integer intent(in) :: n
            integer intent(in) :: m
            real(kind=8) dimension(:),intent(in) :: c
            real(kind=8) dimension(n,m),intent(out) :: x
        end subroutine s
    end interface 
end python module myroutine

! This file was auto-generated with f2py (version:1.23.0.dev0+120.g4da01f42d).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e

и выполнив f2py -c myroutine.pyf myroutine.f90 получается правильный результат.

Дополнительная информация

  • Упаковка C-кода с помощью f2py
  • Раздел F2py в кулинарной книге SciPy
  • Пример F2py: Интерактивная система моделирования ледников
  • Раздел «Взаимодействие с другими языками» в кулинарной книге SciPy.

© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/f2py/f2py-examples.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API