Spec-Zone.ru › NumPy 1.21

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.matmul numpy.dot.
  • Векторные внутренние и внешние произведения, numpy.inner numpy.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

Spec-Zone.ru

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