ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2013年2月5日火曜日

Scipy.WeaveでPythonにC++埋め込み 5 Numpyの利用

前回の続き
前回はweave.inlineメソッドへのNumpy受け渡しの基本事項について書きました。今回は実際に計算をしていきます。
まず、前回にもお見せした、行と列のインデックスを足して3の倍数となる要素を0にする計算のpure Pythonバージョンをお見せします。
def numpy_mult3_0_pure(npArr):
    for r in xrange(npArr.shape[0]):
        for c in xrange(npArr.shape[1]):
            if (r + c) % 3 == 0:
                npArr[r, c] = 0
これを次のように呼び出す。
>>> side = 1000
>>> npArr = np.ones((side, side))
>>> numpy_mult3_0_pure(npArr)
>>> print npArr
結果は、以下。
[[ 0.  1.  1. ...,  1.  1.  0.]
 [ 1.  1.  0. ...,  1.  0.  1.]
 [ 1.  0.  1. ...,  0.  1.  1.]
 ..., 
 [ 1.  1.  0. ...,  1.  0.  1.]
 [ 1.  0.  1. ...,  0.  1.  1.]
 [ 0.  1.  1. ...,  1.  1.  0.]]
まったく前回のコピペです。
これのweaveバージョンは以下のようになります。
def numpy_mult3_0_weave(npArr):
    code = """
    for(int r = 0; r < NnpArr[0]; r++) { 
        for(int c = 0; c < NnpArr[1]; c++) {
            if((r + c) % 3 == 0) {
                NPARR2(r, c) = 0;
            }
        }
    }
    """
    weave.inline(code, ['npArr'])
前回やった、N+(変数名), (変数名大文字)+(次元数)のアクセス用マクロ関数を使えば、上のように書けるのです。どのくらい早くなるのか時間を計測してみます。
side = 1000
npArr = np.ones((side, side))
from context_timer import Timer
with Timer("Pure"):
    numpy_mult3_0_pure(npArr)
with Timer("Weave"):
    numpy_mult3_0_weave(npArr)
このTimerについてはこちらの記事をご覧ください。出力は以下です。
[Pure] Elapsed: 0.272620201111
[Weave] Elapsed: 0.00578308105469
Weaveの方が50倍近く早くなっていることがわかります。こんな感じにPythonコードにC++のコードを書くことで高速化することができます。

Scipy.WeaveでPythonにC++埋め込み 4 Numpyの基本

前回
PythonにはNumpy(とScipy)というMatlabに似たインターフェースを持つ数値計算に非常に便利なパッケージがあるのはご存知でしょう。というか、Weaveについて調べてココにたどり着いたなら、知っていて当然ですね。例えばこんな感じ。
import numpy as np # numpyのインポート

# 乱数配列の生成 2行3列
a = np.random.rand(2,3)
b = np.random.rand(2,3)

# 各要素ごとの掛け算 Matlabだと a .* b
c = a * b

# 行列としての掛け算 Matlabだと a' * b
d = np.dot(a.T, b) # C = A^T * B (Cは2行2列になります)

# 固有値分解
[E, V] = np.linalg.eig(d)
Matlabでできることはほぼ全部できます。しかも、無料で。このページは参考になります。Numpy for Matlab Users
行列演算や要素同士、固有値計算などのメソッドなどはCで実装されているので高速に実行できます。しかし、こういった汎用的な演算でない場合はPython上でループを回したりして配列にランダムアクセスするようなコードを書く必要がでてきます。 Pythonのようなスクリプト言語でそういったコードを書くと激遅になりますよね。例えば、ある配列の行のインデックスと列のインデックスを足した値が3の倍数だったら0に書き換えるというコードをPythonで書くと次のようになります(あまり良い例ではないですが、お許しください!)。
def numpy_mult3_0_pure(npArr):
    for r in xrange(npArr.shape[0]):
        for c in xrange(npArr.shape[1]):
            if (r + c) % 3 == 0:
                npArr[r, c] = 0
これを次のように呼び出す。
>>> side = 1000
>>> npArr = np.ones((side, side))
>>> numpy_mult3_0_pure(npArr)
>>> print npArr
結果は、以下。
[[ 0.  1.  1. ...,  1.  1.  0.]
 [ 1.  1.  0. ...,  1.  0.  1.]
 [ 1.  0.  1. ...,  0.  1.  1.]
 ..., 
 [ 1.  1.  0. ...,  1.  0.  1.]
 [ 1.  0.  1. ...,  0.  1.  1.]
 [ 0.  1.  1. ...,  1.  1.  0.]]
時間を計測すると、0.384463787079[秒]でした。 これをWeaveを使ってC化して高速化します。この例に移る前にNumpyをWeaveに使う場合の基本から見ていきます。 weave.inlineはPython変数を渡すと自動的にC内で適当な型に変換してくれます(converterを指定することも可能)。Numpyのndarrayを渡した場合も自動で色々と変換を施してくれます。次の例を見てみます。
def numpy_converted_vars():
    npArr = np.zeros((2, 3, 4), dtype=np.uint16)
    npArr[1, 1, 3] = 1
    print '(py) ndim:', npArr.ndim
    print '(py) shape:', npArr.shape
    print '(py) strides:', npArr.strides
    code = """
    // Number of dimension
    std::cout << "ndim: " << DnpArr << std::endl;
    
    // Shape
    std::cout << "shape:";
    for (int i = 0; i < DnpArr; i++) std::cout << " " << NnpArr[i];
    std::cout << std::endl;
    
    // Strides
    std::cout << "strides:";
    for (int i = 0; i < DnpArr; i++) std::cout << " " << SnpArr[i];
    std::cout << std::endl;
    
    // Access to data as a three dimensional array
    std::cout << "npArr[1, 1, 3]: " << NPARR3(1, 1, 3) << std::endl;
    """
    weave.inline(code, [u'npArr'])
このメソッド内では、まず2×3×4の形のすべての要素が0の3次元配列を作ります。確認の為に[1,1,3]の要素だけ1を代入しておきます。 データ型がuint16=2[bytes]で、3次元、C-contiguousのメモリ上のデータ配置で、2 * 3 * 4の形なので、上の3つのprintの結果は
(py) ndim: 3
(py) shape: (2, 3, 4)
(py) strides: (24, 8, 2)
となります。stridesのところとかよくわからない人はこの画像を見て理解するかググってください。
Cのinlineコードの中でこれらにアクセスする方法を見ていきます。ndim, shape, stridesはinlineメソッドの内部でN, D, Sという接頭辞をつけてNnpArr(int), DnpArr(npy_int*), SnpArr(npy_int*)という名前で使用可能になります。npy_int型はint64かint32(環境に依存)のtypedefだと思います。実際にCの内部でどのように変換されているかを生成された.cファイルを見てみます。npArrのオブジェクトはCコードの中では、PyArrayObjectというNumpyのC内部での構造体として解釈し、npArr_arrayという名前で使えるようにしてくれています。
PyArrayObject* npArr_array = convert_to_numpy(py_npArr,"npArr");
NumpyのC APIについては各自ぐぐって欲しいのですが、PyArrayObject構造体のメンバにstridesなどがあるので、
npy_intp* NnpArr = npArr_array->dimensions;
npy_intp* SnpArr = npArr_array->strides;
int DnpArr = npArr_array->nd;
で値を取り出してくれています。inlineコードのndim, shape, stridesの出力は次のようになり、python側での出力と一致しています。
ndim: 3
shape: 2 3 4
strides: 24 8 2
肝心のnpArrという名前は以下のように配列のデータへの先頭ポインタのアドレスを取り出したものとして宣言しています。
double* npArr = (double*) npArr_array->data;
一次元配列としてアクセスする場合はnpArr[0]などとアクセスすれば値を取り出せますが、この例では3次元配列なので、stridesを使って要素にアクセス必要があります。たとえばnpArr[i, j, k]にアクセスしたい場合は、以下のようにする必要があります(PyArrayObject*->dataはchar*型)。
*((double*)(npArr_array->data + (i * SnpArr[0] + j * SnpArr[1] + k * SnpArr[2])))
しかし、これはめんどくさい。実はこれをやるためのマクロ関数を最高4次元配列の場合まで定義してくれています。
#define NPARR1(i) (*((double*)(npArr_array->data + (i)*SnpArr[0])))
#define NPARR2(i,j) (*((double*)(npArr_array->data + (i)*SnpArr[0] + (j)*SnpArr[1])))
#define NPARR3(i,j,k) (*((double*)(npArr_array->data + (i)*SnpArr[0] + (j)*SnpArr[1] + (k)*SnpArr[2])))
#define NPARR4(i,j,k,l) (*((double*)(npArr_array->data + (i)*SnpArr[0] + (j)*SnpArr[1] + (k)*SnpArr[2] + (l)*SnpArr[3])
3次元配列の場合はnpArrの名前を大文字にして後ろに3をつけた名前が付けられていますので、上のようにpython側でnpArr[1,1,3]=1で変更した値にNPARR3(1,1,3)でアクセスできています。
npArr[1, 1, 3]: 1
今回はNumpyのweave.inlineへの受け渡しの基本を紹介しました。次回に実際計算してみます。

[Python] try/except/else/finally, with/asなどについてわからなくなったら

初めてのPython(27章) | kobakoba0723の日記
このお方のブログを見る。

[Python] with文を使った実行時間計測クラス(コンテキストマネージャー)

時間計測にはtimeitとかもありますが、もっとお気楽に測る場合の話です。
Matlab - tic, toc functions analog in Python
ここで紹介されていたやり方です。with文では、withの後ろに与えたコンテキストマネージャクラスがステートメント内に入るときに__enter__が呼ばれ(本来asで受け取る変数を返す)、終わりに__exit__が呼ばれます。これを利用して__enter__で時間計測開始を、__exit__で時間計測結果の出力を行い、with文のステートメントの中のプログラムの実行時間を表示します。with文の詳細については「python with文」でぐぐってください。
# context_timer.py
import time
import sys

class Timer(object):
    def __init__(self, name=None, out=sys.stdout):
        self.name = name
        self.out = out

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        if self.name:
            print >> self.out, '[%s]' % self.name,
        print >> self.out, 'Elapsed: %s' % (time.time() - self.tstart)
これを使って、1から1000までの整数の和を計算する時間を測る場合は次のようにwith文を使います。
from context_timer import Timer
with Timer("Sum"):
    val = 0
    for i in xrange(1, 1001):
        val += i
    print val
出力は次のようになります。
500500
[Sum] Elapsed: 0.00104784965515

2013年2月3日日曜日

Windows 7 64bit でPython環境構築

科学計算をやっている人向けにPythonの環境を簡単に構築できるPython(x,y)は非常に便利ですが、32bit向けのインストーラしか用意されていません。そのため、64-bit版を自分でビルドする必要があります。BLASだATLASだ、色々依存するライブラリをインストールしなくてはならないので結構大変です。そこで、UCIのGohlkeさんという方が公開しているインストーラ群を利用します。

まず、Pythonの64bit版を自分でインストールします。
例えば、Pythonのバージョン2.7でのインストールの場合、下記URLから「Python 2.7.3 Windows X86-64 Installer」のリンクをクリックしてインストーラをダウンロード、インストールしてください。
http://www.python.org/getit/
http://www.python.org/ftp/python/2.7.3/python-2.7.3.amd64.msi(インストーラ直リンク)

ここまでは非常に簡単です。本来、ココからがNumpyやScipyなどをインストールするのは大変な作業ですが、Gohlkeさんのページにあるインストーラを使えば簡単です。感謝です。
Unofficial Windows Binaries for Python Extension Packages
インストールしたいパッケージを探し、利用するPythonのバージョンと「amd64」をファイル名に含むインストーラをダウンロードしてインストールしてください。例えば、Scipyをインストールする場合、「scipy-0.11.0.win-amd64-py2.7.‌exe」を利用します。「Requires Numpy-MKL. 」と書かれているので、「numpy-MKL-1.6.2.win-amd64-py2.7.‌exe」もインストールします。この要領でその他のパッケージもインストールできます。


2013年2月1日金曜日

Numpy.Arrayが整数(Integer)型か浮動少数(Float)型か調べる

数値計算していると、整数型の配列を数値で割ったりすると
>>> a= np.ones(10, dtype=np.uint8)
>>> a /= 10.0
>>> print a
[0 0 0 0 0 0 0 0 0 0]

てな具合に0.1ではない結果になってしまう。

こんな場合は、arrayが整数型か浮動少数型か分けて処理をしたいとか、整数型だったら例外を投げるとかいう処理する必要があります。
例えば、
def normalize(arr):
    arr /= arr.sum()
というメソッドがあったとして、これに整数型を渡すと切り捨てられてしまうので、浮動少数型に限定したい。この場合、np.issubdtypeというメソッドで型が整数か浮動少数か調べます。
def normalize(arr):
    if np.issubdtype(arr.dtype, np.integer):
        raise ValueError("arr must be float.")
    arr /= arr.sum()
みたいな感じ。浮動少数か調べるにはnp.floatを使えばいいはず。
他に良い方法があったら教えて欲しいです。

2013年1月13日日曜日

Scipy.WeaveでPythonにC++埋め込み 3 変数を返す

変数を返す

数値型を返す

def test_ret_int():
    print "---- Return int ----"
    code = """
    return_val = 1;
    """
    ret = weave.inline(code)
    print "ret:", ret

Cの中でint型作成して返すサンプルです。Cのコードから値を返すときには、決してreturn文を使わないでください。weave内では、codeに書かれたコードに到達する前にreturn_valというSCXXのpy::objectが宣言されています。(生成されたcppファイルを見るとわかります。)return_valに代入された変数はweave.inlineメソッドの戻り値として受け取ることができます。APIを見るとわかりますが、py::objectのCの数値型や文字列型を代入すると、自動的にPythonのオブジェクトに変換してくれます。結果は次のようになります。
---- Return int ----
ret: 1

タプルなどを返す


def test_ret_tuple():
    print "---- Return tuple ----"
    code = """
    if(scxxFlag) {
        // scxx
        py::tuple ret(2);
        py::object tmp(1); ret.set_item(0, tmp);
        tmp = 3.5; ret.set_item(1, tmp);
        return_val = ret;
    } else {
        // Python C API is more reasnable.
        return_val = Py_BuildValue("id", 1, 3.5);
    }
    """
    scxxFlag = 1
    ret = weave.inline(code, ["scxxFlag", ])
    print "ret (scxx):", ret
    scxxFlag = 0
    ret = weave.inline(code, ["scxxFlag", ])
    print "ret (Py/C API):", ret
return_valはpy::object型なので、SCXXのpy::tuple, py::dictなどを代入することも可能ですし、Python/C APIのPyObject*を渡すこともできます。ここでは、if文のscxxFlagが1の場合はSCXXでtupleを、0の場合はPython/C APIでtupleを作成しています。(boolの受け渡しはサポートしていないっぽいので、scxxFlagをbool型にしませんでした。) 出力は以下。
---- Return tuple ----
ret (scxx): (1, 3.5)
ret (Py/C API): (1, 3.5)
両方とも同じ結果になっていますね。 Python/C APIの Py_BuildValue はとても便利なので使った方が良いと思います。フォーマットのiはint、dはdouble, sは文字列(char*), OはPyObject*を意味します。 listもdictも簡単に生成できるので試してみてください。
return_val = Py_BuildValue("[i,d]", 1, 3.5);// List
return_val = Py_BuildValue("{s:i,s:[d,s]}", "abc", 1, "def", 3.5, "Hoge"); // Dict
前回の例とあわせて、これで値の受け渡しができるようになりました。次回は、数値計算を利用している方向けにNumpyのArrayを利用した例を見ていきます。

Scipy.WeaveでPythonにC++埋め込み 2 変数を渡す

変数を渡す

数値型を渡す

以下はInt型をPythonからCに渡して標準出力するメソッド例です。
def test_pass_int():
    print "---- Pass int ----"
    val = 1
    code = """
    std::cout << "val: " << val << std::endl;
    """
    weave.inline(code, ['val', ], headers=['', ])
結果は次のようになります。
---- Pass int ----
val: 1
weave.inlineの第2引数はargs=[]のオプション引数であり、Cのコード内に渡したいPython側の変数名を文字列のリストとして渡します。すると、inlineはPython側でローカル変数またはグローバル変数で使われている変数を探します。そして、見つかった変数を適切な型に変換してCコードの中で使えるようにしてくれます。上の例ではvalはPythonのPyInt型なのでC側ではそのまま使えないはずですが、ちゃんとCのint型に変換してくれます。ここでさり気なく、C++スタイルの標準出力プリントを使いました。cout, endlを使うには<iostream>が必要なので、オプション引数headersにヘッダの文字列リストを与えています。

タプルとかを渡す

タプルやリストや辞書型を渡したときにどのように扱わうか見てみます。
def test_pass_tuple():
    print "---- Pass tuple ----"
    seq = (1, 0.5, "Hoge")
    code = """
    std::cout << "length: " << seq.len() << std::endl;
    std::cout << "seq[0]: " << static_cast<int>(seq[0]) << std::endl;
    std::cout << "seq[1]: " << static_cast<double>(seq[1]) << std::endl;
    std::cout << "seq[2]: " << static_cast<char>(seq[2]) << std::endl;
    """
    weave.inline(code, ['seq', ], headers=['<iostream>', ])
数値型や文字列型以外のタプルやリストなどのオブジェクトはSCXXというPython C APIのC++ラッパーライブラリクラスに変換されます。しかし、そのまま使っているわけではなく、weave向けに独自に名前とか書き換えているっぽいです。tuple型はpy::tupleに変換されます。APIは直接weave.__file__の下のscxxフォルダにある*.hファイルを読めばすぐわかります。一応ここ( http://projects.scipy.org/scipy/browser/trunk/Lib/weave/scxx)でも見れます。py::list::operator[]でタプルの中の要素をpy::object(正確にはindexed_ref型)として取り出すことができます。py::objectには、数値型、str型、complex型の変換演算子が定義されているので、代入やキャストによって中身をCの型として取り出すことができます。
出力は以下。
---- Pass tuple ----
length: 3
seq[0]: 1
seq[1]: 0.5
seq[2]: Hoge
リストの場合もSCXXのpy::listに変換され、APIはほぼ同じです。
def test_pass_list():
    print "---- Pass list ----"
    seq = [1, 0.5, "Hoge"]
    code = """
    std::cout << "length: " << seq.len() << std::endl;
    std::cout << "seq[0]: " << static_cast<int>(seq[0]) << std::endl;
    std::cout << "seq[1]: " << static_cast<double>(seq[1]) << std::endl;
    std::cout << "seq[2]: " << static_cast<char>(seq[2]) << std::endl;
    """
    weave.inline(code, ['seq', ], headers=['<iostream>', ])
出力は以下
---- Pass list ----
length: 3
seq[0]: 1
seq[1]: 0.5
seq[2]: Hoge
辞書型はSCXXのpy::dict型に変換されます。APIも非常に簡単です。
def test_pass_dict():
    print "---- Pass dict ----"
    dct = {"PyInt":1, "PyFloat":0.5, "PyString":"Hoge"}
    code = r"""
    std::cout << "length: " << dct.len() << std::endl;
    std::cout << "dct[\"PyInt\"]: " << static_cast<int>(dct["PyInt"]) << std::endl;
    std::cout << "dct.has_key(\"PyInt\"): " << dct.has_key("PyInt") << std::endl;
    std::cout << "dct.has_key(\"PyHoge\"): " << dct.has_key("PyHoge") << std::endl;
    py::list keys = dct.keys();
    std::cout << "keys[0] : " << static_cast<char>(keys[0]) << std::endl;
    py::object tmp = py::object(634);
    dct.set_item("SkyTree", tmp);
    std::cout << "dct[\"SkyTree\"] : " << static_cast<int>(dct["SkyTree"]) << std::endl;
    """
    weave.inline(code, ['dct', ], headers=['<iostream>', ])
出力は以下。
---- Pass dict ----
length: 3
dct["PyInt"]: 1
dct.has_key("PyInt"): 1
dct.has_key("PyHoge"): 0
keys[0] : PyInt
dct["SkyTree"] : 634
例にはないですが、変数は参照として渡されているのでinlineの中で変更したdict型は呼び出し側でも変更されます。

今回は変数を渡す例をお見せしました。次回は変数を返す例を見てみます。

2013年1月11日金曜日

Ubuntu10.04LTSにPython2.7.3をインストール numpy,scipy,matplotlib,opencv,pygtk

ソースからインストールします。全然難しくありません。

sqliteパッケージ使うのに必要だったはず
sudo apt-get install sqlite3

インストール先を指定(ルートインストールしない場合)
IPX=$HOME/python27
export PATH=$IPX/bin:$PATH
export LD_LIBRARY_PATH=$IPX/lib:$LD_LIBRARY_PATH
rootでインストールしてしまう場合は
sudo su -でルートになる。
ルートインストールする場合は--prefixオプションはいらないです。

Python-2.7.3をダウンロードして解凍
./configure --prefix=$IPX --enable-shared && make install

EasyInstallをインストール
サイトから setuptools-0.6c11-py2.7.eggをダウンロード
sh setuptools-0.6c11-py2.7.egg --prefix=$IPX

Pipも
$IPX/bin/easy_install pip

ついでにNumpyやら必要なものをインストール

$IPX/bin/pip install numpy
sudo apt-get install liblapack-dev gfortran g++
$IPX/bin/pip install scipy
$IPX/bin/pip install ipython
$IPX/bin/pip install virtualenv virtualenvwrapper
$IPX/bin/pip install matplotlib
再ログインなどするとパスが通っていないので、
IPX=$HOME/python27
export PATH=$IPX/bin:$PATH
export LD_LIBRARY_PATH=$IPX/lib:$LD_LIBRARY_PATH
を.bashrcなどに追加して常にパスが通っているようにしておく。(rootインストールの場合は必要なし)

ついでにOpenCVもインストールする。

sudo apt-get install cmake-curses-gui
wget http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.3/OpenCV-2.4.3.tar.bz2
ダウンロードして解凍。 cmakeに必要なオプションを渡してインストール。
ccmake .. -DCMAKE_INSTALL_PREFIX=${IPX} -DPYTHON_EXECUTABLE=${IPX}/bin/python -DPYTHON_INCLUDE_DIR=${IPX}/include/python2.7 -DPYTHON_LIBRARY=${IPX}/lib/libpython2.7.so
make install 
OK。
$ ipython
>>> import cv2
で使えます。

PyGTKも

依存関係を考慮してシステムのバージョンに合わせる
pygtk 2.17.0
pycairo 1.8.10
pygobject 2.21.1

py2cairoをインストール
wget http://cairographics.org/releases/py2cairo-1.8.10.tar.gz
tar xvjf py2cairo-1.8.10.tar.gz
cd py2cairo-1.8.10
./waf configure --prefix=$IPX
./waf build
./waf installl
pygobjectインストール
sudo apt-get install libffi-dev
http://ftp.gnome.org/pub/GNOME/sources/pygobject/2.21/pygobject-2.21.1.tar.bz2
tar xvjf pygobject-2.21.1.tar.bz2
cd pygobject-2.21.1
./configure --prefix=$IPX
make install
pygtkインストール
sudo apt-get install libgtk2.0-dev
export PKG_CONFIG_PATH=$IPX/lib/pkgconfig:$PKG_CONFIG_PATH
wget http://ftp.gnome.org/pub/GNOME/sources/pygtk/2.17/pygtk-2.17.0.tar.bz2 
あとはPyGObjectと同じ

2013年1月10日木曜日

Scipy.WeaveでPythonにC++埋め込み 1 Hello world

Pythonって超便利ですね。非常に多様な目的に使われる言語ですが、Numpy・Scipyによる数値計算ライブラリがあるので数値計算・グラフ描画もできます。Matlabなんかもういらないのではないかとおもいますね。しかし、MatlabやPythonのようなインタープリタ言語でループを回したりすると一気に実行速度が激遅になります。そこで、開発者(研究者)はプログラムの高速化が必要な部分をC/C++(以下、C)で書いて呼び出したりします。MatlabではMexによってこれが実現できます。Pythonでもctypes, SWIG, Boost.Python, Cython, Scipy.WeaveによってCによる高速化ができます。今回はWeaveを使った例をお見せします。(Cythonもいずれ紹介します。)

誰もが最初にやるやつ

そう、Hello worldです。WeaveはPythonのコードの中にCコードを直接埋め込むことができます。
from scipy import weave

def test_hello_world():
    print "----- Print ----"
    code = r"""
    printf("Hello world!!\n");
    """
    weave.inline(code)

if __name__ == '__main__':
    test_hello_world()
Cコードを埋め込むための一つのメソッドがweave.inlineです。上のように、第1引数にCのコードを書いた文字列を取ります。出力は、
----- Print ----
Hello world!!
となります。
inlineメソッドは呼び出されたときに第一引数の文字列codeのCコードを実行時コンパイルして呼び出します。これだと呼び出す度にコンパイルが行われ余計に計算時間がかかってしまうと思うかもしれません。しかし実際には、inlineは一度コンパイル済みのものと同じ内容のcodeが渡された時にはコンパイル済みのものを使うため、高速にCコードを呼び出すことができます。 何が起こっているか確認するために、verbose=2と強制的に同じcodeでもコンパイルするためのforce=Trueをオプション引数に追加するように変更してみます。
weave.inline(code, verbose=2, force=True)
いろいろverboseされます。
----- Print ----
<weave: compiling>
running build_ext
running build_src
build_src
building extension "sc_de155a42ad0378e80d2e4d68a547cce52" sources
build_src: building npy-pkg config files
customize UnixCCompiler
customize UnixCCompiler using build_ext
customize UnixCCompiler
customize UnixCCompiler using build_ext
building 'sc_de155a42ad0378e80d2e4d68a547cce52' extension
compiling C++ sources
C compiler: c++ -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -I/usr/local/include -DNDEBUG -g -O3 -Wall

compile options: '-I/usr/local/lib/python2.7/site-packages/scipy/weave -I/usr/local/lib/python2.7/site-packages/scipy/weave/scxx -I/usr/local/lib/python2.7/site-packages/numpy/core/include -I/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'
c++: /usr/local/lib/python2.7/site-packages/scipy/weave/scxx/weave_imp.cpp
c++: /Users/USERNAME/.python27_compiled/sc_de155a42ad0378e80d2e4d68a547cce52.cpp
c++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -isysroot / -L/usr/local/lib /var/folders/jp/rhc_v7hn46g05j5v678bjbfm0000gn/T/USERNAME/python27_intermediate/compiler_f217358f4c5a874fd8a2d639e1b28c86/Users/USERNAME/.python27_compiled/sc_de155a42ad0378e80d2e4d68a547cce52.o /var/folders/jp/rhc_v7hn46g05j5v678bjbfm0000gn/T/USERNAME/python27_intermediate/compiler_f217358f4c5a874fd8a2d639e1b28c86/usr/local/lib/python2.7/site-packages/scipy/weave/scxx/weave_imp.o -o /Users/USERNAME/.python27_compiled/sc_de155a42ad0378e80d2e4d68a547cce52.so
running scons
Hello world!!
ごちゃごちゃとありますが、
c++: /Users/USERNAME/.python27_compiled/sc_de155a42ad0378e80d2e4d68a547cce52.cpp
を見るとCコードが生成されている場所がわかります。僕はMac OSX Lionなので、ホームの.python27_compliedにソースコードが自動生成されているのがわかります。Windwosなどでもverbose=2にすれば、どこにソースが生成されたかを知ることができます。このソースを覗いてみるとweaveがどのようにcodeをCコードに変換しているかがわかります(compiled_func関数)。
今回はまったく実用的でないプログラムでしたが、次はweave.inlineに引数を渡す例をお見せします。
変数を渡す編

2012年11月5日月曜日

Mac OSX Lion で Homebrew 経由で Python の pip 使うときに苦労した話

超雑記。

portなどを使っていた環境からbrewへの移行。
portのときどんなふうにインストールしたか記憶がない。
virtualenv, virtualenvwrapperで仮想環境を作っていた。

(Homebrewのインストールは省略。ユーザー権限でインストール。rootじゃない)

brew install python でpython をインストール
.zshenvに
export PATH=/usr/local/bin:/usr/local/share/bin:$PATH
を追加。普通は.bashrc。

同時にインストールされるというpipでnumpyをインストールしようとすると
すでに入っていると言われた

とりあえず pip freezeで得られるパッケージを全削除して
foreach i (`pip freeze | awk -F== '{print $1}'`)
sudo pip uninstall $i
end
(rootでインストールされていたのでsudo)

再度インストール
しかし
/Library/... permission denied
と怒られる。
root権限のところに書こうとしている。前使っていた環境のpipでの書き込み先だ。

brew uinstall python

brew install python --universal
してみた
しかしだめだ

よくよくインストールログを見るとbrew linkが完了しなかったという情報が

brew link python

してみると
pipがすでにあるからリンク貼れなかったぜ馬鹿野郎と怒られる

brew link --overwrite python

して元からあるやつを上書き。
ちゃんと動いた。
前に使っていたpipがpath上に残っていたせいで、そちらのpipの環境を使ってしまっていたのが原因だった。


2012年10月16日火曜日

pyublasのメモ(vector、matrixの受け渡し)

受け渡しに関するメモ

ublas型でも受け渡しができるので便利、ただし、参照型はconstでしか受け取れない。
// コピーなし受け渡し、しかしmatへのアクセスは遅いらしい
template
pyublas::numpy_matrix takeAndReturn(
  pyublas::numpy_matrix mat)
{
 return mat;
}

// ただ返すだけ
template
pyublas::numpy_matrix returnNumpyMatrix(int numRow, int numCol)
{
 pyublas::numpy_matrix mat(numRow, numCol, 0);
 return mat;
}

// ublasをnumpyに変換して返してくれる
template
pyublas::numpy_matrix returnUblasMatrixAsNumpyMatrix(int numRow, int numCol)
{
 ublas::matrix mat(numRow, numCol, 0);
 return mat;
}

// ublasとして返しても変換テーブルで変換して返してくれる。C++で使っている関数がそのまま使える。
template
ublas::matrix returnUblasMatrix(int numRow, int numCol)
{
 ublas::matrix mat(numRow, numCol, 0);
 return mat;
}

// ublasにコピーして渡される
template
void takeUblasMatrix(ublas::matrix mat)
{
}

// コンパイルは通るが、実行時に怒られる
template
void takeUblasMatrixRef(ublas::matrix &mat)
{
}

// ublasに変換してコピーして渡される
template
void takeConstUblasMatrix(const ublas::matrix mat)
{
}

// constだと参照でも渡せる。
template
void takeConstUblasMatrixRef(const ublas::matrix &mat)
{
}

pyublas使うまでのメモ

環境はUbuntu12.04、Boostはインストール済みとする。ちなみに1.46

まずはPyUblasのインストール
ここから最新版をダウンロード http://pypi.python.org/pypi/PyUblas
tar xvzf PyUblas-VERSION.tar.gz
解凍して
cd PyUblas-VERSION
./configure.py --boost-python-libname=boost_python --use-iterators
sudo make install

boost-python-libnameは/usr/libにあるlibboost_python*.soのlibと.soを除いた部分を設定。
今回はCMakeでサンプルをビルドしてみた。別にMakeでもよい
cd test
すると
sample_ext.cppとsample.pyがある
自分で考えろという意味なのか、Makefileとかがない。
調べると必要なものは
Include: boost, python, pyublas
Library: boost_python, python
で、自分の環境では、
Include:
/usr/local/lib/python2.7/dist-packages/PyUblas-ほげほげ/include
/usr/lib/python2.7
Library:
/usr/lib/libboost_python.so
/usr/lib/libpython2.7.so
にあった

ちなみに、PyUblasのIncludeパスはhttp://documen.tician.de/pyublas/faq.html#where-do-the-headers-get-installedを参考に見つけた。

これを元に、Makefileを作っても良いが、楽をするために、CMakeでやった。

cmake_minimum_required(VERSION 2.8)

find_package(Boost COMPONENTS python REQUIRED)
find_package(PythonLibs REQUIRED)
find_path(PYUBLAS_DIR pyublas /usr/local/lib/python2.7/dist-packages/PyUblas-2011.1-py2.7-linux-x86_64.egg/include)

# 共有オブジェクトを作成する
add_library(
  sample_ext
  SHARED
  sample_ext.cpp
)

# インクルードディレクトリ -Iにあたる
include_directories(
  ${Boost_INCLUDE_DIRS}
  ${PYTHON_INCLUDE_DIR}
  ${PYUBLAS_DIR}
)

# リンクディレクトリ -Lにあたる
link_directories(
  ${Boost_LIBRARY_DIRS}
)

# リンク -lにあたる。libboost_python.soとlibpython2.7.soが設定されている
target_link_libraries(
  sample_ext
  ${Boost_LIBRARIES}
  ${PYTHON_LIBRARIES}
)

# libsample_extとならないようにする
set_target_properties(
  sample_ext
  PROPERTIES PREFIX ""
)

パッケージが見つからなかったときの処理とかは書いていない。
これをCMakeLists.txtというファイル名でsample_ext.cppと同じディレクトリにおいて、
そこから
mkdir build
ccmake ..
して見つからなかったところを設定して、(特にPYUBLAS_DIRのところ)
ジェネレートして、makeして、OKだったら
mv sample_ext.so ../
cd ..
python sample.py

2倍されている結果が帰ってくる

2012年8月1日水曜日

Django - settings.py url.pyでreverseを使う

自分用メモ
url.pyなどで、reverseを使うとurl.pyが実行される時点では、urlの設定はされていないため、うまく動かない。
そこで、django.utils.functionalのlazyを使って、reverseを遅延評価させる。
http://stackoverflow.com/questions/1794655/reverse-django-generic-view-post-save-redirect-error-included-urlconf-doesnt

lazyの解説はここがわかりやすい。

追記
Django1.4からreverse_lazyというメソッドが追加されているのでそれを使えば良い
https://docs.djangoproject.com/en/dev/releases/1.4/#reverse-lazy

例えば、

from django.core.urlresolvers import reverse_lazy

LOGIN_URL = reverse_lazy('accounts-login')

2012年6月15日金曜日

管理者権限のない人がpip, virtualenv, virtualenvwrapperをhomeにインストールする

管理者権限のない人がPython2.7とPython3.2.3をインストールしたときのやり方はコチラ

目的

virtualenv環境を入れて、複数の仮想python環境を構築する。

やり方

基本的には以前に書いた記事と同じやり方。setuptools入れるときは、どのバージョンのpythonでもいいはずなので、とりあえずpython2.7を使うことにする。
まずは、ちゃんとhomeのpythonにパスが通す。(bashrcとかで通している人はOK)
export PATH=$HOME/.local/bin:$PATH
つぎに、setuptoolsのインストーラをダウンロード。ちゃんと2.7用のものをダウンロード。
wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg#md5=fe1f997bc722265116870bc7919059ea
sh setuptools-0.6c11-py2.7.egg
easy_install-2.7 pip
pip install vitualenv virtualenvwrapper
ここで、easy_installでpipをinstallするときに、easy_install-2.7を呼ぶようにしないとダメだった。
次に、virtualenvwrapperを設定する。 次を実行するか。(次回起動時にも反映させるために.bashrcや.zshrcなどに追加しておくべき)
source $HOME/.local/bin/virtualenvwrapper.sh
python仮想環境を作成。例えば、python27用をpy27という名前で作成。
mkvirtualenv py27 -p python2.7
python3.2だったら
mkvirtualenv py32 -p python3.2
仮想環境に有効にするには
workon py32
抜けるときは
deactivate

追記

ログイン時に反映させたい場合は.bashrcなどに
export VIRTUALENVWRAPPER_PYTHON=$HOME/.local/bin/python
source $HOME/.local/bin/virtualenvwrapper.sh
virtualenvをインストールしたときのpythonへのパスを書かなくてはならないっぽい。

2012年6月14日木曜日

ユーザーhomeにpython2.7.3(ついでにPython3.2.3も)をインストールしたときのメモ

目的

職場のRHEL5上のhomeにpython2.7をインストールしたい。管理者権限はない。

やり方

インストール先は$HOME/.localとする。.local/以下にbin, libなどが作成される。

Pythonをインストール

pythonのソースを本家からwgetして持ってきて、configure(インストール先の指定は--prefix)してmake installすれば良いかと思ったが、
wget http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz
tar xvzf Python-2.7.3.tgz
cd Python-2.7.3
.configure --prefix=$HOME/.local
make install
ここで、
hg identify: option -i not recognized
hg identify: option -t not recognized
hg identify: option -b not recognized
./Modules/getbuildinfo.c: In function ‘Py_GetBuildInfo’:
./Modules/getbuildinfo.c:45: error: missing terminating " character
./Modules/getbuildinfo.c:45: error: expected expression before ‘)’ token
./Modules/getbuildinfo.c:46: error: missing terminating " character
./Modules/getbuildinfo.c:46: error: missing terminating " character
./Modules/getbuildinfo.c:47: error: missing terminating " character
./Modules/getbuildinfo.c:47: error: missing terminating " character
./Modules/getbuildinfo.c:53: error: ‘buildinfo’ undeclared (first use in this function)
./Modules/getbuildinfo.c:53: error: (Each undeclared identifier is reported only once
./Modules/getbuildinfo.c:53: error: for each function it appears in.)
./Modules/getbuildinfo.c: In function ‘_Py_hgversion’:
./Modules/getbuildinfo.c:72: error: missing terminating " character
./Modules/getbuildinfo.c:72: warning: ‘return’ with no value, in function returning non-void
./Modules/getbuildinfo.c: In function ‘_Py_hgidentifier’:
./Modules/getbuildinfo.c:79: error: missing terminating " character
./Modules/getbuildinfo.c:79: error: expected expression before ‘;’ token
./Modules/getbuildinfo.c:83: error: missing terminating " character
./Modules/getbuildinfo.c:83: error: expected expression before ‘;’ token
make: *** [Modules/getbuildinfo.o] Error 1
こんな感じのエラーがでた。調べてみると、自分の環境では、Mercurialのバージョンが古いらしい。
$ hg --version
Mercurial Distributed SCM (version 0.9.3)

Copyright (C) 2005, 2006 Matt Mackall 
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Mercurialの新しいのをインストール

今は2.2とからしい。古すぎだろw。ということで、先にMercurialの2.2.2をhomeにインストールする。
cd ..
wget http://mercurial.selenic.com/release/mercurial-2.2.2.tar.gz
tar xzvf mercurial-2.2.2.tar.gz
cd mercurial-2.2.2
PREFIX=$HOME/.local make install
インストール先はPREFIXに指定するらしい。なんか最後の方にpython-docutilsかなんかをinstallしろ、とerrorがでるが無視。一応インストール完了。インストール先のbinディレクトリをPATHの先頭に加えれば、2.2.2のhgが呼ばれる。
$ export PATH=$HOME/.local/bin:$PATH
$ $HOME/.local/bin/hg --version
Mercurial Distributed SCM (version 2.2.2)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2012 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Pythonのインストール続き

Pythonのディレクトリに戻り、再度make installする。
cd ../Python-2.7.3
make install
インストールできた。 $HOME/.local/bin/pythonにaliasもしくはPATHを通せば、使えます。
alias python=$HOME/.local/bin/python
# OR
export PATH=$HOME/.local/bin:$PATH
$ python --version
Python 2.7.3

ついでにPython3.2.3もインストール

wget http://www.python.org/ftp/python/3.2.3/Python-3.2.3.tgz
tar xvzf Python-3.2.3.tgz
cd Python-3.2.3
./configure --prefix=$HOME/.local
make install
何の問題もなくインストール完了。$HOME/.localにはパスを通してあったので、
$ python3
Python 3.2.3 (default, Jun 15 2012, 11:09:08) 
[GCC 4.1.2 20071124 (Red Hat 4.1.2-42)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
とpython3で呼べる。
ソースファイルへのリンクが切れていたらコチラ。Python mercurial Python-3.2.3

2012年5月18日金曜日

pip searchがproxyを通さない

会社でプロキシ使っていると色々困ること多いですよね。
pythonの便利なパッケージ管理システムのpip
会社で使っていたら、searchするとエラーを吐くことが判明。
pipのバージョンは1.1。


調べてみると、pip searchがプロキシを通さないという問題があるらしい
The search command ignore proxy settings
多分こんな感じのエラーが出る。
  File "/usr/lib/python2.6/httplib.py", line 776, in _send_output
    self.send(msg)
  File "/usr/lib/python2.6/httplib.py", line 735, in send
    self.connect()
  File "/usr/lib/python2.6/httplib.py", line 716, in connect
    self.timeout)
  File "/usr/lib/python2.6/socket.py", line 500, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
gaierror: [Errno -2] Name or service not known

どうやらxmlrpcがproxyを使わないらしい。
解決方法が示されているが、違うチケットに投げられ、放置されているくさい

そこでリンク先の上の方に書いてあるパッチを当てる。
リンク先ではlstripを使って「http://」を削除しているが、
実はこれだと、host名の先頭にhttpのいずれかの文字があるとそれも消されてしまう。

以下修正版のdiff。

*** download.py 2012-05-17 14:58:47.800679029 +0900
--- download.py.mod20120517 2012-05-17 14:58:01.300737662 +0900
***************
*** 6,11 ****
--- 6,12 ----
  import shutil
  import sys
  import tempfile
+ import httplib
  from pip.backwardcompat import (md5, copytree, xmlrpclib, urllib, urllib2,
                                  urlparse, string_types, HTTPError)
  from pip.exceptions import InstallationError
***************
*** 22,28 ****
             'unpack_file_url', 'is_vcs_url', 'is_file_url', 'unpack_http_url']
  
  
! xmlrpclib_transport = xmlrpclib.Transport()
  
  
  def get_file_content(url, comes_from=None):
--- 23,44 ----
             'unpack_file_url', 'is_vcs_url', 'is_file_url', 'unpack_http_url']
  
  
! class ProxiedTransport(xmlrpclib.Transport):
!     def make_connection(self, host):
!         self.realhost = host
!         proxy = urlopen.get_proxy()
!         if proxy: 
!             if proxy.startswith("http://"):
!                 proxy =proxy[len('http://'):]
!             h = httplib.HTTP(proxy)
!         else: 
!             h = httplib.HTTP(host)
!         return h
!     def send_request(self, connection, handler, request_body):
!         connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
!     def send_host(self, connection, host):
!         connection.putheader('Host', self.realhost)
! xmlrpclib_transport = ProxiedTransport()
  
  
  def get_file_content(url, comes_from=None):


上記の通りに、pipの中のdownload.pyを変更すればOK.
間違った変更を加えると、動かなくなるので注意。

ちなみに、download.pyはわたしの環境では、
/usr/local/lib/python2.6/dist-packages/pip-1.1-py2.6.egg/pip/download.py
とかにある。

これがベストなやり方ではないかもしれないが、とりあえず動く。

2012年5月17日木曜日

ipythonのedのeditorを変更する方法

terminalで
ipython profile create
すると、~/.ipython/profile_default/ipython_config.pyができるので
c.TerminalInteractiveShell.editor = 'emacs -nw'
を加える。 これでedしたときにemacsが開く

職場のred hat el5上に勝手にpip, virtualenv, virtualenvwrapper環境作ったときのメモ

python2.6はすでにインストール済み

まずはsetuptoolsをインストール
ここから、python2.6の.eggファイルをゲットして、

rootで
wget http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086
sh setuptools-0.6c11-py2.6.egg
rm -f setuptools-0.6c11-py2.6.egg
easy_install pip
pip install virtualenv
pip install virtualenvwrapper
ユーザーで
which virtualenvwrapper.shの場所を確認して、
$ emacs .zshrc # bashの人は.bashrc
[ -f /usr/bin/virtualenvwrapper.sh ] && source /usr/bin/virtualenvwrapper.sh # added

2012年4月1日日曜日

python setup.py installしたモジュールのアンインストール

間違えて違うバージョンのpythonでsetup.py installをしてしまい、どうやって消すんだろと思って調べてみた。ちなみに環境はmac osx(unix系なら同じと思う)

するとStack overflowでこんなページを発見(英語)

どうやらuninstallコマンドは存在せず、自力で消すしかないらしい。
しかし次のやり方で簡単にできるっぽいです。

まずもう一回インストールしてみる。そのときに--recordオプションをつけてインストールされたファイルを出力する。

sudo python setup.py install --record files.txt

そして、files.txtの中のファイルを全部消す。

cat files.txt | xargs rm -rvf

このコマンドはわかりますかね?
わからなくてもできますが、もし知りたい人はunixコマンドの勉強をしてください。