numpyの内積(3)array like


2023年 09月 28日

線型代数でのベクトル・内積

数学での内積とnumpyの内積がきちんと同じにはできていないのだが、ここで数学の世界、線型代数での内積の説明について振り返ってみよう。

線型代数の本の最初の方で、ベクトルの内積の話が出てきて、以下の式が示される。

$$ \mathbf{a} = \left( \begin{array}{c} a_1 \\ a_2 \\ \vdots\\ a_n \end{array} \right) , \mathbf{b} = \left( \begin{array}{c} b_1 \\ b_2 \\ \vdots\\ b_n \end{array} \right) $$

$$ \mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n}a_{i} b_{i} = a_1 b_1 + a_2 b_2 + \cdots + a_n b_n$$

高校では、ベクトルは $\vec{a}$ という風に記号の上に右矢印 (→)を書くのだが、大学以上では $\mathbf{a}$ という風に太字で示す。

高校では、$\vec{a}$ と $\vec{b}$ との内積を $\vec{a}\cdot\vec{b}$ と書くが、大学以上では$\mathbf{a} \cdot \mathbf{b} $ または $\mathbf{(a, b)}$ と書く。

高校では、2次元ベクトルを$(x,y)$、3次元ベクトルだけを $(x,y,z)$ と表わす。一般的な多次元ベクトルは扱わないようである。そして、ベクトルは数字を横に並べた横ベクトルだけを扱うようである。

大学の線型代数では、ベクトルは縦に要素を並べて縦ベクトルとして表わし、要素数の制限もない。

行列

mn個の数(複素数)をm行n列に並べたものを(m,n)型の行列(matrix)という。
行列は、英大文字で表わす。

$$ \left( \begin{array}{cccc}
a_{11} & a_{12} & \cdots & a_{1n} \\
a_{21} & a_{22} & \cdots & a_{2n} \\
\vdots & \vdots & \ddots & \vdots \\
a_{m1} & a_{m2} & \cdots & a_{mn}
\end{array} \right) $$

行列には、積が以下のように定義されている。

$$
A = \left( \begin{array}{cccc}
a_{11} & a_{12} & \cdots & a_{1m} \\
a_{21} & a_{22} & \cdots & a_{2m} \\
\vdots & \vdots & \ddots & \vdots \\
a_{l1} & a_{l2} & \cdots & a_{lm}
\end{array} \right) ,
B = \left( \begin{array}{cccc}
b_{11} & b_{12} & \cdots & b_{1n} \\
b_{21} & b_{22} & \cdots & b_{2n} \\
\vdots & \vdots & \ddots & \vdots \\
b_{m1} & b_{m2} & \cdots & b_{mn}
\end{array} \right), $$
$$ A B = \left( \begin{array}{cccc}
c_{11} & c_{12} & \cdots & c_{1n} \\
c_{21} & c_{22} & \cdots & c_{2m} \\
\vdots & \vdots & \ddots & \vdots \\
c_{l1} & c_{l2} & \cdots & c_{ln}
\end{array} \right)
$$

とすれば

$$ c_{ik} = \sum_{j=1}^{m}a_{ij} b_{jk} = a_{i1} b_{1k} + a_{i2} b_{2k} + \cdots + a_{im} b_{mk}
\hspace{10pt}(i=1,2,\cdots,l ; k=1,2,\cdots,n)$$

行列 $A$と$B$の積は、$AB$と表わす。
ベクトル$\mathbf{a}$と$\mathbf{b}$の内積は$\mathbf{a} \cdot \mathbf{b}$ と表わし、ドット$ (\cdot) $で内積を示したが、$A\cdot B$は存在しない。

数式の表記については、『線型代数学入門』齋藤正彦著、東京大学出版会を参考にした。

行列の内積

線型代数の話を始めてしまったので、行列の掛け算をやってみよう。

>>> import numpy as np
>>> M23 = np.array(range(1,7)).reshape(2,3); M23
array([[1, 2, 3],
       [4, 5, 6]])
>>> M32 = np.array(range(1,7)).reshape(3,2); M32
array([[1, 2],
       [3, 4],
       [5, 6]])
レンジが1〜7の1次元の要素数6の配列を用意し、reshape()で、(2,3)型の行列(2次元配列)および(3,2)型の行列(2次元配列)を用意した。
>>> np.dot(M23,M32)
array([[22, 28],
       [49, 64]])
>>> np.dot(M32,M23)
array([[ 9, 12, 15],
       [19, 26, 33],
       [29, 40, 51]])
>>> np.dot(M23,M23)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<__array_function__ internals>", line 180, in dot
ValueError: shapes (2,3) and (2,3) not aligned: 3 (dim 1) != 2 (dim 0)
(2,3)型と(3,2)型のnp.dot()演算と(3,2)型と(2,3)型のnp.dot()演算は、行列の積として処理されるようだ。
さらに、(2,3)型と(2,3)型のnp.dot()演算をしたら、サイズが合わずエラーになってしまった。

vdot()の方も計算してみた。
>>> np.vdot(M23,M32)
91
>>> np.vdot(M32,M23)
91
>>> np.vdot(M23,M23)
91

いずれも、91とスカラー値になったが、np.vdot()は引数で与えられた配列等を全部1次元化(フラット化)してから計算しているのでこうなる。

行列演算は、行列(2次元配列)どうしの演算を行うのだが、より高次の配列同士のnp.dot()は可能なのか、考えてみよう。

つづく

※今回はWordPressに入っているLaTeXのプラグインで数式が使えるかどうかの動作テストの記事になってしまった。