Fedora 23でchainerインストール

今,Fedora 23はgccのバージョンが5.3.1なので,そのままchainerをインストールしようとすると,コンパイルエラーが発生する.

原因は,以下の2点

  • CUDAがgcc 4.9までしか対応していないこと.
  • enumの扱いが変わったこと.

CUDAのgcc対応

コードを見ると,

/usr/local/cuda/include/host_config.h

の115行目に,GCCのバージョンが4.9より大きいとエラーになるような #error が書かれている.
仕方ないので,これをコメントアウトして次のようにするとコンパイルが通るようになる.

#error -- unsupported GNU version! gcc versions later than 4.9 are not supported!
   ↓
//#error -- unsupported GNU version! gcc versions later than 4.9 are not supported!

enumの扱い

元々enumはint型として扱えていたが,それができなくなっている.なので,

typedef enum enum_etype{
    a = 1
    b = 2,
    c = 3
} Etype;

int main(){
    Etype x = 0;
}

のようなコードはコンパイルエラーになる.これを防ぐにはgccのオプションに"-fpermissive" をつければ良い.

なので,chainerをインストールする際に,

alias g++="g++ -fpermissive"
pip install chainer

としたらいけた.正しいやり方かどうかわからないけど.

caffeのビルドでハマるとき

自分用メモです.

fedora22でCaffeのビルド時に,cblas関連のリンカエラーが出る場合.

CaffeのデフォルトのBLASライブラリはAtlasですが,何故かうまくいかないので,OpenBLASを使うように変更する.
やり方は,cmakeの時にBLASをAtlasからOpenに切り替える.

C++11の機能によるparallel_for

C++11からは,std::threadというのが追加され,マルチスレッドプログラミングが標準で使えるようになった.

その機能を使って,std::for_each的な処理をマルチスレッド化したいなと思って調べたら以下のページが見つかった.

A parallel for using std::thread? | stackoverflow

やり方について色々議論があるけど,最後に出ている次のようなコードが良さそう.

template<typename Iterator, class Function>
void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1, const int threshold = 1000)
{
    const unsigned int group = std::max(std::max(ptrdiff_t(1), ptrdiff_t(std::abs(threshold))), ((last-first))/std::abs(nthreads));
    std::vector<std::thread> threads;
    Iterator it = first;
    for (; it < last-group; it += group) {
        threads.push_back(std::thread([=,&f](){std::for_each(it, std::min(it+group, last), f);}));
    }
    std::for_each(it, last, f); // use calling thread while we wait for the others
    std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
}

これは次のように利用できる.

std::vector<double> data={1,2,3,4,5};
for_each(data.begin(), data.end(), [](const int& i){
    // i を使った処理
});

という感じで使える.

typedefをテンプレート化したい(エイリアステンプレートというらしい)

template <typename T>
class A{
    T i;
};

みたいなクラスがあった時に,たとえばclass Aをstd::shared_ptrでくるんだ型をtypedefしたい.
普通に考えると,

template <typename T>
typedef std::shared_ptr<A<T>> SharedA<T>;

って書けばいいように思うけど,これはまだサポートされてないらしい.

調べてみると,

C++でtemplate typedef | bettamodokiのメモ
typedef テンプレート | Microsoft Developer Network
typedefテンプレート | ぼく用あれこれまとめ@wiki

と,「C++0xではできるようになる」という感じの色んな記事が見つかる.

ではと思い,上の記事を -std=c++11 オプションをつけてコンパイルしてみたが通らず….

調べた結果,これはエイリアステンプレートというもので実現されているらしい.

How to typedef a template class? | stackoverflow

これによると,書き方は

template <typename T>
using SharedA = std::shared_ptr<A<T>>;

でいいらしい.実際書いてコンパイルしてみたら通った.

staticメンバ関数メモ

staticメンバ関数はクラスを指定した呼び出ししか出来ないと思ってた.

class A{
    public:
        static int getX(){ return x; }
    private:
        static int x;
};
int A::x=0;

int main(){
    int i = A::getX();
    return 0;
}

しかし,まぁ当然だけどインスタンスからも呼べた.

int main(){
    A a;
    int i = a.getX();
    return 0;
}

それだけ.

OpenCVのradiusSearch

今日OpenCVのradiusSearchを使っていてハマったのでメモ.

radiusSearchとは,「ある点(検索クエリ)から半径(radius)いくら以内にある点を探す」という処理.例えば,画像上にいくつかの特徴点があるときにある特徴点から距離r以内にある点を探す,とか,特徴空間中のある点から距離r以内にある点を探す,といったことに使う.実装上は,前者なら画像上の座標を,後者なら特徴空間中の座標=特徴量を各行の値として入れた行列(列数=点の数,行数=座標や特徴量の次元数)に対してIndexというものを作り,そのIndexを使ってradiusSearchを行う.

普通,何も考えずに上記の処理を行おうとすると,ある点と他の全ての点との距離を計算してソートし,上位のものを返せば良いが,それは計算量が大きい.特に何度もそういった処理を繰り返す場合.

これに対して,予め索引を作っておき,高速に検索できるようにするのがIndex.例えば,(Randomized) kd-treeや,Locality-Sensitive Hashing (LSH)などの手法がある.

以下ではRandomized kd-treeの使い方を簡単に説明する.ここで検索対象のデータは,cv::Mat型の変数dataに,前述のとおり,点数(行)×次元数(列)の行列として格納されているとする.この例では,各点から距離r内にある点のうち最大maxRadius個をすべて求める例である.

// 予めIndexを作成しておく.この例ではRandomized KDTreeをTree数4で
cv::flann::Index idx(data, cv::flann::KDTreeIndexParams(4), cvflann::FLANN_DIST_EUCLIDEAN);

int maxRadius=10; // 最大10個の点を求める
for(int i=0; i<data.rows; i++){
    cv::Mat_<int> indices; // dataの何行目か
    cv::Mat_<float> dists; // それぞれどれだけの距離だったか
    idx.radiusSearch(query, indices, dists, r, maxResults);
    for(int j=0;j<indices.row; j++){
        std::cout << indices(j,0) << "[" << dists.row(j) << "]" << " " << std::endl;
    }
}

こちらの公式ドキュメントでは引数が5個だったが,本当は5個目の引数にmaxResultsが必要(6個目の引数は省略可).

Fedora 20素晴らしい

先日メインマシンをFedora 19から20へアップグレードしたが,また新たに別マシンにFedoraをインストールすることになった.

インストールも非常に簡単.DVDを入れて起動するとGUIでLive DVDとして起動するかインストールするかを聞かれ,インストールを選択すると,インストールが始まる.

インストール時にやることといえば,言語の選択,パーティションの選択と,ユーザ作成ぐらい.あとは勝手に進む.最後に再起動してDVDを取り出せばOK.

画像処理の研究をやっているとOpenCVをよく使うが,開発環境のインストールも非常に簡単.

yum groupinstall "C 開発ツールとライブラリー"
yum install opencv-devel

でOK.これでmakeとかgccとか必要なツールと,OpenCVを使った開発に必要なライブラリがインストールされる.しかも,gccのバージョンはかなり新しい4.8.2で,OpenCVのバージョンも2.4.7(最新版は2.4.8)と嬉しい.また,Pythonも2.7.5が入っている.

CentOSで開発しようとすると,gccもOpenCVも古くて,まずgccをソースコードからコンパイルして入れ,さらにOpenCVをコンパイルしていたが,Fedora 20やっぱり素晴らしい.

LinuxでOpenCV使った開発はFedora 20がとても良いと思う.

g++でリンカエラー

Mac OSX 10.9 Mavericksを使っている。homebrewを使ってインストールしたgcc48のg++でコードをコンパイルしていると、次のようなエラーが出た。

Undefined symbols for architecture x86_64:
 "isalpha(int)", referenced from:
 (略)
 ld: symbol(s) not found for architecture x86_64
 collect2: error: ld returned 1 exit status

isalphaが無いとのこと。isalphaがないとかありえない。これは、cctype functions (e.g. isprint) not defined at link time when using g++ -std=c++11が原因。g++でコンパイルするときに-std=c++11オプションをつけるとcctypeがリンクできないとのこと。

解決方法があって、/usr/include/sys/cdefs.hを開いて

#elif defined(__GNUC__) && defined(__GNUC_STDC_INLINE__)

#elif defined(__GNUC__) && defined(__GNUC_STDC_INLINE__) && !defined(__cplusplus)

に書き換えれば良い。なお、編集時にはsudoを使う。

sudo vim /usr/include/sys/cdefs.h

これでコンパイルが通るはず。

Boost.Graphでdotファイルを読み書き

昨日に引き続き、Boost.Graphの話題。

昨日はBoost.Graphを使うということで、Graphの頂点や辺に複数のプロパティを持たせることについて書いた。

今日はBoost.Graphのプロパティをファイルから読み込む方法。ここで、グラフを保持するファイルはGraphvizのdot形式とする。

dot形式は例えば

digraph G{
1 [label="A"];
2 [label="B"];
1->2 [weight=10];
}

のような形とする。
このファイルをBoost.Graphの形式で読み込むには、 #include を利用して、

//グラフの宣言
Graph g;
 
//グラフのプロパティ設定
boost::dynamic_properties dp(boost::ignore_other_properties);
dp.property("id",boost::get(boost::vertex_id,g)); // 自前で定義したvertex_idの型
dp.property("label",boost::get(boost::vertex_name,g));
dp.property("weight",boost::get(boost::edge_weight,g));
 
//グラフを読み込み
boost::read_graphviz(file, g, dp, "id");

これにより、グラフ中の1,2といった頂点の識別子をidとして、dotファイル中のlabelをlabelとして、weightをweightとして読み込むことができる。

Boost.Graphを使う

C++でグラフ構造を、しかもグラフ理論的なアルゴリズム(ワーシャル-フロイド法(Warshall-Floyd, WF法)とか)を使う必要があって、Boost.Graphを使うことになった。

Boost.Graphライブラリは、Boostの数あるライブラリの中でも、やばいライブラリのうちの一つ。Boost沼の一つ。やばい原因はその汎用性の高さにある。

汎用性がめちゃくちゃ高いため、普通に「よーし使うぞー」と思った時には、まず次のような呪文を唱えなくてはならない。

typedef boost::adjacency_list&lt;
    boost::listS, boost::vecS, boost::directedS,
    boost::property&lt;boost::vertex_name_t, std::string&gt;
    boost::property&lt;boost::edge_weight_t, float&gt;&gt; Graph;

上のコードは、頂点に名前、辺に重みを持つ有向グラフを表す。テンプレート引数の最後の2が、頂点に対するプロパティと辺に対するプロパティ。この例だと、頂点に対し、プロパティとして頂点名をstd::string型で指定、辺に対し、プロパティとして重みをfloat型で指定している。ここの指定の仕方を変えることで、様々なグラフを表現することができる。

細かい使い方は他のブログ等を参照してもらうとして、今回ここで書くのは、各頂点に対して複数のプロパティを持たせる方法。

例えば、頂点に名前(ラベル)と何かの確率の値を持たせたいとする。この場合、Boost::Graphへの独自のproperty追加とdynamic_propertiesを使ったdotファイル読み込みを参考に、

namespace boost{
  enum vertex_probability_t{vertex_probability};
  BOOST_INSTALL_PROPERTY(vertex,probability);
}

という風に型を定義しておく。問題は、どうやって複数のプロパティを頂点に割り当てるか。

ここで、再帰的なテンプレートが使われる。実は、boost::property<>はlispのリストみたいに作られていて、

boost::property&lt;boost::vertex_name_t,std::string,boost::property&lt;boost::hogehoge_t, int, boost::property&lt;boost::foobar_t, float&gt;&gt;&gt;

のように利用できる。これを使って

typedef boost::adjacency_list&lt;
    boost::listS, boost::vecS, boost::directedS,
    boost::property&lt;boost::vertex_name_t, std::string, boost::property&lt;boost::vertex_probability_t, float&gt;&gt;,
    boost::property&lt;boost::edge_weight_t, float&gt;&gt; Graph;

とすることで、名前と確率を頂点に持たせたグラフを作ることができる。