背景
線形代数においては,慣例的にベクトルを列ベクトル(column vector)で定義することが多い.
すなわち,$\boldsymbol{x} = \begin{bmatrix}a&b&c\end{bmatrix}$ ではなく,
$$
\boldsymbol{x} = \begin{bmatrix}a\\ b \\ c\end{bmatrix}
$$
と定義する.
行列の乗法では,例えば $A$ と $B$ の積 $AB$ を考える場合には,$A$ の列数と $B$ の行数を同じ数に揃えなくてはいけない.すなわち,$A$ が $n \times m$ の行列であれば $B$ は $m \times p$ のような形でなければ,積を定義できない.
列ベクトル $\boldsymbol{x}$ に 行列 $A$ を作用させる場合には,$A$ を左から掛けて $A\boldsymbol{x}$ とする.
一方で,行ベクトル $\boldsymbol{x}$ に 行列 $A$ を作用させる場合には,$A$ を右から掛けて $\boldsymbol{x}A$ としなくてはならない.このような表記は一般的ではない1.
numpy.matmal での行ベクトルの扱い
Python/NumPy の多次元配列で行列とベクトルの積(numpy.matmal)を計算してみる.
>>> import numpy as np
>>> A = np.array([[1, 2], [3, 4]])
>>> x = np.array([[1], [2]])
>>> A @ x # 行列積(matmul)には @ を用いる
array([[5],
[11]])
* (dot product) は対象とする行列(あるいはテンソル)の次元によって異なる挙動を示すため,推奨されない.スカラ倍する場合などは * で問題ない.
これは至って問題のない結果といえる.では次の計算はどうか.
>>> A = np.array([[1, 2], [3, 4]])
>>> x = np.array([1, 2])
>>> A @ b
array([5, 11])
これは数学的には矛盾のある結果といえる.背景部分で説明したとおり,$2 \times 2$ の行列と $1 \times 2$ のベクトルはこの順序で積を取ることができない.しかしながら,
上記プログラムはこれを実行して結果を返している.
一つ前の例からも明らかであるが, ここでは(数式的には)以下の処理が行われている.
$$
\left(A\boldsymbol{x}^\top\right)^\top
$$
すなわち,**「指定されたベクトルが行ベクトルだったので,列ベクトルになおして積を取り,結果として得られる列ベクトルを再び行ベクトルに戻して返す」**という処理を行っているのである.便利といえば便利だが,線形代数に詳しい人は少し困惑するのではなかろうか.
もちろん,$\boldsymbol{x}$ が列ベクトルであるので,以下の計算については矛盾がない.
>>> b @ A
array([7, 10])
それでは,再び $\boldsymbol{x}$ を行ベクトルに戻して $\boldsymbol{x} A$ の計算結果がどのようになるか見てみよう.これまでの実験の結果から,「列ベクトルも行ベクトルになおして積をとってくれるのでは」との推察が自然に導かれる.
>>> A = np.array([[1, 2], [3, 4]])
>>> x = np.array([[1], [2]])
>>> x @ A
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 1)
ところが,列ベクトルは行ベクトルへ変換されず,エラーとなってしまう.
これはもちろん数学的には期待通りのエラーであるが,上記の推察からは外れてしまっている.
結論
numpy.matmul
における行ベクトルは,そのコンテキストに応じて内部で列ベクトルに変換され,出力時には再度,行ベクトルへと戻される.ただし,列ベクトルについてはかような解釈は実装されていない.
-
2020/08/07 現在 ↩