Haskell
行列
hmatrix

Haskellの行列計算 - 保存と読み出し、複素行列について

hmatrix について(前書き)

高速な行列計算ライブラリーです。基本的な使い方はほかの方のqiita記事にも詳しく書いてありますので省略しますが、私が躓いたところに関して、身の丈に合った解説をします。

一言だけ初歩的解説しますと、import Numeric.LinearAlgebraで呼び出します。

大きく2点躓きました。1つは複素行列の定義、2つは外部ファイルからの行列読み込みです。そしてそれらの合成にも躓きました。

複素行列

複素行列の定義

hmatrixは複素行列の計算にも対応しいる。(というより、していないと閉じない計算が多い)
定義する場合は、後で複素行列であることを明示すればよい。

let a = (2><2)[1,2,3,4] :: Matrix (Complex Double)
let b = (2><2)[1:+2,2,3,4:+(-4)] :: Matrix (Complex Double)

よくある間違いとして(私がよくやる間違い)

let a = (2><2)[1..4] :: Matrix (Complex Double)
--複素数なので".."表現が使えない。
let b = (2><2)[1:+2,2,3,4:+(-4)]
--Matrix Double として扱おうとしてエラーになる。ちょま、

実行列と複素行列の混ざった計算

haskellはコンパイラが型推論してくれるが、Matrixに関しては自分で変換する必要がある。
実行列 a と複素行列 b の積は

let a = (2><2)[1,0,0,1]::Matrix Double
let b = (2><2)[-1,0,0,1]::Matrix (Complex Double)

let c = (complex a) <> b

とする。ちなみに、Complex Doubleに complex b などと書いても大丈夫なので、場合によっては複素行列になるといった場合にも対応できる(可読性は下がる)。

行列の保存

saveMatrix

saveMatrix "FilePath" "%lf" a

列は半角スペース区切り行は改行区切りで"FilePath"に出力される。カンマでも区切りたいなら、

saveMatrix "FilePath" "%lf," a

のようにする。こうすると半角スペース、改行に加えカンマが毎回挿入される。同様に任意の区切りが可能。

行列の読み出し

ファイルから行列をロードする方法

let a = loadMatrix "FilePath"

でとることができる。しかし、a の型は IO (Matrix Double) のようなIO型になるため、

svd a
a + a

のようなことはできない。そこでIO型からMatrix Doubleを取り出すため、"<-"を用います。
(System.IO.UnsafeのunsafePerformIOという関数を用いる方法もあります。)

a <- loadMatrix "FilePath"
svd a

これにより、ファイルから読み込んだ行列用いて計算し行くことができる。
大きさは自動で判別されるが、列は半角スペース、行は改行で区切られている必要がある。(カンマ区切りはダメ)

複素行列の保存と読み出し

saveMatrix, LoadMatrixは実行列Matrix Doubleにしか対応していない。複素行列を保存する場合、自前で作るか、実部、虚部を分けるかする必要がある。ここでは後者を紹介する。

toComplex, fromComplexを用いると比較的簡単に書くことができる。

save

let a = (2><2)[1:+2,2,3,4]::Matrix(Complex Double)
let (a_r,a_c) = fromComplex a
saveMatrix "real.txt" "%lf" a_r
saveMatrix "complex.txt" "%lf" a_c

2行で書くと

let a = (2><2)[1:+2,2,3,4]::Matrix(Complex Double)
saveMatrix "real.txt" "%lf" (fst$fromComplex a)
saveMatrix "complex.txt" "%lf" (snd$fromComplex a)

プチ解説するとfst, sndは2要素のタプルに対して、第1成分と第2成分を取るものです。

load

a_r <-loadMatrix "real.txt"
a_c <-loadMatrix "complex.txt"
let a = toComplex (a_r,a_c)

あとがき

今後も、このテーマに関して新たな見地を得たら加筆修正しようと思います。忌憚なきご意見を頂ければ幸いです。(近いうちに複素行列を使う例を挙げようと思います。)

修正歴

大きな修正は記録しておきます。(加筆はここには書きません)

  • IO モナドから中身を取り出すなら、unsafePerformIO でもいいですが単純に"<-"で良いのでそこを修正しました。