Spec-Zone.ru › NumPy 1.15

Обобщённый 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'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 name> |
                           <Core dimension name> "," <Core dimension list>
<Core 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.15.4/reference/c-api.generalized-ufuncs.html

Spec-Zone.ru

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