# ルール文法 ## 概要 本章では、artisoc Cloudモデルのルール記述についての仕様を説明します。 モデルのルールは、基本的にPython言語(ver.3系)を用いて記述します。 ## Pythonの基礎文法 artisoc Cloudでは、プログラミング言語Pythonを用いてモデルのルールを記述します。本節では、artisoc Cloudのルール記述に最低限必要なPythonの基礎的な文法を説明します。詳しくはPythonの公式ドキュメントを参照してください。なお、一部にartisoc Cloud特有の内容も含まれていますが、Python使用経験のある方は読み飛ばしても問題ありません。 ### 変数・代入・演算 変数とは、値を保存しておく箱のようなものです。たとえば以下のように記号「=」を用いて記述することで、変数aを作成しそこに値3を保存することができます。これを代入と呼びます。 ``` a = 3 ``` ここで記号「=」は右辺の値を左辺に代入する操作を表します。数学とは異なり、両辺が等しいことを示す記号ではないことに注意してください。 変数名は基本的には自由につけることができますが、以下のようなルールがあります。 - 大文字と小文字は区別される。 - 変数名に空白を含んではいけない。 - 記号は一部を除き使えない。たとえば「-」(ハイフン)は後述するように引き算の演算子として解釈されるため変数名には使えない。一方で「_」(アンダースコア)は使用可能で、変数名内で単語を区切るためによく使われる。 - 変数の1文字目には数字を使えない。 - 以下の文字列はPythonで特別な意味を持つため、変数名として使えない。これをPythonのキーワード(あるいは予約語)という。 - and, as, assert, break, class, continue, def, del, elif, else, except, false, finally, for, from, global, if, import, in, is, lambda, None, nonlocal, not, or, pass, raise - 既に存在する関数名などを変数名に使ってしまうと、その関数が使えなくなってしまうため注意が必要である。 数値や変数を用いて、演算を行うことができます。たとえば以下のように記述することで、変数bに数値7が代入されます。 ``` a = 3 b = a + 4 ``` Pythonの演算で用いることのできる代表的な演算子を以下に示します。 ```eval_rst .. list-table:: :header-rows: 1 :widths: 5, 25 * - 演算子 - 説明 * - **=** - | 右辺を左辺に代入する | 例:変数aに3を代入する | ``a = 3`` * - **\+** - | 足し算 | 例:2に3を足した値をaに代入する | ``a = 2 + 3`` * - **\-** - | 引き算 | 例:3から2を引いた値をaに代入する | ``a = 3 - 2`` * - **\*** - | 掛け算 | 例:3に2を掛けた結果をaに代入する | ``a = 3 * 2`` * - **/** - | 割り算 | 例:3を2で割った値(1.5)をaに代入する | ``a = 3 / 2`` * - **\*\*** - | 累乗 | 3の2乗(9)をaに代入する | ``a = 3 ** 2`` * - **%** - | 割り算の余り | 例:7を3で割った余り(1)をaに代入する | ``a = 7 % 3`` * - **//** - | 割り算の商 | 例:7を3で割った商(2)をaに代入する | ``a = 7 // 3`` ``` ### コメントアウト プログラムの行内で、記号「#」以降には何を記述してもプログラムの動作に影響を与えません。これをコメントアウトと呼び、主にプログラムの説明によく用いられます。 ``` a = 2 + 3 # 2+3の計算結果を変数aに代入 # 変数bにa+5の計算結果を代入 b = a + 5 ``` ### 関数 関数とは一連の処理をまとめたものです。artisoc Cloudにはモデル作りに役立つ多くの関数が用意されています。 たとえばround()は実数型の数値を四捨五入する処理を表します。たとえば以下のように使用することで、4.7を四捨五入した結果(5)を変数に代入することができます。 ``` a = round(4.7) # 変数aに整数5を代入 ``` ここでの数値4.7を関数の引数、数値5を関数の返り値(または戻り値)と呼びます。 関数には返り値を持たないものもあります。たとえばprint関数は、引数の値をコンソールに出力する機能を持ちます。変数の値の確認などによく用いられます。 ``` a = 2 + 3 print(a) # コンソールに「10」が出力される ``` ### if文(条件分岐文) 条件に応じて処理を分岐させる文です。以下のように書きます。 ``` if (条件式): (条件式が真の場合の処理) else: (条件式が偽の場合の処理) ``` 処理を書く部分は、タブを用いて1段字下げをします。これをインデントといい、インデントによって処理の範囲が定義されます。else以下は省略することもできます。 たとえば以下のように用います。 ``` if a < 5: print(“aは5より小さい”) else: print(“aは5以上”) ``` あるいは以下のように、複数の条件式を用いた条件分岐を記述することもできます。 ``` if (条件式1): (条件式1が真の場合の処理) elif (条件式2): (条件式1が偽で、条件式2が真の場合の処理) else: (条件式1も条件式2も偽の場合の処理) ``` ### for文(繰り返し文) 繰り返しの処理を表す文です。 たとえば以下のように書くと、0から9までの数値を順にコンソールに出力します。 ``` for i in range(10): print(i) ``` range(10)によって0から9までの数値の集合を生成し、それら1つ1つを順にiに代入し処理を行うようなイメージです。 artisoc Cloudにおいては、エージェント集合の1つ1つのエージェントに対し操作を行う際によく用います。たとえば以下のように使います。 ``` people = make_agtset(type=Universe.hiroba.hito) # hitoエージェントの集合peopleを生成 for person in people: # peopleの要素1つ1つ(person)について、以下の処理を行う # もしx座標が25未満なら、25にする if person.x < 25: person.x = 25 ``` ここで、make_agtsetはエージェント集合を生成する関数です。エージェント集合peopleの一つひとつの要素(person)をfor文を用いて取り出し、personの属性に応じた処理をif文を用いて行っています。 このように、for文の中でif文を用いるなど、for文やif文は入れ子構造をとることができます。 ### 変数の型 前節では変数について数値計算を例に説明しましたが、変数には数値以外にも様々な種類の値を代入することができます。これを変数の型といいます。以下では、artisoc Cloudを扱うにあたって最低限必要と思われる変数型を紹介します。 **実数型** 実数を扱う変数型です。 コンピュータの性質により計算に微妙な誤差を生じる場合がありますので、場合によっては注意が必要です。 下記の例では変数aに0.1+0.2の計算結果を実数型として代入していますが、計算結果は0.3でなく0.30000000000000004となり、微妙な誤差を生じます。 ``` a = 0.1 + 0.2 print(a) # --> 0.30000000000000004 ``` **整数型** 整数を扱う変数型です。実数型とは異なり、計算に誤差は生じません。なお、整数型同士の割り算の結果は実数型になります。 ``` a = 1 + 2 print(a) # --> 3 (整数型) # aには整数型の数値3が代入される b = 3 / 2 print(b) # --> 1.5 (実数型) ``` **文字列型** 数値ではなく文字列を扱う型です。多くの場合、「’」または「”」で囲まれた文字の並びで表現されます。 ``` a = “artisoc Cloud” print(a) # --> コンソールに「artisoc Cloud」と出力される ``` **エージェント種別型** artisoc Cloud特有の変数型で、エージェントの種別を表します。 主にcreate_agt関数を用いてエージェントを生成するときに用います。下記の例ではエージェント種別型変数Universe.hiroba.hitoを用いて、その種別のエージェントを生成しています。 ``` create_agt(Universe.hiroba.hito) # hitoエージェントを生成 ``` **エージェント型** artisoc Cloud特有の変数型で、具体的な1つのエージェントを表します。エージェントを操作する際などに使用します。 ``` one = create_agt(Universe.hiroba.hito) # hitoエージェントを生成し、変数oneに代入 one.x = 25 # エージェントoneのx座標に25を代入 ``` **セット型** 複数の変数をひとまとめにした集合を表す変数型です。集合の要素となるそれぞれの変数の型は何でもかまいません。また、要素の順番は定義されません。 artisoc Cloudにおいては、関数を用いてエージェントの集合を生成するときによく使います。 ``` # 全てのhito種別エージェントの集合を変数peopleに代入 people = make_agtset(agttype=Universe.hiroba.hito) ``` **リスト型** セット型と同じく複数の変数をひとまとめにした集合を表す変数型ですが、要素に順序が定義されます。記号[]で位置を指定することで、その位置の要素を取得することができます。 ``` # 全てのhito種別エージェントの集合(セット型)を変数peopleに代入 people = make_agtset(agttype=Universe.hiroba.hito) # セット型変数peopleをリストに変換し、属性tallの昇順に並べる people = make_agtlist(people, key=”tall”) # 最も小さい人(リストの0番目)を取得する smallest_person = people[0] ``` ## モデルツリーの構成 モデルツリーはモデルの概形を規定するもので、以下の要素からなります。 - **Universe** - モデルの全体を表す要素。モデル全体の特徴を規定するルールと変数を持つ。モデルにただ一つ存在する。 - **空間** - モデル内の空間を表す要素。Universeの直下に作成することができる。 - **エージェント種別** - モデル内で自立的に行動するエージェントを表す要素。エージェントの特徴を規定するルールと変数を持つ。Universeの直下または空間の直下に作成することができる。空間の直下に作成した場合は空間内で行動するエージェントを表し、位置座標など空間にまつわる変数を持つ。 - **Universe変数** - モデル全体の属性や状態を表す変数で、Universeの直下に作成することができる。 - **空間変数** - 空間の属性や状態を表す変数。配列型の変数であり、空間の座標やレイヤごとに値を持つ。空間の直下に作成することができる。 - **エージェント変数** - エージェントの属性や状態を表す変数。エージェント種別の直下に作成することができる。 ## ルールの全体構成 モデルのルールは、Universeのルールとエージェントのルールの2種類があります。 Universeのルールはモデル全体の挙動を決定するもので、以下のような構成となっています。 - **univ_init** - シミュレーション開始時に一度だけ実行されるルール - **univ_step_begin** - シミュレーションの各ステップの最初に実行されるルール - **univ_step_end** - シミュレーションの各ステップの最後に実行されるルール - **univ_finish** - シミュレーション終了時に一度だけ実行されるルール エージェントのルールはエージェント種別ごとに定義され、各エージェントの行動を決定するものです。以下のような構成となっています。 - **agt_init** - エージェント生成時に一度だけ実行されるルール - **agt_step** - シミュレーションの各ステップで実行されるルール シミュレーション全体を通してのルールの実行順序は以下のようになります。 まずシミュレーション開始時にuniv_initが実行されます。各タイムステップにおいてuniv_step_begin →各エージェントのagt_step →univ_step_end の順でルールが実行されます。シミュレーション終了条件が満たされたとき、univ_finishが実行され、シミュレーションが終了します。 実際のルールエディタは以下のようになっています。ルールを記述する際は、タブを用いてインデント内に記述することに注意してください。また、何もルールを記述しない場合は「pass」と記述してください。 Universeのルールエディタ ``` def univ_init(): pass def univ_step_begin(): pass def univ_step_end(): pass def univ_finish(): pass ``` エージェントのルールエディタ ``` def agt_init(self): pass def agt_step(self): pass ``` ## 変数の呼び出し ### ツリー要素の呼び出し ツリーの要素のうち、以下のものは変数として直接呼び出すことができます。 - Universe変数 - 空間変数 - エージェント種別 #### Universe変数の呼び出し たとえば、Universe変数としてhensuuを定義したとします。hensuuを呼び出すためには、ルール内に以下のように記述します。 ``` Universe.hensuu ``` hensuuに数値3を代入するには、以下のように記述します。 ``` Universe.hensuu = 3 ``` #### 空間変数の呼び出し Universeの下に空間hirobaを定義し、hirobaの下に空間変数takasaを定義したとします。takasaを呼び出すためには、ルール内に以下のように記述します。 ``` Universe.hiroba.takasa ``` 空間変数は3次元配列(リスト)となっており、0番目の要素にx座標、1番目の要素にy座標、2番目の要素にレイヤ番号を持ちます。レイヤ番号0の座標(x,y)=(2,3)に数値5を代入するには、以下のように記述します。 ``` Universe.hiroba.takasa[2, 3, 0] = 5 ``` #### エージェント種別の呼び出し Universeの下に空間hirobaを定義し、hirobaの下にエージェント種別hitoを定義したとします。hitoを呼び出すためには、ルール内に以下のように記述します。 ``` Universe.hiroba.hito ``` エージェント種別hitoからエージェントを1つ生成するためには、create_agt関数を用いて以下のように記述します。 ``` create_agt(Universe.hiroba.hito) ``` エージェント種別はエージェントを作成するためのひな形のようなものです。ルールや変数はエージェント種別に設定しますが、実際にルールや変数を持つのはエージェント種別から生成された個別のエージェントとなります。 ```eval_rst .. image:: images/chapter3/agttype_agt.png :align: center :alt: エージェント種別とエージェント ``` エージェント種別に値を代入することは通常ありません。 ### エージェント変数の呼び出し エージェントは変数を持ちます。全てのエージェントはデフォルトで属性idを、さらに空間エージェントはデフォルトで属性x, y, layer, directionを持ちます。さらに、属性は自由に追加することができます。 ルールからエージェント変数を呼び出すには2つ方法があります。 1つ目の方法は、エージェントのルールエディタからselfを用いてアクセスする方法です。たとえば属性xを呼び出すためには、該当エージェントのルールエディタ内で以下のように記述します。 ``` self.x ``` ここでselfは自分自身のエージェントを意味するエージェント型の変数です。 2つ目は、エージェント型の変数から呼び出す方法です。たとえば以下のように記述します。 ``` taro = create_agt(Universe.hiroba.hito) # hitoエージェントを生成し変数taroに代入 taro.x = 3 # taroのx属性に3を代入 ``` ここで、1行目ではhitoエージェントを1つ生成し、変数taroに代入しています。つまりこれ以降、taroは具体的な1つのhitoエージェントを表します。 2行目でtaro.xと記述することで、taroの持つx属性を呼び出すことができます。 ## artisoc Cloudの関数種別 関数とは一連の処理をまとめたものです。 artisoc Cloudで使える関数には大きく分けて以下の種類があります。 - **Python組み込み関数** - Pythonにもともと組み込まれており、最初から使用可能な関数 - **artisoc Cloud組み込み関数** - artisoc Cloudにもともと組み込まれており、最初から使用可能な関数 - **ユーザ定義関数** - ユーザが作成する関数 以降、このそれぞれについて説明します。 ### Python組み込み関数 Python組み込み関数は、Pythonの機能としてもともと組み込まれている関数です。ルールのどこからでも、関数名のみで呼び出すことができます。 たとえばround()は実数型の引数を四捨五入して整数を返す関数で、以下のように使用します。 ``` a = round(4.7) # 変数aに整数5を代入 ``` Python組み込み関数は基本的に、後述するartisoc Cloud組み込み関数のグローバル関数と同じように使用することができます。 Python組み込み関数についての詳細は、[Pythonの公式ドキュメント](https://docs.python.org/ja/3/library/functions.html)を参照してください。 ### artisoc Cloud組み込み関数 artisoc Cloud組み込み関数は、MASモデルを作成するために便利な機能として、artisoc Cloudにもともと組み込まれており最初から使用可能な関数です。大まかな機能別に分類すると、以下のようなものがあります。 - 数値計算 - 文字列操作 - データ型変換 - エージェント操作(生成・削除) - エージェント操作(その他) - 空間エージェント関数(移動) - 空間エージェント関数(空間操作) - 空間エージェント関数(エージェント集合生成) - エージェント集合操作(基本操作) - エージェント集合操作(生成) - エージェント集合操作(配置) - エージェント集合操作(その他) - 空間操作 - ファイル入出力 - その他 さらに、これらの関数は主に以下のように分類されます。 - **グローバル関数** - ルールのどこからでも関数名だけで呼び出せる関数 - **エージェント関数** - エージェントが持つ関数であり、エージェント経由で呼び出す関数 多くはグローバル関数ですが、以下の3分類の関数はエージェント関数です。これらは空間直下のエージェントのみについて使用することができます。 - 空間エージェント関数(移動) - 空間エージェント関数(空間操作) - 空間エージェント関数(エージェント集合生成) 以降、グローバル関数とエージェント関数それぞれの使い方について説明します。なお、各関数の詳細は関数仕様書をご参照ください。 #### グローバル関数の使い方 グローバル関数はルールのどこからでも関数名だけで呼び出すことができます。 たとえば、エージェントを生成するcreate_agtという関数は引数にエージェント種別をとり、ルールの好きなところに以下のように記述します。 ``` create_agt(Universe.hiroba.hito) # hitoエージェントを生成 ``` #### エージェント関数の使い方 エージェント関数はエージェント自身の行動や操作を表す関数です。 この関数を使うためには、エージェントのルールエディタ内でselfを用います。 たとえば、空間エージェントが持つエージェント関数forward()を用いるためには、エージェントのルールエディタ内に以下のように記述します。 ``` self.forward(1) # 前方に1進む ```    ### ユーザ定義関数 関数はユーザ自身で定義することもでき、これをユーザ定義関数と呼びます。 ユーザ定義関数には、Universeのユーザ定義関数とエージェントのユーザ定義関数があります。 基本的に、Universeのユーザ定義関数はモデル全体に関わる処理について定義し、エージェントのユーザ定義関数はエージェントの行動について定義します。 #### Universeのユーザ定義関数 Universeのユーザ定義関数を作成するためには、Universeのルールエディタの末尾に記述します。 たとえば、エージェントhitoを生成し指定の座標に置くという関数put_hitoを作るためには、以下のように記述します。 ``` def univ_finish(self): ... def put_hito(x, y): h = create_agt(Universe.hiroba.hito) # hitoエージェントの生成 h.x = x # x座標の指定 h.y = y # y座標の指定 ``` この関数を用いるためには、ルールの好きなところで以下のように記述します。 ``` Universe.put_hito(2, 3) # hitoエージェントを生成し、座標(2,3)に置く ``` #### エージェントのユーザ定義関数 エージェントのユーザ定義関数を作成するためには、エージェントのルールエディタの末尾に記述します。 たとえば、エージェントの向いている正面前方(距離dist)の一定範囲(range)にいる特定のエージェント種別(type)の数を調べる関数watch_aheadを作るには、エージェントのルールエディタ末尾に以下のように記述します。 ``` def agt_step(self): ... def watch_ahead(self, dist, range, type): self.forward(dist) # 前方に進む # 周囲のエージェントをneighborに格納し、数を数える neighbor = self.make_agtset_around_own(range, False, agttype=type) n = count_agtset(neighbor) # もとの位置に戻る self.forward(-dist) return n ``` この関数を用いるためには、エージェントのルールエディタ内で以下のように記述します。 ``` # 前方距離2, 範囲1にいるhitoエージェントを数え、変数nに格納 n = self.watch_ahead(2, 1, Universe.hiroba.hito) ``` ## モジュール モジュールとはプログラムを記述する際に有用となる関数などをまとめたものです。artisoc Cloudでは、Pythonが提供している以下のモジュールを使用することができます。全てのPython標準ライブラリを使用できるわけではありませんので、ご注意ください。 - csv - math - random - json - Bio - kiwisolver - networkx - numpy - matplotlib - more_itertools - mpmath - pandas - scipy - copy - datetime   モジュールを使用するためには、ルールエディタ内でimport文を用いてモジュールを読み込みます。 たとえばmathは様々な数学の計算を行うためのライブラリです。univ_init()内でmathを利用するためには、以下のようにします。 ``` def univ_init(): import math # mathモジュールをインポート a = math.asinh(1) # mathモジュールの逆双曲線正弦関数を利用 print(a) # --> 0.8813735870195429 ```