14
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

pythonのsimpyでシミュレーションをしてみた その1

そもそもsimpyとはなにか

simpyはsimulation pythonの略語でpythonのライブラリの一つです。といってもよくあるような連続的な物理シミュレーションを対象としているわけではなく数学的に離散型待ち行列と呼ばれるものを対象としています。つまり時間が離散的な場合において、待ち行列と呼ばれる数学の行列ではなく現実に人が待たなければいけない行列を対象としています。待ち行列の具体例としてスーパーマーケットの行列があげられます。まぁ要はsimpyは待ち行列を離散的に扱ってpythonでシミュレーションしてくれるわけですね。待ち行列の詳しい内容はここでは説明しないのでググってどうぞ。または待ち行列理論の記事を見てください。

対象読者

今回の記事を読んでもらいたい対象者としてはpython3系を使い、pythonの基本ライブラリのrandom、scipy、matplotlib、sys、osを簡単にではあるが使えることを前提として考えていますのでsimpyに関しないことの説明を省かせていただくのであしからず。ですがシミュレーションで必要なのはsimpyだけなので概略をつかむだけでいいならこれらを使えなくても結構ですし、これらが出てくるのはその3以降です。
そして注意してほしいこととして筆者がテキトーに使えるように頑張って英文を理解しただけですので単語の日本語訳はテキトーです。それでも言い方は読み続けても大丈夫です。それ以外の方は参考記事程度に読み進めていってください。

pythonのイテレータ関数(まだpythonの基礎の部分)

pythonでforループを使うときにrange関数を使うときがあると思います。簡単に言うとこのrange関数がイテレータと呼ばれるものです。イテレータとは実際に関数を実装するときにyield文を使います。簡単にrange関数をmyrange関数として実装してみると

myrange.py
def myrange(n):
    j = 0
    while j < n:
        yield j
        j += 1

for i in myrange(3):
    print(i)  # 0 1 2

このmyrange関数はforループで呼び出される度にyieldでいったん止まりそこで数値を返します。そしてfor文で、作成した変数に代入します。そしてまたfor文で呼ばれたときにyieldの後の文から関数が実行されます。これを繰り返してこの関数を抜けたときにこの関数は勝手にエラーを返してforループを抜けます。
こういったイテレータ呼ばれる関数はpythonでも使われる基礎的な文法ですがsimpyで頻出する文法でもあります。

simpyの基本的な文法(ここから本番)

simpyの一番簡単なコードは以下のようになります。

practice.py
import simpy

def myrun(env):
    while True:
        print(env.now)
        yield env.timeout(2)

env = simpy.Environment()
env.process(myrun(env))
env.run(until=10)  # 0 2 4 6 8

まず

import simpy

でsimpyをインポートします。
そのあとにイテレータmyrun()を定義しておきます。(あとで詳しく説明します)
次に

env = simpy.Environment()

でsimpyの環境を作成します。今後このenvとおいた環境のプロセスにイテレータを追加してシミュレートすることになります。つまり今回の場合では先ほどテキトーに説明したイテレータmyrunをこのenv環境のプロセスに追加してenv.run関数でシミュレーションします。myrunは無限ループですが内部でenv環境の時刻を表示してからそのenv環境の2単位時間分(何を単位時間とするかは任意)の時間を待つイテレータです。(つまり2単位時間分そのenv環境からタイムアウトをします。)また、yield env.timeout(1)だと1単位時間分待つのでenv環境が1単位時間分進む毎に呼ばれることになるわけです。そして

env.process(myrun(env))

でenv環境にプロセスを追加して

env.run(until=10)

で10単位時間分だけenv環境の時間を進めます。ここでもしmyrun関数が

def myrun(env):
    print(env.now)
    yield env.timeout(2)

だとすると、一度だけ実行してすぐに関数を抜けるのでイテレータとしてのエラーを吐いて関数の役目は終わります。つまりenv.run()の結果は0だけ表示されて終わります。また、untilを設定しないと無限に計算してくれます。しかしすべてのイテレータがエラーをはいて止まってくれていればenv.run()は無限に計算せずにすべてがエラーをはいたときに止まってくれます。
簡単なsimpyの基本動作はこれですべてです。

次回予告

simpyの重要な道具である資源(というか利用可能サーバ)のsimpy.Resourceとコンテナ(というかこちらが利用可能資源)のsimpy.Containerを説明します。

その2ができました。
2017/2/27にジェネレータ関数の記述をpythonの一般的な記述のイテレータに書き直しました。
2017/3/25に待ち行列に関しての記事へのリンクを貼り付けました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
14
Help us understand the problem. What are the problem?