Spec-Zone.ru › NumPy 2.0

Обобщённый API универсальных функций

Существует общая потребность в циклическом проходе не только по функциям над скалярами, но и по функциям над векторами (или массивами). Эта концепция реализована в NumPy путём обобщения универсальных функций (ufunc). В обычных ufunc элементарная функция ограничена операциями по элементу, в то время как обобщённая версия (gufunc) поддерживает операции «подмассива» по «подмассиву». Библиотека векторов Perl PDL предоставляет аналогичную функциональность, и её термины используются далее.

Каждая обобщённая ufunc содержит информацию о том, какова «ядерная» размерность входов, а также соответствующая размерность выходов (у элементарных ufunc нулевая ядерная размерность). Список ядерных размерностей для всех аргументов называется «подписью» ufunc. Например, ufunc numpy.add имеет подпись (),()->() , определяющую два скалярных входа и один скалярный выход.

Другой пример — функция inner1d(a, b) с подписью (i),(i)->(). Она применяет внутреннее произведение по последней оси каждого входа, но сохраняет остальные индексы неизменными. Например, где a имеет форму (3, 5, N) и b имеет форму (5, N), она вернёт выходную форму (3,5). Подлежащая элементарная функция вызывается 3 * 5 раз. В подписи мы указываем одну ядерную размерность (i) для каждого входа и нулевую ядерную размерность () для выхода, так как она принимает два одномерных массива и возвращает скаляр. Используя то же имя i, мы указываем, что две соответствующие размерности должны иметь одинаковый размер.

Размерности за пределами ядерных размерностей называются «циклическими» размерностями. В приведённом примере это соответствует (3, 5).

Подпись определяет, как размерности каждого массива входа/выхода разбиваются на ядерные и циклические размерности:

  1. Каждая размерность в подписи сопоставляется с размерностью соответствующего входного массива, начиная с конца кортежа формы. Это ядерные размерности, и они должны присутствовать в массивах, иначе будет поднята ошибка.
  2. Ядерные размерности, назначенные одному и тому же имени в подписи (например, i в inner1d в (i),(i)->() ) должны иметь точно совпадающие размеры; вещание не выполняется.
  3. Ядерные размерности удаляются из всех входов, а оставшиеся размерности совместно вещаются, определяя циклические размерности.
  4. Форма каждого выхода определяется из циклических размерностей плюс ядерных размерностей выхода.

Обычно размер всех ядерных размерностей в выходе определяется размером ядерной размерности с тем же именем во входном массиве. Это не требование, и можно определить подпись, где имя появляется впервые в выходе, хотя при вызове такой функции необходимо соблюдать некоторые предосторожности. Примером будет функция euclidean_pdist(a), с подписью (n,d)->(p), которая, имея массив n d-мерных векторов, вычисляет все уникальные парные евклидовы расстояния между ними. Размерность выхода p должна поэтому быть равна n * (n - 1) / 2, но ответственность за передачу массива вывода нужного размера лежит на вызывающей стороне. Если размер ядерной размерности выхода не может быть определён из переданных входных или выходных массивов, будет поднята ошибка.

Примечание: До NumPy 1.10.0 были менее строгие проверки: отсутствующие ядерные размерности создавались путём добавления 1 в начало формы по мере необходимости, ядерные размерности с одинаковым именем вещались вместе, а неопределённые размерности создавались размером 1.

Определения

Элементарная функция

Каждая ufunc состоит из элементарной функции, которая выполняет наиболее базовую операцию над наименьшей частью аргументов массива (например, сложение двух чисел является наиболее базовой операцией при сложении двух массивов). Ufunc применяет элементарную функцию многократно к различным частям массивов. Вход/выход элементарных функций могут быть векторами; например, элементарная функция inner1d принимает два вектора в качестве входных данных.

Подпись

Подпись — это строка, описывающая размерности входов/выходов элементарной функции ufunc. Более подробные сведения см. в разделе ниже.

Ядерная размерность

Размерность каждого входа/выхода элементарной функции определяется его ядерными размерностями (нулевые ядерные размерности соответствуют скалярному входу/выходу). Ядерные размерности сопоставляются с последними размерностями массивов ввода/вывода.

Имя размерности

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

Индекс размерности

Индекс размерности — это целое число, представляющее имя размерности. Он перечисляет имена размерностей в порядке первого появления каждого имени в подписи.

Детали подписи

Подпись определяет «ядерную» размерность входных и выходных переменных, а также определяет сокращение размерностей. Подпись представлена строкой следующего формата:

  • Ядерные размерности каждого массива ввода или выхода представлены списком имён размерностей в скобках, (i_1,...,i_N); скалярный вход/выход обозначается (). Вместо i_1, i_2, и т. д. можно использовать любое допустимое имя Python-переменной.
  • Списки размерностей для различных аргументов разделяются ",". Аргументы ввода/вывода разделяются "->".
  • Если одно и то же имя размерности используется в нескольких местах, это накладывает требование о равенстве размеров соответствующих размерностей.

Формальная синтаксис подписей такова:

<Signature>            ::= <Input arguments> "->" <Output arguments>
<Input arguments>      ::= <Argument list>
<Output arguments>     ::= <Argument list>
<Argument list>        ::= nil | <Argument> | <Argument> "," <Argument list>
<Argument>             ::= "(" <Core dimension list> ")"
<Core dimension list>  ::= nil | <Core dimension> |
                           <Core dimension> "," <Core dimension list>
<Core dimension>       ::= <Dimension name> <Dimension modifier>
<Dimension name>       ::= valid Python variable name | valid integer
<Dimension modifier>   ::= nil | "?"

Примечания:

  1. Все кавычки для ясности.
  2. Неизменённые ядерные размерности, которые имеют одинаковое имя, должны иметь одинаковый размер. Каждое имя размерности, как правило, соответствует одному уровню циклических операций в реализации элементарной функции.
  3. Пробелы игнорируются.
  4. Целое число как имя размерности «замораживает» эту размерность на этом значении.
  5. Если имя размерности заканчивается модификатором «?», размерность является ядерной только в том случае, если она существует во всех входах и выходах, которые её используют; в противном случае она игнорируется (и заменяется размерностью размером 1 для элементарной функции).

Вот несколько примеров подписей:

имя

подпись

общее использование

add

(),()->()

бинарная ufunc

sum1d

(i)->()

сведение

inner1d

(i),(i)->()

умножение вектор-вектор

matmat

(m,n),(n,p)->(m,p)

умножение матриц

vecmat

(n),(n,p)->(p)

умножение вектор-матрица

matvec

(m,n),(n)->(m)

умножение матрица-вектор

matmul

(m?,n),(n,p?)->(m?,p?)

комбинация четырёх вышеперечисленных

outer_inner

(i,t),(j,t)->(i,j)

внутреннее произведение по последней размерности, внешнее произведение по предпоследней и цикл/вещание по остальным.

cross1d

(3),(3)->(3)

векторное произведение, где последняя размерность заморожена и должна быть 3

Последний — это пример замораживания ядерной размерности и может использоваться для повышения производительности ufunc

C-API для реализации элементарных функций

Текущий интерфейс остаётся неизменным, и PyUFunc_FromFuncAndData по-прежнему может использоваться для реализации (специализированных) ufunc, состоящих из элементарных скалярных функций.

Можно использовать PyUFunc_FromFuncAndDataAndSignature для объявления более общей ufunc. Список аргументов такой же, как PyUFunc_FromFuncAndData, с дополнительным аргументом, определяющим подпись как строку C.

Кроме того, функция обратного вызова имеет тот же тип, что и прежде, void (*foo)(char **args, intp *dimensions, intp *steps, void *func). При вызове args — это список длиной nargs , содержащий данные всех входных/выходных аргументов. Для скалярной элементарной функции steps также имеет длину nargs, обозначая сдвиги, используемые для аргументов. dimensions — указатель на единственное целое число, определяющее размер оси, по которой следует производить циклические операции.

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

Первые nargs элементы steps остаются такими же, как и для скалярных ufunc. Следующие элементы содержат сдвиги всех ядерных размерностей для всех аргументов в порядке.

Например, рассмотрим ufunc с подписью (i,j),(i)->(). В этом случае args будет содержать три указателя на данные массивов ввода/вывода a, b, c. Кроме того, dimensions будет [N, I, J] для определения размера N цикла и размеров I и J для ядерных размерностей i и j. Наконец, steps будет [a_N, b_N, c_N, a_i, a_j, b_i], содержащим все необходимые сдвиги.

© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/c-api/generalized-ufuncs.html

Spec-Zone.ru

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