Windowsの更新を確認したところ、アップデートあったので、
いつもどおりにやったら、エラーが出て起動に失敗しました。
自動修復で、どうにか復帰。巻き戻ったので、またアップデートをかけたら、
また、再起動後にエラーが出てしまった(IRQ_NOT_LESS_OR_EQUAL)。今度は、自動修復にも失敗してしまった。
自動修復が失敗した後、詳細オプションで、選べる中に、「そのまま続ける」
というのがあったので、選んでみたがなにも反応がない。
電源を落として、再び、起動しなおし。
次に、リフレッシュを選んだところ、Windows8になって起動した。
とりあえず、 Windows8でも起動してくれたので、ほっとしたところ。
Windowsの更新をすると、ストアに8.1へのアップデートが現れたので、再び8.1へ。
と思ったら、失敗。
その後、チップセットのドライバをA07にして、 再び挑戦したところ、無事に8.1へとアップデートできた。今度は、8.1で全ての更新を適用しても、起動時にエラーが起こることはなく、使えるように復帰しました。
なにがよくて、なにが悪かったのか、はっきりせずにちょっと心配ですが、以前のように動作している様子。リフレッシュ後、元の状態に戻すのには時間がかかるので、もうやりたくないですね。
2014年7月31日木曜日
2014年7月26日土曜日
Windowsのgnuplotで、plot sin(x)をしてみた
Javaは知らないけど、便利なライブラリがあるようなので、
Clojureを触ってみようということでやってみました。
数値を得たら、プロットしてみるということはしばしばあります。 pythonだとmatplotlibを使ったりしますが、 gnuplotを呼び出すことができれば、 グラフ描画をgnuplotにまかせることもできそうです。
gnuplotは、gp463-win32-setup.exeを使ってインストールしたも のを利用しています。(ver.4.6.5でも大丈夫です) コマンドラインでgnuplotが実行できるように、インストールされたgnuplot.exeがあるディレクトリへあらかじめPathを通しておきます。
Clojureは、1.6.0をダウンロードしました。 Getting Startedにあるように、
java -cp clojure-1.6.0.jar clojure.main
で、REPLを起動して利用しています。
以下が、そのスクリプトですが、 プロセスを開始するgpstart、gnuplotのコマンドを送るgpdo、 終了するgpstopという関数を作っています。 sin(x)をプロットするのは、demo関数で行います。
Pythonで書いたスクリプトは以下の通り。
clispやClojureを使ってもgnuplotを操作できそうなので、ひと安心です。
Rubyでもやってみました。
Windows環境でirbを利用する人は少ないのかな。 ActiveScriptRuby 2.1.2-p95を利用してみたのですが、 irbのインタラクティブな環境で、Backspaceがちゃんと動作しませんでした。 検索してみたら、1.9.2での同様な症状と対処法がこちらにありました。 irbに--noreadlineオプションをつけて起動すると、正常にBackspaceできるようになりました。
2016/07/24追記
同じものをC言語で。Windows Vista 32bit MinGWのgcc-4.9.3で確認。
#include<stdio.h>
FILE *gpstart(void);
void gpstop(FILE *p);
void gpdo(FILE *p, char *s);
FILE *gpstart(void) {
/* start gnuplot process */
return popen("gnuplot.exe --persist", "w");
}
void gpstop(FILE *p) {
/* stop gnuplot process */
gpdo(p, "quit");
pclose(p);
}
void gpdo(FILE *p, char *s) {
/* send command string to gnuplot process */
fprintf(p, s);
fprintf(p, "\n");
}
void demo(void) {
/* demo function */
FILE *p;
p = gpstart();
gpdo(p, "set xlabel \"x\"");
gpdo(p, "set ylabel \"y\"");
gpdo(p, "plot sin(x)");
gpstop(p);
}
void main(void) {
demo();
}
数値を得たら、プロットしてみるということはしばしばあります。 pythonだとmatplotlibを使ったりしますが、 gnuplotを呼び出すことができれば、 グラフ描画をgnuplotにまかせることもできそうです。
gnuplotは、gp463-win32-setup.exeを使ってインストールしたも のを利用しています。(ver.4.6.5でも大丈夫です) コマンドラインでgnuplotが実行できるように、インストールされたgnuplot.exeがあるディレクトリへあらかじめPathを通しておきます。
Clojureは、1.6.0をダウンロードしました。 Getting Startedにあるように、
java -cp clojure-1.6.0.jar clojure.main
で、REPLを起動して利用しています。
以下が、そのスクリプトですが、 プロセスを開始するgpstart、gnuplotのコマンドを送るgpdo、 終了するgpstopという関数を作っています。 sin(x)をプロットするのは、demo関数で行います。
(defrecord GnuplotProc [proc out in]) (defn gpstart "Start gnuplot process" [] (let [proc (.start (doto (ProcessBuilder. '("gnuplot" "--persist")) (.redirectErrorStream true))) out (clojure.java.io/writer (.getOutputStream proc)) in (clojure.java.io/reader (.getInputStream proc))] (GnuplotProc. proc out in))) (defn gpstop "Stop gnuplot process" [proc] (.destroy (get proc :proc))) (defn gpdo "send command string to gnuplot process" [proc s] (let [w (get proc :out)] (.write w (str s)) (.newLine w) (.flush w))) (defn demo "Demo function" [] (def p (gpstart)) (gpdo p "set xlabel \"x\"") (gpdo p "set ylabel \"y\"") (gpdo p "plot sin(x)") (read) (gpstop p)) (demo)clispと、pythonでも書いてみました。
(defun gpstart ;Start gnuplot process () (make-pipe-io-stream "gnuplot --persist" :buffered t)) (defun gpstop ;Stop gnuplot process (proc) (let () (gpdo proc "quit") (close proc))) (defun gpdo ;send command string to gnuplot process (proc s) (let () (format proc "~A~%" s) (force-output proc))) (defun demo ;demo function () (let ((p (gpstart))) (gpdo p "set xlabel \"x\"") (gpdo p "set ylabel \"y\"") (gpdo p "plot sin(x)") (read) (gpstop p))) (demo)
Pythonで書いたスクリプトは以下の通り。
import subprocess def gpstart(): """ Start gnuplot process """ proc = subprocess.Popen(["gnuplot", "--persist"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) return proc def gpstop(proc): """ Stop gnuplot process """ gpdo(proc, "quit") proc.kill() def gpdo(proc, s): """ send command string to gnuplot process """ proc.stdin.write(s) proc.stdin.write("\n") proc.stdin.flush() def demo(): """ Demo function """ p = gpstart() gpdo(p, 'set xlabel "x"') gpdo(p, 'set ylabel "y"') gpdo(p, 'plot sin(x)') raw_input() gpstop(p) if __name__ == '__main__': demo()この程度なら、行数はほぼ一緒ですね。
clispやClojureを使ってもgnuplotを操作できそうなので、ひと安心です。
Rubyでもやってみました。
def gpstart # Start gnuplot process IO.popen("gnuplot --persist", "r+") end def gpstop(io) # Stop gnuplot process io.close end def gpdo(io, s) # send command string to gnuplot process io.puts(s) end def demo io = gpstart gpdo(io, "set xlabel \"x\"") gpdo(io, "set ylabel \"y\"") gpdo(io, "plot sin(x)") gets gpstop(io) end if __FILE__ == $0 demo endシンプルですね。
Windows環境でirbを利用する人は少ないのかな。 ActiveScriptRuby 2.1.2-p95を利用してみたのですが、 irbのインタラクティブな環境で、Backspaceがちゃんと動作しませんでした。 検索してみたら、1.9.2での同様な症状と対処法がこちらにありました。 irbに--noreadlineオプションをつけて起動すると、正常にBackspaceできるようになりました。
2016/07/24追記
同じものをC言語で。Windows Vista 32bit MinGWのgcc-4.9.3で確認。
#include
FILE *gpstart(void);
void gpstop(FILE *p);
void gpdo(FILE *p, char *s);
FILE *gpstart(void) {
/* start gnuplot process */
return popen("gnuplot.exe --persist", "w");
}
void gpstop(FILE *p) {
/* stop gnuplot process */
gpdo(p, "quit");
pclose(p);
}
void gpdo(FILE *p, char *s) {
/* send command string to gnuplot process */
fprintf(p, s);
fprintf(p, "\n");
}
void demo(void) {
/* demo function */
FILE *p;
p = gpstart();
gpdo(p, "set xlabel \"x\"");
gpdo(p, "set ylabel \"y\"");
gpdo(p, "plot sin(x)");
gpstop(p);
}
void main(void) {
demo();
}
2014年7月19日土曜日
クイックっぽいソート
Web上をいろいろ見ていたら、Hskellの5行のクイックソートというのがあるみたいで、Pythonでも書いてみました。
Quickソートが何をやっているのかを書き記したようなものみたいになっています。
pythonでもそんなに長くならないですね。
CLISPでも動くように、一対一で書き直してみると、
当たり前ですが、あまり変わらないですね。
def qlikesort(data): if len(data) < 1: return data smaller = [x for x in data[1:] if x < data[0]] larger = [x for x in data[1:] if x >= data[0]] return qlikesort(smaller) +[data[0]]+ qlikesort(larger)
pythonでもそんなに長くならないですね。
CLISPでも動くように、一対一で書き直してみると、
(defun qlike-sort (s) (if (null s) nil (let ((smaller (remove-if (lambda (x) (>= x (car s))) (cdr s))) (larger (remove-if (lambda (x) (< x (car s))) (cdr s)))) (concatenate 'list (qlike-sort smaller) (list (car s)) (qlike-sort larger)))))
当たり前ですが、あまり変わらないですね。
Graffiti入力っぽいキーボードを修正した
DELLのLatitude10でしばらく使っていたGraffiti入力っぽいソフトウェアキーボードに、ショートカット機能を追加してみた。ショートカットストロークのあとに、アルファベットのストロークで、コマンドを実行するものだ。ショートカットで起動させるコマンドは、テキストファイルに書き込んでおくようにした。python+wxPythonで書かれたスクリプトを修正するのは、動作を確認しながら、少しずつ手を加えていき、30分ほどで修正が完了した。
Windowsタブレットは、あたりまえだけどWindows上で作成したスクリプトがそのまま動作するので、自作のスクリプトを動作させる環境としては、とてもよいものだと思う。操作方法も特別なことはないし。最近、8インチのWindowsタブレットが気になるところですが、Latitude10が調子よく動作しているので、しばらくはないかな。
Windowsタブレットは、あたりまえだけどWindows上で作成したスクリプトがそのまま動作するので、自作のスクリプトを動作させる環境としては、とてもよいものだと思う。操作方法も特別なことはないし。最近、8インチのWindowsタブレットが気になるところですが、Latitude10が調子よく動作しているので、しばらくはないかな。
2014年5月23日金曜日
アルファベットで入力
ハングル文字やキリル文字など、普通の日本語キーボードから入力しにくい文字があります。
そのような文字と一対一に対応したアルファベットや記号のシーケンスを利用して、入力できるようにしたら便利そうです。
そこで、アルファベットの文字列と入力したい文字をTAB区切りで並べたテキストファイルを読み込んで、それを参照して入力できるスクリプトをpythonとwxPythonを用いて書いてみました。ハングル文字は、unicode上で規則的に並んでいるようです。それを出力するスクリプトもpythonで書いて読み込むファイルを作成しました。
アルファベットの並びと文字の対応ファイルは選択できるようにしたので、ほかの文字セットの入力にも使えそうです。 シンプルなスクリプトですが、使いどころがありそうです。アルファベットで入力するような方法は、そのままのIMEを使って、特別なキー配列を覚える必要もないのがよいですよね。
アルファベットとハングル文字の対応ファイルは次のpython(2.7)スクリプトを実行して、出力をファイルにリダイレクトするとほぼ完成です。
そのような文字と一対一に対応したアルファベットや記号のシーケンスを利用して、入力できるようにしたら便利そうです。
そこで、アルファベットの文字列と入力したい文字をTAB区切りで並べたテキストファイルを読み込んで、それを参照して入力できるスクリプトをpythonとwxPythonを用いて書いてみました。ハングル文字は、unicode上で規則的に並んでいるようです。それを出力するスクリプトもpythonで書いて読み込むファイルを作成しました。
アルファベットの並びと文字の対応ファイルは選択できるようにしたので、ほかの文字セットの入力にも使えそうです。 シンプルなスクリプトですが、使いどころがありそうです。アルファベットで入力するような方法は、そのままのIMEを使って、特別なキー配列を覚える必要もないのがよいですよね。
アルファベットとハングル文字の対応ファイルは次のpython(2.7)スクリプトを実行して、出力をファイルにリダイレクトするとほぼ完成です。
STARTCHAR = ('g', 'kk', 'n', 'd', 'tt', 'r', 'm', 'b', 'pp', 's', 'ss', '', 'j', 'jj', 'ch', 'k', 't', 'p', 'h') MIDCHAR = ('a', 'ae', 'ya', 'yae', 'eo', 'e', 'yeo', 'ye', 'o', 'wa', 'wae', 'oe', 'yo', 'u', 'wo', 'we', 'wi', 'yu', 'eu', 'ui', 'i') PCHAR = ('', 'g', 'kk', 'gs', 'n', 'nj', 'nh', 'd', 'l', 'lg', 'lm', 'lb', 'ls', 'lt', 'lp', 'lh', 'm', 'b', 'bs', 's', 'ss', 'ng', 'j', 'ch', 'k', 't', 'p', 'h') c = 0 for s in STARTCHAR: for m in MIDCHAR: for p in PCHAR: seq = s+m+p code = 0xAC00 + c uni_line = "%s\t%s" % (seq, unichr(code)) utf8_line = uni_line.encode('utf-8') print utf8_line c = c+1
2014年4月8日火曜日
ちょっとしたpythonのコード片を試すエディタ
ちょっとpythonのコードを試したり、電卓代わりに使ったり、そういうときは、pythonのインタラクティブシェルをよく使います。でも、forループやちょっと関数定義してみたりしたときには、やり直すのが面倒だなと思いながらも、テキストエディタでスクリプトファイルを書いては、コマンドラインから実行していました。
そうこうしているうちに、エディタで書きつつ、その一部を実行してみたりしたいなと思ったしだいです。Mac OS 8.6を使っていたときには、pythonではないですが、そんな感じのメモ帳があってしばしば利用して便利だった覚えがありましたし、SqueakのWorkspaceで遊んでいると、あらためてエディタから、コードを実行できるといいなと思いました。
そこでpythonでも、ちょっとしたエディタを書いてみました。選択したテキストをexecや、eval()に渡すだけというものですが、使ってみると便利ですね。GUIにはいつもwxPythonを利用していますが、今回は、pythonをインストールしたら利用できるTkinterを使いました。pythonのTurtle graphicsで遊ぶときにも、こういうエディタは便利かと思います。
そうこうしているうちに、エディタで書きつつ、その一部を実行してみたりしたいなと思ったしだいです。Mac OS 8.6を使っていたときには、pythonではないですが、そんな感じのメモ帳があってしばしば利用して便利だった覚えがありましたし、SqueakのWorkspaceで遊んでいると、あらためてエディタから、コードを実行できるといいなと思いました。
そこでpythonでも、ちょっとしたエディタを書いてみました。選択したテキストをexecや、eval()に渡すだけというものですが、使ってみると便利ですね。GUIにはいつもwxPythonを利用していますが、今回は、pythonをインストールしたら利用できるTkinterを使いました。pythonのTurtle graphicsで遊ぶときにも、こういうエディタは便利かと思います。
2014年1月31日金曜日
matplotlibのMultiCursor
matplotlibは、wxPythonと簡単に組み合わせて利用できて便利ですね。
matplotlibのグラフ上にマウスカーソルに合わせて動く十字のカーソルを表示するMultiCursorというものがありますが、 twinx()してY2軸を利用すると奇妙なことがおこりました。
カーソルは、Y軸に描画されながらも、その表示位置はY2軸上の値になるという状態です。
もちろん、カーソルを描画する対象をY2軸のほうにすると矛盾のない状態にはなります。
そもそもMultiCursorを利用する対象にY2軸を追加するような利用の仕方は、あまりなさそうです。そうはいっても、そのような要求があったため、Y2軸を追加しても、カーソル位置は、Y軸のほうで決まるようにしてみました。
マウス移動のイベントを処理するonmove()に渡されるeventからキャンバス上のx,y座標が得られるので、それを利用してグラフ軸上のデータに焼きなおすようにしました。
使い方はオリジナルのMultiCursorとほぼ同じですが、parentウィンドウを渡すようにして、そちらへ座標を通知するイベントをポストしています。parentウィンドウでそのイベントを拾えば、カーソル移動にあわせて座標位置を表示できます。
matplotlibのグラフ上にマウスカーソルに合わせて動く十字のカーソルを表示するMultiCursorというものがありますが、 twinx()してY2軸を利用すると奇妙なことがおこりました。
カーソルは、Y軸に描画されながらも、その表示位置はY2軸上の値になるという状態です。
もちろん、カーソルを描画する対象をY2軸のほうにすると矛盾のない状態にはなります。
そもそもMultiCursorを利用する対象にY2軸を追加するような利用の仕方は、あまりなさそうです。そうはいっても、そのような要求があったため、Y2軸を追加しても、カーソル位置は、Y軸のほうで決まるようにしてみました。
マウス移動のイベントを処理するonmove()に渡されるeventからキャンバス上のx,y座標が得られるので、それを利用してグラフ軸上のデータに焼きなおすようにしました。
使い方はオリジナルのMultiCursorとほぼ同じですが、parentウィンドウを渡すようにして、そちらへ座標を通知するイベントをポストしています。parentウィンドウでそのイベントを拾えば、カーソル移動にあわせて座標位置を表示できます。
from matplotlib.widgets import MultiCursor import wx import wx.lib.newevent class MyMultiCursor(MultiCursor): def __init__(self, parent, canvas, axes, color='r', lw=1, useblit = True, horizOn=True, vertOn=True): if matplotlib.__version__ >= '1.3.0': MultiCursor.__init__(self, canvas, axes, color=color, lw=lw, useblit=useblit, horizOn=horizOn, vertOn=vertOn) else: MultiCursor.__init__(self, canvas, axes, color=color, lw=lw, useblit=useblit) # Produce New Event for Cursor Move Notification to parent window. self.parent = parent self.CursorMoveEvent, self.EVT_CURSOR_MOVE = wx.lib.newevent.NewEvent() self.visible = False def xydata(self, x, y): #make new xdata, ydata cwidth, cheight = self.canvas.GetSize() target = None for ax in self.axes: #find the axes in which the mouse cursor is. bbox = ax.get_position() [[left,bottom], [right, top]] = bbox.get_points() xc = (x+1)/float(cwidth) yc = y/float(cheight) if ((left < xc) and (xc < right) and (bottom < yc) and (yc < top)): target = ax break if not target: # if there is no target return (None, None) width = right-left height =top-bottom xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() xdata = xmin + (xc - left) * (xmax - xmin)/width ydata = ymin + (yc - bottom) * (ymax - ymin)/height return (xdata, ydata) def onmove(self, event): event.xdata, event.ydata = self.xydata(event.x, event.y) MultiCursor.onmove(self, event) #make event data evt = self.CursorMoveEvent(pos = (event.xdata, event.ydata)) #post event to parent window wx.PostEvent(self.parent, evt) self.visible = True
2014年1月27日月曜日
matplotlibでorthogonal projectionの3D plot
グラフを作るのに、gnuplotもよいけど、pythonからmatplotlibを使うのももよいですよね。
下の図は、matplotlibで3次元プロットした例ですが、遠近感(perspective effect)が観られます。
しばしば、そのような遠近感の効果をなくして3次元プロットしてみたいという要求があります。
言い換えると、透視投影(perspective projection)から平行投影(orthogonal projection)に変更したいわけです。調べてみたところ、mpl_toolkits.mplot3dの中のproj3dにあるpersp_transformation関数を書き換えればよいということです。
ということで、以下のようにしてみました。
下の図は、matplotlibで3次元プロットした例ですが、遠近感(perspective effect)が観られます。
しばしば、そのような遠近感の効果をなくして3次元プロットしてみたいという要求があります。
言い換えると、透視投影(perspective projection)から平行投影(orthogonal projection)に変更したいわけです。調べてみたところ、mpl_toolkits.mplot3dの中のproj3dにあるpersp_transformation関数を書き換えればよいということです。
ということで、以下のようにしてみました。
#!/usr/bin/env python import matplotlib import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy #--------- Patch for Orthogonal Projection of 3Dplot ------ from mpl_toolkits.mplot3d import proj3d def orthogonal_transformation(zfront, zback): a = 2/(zfront-zback) b = -1*(zfront+zback)/(zfront-zback) c = zback return numpy.array([[1,0,0,0], [0,1,0,0], [0,0,a,b], [0,0,0,c]]) proj3d.persp_transformation = orthogonal_transformation #----------------------------------------------------- #---------- make canvas for figure ------------- fig = plt.figure(figsize=(6, 6), facecolor='w') #---------- 3D Plot ---------------- ax = fig.add_subplot(111, projection='3d', axisbg='w') # making sample data x = numpy.arange(-3, 3, 0.25) y = numpy.arange(-3, 3, 0.25) xm, ym = numpy.meshgrid(x, y) z = numpy.exp(-1*xm**2) * numpy.exp(-1*ym**2) # draw x, y, z labels ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z') # plot 3d surf = ax.plot_surface(xm, ym, z, rstride=1, cstride=1, linewidth=1, antialiased=True) # set view point ax.view_init(elev=30, azim=-45) # display the figures plt.show()このスクリプトでプロットすると、下のようになりました。
2014年1月24日金曜日
WindowsでGraffiti入力っぽいキーボードを修正してみた
Cの関数をPythonから呼び出す方法を使って、例のGraffitiっぽい入力のソフトウェアキーボードをgKeyを修正してみた。
ストロークの判定ルーチンをC言語で書き直して、その部分だけ置き換えてみた。判定用のモジュール内のPythonで書かれた従来関数と同じ名前でラッピングして、モジュール差し替えを行うと、ほとんどいじらずにCルーチンを利用したものができあがる。
pythonでプロトタイプを作って、徐々にCに置き換えていくような作り方もよいかもしれないですね。Cの関数のテストにPythonを使うというのもよさそうです。
ところでgKeyの使用感に違いがあったかというと、pythonのみで作ったものでも十分快適だったので、それほどでもなかった。もともと軽い処理になので、非線形最小二乗フィットほどの効果はないようですね。でも、Cで書いたらもっともっと快適かもという思いに対して、すっきりできた気分。
ストロークの判定ルーチンをC言語で書き直して、その部分だけ置き換えてみた。判定用のモジュール内のPythonで書かれた従来関数と同じ名前でラッピングして、モジュール差し替えを行うと、ほとんどいじらずにCルーチンを利用したものができあがる。
pythonでプロトタイプを作って、徐々にCに置き換えていくような作り方もよいかもしれないですね。Cの関数のテストにPythonを使うというのもよさそうです。
ところでgKeyの使用感に違いがあったかというと、pythonのみで作ったものでも十分快適だったので、それほどでもなかった。もともと軽い処理になので、非線形最小二乗フィットほどの効果はないようですね。でも、Cで書いたらもっともっと快適かもという思いに対して、すっきりできた気分。
2014年1月20日月曜日
wxPython 3.0を使ってみた
wxPythonのページを見に行ったら、3.0がリリースされていました。これまで、2.8を使っていて、2.9を見送ってしまいました。さっそく、3.0をインストールして使ってみたところ、これまでのスクリプトが動かない部分もちらほら。
これまで、wx.Color()使っていたけど、wx.Colour()になったようで、エラーでとまったり。
これはすぐに修正できましたが、matplotlib1.3.1での動作がいまいち。
以下のスクリプト、python2.7 + wxPython2.8ではちゃんとmatplotlibのFigureCanvasのサイズが設定できていました。
#!/usr/bin/env python
# coding: shift_jis
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx
class myFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Matplotlib Test',
style=wx.DEFAULT_FRAME_STYLE)
self.fig = matplotlib.figure.Figure()
ax = self.fig.add_axes([0.2, 0.2, 0.6, 0.6])
ax.plot([0, 1], [0, 1])
self.canvas = FigureCanvas(self, -1, self.fig)
self.canvas.SetSize((1024, 600))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 0, wx.EXPAND|wx.ALL, 0)
self.SetSizer(sizer)
self.Fit()
self.canvas.draw()
print self.canvas.GetSize()
print self.GetClientSize()
if __name__ == '__main__':
app = wx.App(False)
frame = myFrame()
frame.Show()
app.MainLoop()
しかし、wxPython 3.0にしたところ、サイズが変更されません。
これってwxPython 2.9からでしょうか。
しかたがないので、入れ物(FrameとかPanel)のサイズを変更する方法を使うようにしました。
#!/usr/bin/env python
# coding: shift_jis
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx
class myFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Matplotlib Test',
style=wx.DEFAULT_FRAME_STYLE)
self.fig = matplotlib.figure.Figure()
ax = self.fig.add_axes([0.2, 0.2, 0.6, 0.6])
ax.plot([0, 1], [0, 1])
self.canvas = FigureCanvas(self, -1, self.fig)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0)
self.SetSizer(sizer)
self.Fit()
self.canvas.draw()
self.SetClientSize((1024, 600))
print self.canvas.GetSize()
print self.GetClientSize()
if __name__ == '__main__':
app = wx.App(False)
frame = myFrame()
frame.Show()
app.MainLoop()
これまでwxPythonを使ったスクリプトは、動作を確認してそれほど多くの変更が必要ではないことが分かって一安心です。
これまで、wx.Color()使っていたけど、wx.Colour()になったようで、エラーでとまったり。
これはすぐに修正できましたが、matplotlib1.3.1での動作がいまいち。
以下のスクリプト、python2.7 + wxPython2.8ではちゃんとmatplotlibのFigureCanvasのサイズが設定できていました。
#!/usr/bin/env python
# coding: shift_jis
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx
class myFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Matplotlib Test',
style=wx.DEFAULT_FRAME_STYLE)
self.fig = matplotlib.figure.Figure()
ax = self.fig.add_axes([0.2, 0.2, 0.6, 0.6])
ax.plot([0, 1], [0, 1])
self.canvas = FigureCanvas(self, -1, self.fig)
self.canvas.SetSize((1024, 600))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 0, wx.EXPAND|wx.ALL, 0)
self.SetSizer(sizer)
self.Fit()
self.canvas.draw()
print self.canvas.GetSize()
print self.GetClientSize()
if __name__ == '__main__':
app = wx.App(False)
frame = myFrame()
frame.Show()
app.MainLoop()
しかし、wxPython 3.0にしたところ、サイズが変更されません。
これってwxPython 2.9からでしょうか。
しかたがないので、入れ物(FrameとかPanel)のサイズを変更する方法を使うようにしました。
#!/usr/bin/env python
# coding: shift_jis
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx
class myFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Matplotlib Test',
style=wx.DEFAULT_FRAME_STYLE)
self.fig = matplotlib.figure.Figure()
ax = self.fig.add_axes([0.2, 0.2, 0.6, 0.6])
ax.plot([0, 1], [0, 1])
self.canvas = FigureCanvas(self, -1, self.fig)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0)
self.SetSizer(sizer)
self.Fit()
self.canvas.draw()
self.SetClientSize((1024, 600))
print self.canvas.GetSize()
print self.GetClientSize()
if __name__ == '__main__':
app = wx.App(False)
frame = myFrame()
frame.Show()
app.MainLoop()
これまでwxPythonを使ったスクリプトは、動作を確認してそれほど多くの変更が必要ではないことが分かって一安心です。
2014年1月17日金曜日
MinGW gcc とctypesモジュールを用いてpythonからc関数を呼び出してみる
Fortranで記述して、f2pyを使うのもよいのですが、Cで記述した関数が手元にあるもので、そちらを使いたいと思ったしだいです。
Python APIを使ったり、SWIGやCythonを利用したりと、いろいろ方法はある様子ですが、ctypesモジュールを利用して、呼び出す方法を試してみました。
コンパイラは、Visual Studioではなくて、MinGWのgccを使います。関数をコンパイルするだけなら、コンソールからちょっとコマンドを打つだけなので、こちらのほうが楽そうです。
簡単なものから順番に試していきたいと思います。
まずは、一つの整数を渡して二倍して返す関数です。
/* doublenumber.c */
#include
int doublenumber(int n)
{
return n * 2;
}
コンパイルします
gcc -Wall -fPIC -c doublenumber.c
windowsでは、-fPICは無視されるとの警告がでました。
どうやらデフォルトでPICオプションが適用される様子です。
オブジェクトファイルdoublenumber.oができました。
共有ライブラリを作成します。
gcc -shared -o doublenumber.so doublenumber.o
共有ライブラリdoublenumber.soファイルができました。
pythonから利用してみようと思います。
import ctypes
mydll = ctypes.CDLL('doublenumber.so')
mydll.doublenumber(5)
10
とちゃんと利用できています。
実数を引数として与えると、ctypes.ArgumentErrorが発生します。
さて、次に実数型を利用してみましょう。
C関数の引数をdouble vにして、同様に二倍した値を返すものにします。
/* doubledouble.c */
#include
double doublenumber(double v)
{
return v * 2.0;
}
コンパイルして、共有ライブラリにします。
gcc -Wall -c doubledouble.c
gcc -shared -o doubledouble.so doubledouble.o
実数を渡してもエラーが出ます。
型をc言語用に変換して、渡します。
mydll.doublenumber(ctypes.c_double(5.3))
1075131187
あれ、エラーは出ませんが、返り値がおかしいですね。
どうやらデフォルトで、関数はc_intを返すと仮定されているようです。
関数オブジェクトのrestype属性を設定するとよいようです。
mydll.doublenumber.restype = ctypes.c_double
mydll.doublenumber(ctypes.c_double(5.3))
10.6
ちゃんと値が得られました。
さて、引数でc_double指定をするのも大変です。
argtypesを設定してみます。
mydll.doublenumber.argtypes = [ctypes.c_double]
mydll.doublenumber(5.3)
10.6
簡単に5.3を渡すことができました。
整数5を渡しても、自動的に変換されるようです。
次は、文字列を渡す関数を試してみます。
/* charcount.c */
#include
int charcount(char *s)
{
int c;
c = 0;
while (s[c] != '\0')
c++;
return c;
}
そういえばstrlen()なんて関数もありましたね。
コンパイルして共有ライブラリを作成します。
gcc -Wall -c charcount.c
gcc -shared -o charcount.so charcount.o
テストするPythonスクリプトは以下の通り
C言語の文字列は、文字配列のポインタになります。
文字型のポインタは、ctypes.c_char_pで指定できます。
import ctypes
mydll = ctypes.CDLL("charcount.so")
mydll.charcount.argtypes = [ctypes.c_char_p]
mydll.charcount.restype = ctypes.c_int
print mydll.charcount("Hello")
実行すると文字列の長さの 5 がプリントされました。
次に、文字列を渡して、それを変更して返すような関数を作ってみます。
/* editstring.c */
#include
#include
char *editstring(char *s)
{
char buff[1024];
sprintf(buff, "*** %s ***", s);
strcpy(s, buff);
return s;
}
バッファ内で文字列を編集して、それを渡された文字列領域にコピーして、
そのポインタを返す関数です。
渡した文字列領域が編集されることになります。
Pythonの文字列は変更できないので、編集のための文字列バッファを用意するらしいです。
Pythonスクリプトは以下の通りです。
import ctypes
mydll = ctypes.CDLL("editstring.so")
mydll.editstring.argtypes = [ctypes.c_char_p]
mydll.editstring.restype = ctypes.c_char_p
s = ctypes.create_string_buffer("Hello", 1024)
result = mydll.editstring(s)
print '[%s]' % result
実行すると、
[*** Hello ***]
と表示され、編集されたのが確認できます。
返り値の型をc_char_pにして受け取ったresultは、
そのまま文字列としてプリントされますが、sはそうはいきません。
print sとすると c_char_arrayのオブジェクトであると表示されます。
その中の文字列をプリントするときは、
print '%s' % s.value
とするとよいようです。
でも、普通にbuffのポインタを返したらよいかな。
/* newstring.c */
#include
char *newstring(char *s)
{
char buff[1024];
sprintf(buff, "*** %s ***", s);
return buff;
}
こちら、コンパイルすると「関数が局所変数のアドレスを返します」
と警告が出ます。それはそうですよね。
この関数を抜けると、このメモリ領域は、解放されてしまいそうですが、、、。
Pythonスクリプトは以下の通り。
import ctypes
mydll = ctypes.CDLL("newstring.so")
mydll.newstring.argtypes = [ctypes.c_char_p]
mydll.newstring.restype = ctypes.c_char_p
string = mydll.newstring("Hello")
print string
ちゃんと*** Hello ***と出力されました。
このような書き方のほうが書きやすいですね。
直接渡した文字列をいじる必要がなければ、create_string_buffer()
は使わなくてもよいのでしょうか。
type(string)すると、となります。
でもちょっとメモリが心配。
Python側で文字列バッファを用意したほうがいいかな。
次は、複数の実数値が入った配列をPythonから渡して、その和を返すようなC関数を試してみます。
/* sumvalues.c */
#include
double sumvalues(double *v, int n)
{
int i;
double sum;
sum = 0.0;
for (i=0; i {
sum = sum + v[i];
printf("%f\n", v[i]);
}
return sum;
}
配列の長さはCの関数から分かりませんので、その長さも渡すようにしています。
コンパイルして、共有ライブラリを作ります。
gcc -c sumvalues.c
gcc -shared -o sumvalues.so sumvalues.o
Pythonスクリプトは以下の通り
import ctypes
mydll = ctypes.CDLL("sumvalues.so")
mydll.sumvalues.restype = ctypes.c_double
n = 10
nvalues = ctypes.c_double * n
vlist = nvalues(*[1.0+i*1.0 for i in range(n)])
print vlist, len(vlist)
v = mydll.sumvalues(vlist, ctypes.c_int(n))
print v
共有ライブラリをを呼び出すところと、返り値を指定するのは同じです。
配列を使うときは、まず長さ分の型を作ります。
nvaluesという型で、長さがnのctypes.cdoubleの型を作ります。
その型でvlistというn個の実数値が配列に入ったものを作成します。
len()を用いてvlistの長さを調べることができます。
vlistは、c_double_Array_10オブジェクトになっています。
vlistは、ctypesの型になっていますので、あらためてargtypesで設定する必要もありません。長さだけ、ctypes.c_int()の型にして渡します。
1から10までの足し算をすることができました。
実際に利用するときは、Pythonの関数でラッピングするとよいかと思いました。
そのように書いたPythonスクリプトは以下の通りです。
import ctypes
mydll = ctypes.CDLL("sumvalues.so")
mydll.sumvalues.restype = ctypes.c_double
def sumvalues(vlist):
n = len(vlist)
nvalues = ctypes.c_double * n
arg1 = nvalues(*vlist)
v = mydll.sumvalues(arg1, ctypes.c_int(n))
return v
if __name__ == '__main__':
vlist = [1.0+i*1.0 for i in range(10)]
v = sumvalues(vlist)
print v
次は、複数の値を返してもらう関数を書いてみます。
関数は、基本的に一つの値しか返しません。
ですので、引数に値を入れてもらうための変数(へのポインタ)を渡すことにします。
ソースファイル
/* summean.c */
#include
int summean(double *v, int n, double *sum, double *mean)
{
int i;
double s;
s = 0.0;
for (i=0; i {
s = s + v[i];
}
*sum = s;
*mean = s/n;
return 0;
}
コンパイルして共有ライブラリを作成します。
gcc -c summean.c
gcc -shared -o summean.so summean.o
Pythonスクリプトは以下の通り
import ctypes
mydll = ctypes.CDLL("summean.so")
def summean(vs):
n = len(vs)
nvalues = ctypes.c_double * n
vlist = nvalues(*vs)
s = ctypes.c_double(0.0)
m = ctypes.c_double(0.0)
mydll.summean(vlist, ctypes.c_int(n), ctypes.pointer(s), ctypes.pointer(m))
return (s.value, m.value)
if __name__ == '__main__':
vs = [1.0+i*1.0 for i in range(10)]
s, m = summean(vs)
print s, m
前と同様にPythonスクリプトでsummean関数としてラッピングしました。
結果を保存する変数sおよびmをc_double(0.0)で用意します。
関数summeanには、そのポインタを渡す必要があるため、ctypes.pointer()関数を利用しています。
Pythonの関数の返り値として、s.valueおよびm.valueをタプルにして返します。
つぎは、いよいよ構造体です。まずは、シンプルな構造体を渡して、その内容を参照した計算結果が返ってくる関数を作ってみたいと思います。
ここでは、四角の左、右、下、上の座標を格納する構造体です。
ソースファイル
/* squarearea.c *\
#include
typedef struct {
double left;
double right;
double bottom;
double top;
} SQUARE;
double squarearea(SQUARE sq)
{
double area;
double width;
double height;
width = sq.right - sq.left;
height = sq.top - sq.bottom;
area = width * height;
return area;
}
コンパイルして、共有ライブラリを作成します。
gcc -c squarearea.c
gcc -shared -o squarearea.so squarearea.o
次に、Pythonスクリプトです。
Pythonスクリプト側で構造体を取り扱うためには、ctypes.Structureを継承したクラスを作ります。
そのクラスに、_fields_属性を設定することで、Cの構造体と一致させるということです。
_fields_属性は、フィールド名 と フィールド型 を持つ 2要素タプル のリストです。
ctypes.Structureで定義されているコンストラクタが、このフィールドを参照して、初期化するようです。
Pythonスクリプトは、以下のようになりました。
import ctypes
mydll = ctypes.CDLL("squarearea.so")
class SQUARE(ctypes.Structure):
_fields_ = [('left', ctypes.c_double),
('right', ctypes.c_double),
('bottom', ctypes.c_double),
('top', ctypes.c_double)]
if __name__ == '__main__':
sq = SQUARE(0.3, 0.8, 0.4, 3.4)
mydll.squarearea.restype = ctypes.c_double
v = mydll.squarearea(sq)
print v
幅0.5、高さ3ですので、1.5が返ってきます。
返り値の型を設定するのを忘れないようにしましょう。
C言語で返り値として構造体を使うと、複数の値を返すことができて便利ですよね。
先ほどの関数で、高さを1/2したものを返すようにしてみましょう。
ソースファイル
/* squarehalf.c */
#include
typedef struct {
double left;
double right;
double bottom;
double top;
} SQUARE;
SQUARE squarehalf(SQUARE sq)
{
double area;
double width;
double height;
SQUARE hsq;
hsq = sq;
height = sq.top - sq.bottom;
hsq.top = hsq.bottom + height/2.0;
return hsq;
}
hsqにsqを入れて、そのtopの値を、半分の高さになるように設定しなおしています。
コンパイルして、共有ライブラリを作ります。
gcc -c squarehalf.c
gcc -shared -o squarehalf.so squarehalf.o
Pythonスクリプト
import ctypes
mydll = ctypes.CDLL("squarehalf.so")
class SQUARE(ctypes.Structure):
_fields_ = [('left', ctypes.c_double),
('right', ctypes.c_double),
('bottom', ctypes.c_double),
('top', ctypes.c_double)]
if __name__ == '__main__':
sq = SQUARE(0.3, 0.8, 0.4, 3.4)
v = SQUARE()
mydll.squarehalf.restype = SQUARE
v = mydll.squarehalf(sq)
print sq.left, sq.right, sq.bottom, sq.top
print v.left, v.right, v.bottom, v.top
返り値をSQUAREにすることと、返り値を入れる変数vをSQUARE()クラスのインスタンスにしただけです。
0.3 0.8 0.4 3.4
0.3 0.8 0.4 1.9
と出力され、半分の高さの四角を表すデータになりました。
構造体の中に、別の構造体がある場合はどう書けるのでしょうか。
先ほど面積を求めた四角を、左下、右上の座標データPOINTで表してみます。
/* squarearea2.c */
#include
typedef struct {
double x;
double y;
} POINT;
typedef struct {
POINT leftbottom;
POINT righttop;
} SQUARE;
double squarearea(SQUARE sq)
{
double area;
double width;
double height;
width = sq.righttop.x - sq.leftbottom.x;
height = sq.righttop.y - sq.leftbottom.y;
area = width * height;
return area;
}
コンパイルして共有ライブラリにします。
gcc -c squarearea2.c
gcc -shared -o squarearea2.so squarearea2.o
Pythonスクリプトは以下のようになりました。
import ctypes
mydll = ctypes.CDLL("squarearea2.so")
class POINT(ctypes.Structure):
_fields_ = [('x', ctypes.c_double),
('y', ctypes.c_double)]
class SQUARE(ctypes.Structure):
_fields_ = [('leftbottom', POINT),
('righttop', POINT)]
if __name__ == '__main__':
sq = SQUARE(POINT(0.3, 0.4), POINT(0.8, 3.4))
v = SQUARE()
mydll.squarearea.restype = ctypes.c_double
v = mydll.squarearea(sq)
print v
それぞれ対応するクラスを書いただけで、意外なところはないですね。
ただ、クラス定義する順番で、POINTをSQUAREの後で行うと、
SQUAREで利用しているPOINTが定義できないというエラーが起こります。
構造体の定義では、その中に自分自身をもつものもありますね。
再帰的な構造は、中に同じ自分自身の型へのポインタを入れたりします。
不完全型と呼ばれているようです。
簡単のため、
(v1, (v2, (v3, (v4, (v5, NULL)))))
のようなリストに似た形のデータを考えてみます。
ソースファイル
/* showlist.c */
#include
struct CELL {
int value;
struct CELL *next;
};
int showlist(struct CELL *c, int flg)
{
printf("(%d, ", c->value);
if ((struct CELL *)NULL != c->next)
showlist(c->next, 0);
else
printf("NULL");
if (1 == flg)
printf(")\n");
else
printf(")");
return 0;
}
CELLという型の定義の中に、CELLへのポインタが含まれています。
前の例のように、typedef struct { ... } CELL; のような書き方をすると、コンパイル時にエラーがでます。使われるより前の方に、CELLというワードが入るような記述をする必要があるということです。
showlistは、再帰的に呼び出される関数で、渡されたCELLの値を出力したのちに、続くCELLの描画を行うため、showlistを呼び出します。nextに、NULLポインタが入っていれば、そこで終了です。
flgは、最初に呼び出すときに1をセットすると、それ以降とを区別することができます。
コンパイルして、共有ライブラリにします。
gcc -c showlist.c
gcc -shared -o showlist.so sholist.o
このような不完全な構造体定義の場合、Pythonスクリプトは、次のようにするようです。
import ctypes
mydll = ctypes.CDLL("showlist.so")
class CELL(ctypes.Structure):
pass
CELL._fields_ = [('value', ctypes.c_int),
('next', ctypes.POINTER(CELL))]
if __name__ == '__main__':
c = CELL(0, None)
for i in range(5):
c = CELL((i+1), ctypes.pointer(c))
mydll.showlist(ctypes.pointer(c), ctypes.c_int(1))
クラスCELLの定義では、なにも行わずに、あとから改めて_fields_属性を設定するようです。
ctypes.POINTER(CELL)で、CELLへのポインタ型という指定ができます。
実際に利用するときに、CELL型のcというもののポインタは、ctypes.pointer(c)で得ます。紛らわしいですね。
Pythonスクリプトでcの値を設定していき、showlistにcへのポインタと整数1を渡すと、
(5, (4, (3, (2, (1, (0, NULL))))))
と表示されます。この表示は、C関数内で行われたものです。
このようにctypesを使って共有ライブラリを呼び出す方法だと、CのソースファイルにPythonで利用するという配慮をせずに、Pythonから呼び出すことができました。
Pythonスクリプト側で、ちょっと使いやすいように包んであげれば呼び出しやすくなるかと思います。
f2pyを使ってFortranプログラムで計算するのもいいのですが、すでにCの関数を作っているようなら、この方法は便利ですね。
ということで、自作のガウス分布をフィットするルーチンをPythonから呼び出してみました。速度の比較のために、Pythonスクリプトでも書いてみました。numpyを利用せずに、Cのソースをほぼ1対1に直したものです。
#--- C func ---
#1.02713 0.0241351
#4.93666 0.0580069
#-0.938185 0.023901
#2.49769 0.0360503
# time_c = 0.000507835 s
#--- Python func ---
#1.02713 0.0241351
#4.93666 0.0580069
#-0.938185 0.023901
#2.49769 0.0360503
# time_py = 0.00944325 s
#-------------------
# time_py/time_c = 18.5951
#-------------------
200点のデータをフィットしました。やっぱりCで記述されたものは早いですね。
Pythonスクリプトの方は、numpyの機能を利用して実装すると、どこまで速くなるのでしょうか。
Python APIを使ったり、SWIGやCythonを利用したりと、いろいろ方法はある様子ですが、ctypesモジュールを利用して、呼び出す方法を試してみました。
コンパイラは、Visual Studioではなくて、MinGWのgccを使います。関数をコンパイルするだけなら、コンソールからちょっとコマンドを打つだけなので、こちらのほうが楽そうです。
簡単なものから順番に試していきたいと思います。
まずは、一つの整数を渡して二倍して返す関数です。
/* doublenumber.c */
#include
int doublenumber(int n)
{
return n * 2;
}
コンパイルします
gcc -Wall -fPIC -c doublenumber.c
windowsでは、-fPICは無視されるとの警告がでました。
どうやらデフォルトでPICオプションが適用される様子です。
オブジェクトファイルdoublenumber.oができました。
共有ライブラリを作成します。
gcc -shared -o doublenumber.so doublenumber.o
共有ライブラリdoublenumber.soファイルができました。
pythonから利用してみようと思います。
import ctypes
mydll = ctypes.CDLL('doublenumber.so')
mydll.doublenumber(5)
10
とちゃんと利用できています。
実数を引数として与えると、ctypes.ArgumentErrorが発生します。
さて、次に実数型を利用してみましょう。
C関数の引数をdouble vにして、同様に二倍した値を返すものにします。
/* doubledouble.c */
#include
double doublenumber(double v)
{
return v * 2.0;
}
コンパイルして、共有ライブラリにします。
gcc -Wall -c doubledouble.c
gcc -shared -o doubledouble.so doubledouble.o
実数を渡してもエラーが出ます。
型をc言語用に変換して、渡します。
mydll.doublenumber(ctypes.c_double(5.3))
1075131187
あれ、エラーは出ませんが、返り値がおかしいですね。
どうやらデフォルトで、関数はc_intを返すと仮定されているようです。
関数オブジェクトのrestype属性を設定するとよいようです。
mydll.doublenumber.restype = ctypes.c_double
mydll.doublenumber(ctypes.c_double(5.3))
10.6
ちゃんと値が得られました。
さて、引数でc_double指定をするのも大変です。
argtypesを設定してみます。
mydll.doublenumber.argtypes = [ctypes.c_double]
mydll.doublenumber(5.3)
10.6
簡単に5.3を渡すことができました。
整数5を渡しても、自動的に変換されるようです。
次は、文字列を渡す関数を試してみます。
/* charcount.c */
#include
int charcount(char *s)
{
int c;
c = 0;
while (s[c] != '\0')
c++;
return c;
}
そういえばstrlen()なんて関数もありましたね。
コンパイルして共有ライブラリを作成します。
gcc -Wall -c charcount.c
gcc -shared -o charcount.so charcount.o
テストするPythonスクリプトは以下の通り
C言語の文字列は、文字配列のポインタになります。
文字型のポインタは、ctypes.c_char_pで指定できます。
import ctypes
mydll = ctypes.CDLL("charcount.so")
mydll.charcount.argtypes = [ctypes.c_char_p]
mydll.charcount.restype = ctypes.c_int
print mydll.charcount("Hello")
実行すると文字列の長さの 5 がプリントされました。
次に、文字列を渡して、それを変更して返すような関数を作ってみます。
/* editstring.c */
#include
#include
char *editstring(char *s)
{
char buff[1024];
sprintf(buff, "*** %s ***", s);
strcpy(s, buff);
return s;
}
バッファ内で文字列を編集して、それを渡された文字列領域にコピーして、
そのポインタを返す関数です。
渡した文字列領域が編集されることになります。
Pythonの文字列は変更できないので、編集のための文字列バッファを用意するらしいです。
Pythonスクリプトは以下の通りです。
import ctypes
mydll = ctypes.CDLL("editstring.so")
mydll.editstring.argtypes = [ctypes.c_char_p]
mydll.editstring.restype = ctypes.c_char_p
s = ctypes.create_string_buffer("Hello", 1024)
result = mydll.editstring(s)
print '[%s]' % result
実行すると、
[*** Hello ***]
と表示され、編集されたのが確認できます。
返り値の型をc_char_pにして受け取ったresultは、
そのまま文字列としてプリントされますが、sはそうはいきません。
print sとすると c_char_arrayのオブジェクトであると表示されます。
その中の文字列をプリントするときは、
print '%s' % s.value
とするとよいようです。
でも、普通にbuffのポインタを返したらよいかな。
/* newstring.c */
#include
char *newstring(char *s)
{
char buff[1024];
sprintf(buff, "*** %s ***", s);
return buff;
}
こちら、コンパイルすると「関数が局所変数のアドレスを返します」
と警告が出ます。それはそうですよね。
この関数を抜けると、このメモリ領域は、解放されてしまいそうですが、、、。
Pythonスクリプトは以下の通り。
import ctypes
mydll = ctypes.CDLL("newstring.so")
mydll.newstring.argtypes = [ctypes.c_char_p]
mydll.newstring.restype = ctypes.c_char_p
string = mydll.newstring("Hello")
print string
ちゃんと*** Hello ***と出力されました。
このような書き方のほうが書きやすいですね。
直接渡した文字列をいじる必要がなければ、create_string_buffer()
は使わなくてもよいのでしょうか。
type(string)すると、
でもちょっとメモリが心配。
Python側で文字列バッファを用意したほうがいいかな。
次は、複数の実数値が入った配列をPythonから渡して、その和を返すようなC関数を試してみます。
/* sumvalues.c */
#include
double sumvalues(double *v, int n)
{
int i;
double sum;
sum = 0.0;
for (i=0; i
sum = sum + v[i];
printf("%f\n", v[i]);
}
return sum;
}
配列の長さはCの関数から分かりませんので、その長さも渡すようにしています。
コンパイルして、共有ライブラリを作ります。
gcc -c sumvalues.c
gcc -shared -o sumvalues.so sumvalues.o
Pythonスクリプトは以下の通り
import ctypes
mydll = ctypes.CDLL("sumvalues.so")
mydll.sumvalues.restype = ctypes.c_double
n = 10
nvalues = ctypes.c_double * n
vlist = nvalues(*[1.0+i*1.0 for i in range(n)])
print vlist, len(vlist)
v = mydll.sumvalues(vlist, ctypes.c_int(n))
print v
共有ライブラリをを呼び出すところと、返り値を指定するのは同じです。
配列を使うときは、まず長さ分の型を作ります。
nvaluesという型で、長さがnのctypes.cdoubleの型を作ります。
その型でvlistというn個の実数値が配列に入ったものを作成します。
len()を用いてvlistの長さを調べることができます。
vlistは、c_double_Array_10オブジェクトになっています。
vlistは、ctypesの型になっていますので、あらためてargtypesで設定する必要もありません。長さだけ、ctypes.c_int()の型にして渡します。
1から10までの足し算をすることができました。
実際に利用するときは、Pythonの関数でラッピングするとよいかと思いました。
そのように書いたPythonスクリプトは以下の通りです。
import ctypes
mydll = ctypes.CDLL("sumvalues.so")
mydll.sumvalues.restype = ctypes.c_double
def sumvalues(vlist):
n = len(vlist)
nvalues = ctypes.c_double * n
arg1 = nvalues(*vlist)
v = mydll.sumvalues(arg1, ctypes.c_int(n))
return v
if __name__ == '__main__':
vlist = [1.0+i*1.0 for i in range(10)]
v = sumvalues(vlist)
print v
次は、複数の値を返してもらう関数を書いてみます。
関数は、基本的に一つの値しか返しません。
ですので、引数に値を入れてもらうための変数(へのポインタ)を渡すことにします。
ソースファイル
/* summean.c */
#include
int summean(double *v, int n, double *sum, double *mean)
{
int i;
double s;
s = 0.0;
for (i=0; i
s = s + v[i];
}
*sum = s;
*mean = s/n;
return 0;
}
コンパイルして共有ライブラリを作成します。
gcc -c summean.c
gcc -shared -o summean.so summean.o
Pythonスクリプトは以下の通り
import ctypes
mydll = ctypes.CDLL("summean.so")
def summean(vs):
n = len(vs)
nvalues = ctypes.c_double * n
vlist = nvalues(*vs)
s = ctypes.c_double(0.0)
m = ctypes.c_double(0.0)
mydll.summean(vlist, ctypes.c_int(n), ctypes.pointer(s), ctypes.pointer(m))
return (s.value, m.value)
if __name__ == '__main__':
vs = [1.0+i*1.0 for i in range(10)]
s, m = summean(vs)
print s, m
前と同様にPythonスクリプトでsummean関数としてラッピングしました。
結果を保存する変数sおよびmをc_double(0.0)で用意します。
関数summeanには、そのポインタを渡す必要があるため、ctypes.pointer()関数を利用しています。
Pythonの関数の返り値として、s.valueおよびm.valueをタプルにして返します。
つぎは、いよいよ構造体です。まずは、シンプルな構造体を渡して、その内容を参照した計算結果が返ってくる関数を作ってみたいと思います。
ここでは、四角の左、右、下、上の座標を格納する構造体です。
ソースファイル
/* squarearea.c *\
#include
typedef struct {
double left;
double right;
double bottom;
double top;
} SQUARE;
double squarearea(SQUARE sq)
{
double area;
double width;
double height;
width = sq.right - sq.left;
height = sq.top - sq.bottom;
area = width * height;
return area;
}
コンパイルして、共有ライブラリを作成します。
gcc -c squarearea.c
gcc -shared -o squarearea.so squarearea.o
次に、Pythonスクリプトです。
Pythonスクリプト側で構造体を取り扱うためには、ctypes.Structureを継承したクラスを作ります。
そのクラスに、_fields_属性を設定することで、Cの構造体と一致させるということです。
_fields_属性は、フィールド名 と フィールド型 を持つ 2要素タプル のリストです。
ctypes.Structureで定義されているコンストラクタが、このフィールドを参照して、初期化するようです。
Pythonスクリプトは、以下のようになりました。
import ctypes
mydll = ctypes.CDLL("squarearea.so")
class SQUARE(ctypes.Structure):
_fields_ = [('left', ctypes.c_double),
('right', ctypes.c_double),
('bottom', ctypes.c_double),
('top', ctypes.c_double)]
if __name__ == '__main__':
sq = SQUARE(0.3, 0.8, 0.4, 3.4)
mydll.squarearea.restype = ctypes.c_double
v = mydll.squarearea(sq)
print v
幅0.5、高さ3ですので、1.5が返ってきます。
返り値の型を設定するのを忘れないようにしましょう。
C言語で返り値として構造体を使うと、複数の値を返すことができて便利ですよね。
先ほどの関数で、高さを1/2したものを返すようにしてみましょう。
ソースファイル
/* squarehalf.c */
#include
typedef struct {
double left;
double right;
double bottom;
double top;
} SQUARE;
SQUARE squarehalf(SQUARE sq)
{
double area;
double width;
double height;
SQUARE hsq;
hsq = sq;
height = sq.top - sq.bottom;
hsq.top = hsq.bottom + height/2.0;
return hsq;
}
hsqにsqを入れて、そのtopの値を、半分の高さになるように設定しなおしています。
コンパイルして、共有ライブラリを作ります。
gcc -c squarehalf.c
gcc -shared -o squarehalf.so squarehalf.o
Pythonスクリプト
import ctypes
mydll = ctypes.CDLL("squarehalf.so")
class SQUARE(ctypes.Structure):
_fields_ = [('left', ctypes.c_double),
('right', ctypes.c_double),
('bottom', ctypes.c_double),
('top', ctypes.c_double)]
if __name__ == '__main__':
sq = SQUARE(0.3, 0.8, 0.4, 3.4)
v = SQUARE()
mydll.squarehalf.restype = SQUARE
v = mydll.squarehalf(sq)
print sq.left, sq.right, sq.bottom, sq.top
print v.left, v.right, v.bottom, v.top
返り値をSQUAREにすることと、返り値を入れる変数vをSQUARE()クラスのインスタンスにしただけです。
0.3 0.8 0.4 3.4
0.3 0.8 0.4 1.9
と出力され、半分の高さの四角を表すデータになりました。
構造体の中に、別の構造体がある場合はどう書けるのでしょうか。
先ほど面積を求めた四角を、左下、右上の座標データPOINTで表してみます。
/* squarearea2.c */
#include
typedef struct {
double x;
double y;
} POINT;
typedef struct {
POINT leftbottom;
POINT righttop;
} SQUARE;
double squarearea(SQUARE sq)
{
double area;
double width;
double height;
width = sq.righttop.x - sq.leftbottom.x;
height = sq.righttop.y - sq.leftbottom.y;
area = width * height;
return area;
}
コンパイルして共有ライブラリにします。
gcc -c squarearea2.c
gcc -shared -o squarearea2.so squarearea2.o
Pythonスクリプトは以下のようになりました。
import ctypes
mydll = ctypes.CDLL("squarearea2.so")
class POINT(ctypes.Structure):
_fields_ = [('x', ctypes.c_double),
('y', ctypes.c_double)]
class SQUARE(ctypes.Structure):
_fields_ = [('leftbottom', POINT),
('righttop', POINT)]
if __name__ == '__main__':
sq = SQUARE(POINT(0.3, 0.4), POINT(0.8, 3.4))
v = SQUARE()
mydll.squarearea.restype = ctypes.c_double
v = mydll.squarearea(sq)
print v
それぞれ対応するクラスを書いただけで、意外なところはないですね。
ただ、クラス定義する順番で、POINTをSQUAREの後で行うと、
SQUAREで利用しているPOINTが定義できないというエラーが起こります。
構造体の定義では、その中に自分自身をもつものもありますね。
再帰的な構造は、中に同じ自分自身の型へのポインタを入れたりします。
不完全型と呼ばれているようです。
簡単のため、
(v1, (v2, (v3, (v4, (v5, NULL)))))
のようなリストに似た形のデータを考えてみます。
ソースファイル
/* showlist.c */
#include
struct CELL {
int value;
struct CELL *next;
};
int showlist(struct CELL *c, int flg)
{
printf("(%d, ", c->value);
if ((struct CELL *)NULL != c->next)
showlist(c->next, 0);
else
printf("NULL");
if (1 == flg)
printf(")\n");
else
printf(")");
return 0;
}
CELLという型の定義の中に、CELLへのポインタが含まれています。
前の例のように、typedef struct { ... } CELL; のような書き方をすると、コンパイル時にエラーがでます。使われるより前の方に、CELLというワードが入るような記述をする必要があるということです。
showlistは、再帰的に呼び出される関数で、渡されたCELLの値を出力したのちに、続くCELLの描画を行うため、showlistを呼び出します。nextに、NULLポインタが入っていれば、そこで終了です。
flgは、最初に呼び出すときに1をセットすると、それ以降とを区別することができます。
コンパイルして、共有ライブラリにします。
gcc -c showlist.c
gcc -shared -o showlist.so sholist.o
このような不完全な構造体定義の場合、Pythonスクリプトは、次のようにするようです。
import ctypes
mydll = ctypes.CDLL("showlist.so")
class CELL(ctypes.Structure):
pass
CELL._fields_ = [('value', ctypes.c_int),
('next', ctypes.POINTER(CELL))]
if __name__ == '__main__':
c = CELL(0, None)
for i in range(5):
c = CELL((i+1), ctypes.pointer(c))
mydll.showlist(ctypes.pointer(c), ctypes.c_int(1))
クラスCELLの定義では、なにも行わずに、あとから改めて_fields_属性を設定するようです。
ctypes.POINTER(CELL)で、CELLへのポインタ型という指定ができます。
実際に利用するときに、CELL型のcというもののポインタは、ctypes.pointer(c)で得ます。紛らわしいですね。
Pythonスクリプトでcの値を設定していき、showlistにcへのポインタと整数1を渡すと、
(5, (4, (3, (2, (1, (0, NULL))))))
と表示されます。この表示は、C関数内で行われたものです。
このようにctypesを使って共有ライブラリを呼び出す方法だと、CのソースファイルにPythonで利用するという配慮をせずに、Pythonから呼び出すことができました。
Pythonスクリプト側で、ちょっと使いやすいように包んであげれば呼び出しやすくなるかと思います。
f2pyを使ってFortranプログラムで計算するのもいいのですが、すでにCの関数を作っているようなら、この方法は便利ですね。
ということで、自作のガウス分布をフィットするルーチンをPythonから呼び出してみました。速度の比較のために、Pythonスクリプトでも書いてみました。numpyを利用せずに、Cのソースをほぼ1対1に直したものです。
#--- C func ---
#1.02713 0.0241351
#4.93666 0.0580069
#-0.938185 0.023901
#2.49769 0.0360503
# time_c = 0.000507835 s
#--- Python func ---
#1.02713 0.0241351
#4.93666 0.0580069
#-0.938185 0.023901
#2.49769 0.0360503
# time_py = 0.00944325 s
#-------------------
# time_py/time_c = 18.5951
#-------------------
200点のデータをフィットしました。やっぱりCで記述されたものは早いですね。
Pythonスクリプトの方は、numpyの機能を利用して実装すると、どこまで速くなるのでしょうか。
登録:
投稿 (Atom)