Skip to content

Instantly share code, notes, and snippets.

@PhilOwen
Last active November 6, 2016 16:34
Show Gist options
  • Save PhilOwen/9d67e188053fd865bed218b4b1aab9c4 to your computer and use it in GitHub Desktop.
Save PhilOwen/9d67e188053fd865bed218b4b1aab9c4 to your computer and use it in GitHub Desktop.
HaskellのHUnitでhmatrixをテスト

HaskellのテストフレームワークHUnitを使ってみる。
テストしてみるライブラリは、行列計算のhmatrix。

stack test

で、テストを実行できる。
ただ、Doubleの計算誤差で、 連立方程式の解が微妙にズレてテストが失敗する。 失敗メッセージが出るので、参考として残した。 なお、ちゃんとしたソルバを使うと、きれいに計算される。 (テストで浮動小数点数の微妙なズレを許容する比較法を使うのもアリ)

HUnitはJUnitのHaskell版。 ~=?~?=でアサートしたり、 ~:でテストにラベルをつけるなど、 独自の演算子を使っていくのはHaskellぽい (Javaは演算子を独自に定義できない)。 ただ、JUnitは歴史が長いので、Rspecに比べると なんとなく原始的な感じを受ける。 個人的には、普通にHSpecを使うかなぁ。

行列計算のhmatrixは、一通りの行列計算を カバーしているらしい (あまりマニアックなのはないのかもしれないが、 自分はそれが気になるレベルではないので)。
Fortranの数値計算ライブラリBLASとLAPACKを 呼び出して使っているので、かなり速い。 相当枯れた実績ある外部ライブラリなので、 Haskellだから遅いということはまずない。

Haskellだと演算子が簡単に定義できるのと、 型クラスのおかげで型の違うものも自然に計算できて、 コードがかなりすっきりする。 たとえば、matlabなどのように、普通の+演算子で ベクトルどうしや、スカラと行列を足し算できる。 ベクトルで、dotで内積やcrossで外積を計算するのも簡単。

References

name: hello-hunit
version: 0.1.0.0
build-type: Simple
cabal-version: >=1.20
library
exposed-modules: Lib
build-depends: base
, hmatrix
default-language: Haskell2010
test-suite hello-hunit
type: exitcode-stdio-1.0
main-is: Spec.hs
build-depends: base
, hmatrix
, HUnit
default-language: Haskell2010
module Lib(module Numeric.LinearAlgebra) where
import Numeric.LinearAlgebra
import Test.HUnit
import Lib
main :: IO Counts
main = runTestTT $ test [
"Vector addition" ~: do
let actual = vector [3, 5] + vector [7, 11]
let expected = vector [10, 16]
actual ~=? expected
, "Vector multiplied by a scalar" ~: do
let actual = 3 * vector [1, 2::R]
let expected = vector [3, 6::R]
actual ~?= expected
, "Vector dot product" ~: do
let actual = vector [1, 2::R] `dot` vector [3, 5]
let expected = 13.0
actual ~?= expected
, "Vector cross product" ~: do
let actual = vector [1, 0, 0::R] `cross` vector [0, 1, 0::R]
let expected = vector [0.0, 0, 1]
actual ~?= expected
, "Matrix addition for each element" ~: do
let actual = (2><2) [1..] + ident 2
let expected = (2><2) [2, 2, 3, 5::R]
actual ~?= expected
, "Matrix multiplication" ~: do
let actual = (2><2) [1, 0, 0, 2::R] <> (2><2) [1, 1, 1, 1::R]
let expected = (2><2) [1, 1, 2, 2::R]
actual ~?= expected
, "Matrix inversion" ~: do
let actual = inv $ (2><2) [1, 0, 0, 2::R]
let expected = (2><2) [1, 0, 0, 0.5]
actual ~?= expected
, "Solving system of linear equations using solver" ~: do
let actual = linearSolve m v where
m = (2><2) [2, 1, 1, -3]
v = matrix 1 [3, 5]
let expected = Just $ matrix 1 [2, -1::R]
actual ~?= expected
, "Solving system of linear equations using inv" ~: do
let actual = inv m #> v where
m = (2><2) [2, 1, 1, -3]
v = vector [3, 5]
let expected = vector [2, -1::R]
actual ~?= expected
]
resolver: lts-7.7
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment