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.
Представления, возвращаемые функцией einsum, теперь могут быть изменяемыми, когда входной массив изменяемый. Например,
np.einsum('ijk...->kji...', a)теперь будет иметь тот же эффект, что иnp.swapaxes(a, 0, 2), иnp.einsum('ii->i', a)вернёт изменяемое представление диагонали 2D-массива.Введено в версии 1.12.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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/generated/numpy.einsum.html