numpy.einsum
- numpy.einsum(subscripts, *operands, out=None, dtype=None, order='K', casting='safe', optimize=False)[source]
-
Вычисляет соглашение об эйнштейновской сумме по операндам.
Используя соглашение об эйнштейновской сумме, многие распространённые многомерные линейно-алгебраические операции над массивами могут быть представлены простым способом. В неявном режиме
einsumвычисляет эти значения.В явном режиме,
einsumобеспечивает большую гибкость для вычисления других операций над массивами, которые могут не считаться классическими операциями эйнштейновской суммы, отключив или принудительно суммируя по заданным подстрочным индексам.См. примечания и примеры для уточнения.
- Параметры
-
- subscriptsstr
-
Указывает индексы для суммирования в виде списка подстрочных индексов, разделённых запятыми. Выполняется неявное (классическое соглашение эйнштейновской суммы) вычисление, если явно не указан индикатор ‘->’ и подстрочные индексы точного выходного вида.
- operandslist of array_like
-
Это массивы для операции.
- outndarray, optional
-
Если указано, вычисление выполняется в этом массиве.
- dtype{data-type, None}, optional
-
Если указано, вычисление принудительно выполняется с заданным типом данных. Обратите внимание, что вам может потребоваться указать более универсальный
castingпараметр, чтобы разрешить преобразования. По умолчанию None. - order{‘C’, ‘F’, ‘A’, ‘K’}, optional
-
Управляет расположением памяти результата. ‘C’ означает, что он должен быть C-непрерывным. ‘F’ означает, что он должен быть Fortran-непрерывным, ‘A’ означает, что он должен быть ‘F’, если все входные данные являются ‘F’, в противном случае ‘C’. ‘K’ означает, что он должен быть максимально близким к расположению входных данных, включая произвольно переставленные оси. По умолчанию ‘K’.
- casting{‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’}, optional
-
Управляет тем, какой тип преобразования данных может произойти. Не рекомендуется устанавливать это в ‘unsafe’, так как это может отрицательно повлиять на накопления.
- ‘no’ означает, что типы данных не должны преобразовываться вообще.
- ‘equiv’ означает, что разрешены только изменения порядка байтов.
- ‘safe’ означает, что разрешены только преобразования, которые могут сохранить значения.
- ‘same_kind’ означает, что разрешены только безопасные преобразования или преобразования в пределах типа, например, float64 в float32.
- ‘unsafe’ означает, что любые преобразования данных могут быть выполнены.
По умолчанию ‘safe’.
- optimize{False, True, ‘greedy’, ‘optimal’}, optional
-
Управляет тем, должна ли выполняться промежуточная оптимизация. Оптимизация не будет выполнена, если False, а True по умолчанию использует алгоритм ‘greedy’. Также принимает явный список сокращений из функции
np.einsum_path. См.np.einsum_pathдля получения дополнительной информации. По умолчанию False.
- Возвращаемые значения
-
- outputndarray
-
Вычисление на основе соглашения об эйнштейновской сумме.
См. также
-
einsum_path,dot,inner,outer,tensordot,linalg.multi_dot einops-
аналогичный подробный интерфейс предоставляется пакетом einops для покрытия дополнительных операций: транспонирование, преобразование формы/сглаживание, повторение/мозаика, сжатие/расширение и вычисление.
opt_einsum-
opt_einsum оптимизирует порядок сокращения для выражений, похожих на einsum, способом, независимым от бэкенда.
Примечания
Новое в версии 1.6.0.
Соглашение об эйнштейновской сумме может быть использовано для вычисления многих многомерных линейно-алгебраических операций над массивами.
einsumпредоставляет лаконичный способ представления этих операций.Ниже приведен неисчерпывающий список этих операций, которые могут быть вычислены с помощью
einsum, вместе с примерами:- След массива,
numpy.trace. - Возвращение диагонали,
numpy.diag. - Суммирование по осям массива,
numpy.sum. - Транспонирование и перестановки,
numpy.transpose. - Умножение матриц и скалярное произведение,
numpy.matmulnumpy.dot. - Векторные внутренние и внешние произведения,
numpy.innernumpy.outer. - Распространение, поэлементное и скалярное умножение,
numpy.multiply. - Сжатие тензоров,
numpy.tensordot. - Цепочечные операции над массивами в эффективном порядке вычислений,
numpy.einsum_path.
Строка подстрочных индексов — это список подстрочных индексов, разделённых запятыми, где каждый индекс относится к размерности соответствующего операнда. Если индекс повторяется, он суммируется, поэтому
np.einsum('i,i', a, b)эквивалентноnp.inner(a,b). Если индекс встречается только один раз, он не суммируется, поэтомуnp.einsum('i', a)создаёт представлениеaбез изменений. Дополнительный примерnp.einsum('ij,jk', a, b)описывает традиционное умножение матриц и эквивалентноnp.matmul(a,b). Повторные индексы в одном операнде соответствуют диагонали. Например,np.einsum('ii', a)эквивалентноnp.trace(a).В неявном режиме выбранные индексы важны, так как оси результата упорядочены по алфавиту. Это означает, что
np.einsum('ij', a)не влияет на 2D массив, аnp.einsum('ji', a)транспонирует его. Кроме того,np.einsum('ij,jk', a, b)возвращает результат умножения матриц, в то время какnp.einsum('ij,jh', a, b)возвращает транспонированное умножение, так как подстрочный индекс ‘h’ предшествует подстрочному индексу ‘i’.В явном режиме результат может быть непосредственно контролируемым путём указания подстрочных индексов результата. Это требует идентификатора ‘->’, а также списка подстрочных индексов результата. Эта функция увеличивает гибкость функции, так как суммирование может быть отключено или принудительно установлено при необходимости. Вызов
np.einsum('i->', a)похож наnp.sum(a, axis=-1), аnp.einsum('ii->i', a)похож наnp.diag(a). Разница заключается в том, чтоeinsumпо умолчанию не допускает распространение. Кроме того,np.einsum('ij,jh->ih', a, b)напрямую указывает порядок подстрочных индексов результата и, следовательно, возвращает результат умножения матриц, в отличие от примера выше в неявном режиме.Для включения и управления распространением используйте эллипсис. Распространение по умолчанию в стиле NumPy выполняется путём добавления эллипса слева от каждого элемента, например,
np.einsum('...ii->...i', a). Для нахождения следа по первой и последней осям можно выполнитьnp.einsum('i...i', a), а для выполнения произведения матриц-матриц с левыми индексами вместо правых можно выполнитьnp.einsum('ij...,jk...->ik...', a, b).Когда существует только один операнд, ни одна ось не суммируется, и не предоставляется параметр вывода, вместо нового массива возвращается представление в операнде. Таким образом, вычисление диагонали как
np.einsum('ii->i', a)даёт представление (изменено в версии 1.10.0).einsumтакже предоставляет альтернативный способ предоставления подстрочных индексов и операндов в видеeinsum(op0, sublist0, op1, sublist1, ..., [sublistout]). Если форма результата не предоставлена в этом формате,einsumбудет вычисляться в неявном режиме, в противном случае оно будет выполнено явно. Примеры ниже соответствуют вызовамeinsumс методами с двумя параметрами.Новое в версии 1.10.0.
Добавлен аргумент
optimize, который оптимизирует порядок сокращения выражения einsum. Для сокращения с тремя или более операндами это может значительно повысить вычислительную эффективность за счёт большего объёма памяти во время вычислений.
Обычно используется алгоритм «жадный», который, как показали эмпирические тесты, возвращает оптимальный путь в большинстве случаев. В некоторых случаях «оптимальный» вернет превосходный путь, используя более дорогостоящий, исчерпывающий поиск. Для итерационных вычислений может быть целесообразно вычислить оптимальный путь один раз и повторно использовать этот путь, передавая его в качестве аргумента. Ниже приведен пример.
См.
numpy.einsum_pathдля получения более подробной информации.Примеры
>>> a = np.arange(25).reshape(5,5) >>> b = np.arange(5) >>> c = np.arange(6).reshape(2,3)
След матрицы:
>>> np.einsum('ii', a) 60 >>> np.einsum(a, [0,0]) 60 >>> np.trace(a) 60Извлечь диагональ (требуется явная форма):
>>> np.einsum('ii->i', a) array([ 0, 6, 12, 18, 24]) >>> np.einsum(a, [0,0], [0]) array([ 0, 6, 12, 18, 24]) >>> np.diag(a) array([ 0, 6, 12, 18, 24])Суммирование по оси (требуется явная форма):
>>> np.einsum('ij->i', a) array([ 10, 35, 60, 85, 110]) >>> np.einsum(a, [0,1], [0]) array([ 10, 35, 60, 85, 110]) >>> np.sum(a, axis=1) array([ 10, 35, 60, 85, 110])Для многомерных массивов суммирование по одной оси может быть выполнено с помощью эллипса:
>>> np.einsum('...j->...', a) array([ 10, 35, 60, 85, 110]) >>> np.einsum(a, [Ellipsis,1], [Ellipsis]) array([ 10, 35, 60, 85, 110])Вычислить транспонирование матрицы или переупорядочить любое количество осей:
>>> np.einsum('ji', c) array([[0, 3], [1, 4], [2, 5]]) >>> np.einsum('ij->ji', c) array([[0, 3], [1, 4], [2, 5]]) >>> np.einsum(c, [1,0]) array([[0, 3], [1, 4], [2, 5]]) >>> np.transpose(c) array([[0, 3], [1, 4], [2, 5]])Внутренние произведения векторов:
>>> np.einsum('i,i', b, b) 30 >>> np.einsum(b, [0], b, [0]) 30 >>> np.inner(b,b) 30Умножение матрицы на вектор:
>>> np.einsum('ij,j', a, b) array([ 30, 80, 130, 180, 230]) >>> np.einsum(a, [0,1], b, [1]) array([ 30, 80, 130, 180, 230]) >>> np.dot(a, b) array([ 30, 80, 130, 180, 230]) >>> np.einsum('...j,j', a, b) array([ 30, 80, 130, 180, 230])Расширение и скалярное умножение:
>>> np.einsum('..., ...', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.einsum(',ij', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.einsum(3, [Ellipsis], c, [Ellipsis]) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.multiply(3, c) array([[ 0, 3, 6], [ 9, 12, 15]])Внешнее произведение векторов:
>>> np.einsum('i,j', np.arange(2)+1, b) array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) >>> np.einsum(np.arange(2)+1, [0], b, [1]) array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) >>> np.outer(np.arange(2)+1, b) array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]])Сжатие тензора:
>>> a = np.arange(60.).reshape(3,4,5) >>> b = np.arange(24.).reshape(4,3,2) >>> np.einsum('ijk,jil->kl', a, b) array([[4400., 4730.], [4532., 4874.], [4664., 5018.], [4796., 5162.], [4928., 5306.]]) >>> np.einsum(a, [0,1,2], b, [1,0,3], [2,3]) array([[4400., 4730.], [4532., 4874.], [4664., 5018.], [4796., 5162.], [4928., 5306.]]) >>> np.tensordot(a,b, axes=([1,0],[0,1])) array([[4400., 4730.], [4532., 4874.], [4664., 5018.], [4796., 5162.], [4928., 5306.]])Возвращаемые массивы с возможностью записи (с версии 1.10.0):
>>> a = np.zeros((3, 3)) >>> np.einsum('ii->i', a)[:] = 1 >>> a array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])Пример использования эллипса:
>>> a = np.arange(6).reshape((3,2)) >>> b = np.arange(12).reshape((4,3)) >>> np.einsum('ki,jk->ij', a, b) array([[10, 28, 46, 64], [13, 40, 67, 94]]) >>> np.einsum('ki,...k->i...', a, b) array([[10, 28, 46, 64], [13, 40, 67, 94]]) >>> np.einsum('k...,jk', a, b) array([[10, 28, 46, 64], [13, 40, 67, 94]])Цепные операции над массивами. Для более сложных сокращений скорость может быть увеличена путем многократного вычисления «жадного» пути или предварительного вычисления «оптимального» пути и многократного применения его с помощью вставки
einsum_path(с версии 1.12.0). Повышение производительности может быть особенно значительным для больших массивов:>>> a = np.ones(64).reshape(2,4,8)
Базовая
einsum: ~1520мс (измерялось на Intel i5 3,1 ГГц).>>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a)Неоптимальная
einsum(из-за времени многократного вычисления пути): ~330мс>>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')Жадный
einsum(более быстрое приближение оптимального пути): ~160мс>>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy')Оптимальный
einsum(лучшая схема использования в некоторых случаях): ~110мс>>> path = np.einsum_path('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')[0] >>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize=path)
© 2005–2022 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.21/reference/generated/numpy.einsum.html