numo-linalgをmacで動かしてみた

Ruby で数値計算をするのは環境構築がしんどそう．
numo/narrayがいい感じになっているので，linalgを入れようとしたんですが．．．

load error
==============
2.4.0でrequireしたところででた．

``` {.example}
/Users/bob/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/numo-linalg-0.1.4/lib/numo/linalg/loader.rb:162:in `load_library': cannot find backend library for Numo::Linalg (RuntimeError)
	from /Users/bob/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/numo-linalg-0.1.4/lib/numo/linalg.rb:3:in `<top (required)>'
	from /Users/bob/.rbenv/versions/2.4.3/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:133:in `require'
	from /Users/bob/.rbenv/versions/2.4.3/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:133:in `rescue in require'
	from /Users/bob/.rbenv/versions/2.4.3/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:39:in `require'
	from plot_100.rb:47:in `<main>'
```

> gem install numo-linalg-autoloader
を試すが

```
ERROR:  Error installing numo-linalg-autoloader:
	ERROR: Failed to build gem native extension.
```
です．

少し下に書いてあるpath指定のinstallで成功．
numo-autoloaderってもしかして厄介かも．
1. autoloaderありで，明示的にrequireするか，
2. なしで，path指定してinstallするか
の2択かも．

macへのinstall
==============

blas, lapackのinstall
---------------------

``` {.example}
> brew install lapack
==> Downloading https://homebrew.bintray.com/bottles/lapack-3.8.0_1.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring lapack-3.8.0_1.high_sierra.bottle.tar.gz
==> Caveats

... 中略

==> Summary
🍺  /usr/local/Cellar/lapack/3.8.0_1: 28 files, 10MB


> brew install openblas
Updating Homebrew...
==> Auto-updated Homebrew!

... 中略

==> Summary
🍺  /usr/local/Cellar/openblas/0.3.4: 21 files, 117.8MB

```

pathのsetを進められるが，numo-linalgには効かない．

numo-linalgのinstall
--------------------

gem installするときにpathを指定する必要がある．

``` {.example}
> gem uninstall numo-linalg
> gem install numo-linalg -- --with-openblas-dir=/usr/local/opt/openblas
```

<https://gist.github.com/sonots/6fadc6cbbb170b9c4a0c9396c91a88e1>

ruby sample
-----------

最初の一歩．順番が大事みたい．

``` {.ruby}
require 'numo/narray'
require "numo/linalg/use/openblas"
require 'numo/linalg'

p Numo::Linalg::Loader.libs

m = 10
a = Numo::SFloat.new(m).rand
b = Numo::SFloat.new(m).rand
p a*b


p ma = Numo::NArray[[1, 2, 3 ], [3, 4, 5], [6,7,8]]
p Numo::Linalg.eig(ma)
```

``` {.example}
["/usr/local/opt/openblas/lib/libopenblas.dylib"]
Numo::SFloat#shape=[10]
[0.0552492, 0.0212455, 0.241172, 0.201437, 0.401844, 0.586427, 0.344554, ...]
Numo::Int32#shape=[3,3]
[[1, 2, 3], 
 [3, 4, 5], 
 [6, 7, 8]]
[Numo::DComplex#shape=[3]
[14.0664+0i, -1.06637+0i, 3.43928e-17+0i], nil, Numo::DComplex#shape=[3,3]
[[0.265648+0i, 0.744429+0i, 0.408248+0i], 
 [0.491207+0i, 0.190701+0i, -0.816497+0i], 
 [0.829546+0i, -0.63989+0i, 0.408248+0i]]]
```

narray 配列操作
---------------

``` {.example}
flatten([dim0,dim1,...])
transpose([dim0,dim1,...])
expand_dims(dim)
diagonal(offset, [ax1,ax2])
reshape
```

narray 配列演算
---------------

NArray には以下の演算メソッドがある． '+,-,\*,/,%,divmod,\*\*,-@,abs'

narray 統計メソッド
-------------------

次の統計メソッドが定義されている． sum, prod, mean, stddev, var, rms,
min, min\_index, max, max\_index, minmax, cumsum, cumprod, sort,
sort\_index, median

linalgの関数
------------

Matrix and vector products
:   dot, matmul

Decomposition
:   lu, lu\_fact, lu\_inv, lu\_solve, ldl, cholesky, cho\_fact,
    cho\_inv, cho\_solve, qr, svd, svdvals, orth, null\_space

Matrix eigenvalues
:   eig, eigh, eigvals, eigvalsh

Norms and other numbers
:   norm, cond, det, slogdet, matrix\_rank, matrix\_power

Solving equations and inverting matrices
:   solve, lstsq, inv, pinv

ruby sample II
--------------

下の例はnumo/linalgを使って，データフィッティングして
numo/gnuplotでグラフを書かせた例．

``` {.ruby}
require 'numo/gnuplot'
require 'numo/narray'

pairs = []
File.readlines("data.txt").each do |line|
  vol, _d, _d, _d, _d, ene = line.split(/\s+/)
  pairs << [vol, ene]
end
pairs.sort!{|a,b| a[0].to_f <=> b[0].to_f }
p pairs

x,y=[],[]
pairs.each do |pair|
  x << pair[0].to_f
  y << pair[1].to_f
end

require "numo/narray"

def ff(x,i)
  x**i
end

m = 5
n = 3
av = Numo::DFloat.zeros(m, n)
yy = Numo::DFloat.zeros(m)
n.times do |i|
  m.times do |j|
    av[j,i]=ff(x[j],i)
    yy[j] = y[j]
  end
end

require 'numo/linalg'
p ai = Numo::Linalg.inv(Numo::Linalg.dot(av.transpose,av))
p b = Numo::Linalg.dot(av.transpose,yy)
p vars = Numo::Linalg.dot(ai,b)

eq = ''
vars.each_with_index do |var, i|
  eq << " + " + sprintf("%+6.2f",var) + "*x** #{i} "
end
#p eq = "#{vars[0]} + #{vars[1]}*x + #{vars[2]}*x**2"

Numo.gnuplot do
  set output: 'e_v.gif'
  set term: 'gif'
  set xlabel: 'Volume [%]'
  set ylabel: 'Energy [eV]'
  plot [x, y, w: :lp, pt: 6],
  [eq, w:"lines"]
end

system 'open ./e_v.gif'
```

``` {.example}
[["-2.00", "-561.7895200000"], ["-1.00", "-564.1426100000"], ["+0.00", "-565.4727300000"], ["+1.00", "-565.8513000000"], ["+2.00", "-565.3645700000"]]
Numo::DFloat#shape=[3,3]
[[0.485714, -0, -0.142857], 
 [0, 0.1, 0], 
 [-0.142857, 0, 0.0714286]]
Numo::DFloat#shape=[3]
[-2822.62, -8.85879, -5638.61]
Numo::DFloat#shape=[3]
[-565.471, -0.885879, 0.473656]
```

![e_v.gif](https://qiita-image-store.s3.amazonaws.com/0/151211/360437ae-dab0-1160-8b7c-80e1199272d9.gif)