numpy.einsum#
- numpy.einsum(индексы, *операнды, выход=None, dtype=None, порядок='K', приведение типов='safe', оптимизировать=False)[источник]#
Вычисляет соглашение суммирования Эйнштейна для операндов.
Используя соглашение суммирования Эйнштейна, многие распространенные многомерные, линейные алгебраические операции с массивами могут быть представлены простым способом. В неявный mode
einsumвычисляет эти значения.В явный mode,
einsumпредоставляет дополнительную гибкость для вычисления других операций с массивами, которые могут не считаться классическими операциями суммирования Эйнштейна, путём отключения или принудительного суммирования по указанным индексным меткам.См. примечания и примеры для уточнения.
- Параметры:
- индексыstr
Задаёт индексы для суммирования в виде разделённого запятыми списка меток индексов. Выполняется неявное (классическое суммирование Эйнштейна) вычисление, если явный индикатор ‘->’ не включён вместе с метками индексов точной формы вывода.
- операндысписок array_like
Это массивы для операции.
- выходndarray, необязательно
Если предоставлен, вычисление выполняется в этот массив.
- dtype{data-type, None}, необязательный
Если указан, принудительно использует указанный тип данных для вычислений. Обратите внимание, что вам, возможно, также придется указать более либеральный приведение типов параметр для разрешения преобразований. По умолчанию None.
- порядок{‘C’, ‘F’, ‘A’, ‘K’}, опционально
Управляет расположением памяти вывода. 'C' означает, что он должен быть C-смежным. 'F' означает, что он должен быть Fortran-смежным, 'A' означает, что он должен быть 'F', если все входы 'F', иначе 'C'. 'K' означает, что он должен быть как можно ближе к расположению входов, включая произвольно переставленные оси. По умолчанию 'K'.
- приведение типов{‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’}, опционально
Управляет тем, какие преобразования типов данных могут происходить. Установка этого значения в 'unsafe' не рекомендуется, так как это может негативно повлиять на накопления.
'no' означает, что типы данных не должны преобразовываться вообще.
'equiv' означает, что разрешены только изменения порядка байтов.
‘safe’ означает, что разрешены только преобразования, которые могут сохранить значения.
'same_kind' означает, что разрешены только безопасные преобразования или преобразования внутри одного типа, например, из float64 в float32.
‘unsafe’ означает, что могут быть выполнены любые преобразования данных.
По умолчанию 'safe'.
- оптимизировать{False, True, 'greedy', 'optimal'}, опционально
Определяет, должно ли происходить промежуточное оптимизация. Оптимизация не произойдет, если False, а True по умолчанию использует алгоритм 'жадный'. Также принимает явный список сокращений из
np.einsum_pathфункция. См.np.einsum_pathдля получения дополнительных сведений. По умолчанию False.
- Возвращает:
- выводndarray
Вычисление на основе соглашения о суммировании Эйнштейна.
Смотрите также
einsum_path,dot,inner,outer,tensordot,linalg.multi_doteinsumАналогичный подробный интерфейс предоставляется einops пакет для покрытия дополнительных операций: транспонирование, изменение формы/сглаживание, повторение/плитка, сжатие/расширение и редукции. The opt_einsum оптимизирует порядок свертки для выражений, подобных einsum, независимо от бэкенда.
Примечания
Соглашение суммирования Эйнштейна может использоваться для вычисления многих многомерных линейных алгебраических операций с массивами.
einsumпредоставляет краткий способ представления этих данных.Неисчерпывающий список этих операций, которые могут быть вычислены с помощью
einsum, показан ниже вместе с примерами:След массива,
numpy.trace.Возвращает диагональ,
numpy.diag.Суммирования по осям массива,
numpy.sum.Транспонирования и перестановки,
numpy.transpose.- Умножение матриц и скалярное произведение,
numpy.matmul
- Умножение матриц и скалярное произведение,
- Векторные внутренние и внешние произведения,
numpy.inner
- Векторные внутренние и внешние произведения,
- Трансляция, поэлементное и скалярное умножение,
Сжатия тензоров,
numpy.tensordot.- Цепочка операций с массивами, в эффективном порядке вычислений,
Строка подстрочных индексов представляет собой разделённый запятыми список меток индексов, где каждая метка относится к измерению соответствующего операнда. Всякий раз, когда метка повторяется, она суммируется, поэтому
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).В неявный режим(exception_class, ...)
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)ifaявляется одномерным массивом, иnp.einsum('ii->i', a)похож наnp.diag(a)ifaявляется квадратным двумерным массивом. Разница в том, что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вызовы с двумя параметрическими методами.Представления, возвращаемые einsum, теперь доступны для записи, когда входной массив доступен для записи. Например,
np.einsum('ijk...->kji...', a)теперь будет иметь тот же эффект, что иnp.swapaxes(a, 0, 2)иnp.einsum('ii->i', a)вернет доступное для записи представление диагонали 2D-массива.Добавлен
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(более быстрое приближение оптимального пути): ~160ms>>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy')
Мы можем преобразовать в массив numpy, используя
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)