2009年9月24日木曜日

Python ctypes からmecabを使う例

PythonとC/C++のインターフェイスとして有望だと思ったctypesとboost.python。
まずはctypesを利用する例。
もちろん自分でC/C++で書いた関数を呼び出すのでも構わないが、既存のCライブラリをPythonから利用したいというシチュエーションも多いため、まずはそういう例。

というわけで、形態素解析ソフトとして良く使われているmecabのライブラリをPythonから使えるようにしてみる。
ただし、実はmecabのPythonインターフェイスはmecabの開発元から配布されている事に気付いたので、ほんの触りだけ。
ctypesの使い方がなんとなく分かると思う。
要するにシェアードライブラリをそのまま読み込み、その中の関数を呼び出してくれる。
PythonとC間で引数や戻り値を正しくやりとりするために、必要に応じてその型情報を設定してやる必要がある。

ちなみにtext.txtには、インストールしてあるmecab用辞書に合わせて文字コードをUTF-8にした日本語文章が入っている。
他の文字コードの場合には適宜変換が必要になる。
また、unicode文字列を渡すことはできない。(segfaultになる)


import ctypes as C

MECAB_LIB_PATH = '/usr/lib/libmecab.so.1'
libmecab = C.CDLL(MECAB_LIB_PATH)
libmecab.mecab_sparse_tostr.restype = C.c_char_p
libmecab.mecab_version.restype = C.c_char_p
libmecab.mecab_strerror.restype = C.c_char_p

class MyMecab:

def __init__(self, option=''):
self.mecab = libmecab.mecab_new2('mecab ' + option)

def __del__(self):
libmecab.mecab_destroy(self.mecab)

def parse(self, text):
return libmecab.mecab_sparse_tostr(self.mecab, text)

def version(self):
return libmecab.mecab_version()

def error_message(self):
return libmecab.mecab_strerror(self.mecab)

if __name__ == '__main__':
filename = 'test.txt'
text = file(filename).read()
mecab = MyMecab()
result = mecab.parse(text)
print result

2009年9月23日水曜日

PythonとC/C++のインターフェイス開発の手段

手元のマシンにも4GBのメモリを入れ、もっと大きなメモリのデスクトップマシンも用意したが、その一番の理由は大規模なテキストの処理や行列演算などを行ないたいからだ。
行列演算そのものは、特異値分解(SVD)などの一般的なものがほとんどになるため、そのためのライブラリが必要になる。
さらに、できればC/C++でゴリゴリやるよりもスクリプトでやりたい。

それで、やはり馴染みのPythonとその数値演算用モジュールnumpyを使おうと思っている。
SVDをはじめ大抵のことはそれでなんとかなると思う。
しかし、中には自分で細かいところから書かなければいけない事もある。
numpyの良い点は、実際の計算のほとんどはC(Fortran?)で書かれた拡張モジュール内で行なわれるので、Python<->C間でのデータの受け渡しなどのわずかなオーバーヘッドがあるだけで、とても効率良く計算できることだ。例えば行列のかけ算などは実際にはそのすべてがCモジュール内で行なわれる。
しかし行列の要素の一つ一つに個別にアクセスするようなコードをPythonで書くと、その一つ一つでオーバーヘッドが生じ、実計算と比率として無視できなくなってくる。

ということで、やはり自分がやりたい計算はC/C++で書いて、それをPythonからシームレスに呼び出せるようにしたい。
そういう目的のための基本はPythonのCライブラリを利用して正規の拡張モジュールを作成することだろうが、リファレンスカウントなどの面倒も自分でみなければいけないとか何かと面倒っちい。
また、既に存在するC/C++のリソースを取り込むのも面倒だ。
幸い正規のモジュール開発方法以外にも、Pythonには様々なC/C++インターフェイス開発方法が用意されている。

その中で最終的に選択肢として残したのは、ctypesとboost.pythonだ。

ctypesはなんと言っても2.6で正規モジュールになり、2.5.xにもバックポートされていること、またPython3.xでも正規モジュールになっており、先々を考えても安心感があることが大きい。
さらにctypesの良い点は、既存のライブラリ(shared object)があれば、Cコードを書く必要がなく、Pythonサイドだけで方が付くという点だ。
もっともその分Python内でライブラリ関数の引数の型情報の設定などが必要だが。

boost.pythonは、C++のSTLの拡張とも言えるboostライブラリに含まれている。
boostはとても活発に開発されており、標準化の進捗と共にその一部はC++の規格自体に取り込まれるらしい。
そういう活発な開発母体があるので、Python 3.xにも対応するのではないかと期待している。
また、C++用ということもあり、C++のSTLや、もちろんboostのその他のライブラリも利用できるため、何かと便利だ。
で、実際に試してみたのだが、思っていた以上に簡単に使うことができた。
二つの点を除いては、もうこれが本命と言って良いと思う。
一つは、あくまでもPythonの外部から提供されているものであり、ctypesのようにPython正統のものではないこと。
もう一つは、いずれこの点も書きたいが、並列処理との兼ね合い。

ということで、ctypesとboost.pythonがお勧め。
サンプルはいずれ。

Debian GNU/Linux amd64で困ったこと

64ビット版でもほとんど何の問題もないが、若干i386にはあるがamd64には無いパッケージああるようだ。
例えばmozartがない。
$ apt-cache search mozart
とすると、mozart-docなんてのがあるのに、本体がない。

色々ググると、どうやらmozartは64ビット環境ではコンパイルできないらしい。ポインタのサイズとintのサイズが等しいことを前提とするようなプログラミングがされてしまっているようで、そこで問題が出ているらしい。
単純に -m32オプションを付けてコンパイルしただけではだめなのかな。
そのうちソースを持ってきて試してみたいが、そんなことだけでなんとかなるならもう誰かやっていると思われる。
CTMCPを読み進むのにmozartを使いたいが、いざとなったらVMwareの上に32ビット版でも入れようか...
chrootして32ビット環境を、なんて話もあるらしいが、そういうことやったことはないのでまた一調べしてからじゃないと試せないし、他には特にどうしても32ビットで使いたいものもないので、多分VMwareに落ち着くだろう。

そうそう、さすがにCore2Duo+4GBmemだとVMware上のVistaも快適快適。
CPUパワーは正義なり・・・

Linux 32ビット版と64ビット版の違い -- データサイズ

64ビット版のLinuxに移行しても、普通に使っている範囲では32ビット版との違いを意識する事はまずない。
しかし内部ではちゃんと変化がある。

末尾に付けたtest.cというファイルは、
$ gcc test.c
でコンパイルできる。そして実行すると
int = 4
int* = 8
void* = 8
long int = 8
long long = 8
short int = 2
float = 4
double = 8
char = 1
wcahr_t = 4
となる。

これが32ビット版のLinux上では
int = 4
int* = 4
void* = 4
long int = 4
long long = 8
short int = 2
float = 4
double = 8
char = 1
wcahr_t = 4
となるはずだ。

long longが4->8バイトになっている以外に、ポインタが64ビットになっている事が分かる。64ビットアドレッシングになっているからだ。
ちなみに、amd64(64ビット版)の上でも32ビット版としてコンパイルすることはできる。
$ gcc -m32 test.c
とすれば良い。
ただしシステム全体は64ビットで、ライブラリ等ももちろん64ビット版としてコンパイルされているので、-m32でコンパイルするにはlib32で始まる32ビット用パッケージをインストールする必要がある。
$ apt-cache search lib32
などとすればそれらしいものが見付かる。


# filename: test.c
#include < stdio.h>
#include < wchar.h>

int main()
{
printf("int = %d\n", sizeof(int));
printf("int* = %d\n", sizeof(int*));
printf("void* = %d\n", sizeof(void*));
printf("long int = %d\n", sizeof(long int));
printf("long long = %d\n", sizeof(long long));
printf("short int = %d\n", sizeof(short int));
printf("float = %d\n", sizeof(float));
printf("double = %d\n", sizeof(double));
printf("char = %d\n", sizeof(char));
printf("wcahr_t = %d\n", sizeof(wchar_t));
}

Linux 32ビット版と64ビット版の違い -- アドレス空間

というわけで今やメイン環境はDebian GNU/Linux x86_64(64ビット版)になった。

そもそも何故64ビット版に移行したのかというと、このノートもメモリが4GBだが、もっと多くのメモリを搭載したデスクトップマシンを使えるようになったが、その時にアドレス空間が32ビットという足枷を外したかったからだ。
Core2Duo(Pentium4位から?)には実は拡張が施されていて、32ビットモードでもアドレス空間だけは36ビットでアドレッシングできるらしい。
4GBというと丁度32ビットアドレッシングの限界なわけで、それ以上のメモリを積んでも扱えなくなってしまうわけだが、36ビットになって4ビット増えた事でその16倍、つまり64GBまでは扱えるようになっていたらしい。今度手に入れたデスクトップもさすがにそこまでのメモリは積んでいないので、32ビット版のLinuxでも良いのかも知れない。

が、実は32ビット版には、1プロセスでは2GBまでしか利用できないという限界もあるのだそうだ。さらに、実際のカーネルによるメモリ配置と利用法により、実際には2GBすら使えないらしい。
これまで使っていたマシンで最大のものでも2GBメモリだったので、そこまでを現実問題として考える事はなかった。しかしもはやノートでも4GB、そしてそれ以上のメモリが利用できるようになった。

そして64ビット版では、アドレス空間は64ビットに綺麗に広がり、1プロセスでの上限の壁も消えているらしい。
という事で、64ビット版に移行した第一の目的は、大規模メモリを無駄なく利用できるようにするためだ。

Linux x86_64 (64ビット版)

ふう、なんと先月は一度も書かなかった。やっぱ続かないな...
というわけで久し振りだけど、無理せず少しずつメモがわりに書いていこうと思う。
なるべくググってもあまり情報が見付からなかった事を中心に。

一時はVistaをメインにしてみようかなどとも考えてVista上にEmacsを導入したりC++コンパイラを入れたりとやっていたが、結局Linuxに戻ったという話。
しかし戻った先は64ビット版だった。

実は先月自宅メインマシンとすべくThinkPad T500を購入した。
IBMからlenovoに変わってからのモデルはちょっと敬遠していたが、他に移る先も見付からず、設計はまだ日本の大和らしいという事でThinkPadに落ち着いてしまった。
ディスプレイは1680x1050、WSXGA+という規格なのだそうだが、最近主流のワイドというのだろうか、横長だ。正直、横より縦が欲しいし、液晶の上、筐体としてはまだ余裕あるよなぁと思うものの、昨今の液晶パネル生産・供給事情というものもあるのかもしれない。
それでもこれだけ横があると、Gnomeのパネルを左右どちらかに縦にして置いて、そしてEmacsを二枚並べても余裕がある。これはいい。
そして今なら当然と言えば当然だが、自宅ノートでは初めてCore2Duo搭載マシンだ。
メモリもこの際4GBにした。
一昔前と比べるととにかく安い。これで12万ちょっとだった。
まだタマが入っていた頃のLet's note、あれはいくらだっただろう・・・CPUは何だっただろう・・・メモリは・・・ディスプレイは・・・・・・本当に隔世の間がある。
ハードディスクにしても容量が足りなくて、ext2に圧縮パッチを当てて自動圧縮して稼いでいたな。

それで、結局というかやっぱりというか、Debian GNU/Linuxに戻ってきたわけだが、この機に64ビット版に移行した。
Debianのディストリビューションには様々なプラットフォーム向けがあるが、フツーのPC、つまりIntelのPentium系列の場合には一般的に i386 というものが使われている。
しかしPentium4あたりからだったか、忘れたけれど、今のCore2Duoには64ビット拡張が施されていて、「64ビット版」として動作する。
ここでわざわざカッコ付きにしたのは、64ビット版ってどういう意味?というのをテキトーに流したかったから。何が64ビットだと64ビット版だとか議論し初めると、なかなか複雑な話になるようだ。
とりあえずは、レジスタも64ビット長になり本数も増え、64ビット演算もあるらしい。
マシンインストラクションの構成と構造も32ビットモードと64ビットモードで異なるらしい。

細かい事を考えなくても、今時のフツーのPCを使っていれば、i386版ではなく amd64 版のインストーラを使うだけで、何の問題も違いもなくインストール作業は終了する。
とりあえず lenny の amd64 用イメージをダウンロードしてCD-Rに焼き、最低限だけインストール(インストールの最後の方で聞かれる環境だのサーバー環境だの、そういうのも一切チェックを外して入れない)し、すぐに /etc/apt/sources.list を編集して sid にし、apt-get update, apt-get dist-upgradeを行なった。
その後リブートし、必要なパッケージを随時インストールした。

とりあえず見た目は32ビット版と何も変わらない。 uname -a とするとカーネルのバージョンに amd64 という文字列が含まれている位だ。