洋食の日記

「だ・である」調ではなく「です・ます」調で書きはじめれば良かったなと後悔してる人のブログです

ひどい腱鞘炎になると一年たってもなおらない

4月になった。手に痛みを感じて湿布貼ったりし始めたのが去年の4月頃なので、腱鞘炎になってだいたい1年がたった。個人的には、痛みを和らげる塗り薬よりも、サポーターでガチガチに固めるのとストレッチが効果があった。もちろん、休むのが大事で、土日にバリバリコードを書くのは控えている。サポーターは、腕も痛むようになったので、ひじ用のモノも買った。痛むのは、肘より下の部分だが、効果があった。

ストレッチは、腕を伸ばすのと、指を1本ずつ引っ張るのをやってる。↓の倉敷平成病院のPDFでいうと①④⑤になる。毎日続けてたら、ピーク時に比べて、かなりラクになった。

http://www.heisei.or.jp/wp-content/uploads/2015/05/nagomi201510reha.pdf

以上、近況でした。

ひどい腱鞘炎になると半年たってもなおらない

はじめに

今後のため記録として残す。タイトルのとおりで、手が全て腱鞘炎という、ひどい腱鞘炎になって半年以上が経ったが、完治する気配がない。今でも寝起きや仕事終わりでは、人差し指や小指は曲げると激痛が走る。原因は手指の使いすぎなので、今の仕事を辞めてしまって、3ヶ月とか半年とかのんびりすれば治るだろうが、長く無職できるほどの貯蓄はないので、働きながら治すよりほかない。

対策

まずサポーターをつけるようにした。痛みの軽減にも効果があるのかもしれないが、とにかくタイポングが楽になった。指に強い痛みがある場合は、指にもサポーターをつけて、手をガチガチに固定する。指が曲げられなくなるので、キーボードに指を突き刺すようにタイプする。タイポが増えるので、指サポーターは、我慢できないほど痛いときのみにしている。

中学生の頃からトラックボールを使っている。少し高いが、Logicoolの傾くものが一番よかった。同じLogicoolのグレードを落とした5,000円くらいのものを使っていたが、思っていた以上に、傾くことが重要だった。手の甲の痛みが、かなり軽減した。

キーボードはHHKBの静音のものに落ち着いた。赤軸の分割キーボードにしたことがあったが、キースイッチの跳ね返りが違うのか、指の関節が痛むようになった。HHKBの静音スイッチが、自分の指にはあっていたようだ。いまは、さらに衝撃を軽減するため、HHKBの下にタオルを敷いている。

パームレストは、FILCOの分割タイプのものを、キーボード用とトラックボール用で2セット買って使っている。分割タイプのほうが、置き方の融通がきいて良い。

プログラミング

とにかく手を使うのをやめることにした。土日は、映画を見るか、本を読むかになった。子どもの頃からプログラミングをしていたので、それを封じられるのが意外としんどい。深追いをせず最低限もので、それでいて身になることをやることにした。Next.jsとかのTutorialを潰していく生活をしている。そのなかでReact Nativeがだいぶおもしろかった。過去にモバイルアプリ開発はやっているが、IDE縛りがしんどかったりして、あまり開発が楽しい印象はなかった。React Nativeは、普段のVimでの開発と変わらないスタイルで、JS/TSでReactを書いていればモバイルアプリが作れる。Expoが優秀というか、これが煩わしい何もかもを吸収してる。外部ライブラリも色々あるので、やりたいことに対して、だいたい誰かが何か作っていて、それを使えば良い感じになっている。入門書では、以下のものが良かった。ひととおり、できるようになる。

映画

新型コロナで自粛生活なこともあってか、旅情感のあるものを欲するようになった。石坂浩二さんが演じる金田一耕助の獄門島は、音楽も映像の雰囲気も良い。瀬戸内海の島で連続殺人事件が起きるのだが、なんだか避暑地への旅行の雰囲気がある。

砂の器も、物語は重厚なものだが、捜査の過程に旅情感がある。前半、寺の門前で瓜を割って食べるシーンが良い。

メンタル

半年以上ケガしてる様なものなので、当然良くない。プログラミングを封じられてるのが良くない。以前は、実装が追いつかないほど、あれ作ろう、これを改良しようという感じだったが、今はなにも思いつかない。これは意外だった。最初は、アイディアを溜め込んでおいて、手が治ったら着手するイメージを持っていた。しかし、開発ができないと次第にやる気が失われ、アイディアも出なくなった。人生で一番空っぽな感覚がある。ただ大人しくはできてるので、腱鞘炎の治療としては良いのかもしれない。あと本もよく読んでいるので、インプットな時期と割り切っている。不思議と闘志みたいなものは失われないもので「手が治ったらデカい事やったるぞ(できるとは言ってない)」みたいなのは漠然とある。

おわりに

ひどい腱鞘炎になって、自分の体にリミットがあるというのを知った。年齢的なこともあるのだろう。手が動かせるようなったら、あれやこれやではなく、大きなことに集中して取り組みたい(漠然)。結局のところ、3ヶ月以上の休暇と、そのための財が欲しい。

近似最近傍探索ライブラリHnswlibのRuby bindingを作った

はじめに

Hnswlibは、C++で書かれたHierarchical Navigable Small World graphsによる近似最近傍探索ライブラリである。近似最近棒探索のベンチマークでも上位に登場する。Ruby bindingがなかったので作成した。

hnswlib | RubyGems.org | your community gem host

使い方

インストールは、普通にgemコマンドでインストールできる。外部ライブラリもPythonも必要ない。

$ gem install hnswlib

APIは単順にバインドしたものと、それらをラップしたAnnoyライクなHnswIndexを用意した。 検索インデックスの作成は、以下のようになる。データを追加すれば、それでグラフ構造が内部で作られるので、build_indexみたいなメソッドはない。 データベクトルはRuby Arrayで表す。

require 'hnswlib'

n_features = 40 # 検索インデックスに追加するベクトルの次元数
max_item = 1000 # 検索インデックスに追加するデータ数

t = Hnswlib::HnswIndex.new(n_features: n_features, max_item: max_item)

# ランダムな値からなるベクトルを1000件追加する.
max_item.times do |i|
  v = Array.new(n_features) { rand }
  t.add_item(i, v)
end

# インデックスを保存する.
t.save('foo.ann')

検索は、検索インデックス中のベクトルを番号で指定し、それをクエリとして検索を行うget_nns_by_itemと、クエリとして任意のベクトルを与えて検索を行うget_nns_by_vectorを用意した。

require 'hnswlib'

# 作成した検索インデックスを読み込む.
n_features = 40
max_item = 1000
u = Hnswlib::HnswIndex.new(n_features: n_features, max_item: max_item)
u.load('foo.ann')

# アイテム番号0番に近い10件を検索する.
# 近傍とされるアイテムの番号がRuby Arrayで返る.
neighbor_ids = t.get_nns_by_item(0, 10)

# 任意のベクトルに近い10件を検索する.
# include_distancesにtrueを与えると, 距離も返す.
q = Array.new(n_features) { rand }
neighbor_ids, neighbor_dists = t.get_nns_by_vector(1, 10, include_distances: true)

おわりに

Hnswlibは、ベンチマークではAnnoyよりも良い検索性能を得ているので、Annoyでイマイチなときに使ってみると良いかもしれない。

github.com

もう3ヶ月ぐらいヒドい腱鞘炎に悩まされていて、長時間の作業は難しく、オリジナルなものを作るのはシンドイので、bindingライブラリを作った。

インストール時にBLISをビルドしてNumo::Linalgのバックグラウンドライブラリに使用するGemを作成した

はじめに

Numo::OpenBLASのBLIS版を作成した。Numo::OpenBLASを作ったのが、だいたい1年前だった。

インストール時にOpenBLASをビルドしてNumo::Linalgのバックグラウンドライブラリに使用するGemを作成した - 洋食の日記

BLISは、最適化BLASの一種で、線形代数計算に対するモダンなAPIも提供している。条件によっては、OpenBLASよりも高速に計算できる様だ。

GitHub - flame/blis: BLAS-like Library Instantiation Software Framework

blis/Performance.md at master · flame/blis · GitHub

作ったきっかけは、Numo::OpenBLASに寄せられた「BLISをつかうオプションがあると良いんじゃない?」というIssueで、同じ層のNumo::OpenBLASにBLISを混ぜるのは文脈的にややこしいので、別のGemにした。最初は、Numo::Linalg::LoaderでBLISを読み込めるようにするPull Requestを作ろうと思ったが、各OSのパッケージでBLISのビルドのオプションにバラつきがあったので、Numo::OpenBLASのように、Gemのインストール時にダウンロードとビルドを行う専用のモノを用意することにした。

使い方

普通にgemコマンドでインストールできる。インストール時に、BLISとLAPACKをダウンロードしてビルドするので、ちょっと時間が必要になる。

$ gem install numo-blis

使用方法はNumo::BLISをrequireするのみで、内部的にNumo::NArrayとNumo::Linalgがrequireされて、ビルドしたBLISとLAPACKがバックグラウンドライブラリに使用される。

require 'numo/blis'

x = Numo::DFloat.new(5, 2).rand
c = x.transpose.dot(x)
eig_val, eig_vec = Numo::Linalg.eigh(c)

Numo::BLISとしては、これだけである。

Numo::BLISが行っていること

Numo::BLISがインストール時に行っていることを、手動でやってみる。BLIS・LAPACKのビルドと、Numo::Linalgでの読み込みである。

まず、BLISとLAPACKGithubからダウンロードする。

$ wget https://github.com/flame/blis/archive/refs/tags/0.8.1.tar.gz
$ tar xvzf 0.8.1.tar.gz
$ wget https://github.com/Reference-LAPACK/lapack/archive/refs/tags/v3.10.0.tar.gz
$ tar xvzf v3.10.0.tar.gz

BLISではconfigureでビルドオプションを指定する。helpオプションでどういうものがあるか確認する。

$ cd blis-0.8.1
$ ./configure --help
...
   -t MODEL, --enable-threading[=MODEL], --disable-threading

                 Enable threading in the library, using threading model
                 MODEL={openmp,pthreads,no}. If MODEL=no or
                 --disable-threading is specified, threading will be
                 disabled. The default is 'no'.
...
   --enable-cblas, --disable-cblas

                 Enable (disabled by default) building the CBLAS
                 compatibility layer. This automatically enables the
                 BLAS compatibility layer as well.
...

重要なのが、マルチスレッドとCBLAS互換に関するオプションがデフォルトで無効になっている点である。特に、CBLAS互換がないとNumo::Linalgでうまく動作してくれないので、これは有効にする必要がある。そんなわけで、configureは次のようになる。後ろについている「auto」は、CPUアーキテクチャを自動で検出してね、というモノである。

$ mkdir build; cd build
$ ../configure --enable-cblas --enable-threading=pthreads --prefix=インストール先ディレクトリ auto
...
$ make
$ make install

BLISのビルドができたので、次はLAPACKをビルドする。LAPACKではcmakeを使う。

$ cd lapack-3.10.0
$ mkdir build; cd build

BLASに先程ビルドしたBLISを指定する必要がある。CMakeのFindBLASを使っている様なので、BLAS_LIBRARIESでパスを指定すればよい。 その他のオプションは、CMakeLists.txtを見て確認した。Numo::LinalgはCインターフェースのLAPACKEを必要とするので、LAPACKEオプションをONにする。

$ cmake -DLAPACKE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX='インストール先ディレクトリ' -DBLAS_LIBRARIES='先程ビルドしたBLISをlibblas.soとかまで指定する' ../
...
$ make
$ make install

あとはこれらをNumo::Linalgで読み込むと、使えるようになる。

require 'fiddle'
require 'numo/linalg/linalg'

Fiddle.dlopen('/hoge/fuga/lib/libblis.so')
Fiddle.dlopen('/hoge/fuga/lib/liblapacke.so')
Numo::Linalg::Blas.dlopen('/hoge/fuga/lib/libblis.so')
Numo::Linalg::Lapack.dlopen('/hoge/fuga/lib/liblapacke.so') 

ここで、注意が必要なのが、BLISは環境変数でスレッド数を指定しないといけないことである。指定しないとシングルスレッドになってしまう。

BLIS_NUM_THREADS=8 ruby hoge.rb

Numo::BLISでは、ENV['BLIS_NUM_THREADS']で確認していて、これがnilであれば、Etc.nprocessorsをセットしている。

おわりに

Numo::OpenBLASをベースにすれば良いので、BLISのビルド方法を調べて、一日でガッと作った。未だ腱鞘炎に悩まされていて、大した開発ができてないのだが、久しぶりにgemを作って、良いストレス解消になった。

RBSでメソッドをオーバーロードするときは三点リーダーを書く

はじめに

タイトルのとおり。RBSによる型注釈が提供されているメソッドをmonkey patchして、その型注釈をpatch.rbsに書きたい時がある。Rubyのノリで書くと、重複定義エラーになる。

ディレクトリ構成

以降の文章は、次のようなディレクトリ構成で、

|- lib
|   `- hoge.rb
|- sig
|   |- patch.rbs
|   `- hoge.rbs
|- Gemfile
`- Steepfile   

bundle exec steep check が動くような感じで、ファイルを用意している。

# Gemfile
source 'https://rubygems.org'

gem 'rbs', '~> 1.2'
gem 'steep', '~> 0.44'
# Steepfile
target :lib do
  signature "sig"

  check "lib"
end

たとえば

Javascriptみたいに実数と文字列の足し算をRubyでもしたいとする(本当はしたくない)。

> 3.1 + '4'
"3.14"

そのために、Floatクラスをmonkey patchする。

# lib/hoge.rb

class Float < Numeric
  alias_method :_org_add, :+

  def + (b)
    return self.to_s + b if b.is_a?(String)
    _org_add b
  end

  private :_org_add
end

これで実数と文字列の足し算ができる。

$ irb
irb(main):001:0> require_relative 'lib/hoge.rb'
=> true
irb(main):002:0> 3.1 + '4'
=> "3.14"
irb(main):003:0> 3.1 + 4
=> 7.1

型注釈を書く

以上のmonkey patchに対してRBSを書くとすると、まず、以下のようなものが思いつく。

# sig/patch.rbs

class Float < Numeric
  alias _org_add +

  def +: (Complex) -> Complex
       | (Numeric) -> Float
       | (String) -> String
end

これで bundle exec steep check すると、重複定義エラーになる。 Floatがcore/float.rbshttps://github.com/ruby/rbs/blob/master/core/float.rbs)で定義されているためである。

(前後省略)

gems/rbs-1.2.1/core/float.rbs:40:2: [error] Non-overloading method definition of `+` in `::Float` cannot be duplicated
│ Diagnostic ID: RBS::DuplicatedMethodDefinition
│
└   def +: (Complex) -> Complex

解決策

三点リーダーを書いて、オーバーロードであることを示す。

# sig/patch.rbs

class Float < Numeric
  alias _org_add +

  def +: (Complex) -> Complex
       | (Numeric) -> Float
       | (String) -> String
       | ... # オーバーロードであると示す
end

これで重複定義エラーがでなくなる。

$ bundle exec steep check
# Type checking files:

..........................................................

No type error detected. 🫖

おわりに

Rubyはmonkey patchが容易なので、よく出くわすエラーだと思う。公式の文法ドキュメントを見て考えたが、実はガイドの方に書いてありました: rbs/sigs.md at master · ruby/rbs · GitHub