Spec-Zone.ru › NumPy 1.21

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

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

Каждая обобщенная 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’s (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. Если имя дополнено модификатором «?» (question mark), размерность является основной размерностью только в том случае, если она существует во всех входных и выходных значениях, которые ее разделяют; в противном случае она игнорируется (и заменяется размерностью размером 1 для элементарной функции).

Ниже приведены некоторые примеры подписей:

name

подпись

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

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] для определения размера цикла и размеров I и J для основных размерностей i и j. Наконец, steps будет [a_N, b_N, c_N, a_i, a_j, b_i], содержащим все необходимые шаги.

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

Spec-Zone.ru

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