Spec-Zone.ru › NumPy 1.14

Обобщённый 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 | <Dimension name> |
                           <Dimension name> "," <Core dimension list>
<Dimension name>       ::= valid Python variable name

Примечания:

  1. Все кавычки указаны для большей ясности.
  2. Основные размерности, имеющие одинаковое имя, должны иметь ровно одинаковый размер. Каждое имя размерности, как правило, соответствует одному уровню циклов в реализации элементарной функции.
  3. Пробелы игнорируются.

Вот несколько примеров сигнатур:

add (),()->()
inner1d (i),(i)->()
sum1d (i)->()
dot2d (m,n),(n,p)->(m,p) умножение матриц
outer_inner (i,t),(j,t)->(i,j) внутреннее произведение по последней размерности, внешнее по предпоследней, а цикл/вещание по остальным.

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–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.14.5/reference/c-api.generalized-ufuncs.html

Spec-Zone.ru

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