Flask-Cacheを使ってみた

自作のブログシステムのなかにデータベースに頻繁にアクセスしすぎて困るAPIがあるので、Flask-Cacheを使ってキャッシュするようにした。

設定を読ませてからcacheの設定をしないといけない(まぁ当たり前か)のだけどapp.config.from_objectで読ませる前にCacheしてたらキャッシュが効かなくてちょっとはまった。

CACHE_TYPE = 'simple'

app = Flask(__name__)
app.config.from_object(__name__)
cache = Cache(app) # 設定読ませたあとに

ソースは読んでたらキャッシュのタイプはwerkzeugに任せているらしいので、そっちのドキュメントを読めば大丈夫な感じですね。

python-amazon-product-apiが動かなくなった

あーそういえばメール来てたなと思い探してみたら、アソシエイトタグが必須になったらしい。

ドキュメントにはナニも書いてないので、ソース見たらアソシエイトタグが含められるようになったいたので、

api = amazonproduct.API(AWS_KEY, SECRET_KEY, 'jp', ASSOCIATE_TAG)

とロケールのあとにアソシエイトタグ含めておけばOK

Jenkins+GitHubでPythonプロジェクトの継続的インテグレーション

この前の勉強会でJenkinsについて色々知った。JenkinsってJava向けだろうと思ってたら全然違って基本何でもいけるらしい(シェルから動かせるから)。あとgithubのプロジェクトもいけるらしいので早速pygamessっていう量子化学計算用のモジュールを使って試してみた。

最近GAMESSを仕事で使うことが多いんだけど、きちんとテストしてないと不安ですからね。

Jenkins

さくっと導入するならTraclightningがいいらしいんだけど、Jenkinsを単独で入れた。macだとパッケージをダウンロードして実行するだけでlocalhostの8080に常駐するので楽ちん

あとは、Gitのプラグインを追加でインストールしておく。

Python

Pythonのテストといえばnoseですね。あと、Python版のPerl Testingみたいな本ないのかな?

ProductName Perl Testing: A Developer's Notebook (Developers Notebook)
Ian Langworth
Oreilly & Associates Inc / 2160円 ( 2005-08 )


JenkinsとPythonの連携を参考にした。まずはnoseとunittest-xml-reportingをインストールしておく。

あとはJenkinsの設定のシェルの実行っていうところにnoseのテストを書いておく。

nosetests -v --with-xunit -w tests

これでnosetests.xmlっていうファイルにテスト結果が出力されるようになるので、JenkinsのオプションのJUnitテストの結果集計というところにチェックを入れて、nosetests.xmlを追加しておく。

python Jenkins

4つテストを走らせて、1つFailしている。

これは環境変数とかの問題で、gamessの実行ファイルがきちんと呼び出せてないせいで、テストがこけているんだけど、Jenkinsのほうにエラーの内容が吐かれてないのでどこでこけているのかつかめていない。

そもそもrungms使うのが間違っているのかなぁ。あとでよく考える。

PythonでSelenium2.0のWebDriverを動かす

seleniumも覚えようかなぁとmacbook(10.5)で触ってみた。

selenium2.8とselenium 2.8.1のpythonモジュールではfirefoxが立ち上がらずに死ぬので、firefoxで動かすことに固執せずにとっとと諦めてchromeでやってみた。

Chromeで動かす場合にはchromedriverというものをダウンロードしてきて、適切な場所に配置(Macだと/Applications/Google Chrome.app/Contents/MacOS/)する必要があります。

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
import time

browser = webdriver.Chrome()
browser.get("http://www.yahoo.com")
assert "Yahoo!" in browser.title
elem = browser.find_element_by_name("p")
elem.send_keys("seleniumhq" + Keys.RETURN)
time.sleep(0.2)
try:
    browser.find_element_by_xpath("//a[contains(@href,'http://seleniumhq.org')]")
except NoSuchElementException:
    assert 0, "can't find seleniumhq"
browser.close()

というわけで触りだけ。

FitNesse+Selenium+Jenkinsによるテストケース継続的インテグレーションなんて素敵な匂いがしますな。

pythonで動かせるんだったらnosetestと一緒にごにょごにょすればそのままJenkinsと連携できそうな気もするが。

追記 11.10.14

でもって、selenium-server-standalone-2.8.0.jar

:::sh java -jar selenium-server-standalone-2.8.0.jar

ってな感じで起動しておいて、以下のスクリプトを動かす

って書いてたけど、Chrome使う場合は別に動かす必要なかった。

第4回 静岡ITPro勉強会 インフラ部に参加した

インフラ系じゃないけど、色々勉強になるので、イイですよね。

Jenkinsの話は面白かった、GitHubのコードをテストしてくれるのは素敵だ。

Pythonだとbuildbotがあるけど、どっち使ったらいいんだろうかね。

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


ちなみに私はSphinxの紹介をしてきました。Sphinx便利ですよね。

それから静岡にもPythonのユーザーグループ欲しいよねっていうことになって、[三島|富士|静岡].pyでも作るかねって話になってます。A君メソッド的には飲み会(#0)から始めるんだろうけど、丁度明日つくる会があるので、興味があったら遊びに来るといいと思います。

GAMESSで励起状態の構造最適化をする

光異性化とかAMES予測とか代謝予測とか、量子化学計算が創薬シーンで果たす役割が大きくなっているのは、探索創薬自体が発見学というよりはメカニズムベースでモノを考えるようになってきているからかなぁと。それからFMOなんかも重要な技術ですね。

さて、ちょっと励起状態での構造最適化計算が必要になったので、pygamessでCIS計算できるようにしておきました。

test用にGAMESSのEXAM34のホルムアルデヒドの励起状態計算のサンプルを使っています。GAMESSで計算する場合にはpc-chem.infoのホルムアルデヒドの励起状態計算が参考になります。こういう良質なコンテンツもっと増えてくんないかな。

input用のmol

exam34_energy.log
 OpenBabel09231115413D

  4  3  0  0  0  0  0  0  0  0999 V2000
    0.0100   -0.8670    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
    0.0000    0.3455    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.0100    0.9296   -0.9377 H   0  0  0  0  0  0  0  0  0  0  0  0
   -0.0100    0.9296    0.9377 H   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  2  0  0  0  0
  2  4  1  0  0  0  0
  3  2  1  0  0  0  0
M  END

計算用スクリプト

import pygamess
import openbabel as ob
g = pygamess.Gamess()
obc = ob.OBConversion()
obc.SetInAndOutFormats("mol","mol")
mol = ob.OBMol()
obc.ReadFile(mol, "h2co.mol")
g.contrl['cityp'] = 'cis'
g.run_type('optimize')
g.basis_type('631+gdp')
g.gamess_input(mol)
newmol = g.run(mol)
print newmol.GetEnergy()
obc.WriteFile(newmol,'h2co_singlet.mol')

励起状態のホルムアルデヒドの安定構造は若干ピラミッド型の構造を取るって知ってた?

h2co

ところで、光異性化のサンプルとして面白いのはやっぱスチルベンかなぁと思ったんだけど、HOMO-LUMOの2電子励起を考慮しないといけないらしいので、CISじゃ計算できないじゃんと。

もう少し面白いサンプルないかなぁ。

ベルマンフォード法とダイクストラのアルゴリズム

プログラミングコンテストチャレンジブックから

ProductName プログラミングコンテストチャレンジブック
秋葉 拓哉
毎日コミュニケーションズ / 3444円 ( 2010-09-11 )


ベルマンフォード

高々|V|-1回で最安定経路にたどり着くっていうのがなかなか理解できなくて、紙に書きながら理解した。

edge = [[0,1,2],[0,2,5],[1,2,4],[1,3,6],[1,4,10],[2,3,2],[3,5,1],[4,5,3],[4,6,5],[5,6,9],
       [1,0,2],[2,0,5],[2,1,4],[3,1,6],[4,1,10],[3,2,2],[5,3,1],[5,4,3],[6,4,5],[6,5,9]]

def shortest_path(edge,num_v,start):
    inf = float("inf")
    d = [inf for f in range(num_v)]
    d[start] = 0;
    while True:
        update = False
        for e in edge:
            if d[e[0]] != inf and d[e[1]] > d[e[0]] + e[2]:
                d[e[1]] = d[e[0]] + e[2]
                update = True
        if not update:
            break
    return d

print shortest_path(edge,7,0)[6]

ダイクストラ

こっちは馴染み深い

edge = [[0,1,2],[0,2,5],[1,2,4],[1,3,6],[1,4,10],[2,3,2],[3,5,1],[4,5,3],[4,6,5],[5,6,9],
       [1,0,2],[2,0,5],[2,1,4],[3,1,6],[4,1,10],[3,2,2],[5,3,1],[5,4,3],[6,4,5],[6,5,9]]

def dijkstra(edge,num_v,start):
    inf = float("inf")
    cost = [[inf for i in range(num_v)] for j in range(num_v)]
    for e in edge: cost[e[0]][e[1]] = e[2]
    used = [False for i in range(num_v)]
    d = [inf for j in range(num_v)]

    d[start] = 0;
    while True:
        v = -1
        for u in range(num_v):
            if (not used[u]) and (v == -1 or d[u] < d[v]):
                v = u
        if v == -1: break
        used[v] = True
        for x in range(num_v):
            d[x] = d[x] if d[x] < d[v] + cost[v][x] else d[v] + cost[v][x]
        print d
    return d[num_v-1]

print dijkstra(edge,7,0)

最短経路の本もいいっすね。数学ガールとともに子供の為にとってある。

ProductName 最短経路の本
R. ブランデンベルク
シュプリンガー・ジャパン株式会社 / 3675円 ( 2007-12-13 )


PythonでBinary Indexed Tree

プログラミングコンテストチャレンジブック(3-4)

Binary Indexed Treeって面白いデータ構造だ。

ProductName プログラミングコンテストチャレンジブック
秋葉 拓哉
毎日コミュニケーションズ / 3444円 ( 2010-09-11 )


このデータ構造を使ってバブルソートの交換回数を数えるんだけど、理屈がいまいちわからん。BITの示しているのが交換しなくていい組の数なのはわかるんだけど、なぜ左から順繰りにアペンドしていくのかがしっくりこない。

class Bit(object):
    def __init__(self,l):
        self.size = l
        self.bit = [0] * (self.size+1)

    def sum(self, i):
        s = 0
        while i > 0:
            s += self.bit[i]
            i -= i & -i
        return s

    def add(self, i, x):
        while i <= self.size:
            self.bit[i] += x
            i += i & -i

    def __str__(self):
        return str(self.bit)

if __name__ == '__main__':

    a = [3,1,4,2]
    ans = 0
    bit = Bit(len(a))
    for i,v in enumerate(a):
        ans += i - bit.sum(v)
        bit.add(v,1)
        print "%d: ans=%d v=%d %s" % (i, ans, v, bit)

    print ans

実行するとこんな感じ

0: ans=0 v=3 [0, 0, 0, 1, 1]
1: ans=1 v=1 [0, 1, 1, 1, 2]
2: ans=1 v=4 [0, 1, 1, 1, 3]
3: ans=3 v=2 [0, 1, 2, 1, 4]
3

わかりやすい説明ないかなぁ。

devquizのスライドパズル

Pythonで。

copyモジュールを使うと遅いのでコピーしないようにした。アルゴリズムは、壁を考慮したマンハッタン距離をつかったBFS枝刈り。狩りかたは探索時のベストスコアを覚えていてそれより閾値以下の経路をバシバシ切る。

キツすぎて経路が見つからない場合は閾値を一つ上げて再探索。一問当たりの制限時間を決めておいて時間の掛かり過ぎる問題はカット。

それからmultiprocessingモジュールのpmapをはじめてつかったけど、これは超便利ですね。なにも考えなくてもコアの数だけ速くなるし

これで、2.4Gのcore2 duoのmacbookで3600問解けた。

ファイルが2つにわかれているのは片方をCythonで高速化しようと奮闘した結果、速く出来なかったという。最初からクラスにしないで解くべきだったなぁと。

あと、debquizで触れたGoにすっかり魅了されましたね。静的型付けでコンパイルできる言語もそこそこできるようになっておきたいなぁ。

slidepuzzle.py

import time
from os import environ
DEBUG = environ.get('debug', False)

class SlidePuzzle(object):
    """slidepuzzle"""

    def __init__(self, width, height, initdata):
        self.width = width
        self.height = height
        self.data = [c for c in initdata]
        self.pos0 = self.find_pos0()
        self.valid = {}
        for vi,vc in enumerate('123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:width*height-1]):
            self.valid[vc] = vi
        self.maze_score_table = self.maze_score_init()
        self.score = self.calc_score()

    def display(self):
        l = len(self.data)
        w = self.width
        d = self.data
        return "".join(["".join(d[i:(i+w if i+w < l else l) ])+'\n' for i in range(0,l,w)])[:-1]

    def calc_score(self):
        return self.maze_score()

    def can_move(self,direction,last_moved):
        if direction == 'U':
            if last_moved == 'D': return False
            return False if (self.pos0) / self.width == 0 or self.data[self.pos0-self.width] == '=' else True
        elif direction == 'D':
            if last_moved == 'U': return False
            return False if (self.pos0) / self.width == self.height-1 or self.data[self.pos0+self.width] == '=' else True
        elif direction == 'L':
            if last_moved == 'R': return False
            return False if self.pos0 % self.width == 0 or self.data[self.pos0-1] == '=' else True
        elif direction == 'R':
            if last_moved == 'L': return False
            return False if self.pos0 % self.width == self.width-1 or self.data[self.pos0+1] == '=' else True
        else:
            return False

    def next_pattern(self,last_moved):
        c = 0
        if last_moved != 'D':
            if (self.pos0) / self.width != 0 or self.data[self.pos0-self.width] != '=':
                c += 1
        if last_moved == 'U':
            if (self.pos0) / self.width != self.height-1 or self.data[self.pos0+self.width] != '=':
                c += 1
        if last_moved == 'R':
            if self.pos0 % self.width != 0 or self.data[self.pos0-1] != '=':
                c += 1
        if last_moved == 'L':
            if self.pos0 % self.width != self.width-1 or self.data[self.pos0+1] != '=':
                c += 1
        return c

    def move(self, direction):
        if direction == 'U':
            self.swap(self.pos0,self.pos0-self.width)
            self.pos0 = self.pos0 - self.width
        elif direction == 'D':
            self.swap(self.pos0,self.pos0+self.width)
            self.pos0 = self.pos0 + self.width
        elif direction == 'L':
            self.swap(self.pos0,self.pos0-1)
            self.pos0 = self.pos0 - 1
        elif direction == 'R':
            self.swap(self.pos0,self.pos0+1)
            self.pos0 = self.pos0 + 1

    def move_string(self, mstr):
        for m in mstr: self.move(m)

    def reverse_move(self, direction):
        if direction   == 'U': self.move('D')
        elif direction == 'D': self.move('U')
        elif direction == 'L': self.move('R')
        elif direction == 'R': self.move('L')

    def reverse_move_string(self, mstr):
        for m in mstr[::-1]: self.reverse_move(m)

    def find_pos0(self):
        return self.data.index('0')

    def swap(self, i, j):
         self.data[i], self.data[j] = self.data[j], self.data[i]

    def maze_score(self):
        """calculates sum of modified Manhattan distances"""
        cdef int manhattan_score = 0
        cdef int i, manhattan_dist

        for i,c in enumerate(self.data):
            if c != '=' and c != '0':
                dest = self.valid[c]
                if dest<i:
                    manhattan_dist = self.maze_score_table[dest][i]
                else:
                    manhattan_dist = self.maze_score_table[i][dest]
                manhattan_score += manhattan_dist
        return manhattan_score

    def maze_score_init(self):
        table = [[1000]*(self.width * self.height) for l in [1000]*(self.width * self.height)]
        for i,c in enumerate(self.data):
            if c != '=':
                if i%self.width-1 >= 0 and self.data[i-1] != '=':
                    table[i][i-1] = 1;table[i-1][i] = 1;
                if i%self.width+1 < self.width and self.data[i+1] != '=':
                    table[i][i+1] = 1; table[i+1][i] = 1
                if i-self.width-1 > 0 and self.data[i-self.width] != '=':
                    table[i][i-self.width] = 1; table[i-self.width][i] = 1
                if i+self.width < len(self.data) and self.data[i+self.width] != '=':
                    table[i][i+self.width] = 1; table[i+self.width][i] = 1
        search_path = []

        for i in range(len(self.data)):
            table[i][i] = 0

        for i in range(len(self.data)-1):
            for j in range(i+1,len(self.data)):
                if self.data[i] != '=' and self.data[j] != '=':
                    search_path.append((i,j))

        while len(search_path) > 0:
            s,g = search_path.pop(0)
            minimum_path = table[s][g]

            if not minimum_path < 1000:
                for m in range(len(self.data)):
                    mp = table[s][m] + table[m][g]
                    if mp < minimum_path:
                        table[s][g] = mp
                        table[g][s] = mp
                        minimum_path = mp
                    if not minimum_path < 1000:
                        search_path.append((s,g))
                        #print "%d,%d not found" % (s,g)
        return table

class SlidePuzzleSolver(object):
    """slidepuzzle solver"""

    def __init__(self,width,height,data,threashold=10,maxtime=10):
        self.puzzle = SlidePuzzle(width,height,data)
        self.candidates = ['']
        self.stats = {''.join(self.puzzle.data):1}
        self.backward_candidates = ['']
        self.backward_stats = {'123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:width*height-1]+'0':''}
        self.threashold = threashold
        self.maxtime = maxtime

    def solve(self):
        best = 100
        start = time.time()

        for rval in range(100):
            # start forward search 
            for cand in self.candidates:
                if time.time() - start > self.maxtime:
                    return ''
                self.puzzle.move_string(cand)
                last_moved = cand[-1] if len(cand) > 0 else ''
                for direction in ['U','D','L','R']:
                    if self.puzzle.can_move(direction,last_moved):
                        new_cand = cand + direction
                        self.puzzle.move(direction)
                        score = self.puzzle.calc_score()
                        stat = ''.join(self.puzzle.data)
                        if DEBUG: print self.puzzle.display(),'\n'
                        if score == 0: 
                            return new_cand
                        else:
                            if self.stats.get(stat,0) == 0:
                                self.stats[stat] = 1
                                if score < best: best = score
                                if self.puzzle.next_pattern(direction) == 1:
                                    self.candidates.insert(0,new_cand)
                                elif score < best + self.threashold + rval:
                                    self.candidates.append(new_cand)
                        self.puzzle.reverse_move(direction)
                self.puzzle.reverse_move_string(cand)
            # end forward search
        return ''

solve.py

import sys
from slidepuzzle import SlidePuzzleSolver
from multiprocessing import Pool

def answer (problem_t):
    problem_num, problem = problem_t
    prefix = "problem %d: (%s) " % (problem_num+1,problem)
    mylist = problem.split(',')
    width = int(mylist[0])
    height = int(mylist[1])
    data = mylist[2]

    solver = SlidePuzzleSolver(width, height, data,5,120)
    result = solver.solve()
    if len(result) == 0:
        sys.stderr.write(prefix + 'not found\n')
    else:
        sys.stderr.write(prefix + '!!! solved !!!\n')
    return result

class Calculate(object):
    def run(self, problems):
        p = Pool(2)
        return p.map(answer, enumerate(problems))

if __name__ == '__main__':
    with open("slidepuzzle.txt") as f:
        lim = [int(i) for i in f.readline()[:-1].split()]
        Llim,Rlim,Ulim,Dlim = lim[0], lim[1], lim[2], lim[3]
        Total_boards = int(f.readline()[:-1])
        problems = [l.rstrip() for l in f.readlines()]

    cl = Calculate()
    results = cl.run(problems)
    print 'solved: ', len([p for p in results if len(p) > 0])

devquizの一人ゲーム

5で割り切れて2で割り切れない場合に分岐させつつBFS

import sys

def halve(numbers):
   return [int(n / 2) for n in numbers]

def sweep(numbers):
   return [n for n in numbers if n % 5 != 0]

def get_data(f):
   N = int(f.readline())
   numbers = [int(n) for n in f.readline().split()]
   return (N, numbers)

def is_sweep_ok(numbers):
   for n in numbers:
       if n % 5 != 0:
           return False
   return True

def calc_cycle(numbers_set, count):
   #print numbers_set
   new_numbers_set = []
   for numbers in numbers_set:
       if is_sweep_ok(numbers):
           return count
       else:
           new_numbers_set.append(halve(numbers))
           for number in numbers:
               if (number % 5 == 0 and number % 2 != 0):
                   new_numbers_set.append(sweep(numbers))
                   break
   return calc_cycle(new_numbers_set, count + 1)

if __name__ == '__main__':
   with open(sys.argv[1]) as f:
       T = int(f.readline())
       total_count = 0
       while(total_count < T):
           N, numbers = get_data(f)
           if len(numbers) != N:
               print "error invalid numbers"
           print calc_cycle([numbers], 1)
           total_count += 1