1. ogata-k

    Posted

    ogata-k
Changes in title
+pythonのsimpyでシミュレーションをしてみた その1
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,77 @@
+#そもそも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関数として実装してみると
+
+```python: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の一番簡単なコードは以下のようになります。
+
+```python: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を説明します。
+コメントをお待ちしておりまーす\\(^o^)/