假设两个矩阵:
a = np.array([[1,2],
[3,4]])
b = np.array([[5,6],
[7,8]])
这会给 Kronecker 产品 ab = np.kron(a,b)
:
array([[ 1, 2, 2, 4],
[ 3, 4, 6, 8],
[ 3, 6, 4, 8],
[ 9, 12, 12, 16]])
现在假设在两个数组中有这些矩阵的三个副本,如下所示:
c = np.stack([a,a,a])
d = np.stack([b,b,b])
我想计算 c
和 d
的 Kronecker 积,使得输出是一个 3 索引数组,对应于 ab
的 3 个副本,即形状为 (3,4,4)
.然而,简单地执行 kron(c,d)
输出形状 (9,4,4)
,它的条目比需要的多,并且无法适当地重新整形。你能帮助了解如何做到这一点吗?
答案 0 :(得分:0)
这行得通,假设中间列表没有问题(numpy 在分配数组之前需要大小):
import numpy as np
a = np.array([[1, 2],
[3, 4]])
b = np.array([[5, 6],
[7, 8]])
c = np.stack([a, a, a])
d = np.stack([b, b, b])
result = np.array(list(np.kron(x, y) for x, y in zip(c, d)))
print(result)
print(result.shape)
输出:
[[[ 5 6 10 12]
[ 7 8 14 16]
[15 18 20 24]
[21 24 28 32]]
[[ 5 6 10 12]
[ 7 8 14 16]
[15 18 20 24]
[21 24 28 32]]
[[ 5 6 10 12]
[ 7 8 14 16]
[15 18 20 24]
[21 24 28 32]]]
(3, 4, 4)
答案 1 :(得分:0)
res=np.zeros((3,4,4))
res[:] = np.kron(a,b)
应该可以,向所有 3 个平面广播 kron
。
kron
是 a
和 b
的外积的特殊重排。 A (2,2,2,2) 重新排列为 (4,4)。我在另一篇文章中研究了详细信息:
您的 (3,4,4) 可以从 (3,2,2,2,2) 获得,但它不是标准的,因此没有开箱即用的功能。你可以尝试调整我的答案。
In [246]: a = np.array([[1,2],
...: [3,4]])
...: b = np.array([[5,6],
...: [7,8]])
In [249]: np.kron(a,b)
Out[249]:
array([[ 5, 6, 10, 12],
[ 7, 8, 14, 16],
[15, 18, 20, 24],
[21, 24, 28, 32]])
正如我之前展示的,kron
可以通过对 outer
乘积应用转置和形状来生成。我们可以将 einsum
用于外部和转置:
In [253]: np.einsum('ij,kl->ikjl',a,b) # ikjl instead ijkl
Out[253]:
array([[[[ 5, 6],
[10, 12]],
[[ 7, 8],
[14, 16]]],
[[[15, 18],
[20, 24]],
[[21, 24],
[28, 32]]]])
In [254]: np.einsum('ij,kl->ikjl',a,b).reshape(4,4)
Out[254]:
array([[ 5, 6, 10, 12],
[ 7, 8, 14, 16],
[15, 18, 20, 24],
[21, 24, 28, 32]])
将其推广到数组 (3,2,2) 形状,我们可以添加一个额外的“批次”维度:
In [255]: c = np.stack([a,a,a])
...: d = np.stack([b,b,b])
In [256]: c
Out[256]:
array([[[1, 2],
[3, 4]],
[[1, 2],
[3, 4]],
[[1, 2],
[3, 4]]])
In [257]: c.shape
Out[257]: (3, 2, 2)
In [258]: np.einsum('aij,akl->aikjl',c,d).reshape(3,4,4)
Out[258]:
array([[[ 5, 6, 10, 12],
[ 7, 8, 14, 16],
[15, 18, 20, 24],
[21, 24, 28, 32]],
[[ 5, 6, 10, 12],
[ 7, 8, 14, 16],
[15, 18, 20, 24],
[21, 24, 28, 32]],
[[ 5, 6, 10, 12],
[ 7, 8, 14, 16],
[15, 18, 20, 24],
[21, 24, 28, 32]]])
但如果我们知道 c
和 d
只是 a
和 b
的复制,那么广播解决方案更快
In [260]: res = np.zeros((3,4,4),int)
In [261]: res[:] = np.kron(a,b)
甚至更好(无复制品的克朗视图):
np.broadcast_to(np.kron(a,b),(3,4,4))
一些时间:
In [280]: timeit np.einsum('aij,akl->aikjl',c,d).reshape(3,4,4)
10.2 µs ± 22.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [281]: timeit res=np.zeros((3,4,4),int);res[:] = np.kron(a,b)
47.5 µs ± 1.66 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [282]: timeit np.broadcast_to(np.kron(a,b),(3,4,4))
57.6 µs ± 1.76 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [283]: timeit np.array(list(np.kron(x, y) for x, y in zip(c, d)))
143 µs ± 319 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
我有点惊讶 einsum
的速度有多快。此外,braodcast_to
的速度并不快,尽管这在很大程度上是 kron
的错(我之前的回答显示速度较慢)。
答案 2 :(得分:0)
我的解决方案是这样的
import numpy as np
def kron(a, b):
ai = a[0]
bi = b[0]
b = np.asanyarray(b)
a = np.array(a, copy=False, subok=True, ndmin=b.ndim)
ndb, nda = bi.ndim, ai.ndim
b_size = a.shape[0]
as_ = ai.shape
bs = bi.shape
nd = ndb
if (ndb != nda):
if (ndb > nda):
as_ = (1,) * (ndb - nda) + as_
else:
bs = (1,) * (nda - ndb) + bs
nd = nda
res = np.einsum("ij,ik->ijk", a.reshape((a.shape[0], -1)), b.reshape((b.shape[0], -1)))
res = res.reshape((b_size, )+(as_ + bs))
axis = nd - 1
result = []
for i in range(b_size):
r = res[i]
for _ in range(nd):
r = np.concatenate(r, axis=axis)
result.append(r)
return np.array(result)
x = np.array([[1,2],
[3,4]])
y = np.array([[5,6],
[7,8]])
c = np.stack([x,x,x])
d = np.stack([y,y,y])
k = kron(c, d)
result = np.array(list(np.kron(x, y) for x, y in zip(c, d)))
print(np.allclose(k, result))
正如@hpaulj 在他们的回答中提到的那样,没有内置函数可以执行此操作,您必须对外部产品执行一些非平凡的重塑操作才能使其正常工作。
解决方案已根据 kron in numpy 的实现进行了修改。
我基本上只是在 numpy 实现用于执行连接操作的循环顶部添加了另一个外部循环。这是为了分别处理 c
和 d
元素之间的每个外积。其他的都差不多。
请注意,这不是 numpy 的 kron 实现的泛化,因为这不适用于您只想执行简单的 kron
操作的情况。至少不是没有一些额外的修改。为了清楚起见,我也忽略了 numpy 正在处理的一些边缘情况。但是,这应该适用于您的用例。