ござるのブログ

覚え書きいろいろ

NArrayっぽいやつをいろいろ試しました

はじめに

MF / Numo::NArray を試してみる」を拝見し、NumRu::NArrayもあると思ったので試してみました。ほぼ真似です。すみません。

実行環境は以下の通りです。

NArray 0.6.1.2

他のNArrayっぽいやつに多大な影響を与えたと思われる旧バージョンです。これのおかげで、いろんなデータ解析が捗りました。作者様ありがとうございます。

require 'narray'
require 'benchmark'

SIZE = 16384

Benchmark.bmbm do |x|
  x.report do
    a = NArray.float(SIZE, SIZE).random
    b = NArray.float(SIZE, SIZE).random
    c = a * b
  end
end

実行結果

 benchmark_narray.rb:10:in `float': allocation size is too large (ArgumentError)
    from benchmark_narray.rb:10:in `block (2 levels) in <main>'

旧NArrayではこんなに大きいサイズは扱えないようです。

NumRu::NArray 1.0.3

NumRu::NArrayNArrayにbig memory supportを加えたものだそうです。

require 'numru/narray'
require 'benchmark'

# ENV['OMP_NUM_THREADS'] = '4'

include NumRu

SIZE = 16384

Benchmark.bmbm do |x|
  x.report do
    a = NArray.float(SIZE, SIZE).random
    b = NArray.float(SIZE, SIZE).random
    c = a * b
  end
end

実行結果

       user     system      total        real
   9.370000   7.030000  16.400000 ( 20.239738)

NArrayではエラーになったサイズの計算ができました。

Numo::NArray 0.9.0.1

Numo::NArrayは、期待の新NArrayです。GSLとのインタフェースもあるようです。すばらしいです。

require 'numo/narray'
require 'benchmark'

include Numo

SIZE = 16384

Benchmark.bmbm do |x|
  x.report do
    a = DFloat.new(SIZE, SIZE).rand
    b = DFloat.new(SIZE, SIZE).rand
    c = a * b
  end
end

実行結果

       user     system      total        real
   7.840000   2.570000  10.410000 ( 12.739366)

おお、NumRu::NArrayより速い。

NMatrix 0.2.1

NMatrixは、Rubyでscientific computingをやろうというSciRubyのfast numerical linear algebra libraryです。以前はNArrayと同時にはインストールできなかったのですが、require 'nmatrix/nmatrix'とすれば使えるようになったようです。

require 'nmatrix/nmatrix'
require 'benchmark'

SIZE = 16384

Benchmark.bmbm do |x|
  x.report do
    a = NMatrix.random([SIZE, SIZE])
    b = NMatrix.random([SIZE, SIZE])
    c = a * b
  end
end

実行結果

       user     system      total        real
 129.120000   6.300000 135.420000 (137.681446)

遅いです。

GSL::Matrix 1.16.0.6

GSL::Matrixは、GSLRubyバインディングで使えます。

require 'gsl'
require 'benchmark'

include GSL

SIZE = 16384

Benchmark.bmbm do |x|
  x.report do
    r = Rng.alloc
    a = Matrix.alloc(SIZE, SIZE).map { r.uniform }
    b = Matrix.alloc(SIZE, SIZE).map { r.uniform }
    c = a.mul_elements(b)
  end
end

実行結果

       user     system      total        real
  54.000000   6.220000  60.220000 ( 62.904272)

このコードでは乱数を生成してセットする部分にワンクッションあるのでどうしても遅くなります。でも、NMatrixよりは速いです。

まとめ

今回は、Numo::NArrayが最も速いという結果になりました。

各OSのPATHの初期値

はじめに

FreeBSD 10.1で

$ pkg install ctags

として、Exuberant Ctagsを入れたのに

$ ctags -R
ctags: illegal option -- R
usage: ctags [-BFTaduwvx] [-f tagsfile] file ...

と言われます。/usr/local/bin/ctagsではなく/usr/bin/ctagsが呼ばれていて、FreeBSDPATHの初期値が、Debianと違うことに今さら気付きました。以下、自分のためのメモです。

Debian Sidの場合

/etc/login.defsで設定。

ENV_SUPATH      PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ENV_PATH        PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

/usr/binよりも/usr/local/binが優先される。

OSX Yosemiteの場合

/etc/paths(と/etc/paths.d/)で設定

/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

/usr/binよりも/usr/local/binが優先される。

FreeBSD 10.1の場合

/etc/login.confで設定

default:\
        :passwd_format=sha512:\
        :copyright=/etc/COPYRIGHT:\
        :welcome=/etc/motd:\
        :setenv=MAIL=/var/mail/$,BLOCKSIZE=K:\
        :path=/sbin /bin /usr/sbin /usr/bin /usr/games /usr/local/sbin /usr/local/bin ~/bin:\

/usr/local/binよりも/usr/binが優先される。

最後に

わかってないと地味にひっかかりそう…

Ruby/TkでTk拡張を使っているRubyスクリプトをexe化

はじめに

Ruby/TkとTk拡張で作ったちょっとしたツールWindows利用者に使ってもらう場合、RubyやTk拡張をインストールしてもらう必要があり、気軽に使ってもらうのは難しいです。そこでocraを使って単一のexeファイルにしたのですが、結構てこずりました。

RubyとActiveTclとTk拡張(Plotchart)のインストール

RubyActiveTclのインストールについては前の記事を見てください。

Plotchartは、前の記事ではteacupでインストール後C:\Ruby21\lib\ruby\2.1.0\tkextlib\setup.rbを編集してRuby/Tkに認識させましたが、これだとC:\Tcl\lib\teapot\package\tcl\lib\Plotchart2.3.3C:\Ruby21の外にあるため(?)、ocraでexe化する時に取り込まれません。

そこでsetup.rbは編集せず、Plotchart2.3.3フォルダごとC:\Ruby21\lib\tcltk内にコピーしました。C:\Ruby21\lib\tcltk内は、下記のような感じになります。

f:id:gzalt:20150216215501p:plain

ocraのインストール

「スタートメニュー」→「Ruby 2.1.5p273」→「Rubyコマンドプロンプトを開く」から

C:\Users\hoge>gem install ocra

と入力しocraをインストールしますが、

ERROR:  Could not find a valid gem 'ocra' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://api.rubygems.org/latest_specs.4.8.gz)

と言われるかもしれません。これはバグらしいので、こちらのサイトの記載の通りここにあるAddTrustExternalCARoot-2048.pemC:\Ruby21\lib\ruby\2.1.0\rubygems\ssl_certsにコピーして、再度試してください。

ocraでexe化

無事ocraがインストールできたら、こちらのサイトを参考に、前の記事スクリプトをexe化してみます。

C:\Users\hoge>ocra plot.rbw C:\Ruby21\lib\tcltk --no-autoload --add-all-core

うまくいきましたと言いたいところですが

C:/Users/hoge/plot.rbw:18:in `<top (required)>': uninitialized constant DATA (NameError)

と言われるので、少しコードを修正します。

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require 'csv'
require 'tk'
require 'tkextlib/tcllib/plotchart'

# plotchart.rb を修正するモンキーパッチ
module Tk::Tcllib::Plotchart
  class XYPlot
    def pixel_to_coords(x, y)
      list(tk_call_without_enc('::Plotchart::pixelToCoords', @path, x, y))
    end
  end
end

# データ
data = <<EOS
32.78,1074
62.72,12131
92.70,13944
122.72,13110
152.79,11859
182.90,13204
213.05,11647
243.24,8780
273.47,7412
303.75,6632
334.07,6460
364.43,6369
394.83,6503
425.28,6566
455.76,5250
486.29,2766
516.86,1574
547.48,1062
578.13,914
608.83,3532
639.57,15018
670.35,18992
701.18,6495
732.04,660
762.95,177
793.90,157
824.89,134
855.93,154
887.00,131
918.12,124
949.28,129
980.48,113
1011.73,96
1043.01,103
1074.34,112
1105.72,92
1137.13,95
1168.58,86
1200.08,85
1231.62,66
1263.20,50
1294.83,29
1326.49,40
1358.20,33
1389.95,47
1421.74,93
1453.57,111
1485.45,67
1517.37,41
1549.33,19
1581.33,17
1613.38,11
1645.46,13
1677.59,18
1709.76,15
1741.98,10
1774.23,6
1806.53,13
1838.87,15
1871.25,11
1903.67,6
1936.14,8
1968.64,6
2001.20,8
EOS

# データを読み込み
en, cn = CSV.parse(data).map { |r| [r[0].to_f, r[1].to_i] }.transpose

# データをプロット
pl = Tk::Tcllib::Plotchart::XLogYPlot
     .new([0, en.size, 10**Math.log10(en.size).floor],
          [1, 10**Math.log10(cn.max).ceil],
          width: 600, height: 400) do
  title 'Spectrum'
  xtext 'Channel'
  ytext 'Counts/Channel'
  dataconfig('series1', color: :red)
  cn.each_with_index { |c, i| plot('series1', i, c) }
  pack(fill: :both)
end

# マウスの位置の値の表示
label = TkLabel.new.pack
pl.bind(:Motion, '%x %y') do |x, y|
  ch = pl.pixel_to_coords(x, y)[0].to_i
  if 0 <= ch && ch < en.size
    label.text(sprintf('%10d ch, %10.1f keV, %10d counts',
                       ch, en[ch], cn[ch]))
  end
end

# イベントループ
Tk.mainloop

再度exe化を試みます。

C:\Users\hoge>ocra plot.rbw C:\Ruby21\lib\tcltk --no-autoload --add-all-core

グラフが表示されるので[x]をクリックして終了させると、以下のようにうまくexe化されました。

f:id:gzalt:20150216222753p:plain

サイズは5.14MBになりました。

実行結果

できあがったexeをダブルクリックすると、下記のようなグラフが表示されます。

f:id:gzalt:20150216124756g:plain

RubyActiveTclがインストールされていないWindows環境でも動作するはずです。

最後に

ちょっと大変でしたが、一度やればいいことなので、まあいいか。

Windows 7上でRuby/Tkを使ってPlotchart

はじめに

以前の記事で書いたコードがWindows 7でも動くようにします。

Ruby 2.1.5のインストール

RubyWindows版バイナリは何種類かありますが、今回はRubyInstaller for Windowsを使います。現時点の最新であるRuby 2.1.5をC:\Ruby21にインストールしますが、その際、下記のように「Tcl/Tkサポートをインストールする」に必ずチェックを入れます。加えて、「.rbと.rbwファイルをRubyに関連づける」にチェックを入れておくと、Rubyスクリプトをダブルクリックするだけで実行できて便利です。

f:id:gzalt:20150216081504p:plain

ActiveTclのインストール

RubyInstaller for WindowsRuby 2.1.5に付属のTcl/Tkは8.5なので、ActiveTclWindows (x86) 8.5.17.0をC:\Tclにインストールしました。その後、こちらのサイトをマネさせていただいて、C:\Ruby21\lib\ruby\2.1.0\tkextlib\setup.rbに以下を追記しました。

['C:\Tcl\lib\teapot\package\tcl\lib',
 'C:\Tcl\lib\teapot\package\win32-ix86\lib'].each do |dir|
  TkPackage.add_path(File.expand_path(dir))
end

これでRubyからActiveTclの拡張が使えるようになりました。

Plotchartのインストール

ActiveTclにはPlotchartが含まれていないので、コマンドプロンプトから下記のように追加します。

C:\Users\hoge>teacup install Plotchart

これでRuby/TkPlotchartが使えるようになりました。

実は、PlotchartをインストールするためにActiveTclに含まれているteacupが必要なだけだったので、この後ActiveTclをアンインストールしても、RubyInstaller for Windows付属のTcl/Tkだけで、Plotchartが使えたりします。

実行結果

以下のように、以前の記事で書いたコードがそのままWindows 7で動きました。

f:id:gzalt:20150216124756g:plain

最後に

ちょっとしたツールRuby/Tkで作ってWindows利用者に配布したかったのですが、上記のような作業を皆にやってもらうのは大変なので、ocraを試したいと思います。

VirtualBox上のFreeBSD 10.1でホストのOSX Yosemiteとsshfsでファイル共有

はじめに

OSX Yosemite上のVirtualBox内にFreeBSD 10.1を入れました。早速

$ pkg install virtualbox-ose-additions

として、virtualbox-ose-additionsをインストールしましたが、現時点で「共有ホルダー」は使えないようです。(ここで開発中らしいです。期待しています。)

そこで、sshfsでOSX YosemiteのフォルダをFreeBSDにマウントしたのですが、ちょっとてこずりました。以下、手順メモです。

OSX Yosemiteでリモートログインを有効にする

これが無いと始まりません。「システム環境設定」→「共有」→「リモートログイン」にチェックを入れます。

f:id:gzalt:20150215093844p:plain

VirtualBoxの設定

下記のように、外部との通信用の「アダプター1」(NAT)に加えて、OSX Yosemiteとやりとりするための「アダプター2」(ホストオンリーアダプター)を追加します。

f:id:gzalt:20150215101601p:plain

FreeBSD 10.1の設定

fusefs-sshfsのインストール

$ pkg install fusefs-sshfs

/boot/loader.confの編集

FUSEを有効化する。

fuse_load="YES"

/etc/rc.conffusefs_enable="YES"とするという記事を見かけますが、変わったようです。(参考)

/etc/sysctl.confの編集

ユーザランドでマウントが利用できるようにする。

vfs.usermount=1

/etc/rc.confの編集

ifconfig_em0="DHCP"
ifconfig_em1="inet 192.168.56.203 netmask 255.255.255.0"
vboxguest_enable="YES"
vboxservice_enable="YES"

em1が「アダプター2」(ホストオンリーアダプター)です。OSX Yosemitevboxnet0192.168.56.1なので、適当に192.168.56.203の固定アドレスを設定しました。

/etc/groupの編集

operator:*:5:root,[自分のアカウントを追記]

やっとマウント

FreeBSDを一旦再起動して、ログイン後

$ mkdir hostdir
$ sshfs -o idmap=user 192.168.56.1: hostdir

とすると、パスワードを聞かれるので入力します。うまくいけば、OSX Yosemiteのホームディレクトリがhostdirにマウントされて、読み書きできるようになるはずです。

最後に

かなり前にFreeBSD(98)をいじって遊んだことがあるので、いろいろなつかしいです。

OSX Yosemiteにrbenvとruby-buildでRuby 2.2.0を入れる時のconfigureオプション

自分がOSX Yosemiterbenvruby-buildRuby 2.2.0を入れる時のconfigureオプションを、覚え書きとして残しておきます。

$ RUBY_CONFIGURE_OPTS="--enable-pthread --enable-shared \
--with-readline-dir=`brew --prefix readline` \
--with-openssl-dir=`brew --prefix openssl` \
--with-libyaml-dir=`brew --prefix libyaml` \
--with-opt-dir=`brew --prefix gdbm`:`brew --prefix gmp`:`brew --prefix libffi`" \
rbenv install 2.2.0

見ての通りHomebrewreadline、openssl、libyaml、gdbm、gmp、libffiを入れて、それらを使うようにしています。

Tcl/Tkに関するオプションは何もありませんが、Ruby/TkOSX Yosemiteに入っているTcl/Tk 8.5が普通に使えました。

Ruby/Tkで少しだけインタラクティブなグラフを表示する

はじめに

以前書いたように、Ruby/TkPlotchartでグラフを表示できるのですが、それだけではつまらないので、ちょっとだけマウスと連動させてみます。

コード

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require 'csv'
require 'tk'
require 'tkextlib/tcllib/plotchart'

# plotchart.rb を修正するモンキーパッチ
module Tk::Tcllib::Plotchart
  class XYPlot
    def pixel_to_coords(x, y)
      list(tk_call_without_enc('::Plotchart::pixelToCoords', @path, x, y))
    end
  end
end

# データを読み込み
en, cn = CSV.parse(DATA).map { |r| [r[0].to_f, r[1].to_i] }.transpose

# データをプロット
pl = Tk::Tcllib::Plotchart::XLogYPlot
     .new([0, en.size, 10**Math.log10(en.size).floor],
          [1, 10**Math.log10(cn.max).ceil],
          width: 600, height: 400) do
  title 'Spectrum'
  xtext 'Channel'
  ytext 'Counts/Channel'
  dataconfig('series1', color: :red)
  cn.each_with_index { |c, i| plot('series1', i, c) }
  pack(fill: :both)
end

# マウスカーソルの位置の値を表示
label = TkLabel.new.pack
pl.bind(:Motion, '%x %y') do |x, y|
  ch = pl.pixel_to_coords(x, y)[0].to_i
  if 0 <= ch && ch < en.size
    label.text(sprintf('%10d ch, %10.1f keV, %10d counts',
                       ch, en[ch], cn[ch]))
  end
end

# イベントループ
Tk.mainloop

__END__
32.78,1074
62.72,12131
92.70,13944
122.72,13110
152.79,11859
182.90,13204
213.05,11647
243.24,8780
273.47,7412
303.75,6632
334.07,6460
364.43,6369
394.83,6503
425.28,6566
455.76,5250
486.29,2766
516.86,1574
547.48,1062
578.13,914
608.83,3532
639.57,15018
670.35,18992
701.18,6495
732.04,660
762.95,177
793.90,157
824.89,134
855.93,154
887.00,131
918.12,124
949.28,129
980.48,113
1011.73,96
1043.01,103
1074.34,112
1105.72,92
1137.13,95
1168.58,86
1200.08,85
1231.62,66
1263.20,50
1294.83,29
1326.49,40
1358.20,33
1389.95,47
1421.74,93
1453.57,111
1485.45,67
1517.37,41
1549.33,19
1581.33,17
1613.38,11
1645.46,13
1677.59,18
1709.76,15
1741.98,10
1774.23,6
1806.53,13
1838.87,15
1871.25,11
1903.67,6
1936.14,8
1968.64,6
2001.20,8

実行結果

OSX Yosemiteで実行した結果です。下記のように、マウスカーソルの位置の値がグラフの下に表示されます。

f:id:gzalt:20141219145331g:plain

Ruby 2.2.0同梱のplotchart.rbのバグ

グラフ内におけるマウスカーソルの座標を取得するためにTk::Tcllib::Plotchart::XLogYPlot#pixel_to_coordsを使ったのですが、Ruby 2.2.0 同梱のplotchart.rbにはバグがあるので、下記のようにモンキーパッチを当てています。

module Tk::Tcllib::Plotchart
  class XYPlot
    def pixel_to_coords(x, y)
      list(tk_call_without_enc('::Plotchart::pixelToCoords', @path, x, y))
    end
  end
end

既に修正が本家に取り込まれたようなので、そのうち必要なくなると思います。

最後に

Ruby/TkPlotchartさえ入っていればWindowsでも動きます。しかし、WindowsPlotchartを使えるようにするまでがちょっと面倒でした(こちらに書きました)。