API обобщённых универсальных функций
Существует общая потребность в циклическом проходе не только по функциям над скалярами, но и по функциям над векторами (или массивами). Эта концепция реализована в NumPy путём обобщения универсальных функций (ufuncs). В обычных ufuncs базовая функция ограничена операциями по элементу, тогда как обобщённая версия (gufuncs) поддерживает операции «подмассива» по «подмассиву». Библиотека векторов Perl PDL предоставляет аналогичную функциональность, и её термины используются далее.
У каждой обобщённой ufunc есть информация, которая указывает, какова «основная» размерность входов, а также соответствующая размерность выходов (у элементных ufuncs размерность ядра равна нулю). Список основных размерностей для всех аргументов называется «сигнатурой» ufunc. Например, ufunc numpy.add имеет сигнатуру (),()->() , определяющую два скалярных входа и один скалярный выход.
Ещё один пример — функция inner1d(a, b) со сигнатурой (i),(i)->() . Она применяет скалярное произведение по последней оси каждого входа, но сохраняет остальные индексы неизменными. Например, когда a имеет форму (3, 5, N) , а b имеет форму (5, N) , это вернёт выходной массив формы (3,5) . Базовая функция вызывается 3 * 5 раз. В сигнатуре мы задаём одну основную размерность (i) для каждого входа и нулевую основную размерность () для выхода, так как она принимает два одномерных массива и возвращает скаляр. Используя то же имя i, мы указываем, что две соответствующие размерности должны иметь одинаковый размер.
Размерности, превышающие основные, называются «циклическими» размерностями. В приведённом выше примере это соответствует (3, 5).
Сигнатура определяет, как размерности каждого массива ввода/вывода разбиваются на основные и циклические размерности:
- Каждая размерность в сигнатуре сопоставляется с размерностью соответствующего переданного массива, начиная с конца кортежа формы. Это основные размерности, и они должны присутствовать в массивах, иначе произойдёт ошибка.
- Основные размерности, присвоенные одному и тому же имени в сигнатуре (например,
iвinner1dв(i),(i)->()), должны иметь точно совпадающие размеры; вещание не выполняется. - Основные размерности удаляются из всех входов, а оставшиеся размерности совместно транслируются, определяя циклические размерности.
- Форма каждого выхода определяется циклическими размерностями плюс основными размерностями выхода.
Как правило, размер всех основных размерностей в выходе определяется размером основной размерности с тем же именем в массиве ввода. Это не является требованием, и можно определить сигнатуру, в которой имя появляется впервые в выходе, хотя при вызове такой функции необходимо соблюдать некоторые меры предосторожности. Например, функция 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 для базовой функции).
Вот несколько примеров сигнатур:
имя | сигнатура | общее использование |
|---|---|---|
add |
| бинарная ufunc |
sum1d |
| редукция |
inner1d |
| умножение вектор-вектор |
matmat |
| умножение матриц |
vecmat |
| умножение вектор-матрица |
matvec |
| умножение матрица-вектор |
matmul |
| комбинация четырёх вышеперечисленных |
outer_inner |
| внутреннее произведение по последней размерности, внешнее произведение по предпоследней, циклическое/вещание по остальным. |
cross1d |
| векторное произведение, где последняя размерность заморожена и должна быть равна 3 |
Последний — это пример заморозки основной размерности и может использоваться для повышения производительности ufunc
C-API для реализации базовых функций
Текущий интерфейс остаётся неизменным, и PyUFunc_FromFuncAndData по-прежнему может использоваться для реализации (специализированных) ufuncs, состоящих из скалярных базовых функций.
Можно использовать PyUFunc_FromFuncAndDataAndSignature для объявления более общей ufunc. Список аргументов такой же, как PyUFunc_FromFuncAndData, с дополнительным аргументом, определяющим сигнатуру как строку C.
Кроме того, функция обратного вызова имеет тот же тип, что и раньше, void (*foo)(char **args, intp *dimensions, intp *steps, void *func). При вызове args — это список длины nargs , содержащий данные всех аргументов ввода/вывода. Для скалярной базовой функции steps также имеет длину nargs, обозначая смещения, используемые для аргументов. dimensions — указатель на единственное целое число, определяющее размер оси, по которой происходит циклическое прохождение.
Для нетривиальной сигнатуры dimensions также будет содержать размеры основных размерностей, начиная со второго элемента. Для каждого уникального имени размерности предоставляется только один размер, и размеры приводятся в соответствии с первым появлением имени размерности в сигнатуре.
Первые nargs элементов steps остаются такими же, как и для скалярных ufuncs. Следующие элементы содержат смещения всех основных размерностей для всех аргументов в порядке.
Например, рассмотрим 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–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.18/reference/c-api/generalized-ufuncs.html