Spec-Zone.ru › NumPy 2.0

Три способа обертки — начало работы

Обертка функций Fortran или C для Python с помощью F2PY состоит из следующих шагов:

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

    • По желанию, файлы сигнатур, созданные F2PY, можно редактировать для оптимизации оберточных функций, что может сделать их «умнее» и более «pythonic».
  • F2PY читает файл сигнатур и записывает модуль Python C/API, содержащий связи Fortran/C/Python.
  • F2PY компилирует все исходные файлы и создаёт модуль расширения, содержащий обертки.

    • При построении модулей расширения F2PY использует meson и раньше использовал numpy.distutils. Для различных систем сборки см. F2PY и системы сборки.

Примечание

См. 1 Миграция на meson для информации о миграции.

  • В зависимости от вашей операционной системы, вам может потребоваться отдельно установить заголовки Python разработки (которые предоставляют файл Python.h). В дистрибутивах Linux на основе Debian этот пакет должен называться python3-dev, в дистрибутивах на основе Fedora — python3-devel. Для macOS, в зависимости от того, как был установлен Python, результат может отличаться. В Windows заголовки обычно уже установлены, см. F2PY и Windows.

Примечание

F2PY поддерживает все операционные системы, на которых тестируется SciPy, поэтому их панель системных зависимостей является хорошей справкой.

В зависимости от ситуации, эти шаги можно выполнить в одной составной команде или поэтапно; в этом случае некоторые шаги можно опустить или объединить с другими.

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

Следующий пример кода Fortran 77 будет использован для иллюстрации, сохраните его как fib1.f:

C FILE: FIB1.F
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE FIB1.F

Примечание

F2PY анализирует сигнатуры Fortran/C для построения оберточных функций, которые будут использоваться с Python. Однако это не компилятор, и он не проверяет дополнительные ошибки в исходном коде, а также не реализует все стандарты языка. Некоторые ошибки могут пройти незамеченными (или как предупреждения) и должны быть проверены пользователем.

Быстрый способ

Самый быстрый способ обернуть подпрограмму Fortran FIB для использования в Python — это выполнить

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

или, в качестве альтернативы, если доступен инструмент командной строки f2py,

f2py -c fib1.f -m fib1

Примечание

Поскольку команда f2py может быть недоступна во всех системах, особенно в Windows, мы будем использовать команду python -m numpy.f2py в этом руководстве.

Эта команда компилирует и оборачивает fib1.f (-c) для создания модуля расширения fib1.so (-m) в текущем каталоге. Список параметров командной строки можно увидеть, выполнив python -m numpy.f2py. Теперь в Python подпрограмма Fortran FIB доступна через fib1.fib:

>>> import numpy as np
>>> import fib1
>>> print(fib1.fib.__doc__)
fib(a,[n])

Wrapper for ``fib``.

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

Other parameters
----------------
n : input int, optional
    Default: len(a)

>>> a = np.zeros(8, 'd')
>>> fib1.fib(a)
>>> print(a)
[  0.   1.   1.   2.   3.   5.   8.  13.]

Примечание

  • Обратите внимание, что F2PY распознал, что второй аргумент n является размерностью первого массива-аргумента a. Поскольку по умолчанию все аргументы являются только входными аргументами, F2PY заключает, что n может быть необязательным со значением по умолчанию len(a).
  • Можно использовать разные значения для необязательного n:

    >>> a1 = np.zeros(8, 'd')
    >>> fib1.fib(a1, 6)
    >>> print(a1)
    [ 0.  1.  1.  2.  3.  5.  0.  0.]
    

    но при несовместимости с входным массивом a возникает исключение:

    >>> fib1.fib(a, 10)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    fib.error: (len(a)>=n) failed for 1st keyword n: fib:n=10
    >>>
    

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

  • Когда массив NumPy, который является Fortran смежным и имеет dtype соответствующий предполагаемому типу Fortran, используется в качестве входного аргумента массива, тогда его указатель C напрямую передаётся в Fortran.

    В противном случае F2PY создаёт смежный копию (с соответствующей dtype) входного массива и передаёт указатель C копии в подпрограмму Fortran. В результате любые возможные изменения в (копии) входного массива не влияют на исходный аргумент, как показано ниже:

    >>> a = np.ones(8, 'i')
    >>> fib1.fib(a)
    >>> print(a)
    [1 1 1 1 1 1 1 1]
    

    Очевидно, это неожиданно, так как Fortran обычно передаёт по ссылке. То, что вышеприведенный пример работал с dtype=float считается случайностью.

    F2PY предоставляет атрибут intent(inplace), который изменяет атрибуты входного массива, так что любые изменения, внесённые процедурой Fortran, будут отражены в входном аргументе. Например, если указано директиву intent(inplace) a (см. Атрибуты для подробностей), то приведенный выше пример будет выглядеть так:

    >>> a = np.ones(8, 'i')
    >>> fib1.fib(a)
    >>> print(a)
    [  0.   1.   1.   2.   3.   5.   8.  13.]
    

    Однако рекомендуемый способ, чтобы изменения, внесённые процедурой Fortran, распространялись на Python, — это использование атрибута intent(out). Такой подход более эффективен и также более чистый.

  • Использование fib1.fib в Python очень похоже на использование FIB в Fortran. Однако использование in situ выходных аргументов в Python не рекомендуется, поскольку в Python нет механизмов защиты от неправильных типов аргументов. При использовании Fortran или C компиляторы обнаруживают любые несовпадения типов во время компиляции, но в Python типы должны проверяться во время выполнения. Следовательно, использование in situ выходных аргументов в Python может привести к трудно обнаруживаемым ошибкам, помимо того, что код будет менее читабельным при реализации всех необходимых проверок типов.

Хотя подход к обертыванию процедур Fortran для Python, обсуждаемый до сих пор, очень прост, у него есть несколько недостатков (см. комментарии выше). Недостатки обусловлены тем, что F2PY не может определить истинное намерение аргументов; то есть существует неоднозначность в различении входных и выходных аргументов. Следовательно, F2PY по умолчанию предполагает, что все аргументы являются входными аргументами.

Существуют способы (см. ниже), чтобы устранить эту неоднозначность, «обучив» F2PY истинному намерению аргументов функций, и F2PY затем способен генерировать более явные, удобные в использовании и менее подверженные ошибкам обертки для функций Fortran.

Умный способ

Если мы хотим иметь больший контроль над тем, как F2PY будет обрабатывать интерфейс с нашим кодом Fortran, мы можем применить шаги обертывания по одному.

  • Сначала мы создадим файл сигнатур из fib1.f, выполнив:

    python -m numpy.f2py fib1.f -m fib2 -h fib1.pyf
    

    Файл сигнатур сохраняется в fib1.pyf (см. флаг -h) и его содержимое показано ниже.

    !    -*- f90 -*-
    python module fib2 ! in 
        interface  ! in :fib2
            subroutine fib(a,n) ! in :fib2:fib1.f
                real*8 dimension(n) :: a
                integer optional,check(len(a)>=n),depend(a) :: n=len(a)
            end subroutine fib
        end interface 
    end python module fib2
    
    ! This file was auto-generated with f2py (version:2.28.198-1366).
    ! See http://cens.ioc.ee/projects/f2py2e/
    
  • Далее мы сообщим F2PY, что аргумент n является входным аргументом (используя атрибут intent(in)) и что результат, т. е. содержимое a после вызова функции Fortran FIB, должен возвращаться в Python (используя атрибут intent(out)). Кроме того, массив a должен быть создан динамически с размером, определяемым входным аргументом n (используя атрибут depend(n) для указания этой зависимости).

    Содержимое соответствующим образом изменённой версии fib1.pyf (сохранённой как fib2.pyf) таково:

    !    -*- f90 -*-
    python module fib2 
        interface
            subroutine fib(a,n)
                real*8 dimension(n),intent(out),depend(n) :: a
                integer intent(in) :: n
            end subroutine fib
        end interface 
    end python module fib2
    
  • Наконец, мы создаём модуль расширения с numpy.distutils, выполнив:

    python -m numpy.f2py -c fib2.pyf fib1.f
    

В Python:

>>> import fib2
>>> print(fib2.fib.__doc__)
a = fib(n)

Wrapper for ``fib``.

Parameters
----------
n : input int

Returns
-------
a : rank-1 array('d') with bounds (n)

>>> print(fib2.fib(8))
[  0.   1.   1.   2.   3.   5.   8.  13.]

Примечание

  • Подпись fib2.fib теперь более точно соответствует намерению подпрограммы Fortran FIB: учитывая число n, fib2.fib возвращает первые n числа Фибоначчи в виде массива NumPy. Новая сигнатура Python fib2.fib также исключает неожиданное поведение в fib1.fib.
  • Обратите внимание, что по умолчанию использование одного intent(out) также подразумевает intent(hide). Аргументы, для которых указан атрибут intent(hide) не будут отображаться в списке аргументов оберточной функции.

Для более подробной информации см. Файл сигнатур.

Быстрый и умный способ

«Умный» способ обертывания функций Fortran, описанный выше, подходит для обертывания (например, сторонних) кодов Fortran, для которых изменения исходных кодов нежелательны или даже невозможны.

Однако, если редактирование кода Fortran приемлемо, то в большинстве случаев можно пропустить генерацию промежуточного файла сигнатур. Специфические атрибуты F2PY можно вставить непосредственно в исходные коды Fortran с помощью директив F2PY. Директива F2PY состоит из специальных строк комментариев (начинающихся с Cf2py или !f2py, например), которые игнорируются компиляторами Fortran, но интерпретируются F2PY как обычные строки.

Рассмотрим изменённую версию предыдущего кода Fortran с директивами F2PY, сохранённую как fib3.f:

C FILE: FIB3.F
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
Cf2py intent(in) n
Cf2py intent(out) a
Cf2py depend(n) a
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE FIB3.F

Модуль расширения можно теперь создать одной командой:

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

Обратите внимание, что полученная обертка для FIB так же «умна» (однозначна), как и в предыдущем случае:

>>> import fib3
>>> print(fib3.fib.__doc__)
a = fib(n)

Wrapper for ``fib``.

Parameters
----------
n : input int

Returns
-------
a : rank-1 array('d') with bounds (n)

>>> print(fib3.fib(8))
[  0.   1.   1.   2.   3.   5.   8.  13.]

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

Spec-Zone.ru

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