はじめに
現在、numpyのndarrayで書かれたプログラムを、疎行列に対応するべくscipy.sparceを使って書き直しています。
その中で、特に行列積の書き方が双方いろいろあり、しかも統一されていませんでした。
そこで、双方の大切そうな内容をまとめました。
結論から言うと、行列積は@を使用しましょう。
用語
行列積
線形代数で習う、行列同士の掛け算
A=\begin{bmatrix}
3&4 \\
5&6
\end{bmatrix},
B=\begin{bmatrix}
1&2 \\
3&4
\end{bmatrix}
A\cdot B=
\begin{bmatrix}
3*1+4*3&3*2+4*4 \\
5*1+6*3&5*2+6*4
\end{bmatrix}=
\begin{bmatrix}
15&22 \\
23&34
\end{bmatrix}
アマダール積
行列の同じ位置の要素を掛け合わせる。
A=\begin{bmatrix}
3&4\\
5&6
\end{bmatrix},
B=\begin{bmatrix}
1&2 \\
3&4
\end{bmatrix}
A\odot B=
\begin{bmatrix}
3*1&4*2 \\
5*3&6*4
\end{bmatrix}=
\begin{bmatrix}
3&8 \\
15&24
\end{bmatrix}
おそらく
ネット上で解説記事がなく、自分でプログラムを作って確認。バージョンが古いこともあるかも。
結果
異なる型同士の演算は省略しました。
aは列名の行列、bはsparse行列を表します。
つまり、a+aは、
numpy.ndarrayの列では、numpy.ndarray + numpy.ndarray
numpy.matrixの列では、numpy.matrix + numpy.matrix
scipy.sparseの列では、scipy.sparse + scipy.sparse
を意味します。
| a:列要素 b:sparse | numpy.ndarray | numpy.matrix | scipy.sparse |
|---|---|---|---|
| a+a | 要素ごとの和 | 要素ごとの和 | 要素ごとの和 |
| a-a | 要素ごとの差 | 要素ごとの差 | 要素ごとの差 |
| a/a | 要素ごとの商 | 要素ごとの商 | 要素ごとの商 |
| a ±スカラー | すべての要素に | すべての要素に | エラー |
| a /スカラー | すべての要素に | すべての要素に | すべての要素に |
| a*スカラー | 各要素のスカラー倍 | 各要素のスカラー倍 | すべての要素に |
| numpy.multiply(a,a) | アダマール積 | アダマール積 | おかしくなる |
| a*a | アダマール積 | 行列積 | 行列積 |
| numpy.dot(a,a) | 1D内積 2D行列積(非推奨) | おそらく行列積 | おかしくなる |
| numpy.matmul(a,a) | 1D内積 2D行列積 | おそらく行列積 | |
| a@a | 1D内積 2D行列積 | おそらく行列積 | おそらく行列積 |
| a**5 | アダマール積5回 | 行列積5回 | 行列積5回 |
| a**-1 | アダマール積(float) | 逆行列 | |
| numpy.linalg.inv(a) | 逆行列 | 逆行列 | |
| a.I | I属性なし | 逆行列 | |
| numpy.linalg.det() | 行列式 | 行列式 | |
| b.dot(b) | 行列積 | ||
| b.multiply(b) | 行列積 | ||
| scipy.sparse.linalg.inv(b) | 逆行列 | ||
| a.T | 転置 | おそらく転置 | 転置 |
祖行列に関するTips
なお、処理速度はcsr_matrix同士、または、csc_matrix同士が高速。異なるクラスを混在させたままにするよりもどちらかに統一して処理したほうがよい。
csr_matrixは〜行目を取り出す操作、csc_matrixは〜列目を取り出す操作が高速である
同じ型同士の和・積は高速である。つまりcsr_matrix同士の和・積、csc_matrix同士の和・積はともに高速である
オブジェクトの生成は高速だが算術演算などはサポートされていない。CSRやCSCへの変換も高速なので、COOを生成後、CSRに変換し演算を行うのが基本的な使い方。
最後に
行列積について、*は挙動が違いますが、@は3つ型で同じ挙動なので、お勧めです。
穴抜けの部分、間違いなどありましたら教えてください。
参考