Самый быстрый способ умножения массивов матриц в Python (numpy) - PullRequest
4 голосов
/ 07 сентября 2011

У меня есть два массива комплексных матриц 2 на 2, и мне было интересно, какой будет самый быстрый способ их умножения. (Я хочу сделать умножение матриц на элементах матричных массивов.) В настоящее время у меня есть

numpy.array(map(lambda i: numpy.dot(m1[i], m2[i]), range(l)))

Но можно ли добиться большего успеха, чем это?

Спасибо

v923z

Ответы [ 5 ]

1 голос
/ 26 сентября 2014

numpy.einsum является оптимальным решением для этой проблемы, и оно упоминается в самом низу справочника DaveP.Код чистый, очень простой для понимания и на порядок быстрее, чем циклически проходить по массиву и выполнять умножение один за другим.Вот пример кода:

import numpy
l = 100

m1 = rand(l,2,2)
m2 = rand(l,2,2)

m3 = numpy.array(map(lambda i: numpy.dot(m1[i], m2[i]), range(l)))
m3e = numpy.einsum('lij,ljk->lik', m1, m2)

%timeit numpy.array(map(lambda i: numpy.dot(m1[i], m2[i]), range(l)))
%timeit numpy.einsum('lij,ljk->lik', m1, m2)

print np.all(m3==m3e)

Вот возвращаемые значения при запуске в блокноте ipython: 1000 петель, лучшее из 3: 479 мкс на петлю10000 петель, лучшее из 3: 48,9 мкс на петлюПравда

1 голос
/ 13 сентября 2011

Я думаю, что вы ищете ответ здесь .К сожалению, это довольно грязное решение, связанное с изменением формы.

0 голосов
/ 30 ноября 2015

Может быть, это слишком старый вопрос, но я все еще искал ответ.

Я попробовал этот код

a=np.asarray(range(1048576),dtype='complex');b=np.reshape(a//1024,(1024,1024));b=b+1J*b
%timeit c=np.dot(b,b)
%timeit d=np.einsum('ij, ki -> jk', b,b).T

Результаты: для «точки»

10 loops, best of 3: 174 ms per loop

для 'einsum'

1 loops, best of 3: 4.51 s per loop

Я проверил, что c и d одинаковы

(c==d).all()
True

все еще 'точка' - победитель, я все еще ищу лучший метод, но безуспешно

0 голосов
/ 08 сентября 2011

Если m1 и m2 являются одномерными массивами комплексных матриц 2x2, то они по существу имеют форму (l, 2,2). Таким образом, умножение матрицы на двух последних осях эквивалентно суммированию произведения последней оси m1 со второй по последнюю ось m2. Это именно то, что делает np.dot:

Но это не то, что делает np.dot.

 a = numpy.array([numpy.diag([1, 2]), numpy.diag([2, 3]), numpy.diag([3, 4])])

создает (3,2,2) массив матриц 2 на 2. Тем не менее, numpy.dot (a, a) создает 6 матриц, а форма результата - (3, 2, 3, 2). Это не то, что мне нужно. Мне нужен массив, содержащий numpy.dot (a [0], a [0]), numpy.dot (a [1], a [1]), numpy.dot (a [2], a [2] ) ...

[np.dot(m1[i],m2[i]) for i in range(l)]

должно работать, но я еще не проверил, является ли оно более быстрым, чем отображение лямбда-выражения.

Приветствия

v923z

РЕДАКТИРОВАТЬ: цикл for и карта работает примерно с одинаковой скоростью. Это приведение к numpy.array, которое отнимает много времени, но это должно быть сделано для обоих методов, поэтому здесь нет никакой выгоды.

0 голосов
/ 08 сентября 2011

Если m1 и m2 являются одномерными массивами комплексных матриц 2x2, то они по существу имеют форму (l,2,2).Таким образом, умножение матриц по двум последним осям эквивалентно суммированию произведения последней оси m1 со второй по последнюю ось m2.Это именно то, что делает np.dot :

np.dot(m1,m2)

Или, поскольку у вас есть сложные матрицы, возможно, вы хотите сначала взять комплексное сопряжение m1.В этом случае используйте np.vdot.

PS.Если m1 является списком комплексных матриц 2x2, то, возможно, посмотрите, сможете ли вы изменить порядок своего кода, чтобы с самого начала сделать m1 массивом формы (l,2,2).это невозможно, понимание списка

[np.dot(m1[i],m2[i]) for i in range(l)]

будет быстрее, чем использование map с lambda, но выполнение l np.dot с будет медленнее, чем выполнение одного np.dotна двух массивах формы (l,2,2), как указано выше.

...