疎行列(スパース行列)を使う
疎行列表現ではすべての要素を保存する訳ではないため,便利なことがある.例えばある1つの特定の値が頻繁に使われている場合には,疎行列表現が非常に適していることもあるだろう.Wolfram言語には行列,ベクトル,テンソルの疎行列表現としてSparseArrayがある.
このチュートリアルではWolfram言語におけるSparseArrayオブジェクトの作成方法とその扱い方について述べる.疎行列の線形代数計算については「行列の計算」を参照されたい.
基本操作
Wolfram言語において,疎行列を表すための基本的なオブジェクトはSparseArrayである.
SparseArray[list] | 一般リストのSparseArray表現 |
SparseArray[{{i1,j1}->v1,{i2,j2}->v2,…},{m,n}] | |
要素{ik,jk}の値が vk である m×n 疎配列 | |
SparseArray[{{i1,j1},{i2,j2},…}->{v1,v2,…},{m,n}] | |
同上の疎配列 | |
SparseArray[data,{m,n},def] | デフォルトの要素が def である m×n の疎行列 |
SparseArray[Band[b]->v,{m,n}] | m×n の疎帯行列 |
Normal[array] | SparseArrayに対応する一般のリスト |
ArrayRules[m] | 非零要素の位置 |
SparseArray
この2つの形式の相互の利点についての詳細は「SparseArrayの規則の入力」で述べる.
Wolfram言語は疎配列の作成に使われる特定のパターンの簡略化のために,記号代数技術を用いる.これにより,疎配列を作成する際に,要素がその配列に実際に含まれているかどうかをすべての要素について調べなくてもよくなる.そのため計算時間が大幅に削減される.
原則として,SparseArrayで使う規則はそのままWolfram言語の他の規則でも使える.
SparseArrayの規則の入力
1つ目の形式には多くの規則が含まれているが,これは指標の指定とパターンの使用を併用したいときに便利な方法である.これは小さな例の場合にも,より読みやすい.2つ目の形式はより効率がよく,例えばすでにファイルからデータを読み込んであり,指標のみを指定したい場合等に適している.
パックアレーについての詳細は「パックアレー」で述べる.
疎帯行列
ある種の帯状構造を持つ疎行列を構築したい場合は,Bandをって実行する.
SparseArray[Band[b]->v,{m,n}] | m×n の疎帯行列 |
Band[{i,j}] | 位置{i,j}から始まる対角の帯行列 |
Band[{imin,jmin},{imax,imax}] | {imin,jmin}から{imax,imax}までの対角帯行列 |
Band[{imin,jmin},{imax,imax}{di,dj}] | {imin,jmin}から刻み幅{di,dj}で移動する対角帯行列 |
一般にBandを使って疎行列を生成することができると,その方が格段に効率的である.
単位疎行列と対角疎行列
Wolfram言語には単位疎行列や対角疎行列を生成する方法がいくつもある.
IdentityMatrix[n,Sparse->True] | 単位疎行列 |
DiagonalMatrix[SparseArray[{a,b,c,d}]] | |
対角疎行列 |
行列のすべての要素がどの程度機械精度数であるかに注目する.これにより計算の効率が向上する.Wolfram言語が扱うことができるその他の行列については「行列の型」で詳しく述べる.
Normal
ArrayRules
構造操作
行列の一部の抽出
疎行列の要素,行,列の抽出はWolfram言語関数Partを使えば極めて容易である.一般にPartは[[ ]]という表記で入力する.
m[[i,j]] | i,j 番目の要素 |
m[[i]] | i 行目 |
m[[i;;j]] | i 行目からj 行目 |
m[[All,i]] | i 列目 |
m[[All,i;;j]] | i 列目からj 列目 |
m[[{i1,…,ir},{j1,…,js}]] | 行の指標がik,列の指標がjk であるr×s の部分行列 |
Tr[m,List] | m の対角要素のリスト |
複数の部分の抽出
行列の要素の設定
割当て式の左辺にWolfram言語関数Partを指定すると,要素,行,列を設定して簡単に疎行列に変更を加えることができる.
m={{a11,a12,…},{a21,a22,…},…} | m に行列を割り当てる |
m[[i,j]]=v | 要素{i,j}を v で再設定する |
m[[i]]=v | i 行目の要素をすべて v で再設定する |
m[[i]]={v1,v2,…} | i 行目の要素を{v1,v2,…}で再設定する |
m[[All,j]]=v | j 列目の要素をすべて v で再設定する |
m[[All,j]]={v1,v2,…} | j 行目の要素を{v1,v2,…}で再設定する |
複数の部分の設定
部分行列の抽出
m[[i0;;i1,j0;;j1]] | i0行目からi1行目,j0列目からj1列目までの部分行列を抽出する |
m[[i0;;i1]] | i0行目からi1行目までの部分行列を抽出する |
m[[All,j0;;j1]] | j0列目からj1列目までの部分行列を抽出する |
行と列の削除
行や列の削除にはDropを使用する.
Drop[m,{i0,i1}] | i0行目からi1行目までを削除する |
Drop[m,{},{j0,j1}] | j0列目からj1列目までを削除する |
Drop[m,{i0,i1},{j0,j1}] | i0行目からi1行目まで,j0列目からj1列目までを削除する |
行と列の挿入
行の挿入にはInsertを使う.
Insert[m,r,i] | 行列m の位置i に行r を挿入する |
行列の拡張
転置
要素の回転
行列のテスト
Wolfram言語には疎行列をテストし,サイズ情報を得るための関数が数多く存在する.
VectorQ[expr] | expr がベクトル形式である場合はTrueを,それ以外の場合はFalseを返す |
MatrixQ[expr] | expr が行列形式の場合はTrueを,それ以外の場合はFalseを返す |
ArrayQ[t,n] | t が階数n のテンソルであるかどうかを調べる |
Dimensions[expr] | ベクトルまたは行列の次元のリスト |
ArrayDepth[t] | テンソルの階数を求める |
mi==mj | 2つの行列が等しいかどうか比較する |
EqualはどのようなWolfram言語式についても使用できる.行列の属性を使って総括的に2つの行列が等しいかどうか比較する場合は,行列ノルムを比較する方がよいことがある.これについては「行列ノルム」で述べる.
その他の構造操作
このセクションでは,行列を扱う上で役に立つその他の構造操作について述べる.
Flatten[m] | m の中のネストしたリストをフラットにする |
Flatten[m,n] | m の中のネストしたリストをレベルn までフラットにする |
Partition[m,n] | m を長さn のサブリストに分割する |
Join[m1,m2] | m1とm2を連結する |
Append[m,r] | m の最後に行r を挿入する |
Prepend[m,r] | m の最初に行r を挿入する |
この操作はInsertを使っても行える.「行と列の挿入」を参照のこと.
要素の操作
これらの計算では,実際に保管されている要素(デフォルトの値も含む)についてのみ計算すればよいので,素早く計算できる.
リストへの縫込み
疎配列に縫込み処理を行うと,その操作は実際に保管されている要素(デフォルトの要素も含む)についてのみ適用される.従って,fun[0]が計算されるのは1回だけである.
マップ
Mapを使って疎配列に関数を適用すると,関数は実際に保管されている要素(デフォルトの要素も含む)のみに適用される.
疎行列の可視化
このセクションでは疎行列のフォーマットとプロットのための関数について述べる.疎行列はシステムに統合しやすいので,このセクションの例の多くは密行列での操作に類似している.密行列の可視化については「行列の可視化」で説明してある.
MatrixForm[mat] | 行列の要素を二次元配列で表示する |
MatrixPlot[mat] | mat の構造パターンを示す |
疎行列のフォーマット
MatrixFormで得られる形は密行列であり,疎のパターンが分かりやすい.しかし,特に配列の階数が高くなった場合に,出力が大きくなりがちである.
疎行列のプロット
疎行列のプロットには関数MatrixPlotが便利である.詳細は「行列のプロット」を参照のこと.
疎行列のインポートとエキスポート
Wolfram言語には,インポート・エキスポートのためのさまざまなツールがある.後で同僚がWolfram言語で作業を続けられるようにデータをファイルに保存することもできるが,そのためには式のインポート・エキスポート関数を使った方がよい.これについては「式の入出力」で述べた.
Wolfram言語の外部のソースの行列を特定のデータ形式で操作したいときは,関数ImportおよびExportが便利である.関数Importは多種の形式をサポートしており,そのうちのいくつかは疎行列に関連したものである.
Harwell–BoeingおよびMatrix Market形式の行列の例はhttp://math.nist.gov/MatrixMarket/index.htmlを参照のこと.
行列の掛け算
外積
疎な外積のこの作成方法は,Wolfram言語の疎配列を一般的な疎なデータ構造として使用したい場合に非常に便利である.
行列の置換
行列演算の多くは,行列を特定の方法で並べることで行われる.例えば,演算の中には対角に要素を置くために行列を並べ替えを試みるものや,特定の要素を密ブロックのグループにまとめようとするものがある.Wolfram言語関数Partは行列の行と列の置換に非常に適した関数である.
m[[perm]] | 行列の行に置換を適用する |
m[[All,perm]] | 行列の列に置換を適用する |
m[[perm,perm]] | 行列の行と列に置換を適用する |
m[[perm]]=m | 行列の行に逆置換を適用する |
m[[All,perm]]=m | 行列の列に逆置換を適用する |
式の疎配列への変換
行列は線形方程式系の表現において効率的であるため,科学・工学分野で非常に重要である.技術計算システムの中でもWolfram言語はユニークで,行列の操作と表現に非常に効率的な方法を統合しており,行列と方程式系の間の行き来が簡単である.次の例では疎行列に未知のベクトルを掛け合せる.結果として方程式の系が得られる.
線形方程式系を直接扱える機能は,例えば差分解の生成等,特定の応用分野において非常に有用である.これについては「差分解法」で例示する.
SparseArrayデータ形式
疎配列にはいくつかの形式が使える.どの形式にも長所,短所がある.Wolfram言語では内部での保管の形式としてCSR(Compressed Sparse Row)形式が使われている.これについて以下の行列の例を使って説明する.
この形式は一般性が高いので,任意の階数のテンソルの表現に使用できる.この形式の利点は,この他に,同じ行にあるデータ要素が互いに隣り合って保管されるため,キャッシュパフォーマンスがよいということも挙げられる.反対に,行列に新しい要素を挿入するということには最適化されていないという不便さもある.