@演算子
行列積を計算するための演算子として「@」が利用できる。この演算子のふるまいを細かく確認してみよう。まず、
A : 2D array
x : 1D array
b : 0D array
とする。
code:p01.py
import numpy as np
A = np.array(1, 2, 3], 4, 5, 6, [7, 8, 9)
x = np.array(1, 2 , 3)
b = np.array(2)
print(A.ndim, A.shape)
print(x.ndim, x.shape)
print(b.ndim, b.shape)
# 2 (3, 3)
# 1 (3,)
# 0 ()
2D同士
結果は2D、通常の行列積が計算される。matmul関数【numpy】、 dot関数【numpy】と同じ。
code:(続き)p01.py
y = A@A
print(y.ndim, y.shape)
print(y)
# 2 (3, 3)
# [ 30 36 42
# 66 81 96
# 102 126 150]
2Dと1D
結果は1Dとなる。matmul関数【numpy】、 dot関数【numpy】と同じ。
code:(続き)p1.py
y1 = A@x
print(y1.ndim, y1.shape)
print(y1)
y2 = x@A
print(y2.ndim, y2.shape)
print(y2)
# 1 (3,)
# 14 32 50
# 1 (3,)
# 30 36 42
行列$ Aとベクトル$ xの積について
ベクトル$ xを適当な長さの縦ベクトルとすると、計算可能なのは$ Ax, x^\top Aの場合である。
しかしながら、1D配列は軸を1本しか持たないので縦横の区別は無く、転置といった操作を行うことはできない。
そのため、ここでの処理は前後どちらからベクトルを掛けた場合でも、計算可能な形状に解釈され、行列積が計算されている。
ただし、結果の次元は1Dであることに注意。
× 2Dと0D
計算できない。dot関数【numpy】とは異なる動作である。
code:(続き)p01.py
y = A@b
print(y.ndim, y.shape)
print(y)
# ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)
1D同士
結果は0D、内積が求まっている。@演算子、dot関数【numpy】と同じ。
code:(続き)p01.py
y = x@x
print(y.ndim, y.shape)
print(y)
# 0 ()
# 14
× 1Dと0D
計算できない。dot関数【numpy】とは異なる動作である。
code:(続き)p01.py
y = x@b
# ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)
× 0D同士
計算できない。dot関数【numpy】とは異なる動作である。
code:(続き)p01.py
y = b@b
# ValueError: matmul: Input operand 0 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)
まとめると、@演算子で計算を行うには、2つのオペランドのうち少なくともどちらかが、いずれかの軸方向に1以上の長さをもつ必要があるようだ。その他の四則演算については対応しており、ブロードキャストを伴う要素毎の演算が行われる。
code:(続き)p04.py
y3 = a*A
3次元以上の配列は未確認。
まとめ
2D @ 2D → 2D
2D @ 1D → 1D
0D → 計算できない