blue_field

技術的なメモとか読書記録とかいろいろ(の予定

Goで簡単な行列計算

Golangでの簡単な行列計算。

ライブラリ

gonum/matrix を利用してみる。

実装

Goのバージョンは1.4。

行列の作成

行列は、mat64.NewDenseという関数を利用して宣言する。 下記の例では、[1 2 3]という行列をxに代入している。

// matrix.go
package main
    
import (
    "fmt"

    "github.com/gonum/matrix/mat64"
)

func main() {
    elem := []float64{1, 2, 3}

    x := mat64.NewDense(3, 1, elem)
    fmt.Println(x)
}

実行。

$ go run matrix.go
&{{3 1 1 [1 2 3]}}

NewDenseの実装を覗いてみる。

// matrix/mat64/dense.go
func NewDense(r, c int, mat []float64) *Dense {
    if mat != nil && r*c != len(mat) {
        panic(ErrShape)
    }
    if mat == nil {
        mat = make([]float64, r*c)
    }
    return &Dense{RawMatrix{
        Rows:   r,
        Cols:   c,
        Stride: c,
        Data:   mat,
    }}
}

列数, 行数, 要素を表す配列の3つを引数として渡し、 返り値のインスタンスには、列数, 行数, Stride(=行数), 行列が含まれる。

行列の要素へのアクセス

Atを使い、位置(列と行)を引数に指定。

elem := []float64{1, 2, 3}
x := mat64.NewDense(3, 1, elem)
fmt.Println(x.At(1, 0))
// => 2

行列の和や積

行列の和や積などの算出。

和はAdd, 差はSubという関数を利用。 返り値はなく、メソッドのレシーバに演算結果が代入される。

func main() {
    elem_x := []float64{1, 2, 3}
    elem_y := []float64{4, 5, 6}
    elem_a := make([]float64, 3)

    x := mat64.NewDense(3, 1, elem_x)
    y := mat64.NewDense(3, 1, elem_y)
    a := mat64.NewDense(3, 1, elem_a)

    a.Add(x, y)
    fmt.Println(a)
    // => &{{3 1 1 [5 7 9]}}

    a.Sub(x, y)
    fmt.Println(a)
    // => &{{3 1 1 [-3 -3 -3]}}
}

続いて行列積。Mulを利用。 下記のようにinit関数を指定しておかないと、panic: mat64: no blas engine registered: call Register()というpanicが発生した。

package main

import (
    "fmt"

    "github.com/gonum/blas/goblas"
    "github.com/gonum/matrix/mat64"
)

func main() {
    elem_x := []float64{1, 2, 3, 4}
    elem_y := []float64{5, 6, 7, 8}
    elem_a := make([]float64, 4)

    x := mat64.NewDense(2, 2, elem_x)
    y := mat64.NewDense(2, 2, elem_y)
    a := mat64.NewDense(2, 2, elem_a)

    a.Mul(x, y)
    fmt.Println(a)
    // => &{{2 2 2 [19 22 43 50]}}
}

func init() {
    mat64.Register(goblas.Blas{})
}

2x2行列の積がちゃんと出てる。

逆行列

逆行列の算出には、Inverseを利用。

func main() {
    elem_x := []float64{2, 5, 1, 3}

    x := mat64.NewDense(2, 2, elem_x)
    inv_x, _ := mat64.Inverse(x)
    fmt.Println(inv_x)
    // => &{{2 2 2 [3 -5 -1 2]}}
}

[[2, 5], [1, 3]]逆行列は、[[3, -5], [-1, 2]]。OK。

https://github.com/gonum/matrix/blob/master/mat64/matrix.go に、Inverseの実装が載ってる。

番外: Rubyでやる

上記でやったようなことをRubyでやるとどうなるか。 Rubyのバージョンは、2.2.0preview2。

Matrixクラスを利用。

# matrix.rb
require 'matrix'

a = Matrix[[1, 2], [3, 4]]
b = Matrix[[5, 6], [7, 8]]

sum = a + b
# => Matrix[[6, 8], [10, 12]]

mul = a * b
# => Matrix[[19, 22], [43, 50]]

c = Matrix[[2, 5], [1, 3]]
c.inverse
# => Matrix[[(3/1), (-5/1)], [(-1/1), (2/1)]]

...やっぱり簡単に書ける。

おわり

Goの簡単な行列演算などを試してみた。 はじめから分かってたけど、扱いやすさではRubyPythonにやはり軍配が上がりますかね。。 あと、行列を転置する方法がよくわからなかったw

数値計算系のライブラリもう少し充実してくれたりすると嬉しいですね。