Spec-Zone.ru › NumPy 2.0

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
einsum

Аналогичный подробный интерфейс предоставляет пакет einops для покрытия дополнительных операций: транспонирование, изменение формы/сглаживание, повторение/дублирование, сжатие/расширение и сокращения. 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), если a — это одномерный массив, и np.einsum('ii->i', a) аналогичен np.diag(a), если a — это квадратный 2D-массив. Разница в том, что einsum по умолчанию не поддерживает расширение. Кроме того, np.einsum('ij,jh->ih', a, b) непосредственно указывает порядок индексных меток выходных данных и поэтому возвращает произведение матриц, в отличие от примера выше в неявном режиме.

Для включения и управления расширением используйте эллипсис. Расширение по умолчанию в стиле NumPy выполняется путём добавления эллипса слева от каждого члена, как np.einsum('...ii->...i', a). np.einsum('...i->...', a) аналогично np.sum(a, axis=-1) для массива 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.

Возвращаемые из einsum представления теперь могут быть изменяемыми, когда входной массив изменяем. Например, np.einsum('ijk...->kji...', a) теперь будет иметь тот же эффект, что и np.swapaxes(a, 0, 2), а np.einsum('ii->i', a) вернёт изменяемое представление диагонали двумерного массива.

Новая функция в версии 1.12.0.

Добавлен аргумент optimize, который оптимизирует порядок сокращения в выражении einsum. Для сокращения с тремя или более операндами это может значительно повысить вычислительную эффективность за счёт увеличения потребления памяти во время вычислений.

Обычно применяется алгоритм «жадный» (greedy), который в большинстве случаев, по результатам эмпирических тестов, возвращает оптимальный путь. В некоторых случаях «оптимальный» (optimal) вернёт наилучший путь с помощью более дорогостоящего и полного поиска. Для итеративных вычислений может быть целесообразно вычислить оптимальный путь один раз и повторно использовать этот путь, передав его в качестве аргумента. Пример приведён ниже.

См. 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')

Жадный (greedy) вариант 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–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/generated/numpy.einsum.html

Spec-Zone.ru

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