MATSimではエージェントを、どういう単位で実現していますか? 私の場合、Go言語のgoroutineを使って、人数分のスレッドをつくって、その中にメソッドを書き込んでいますが、MATSimは、こういうプログラマブルな記述をエージェントに実装する仕組みになっているのですか?

MATSimではエージェントを、どういう単位で実現していますか?
私の場合、Go言語のgoroutineを使って、人数分のスレッドをつくって、その中にメソッドを書き込んでいますが、MATSimは、こういうプログラマブルな記述をエージェントに実装する仕組みになっているのですか?

 

いい質問です。結論から言うと、

MATSim の「エージェント」は、人数分のスレッドが立っていて、その中にメソッドを書くような仕組みではありません。
1人ずつプログラムを書くというより、共通の振る舞いロジック+個別の状態データという構成になっています。

江端さんの Go + goroutine のイメージと対比しながら整理します。


1. MATSimでのエージェントの「単位」

MATSim では、論理的なエージェントは大きく分けて2段階で表現されています。

(1) 計画レベル:Person + Plan

  • Person

    • 住民1人=Id<Person> で識別されるエージェント

    • 属性(年齢・性別・地区 …)は PersonAttributes に持たせる

  • Plan

    • その人の一日の台本Activity(自宅・職場・買い物)と Leg(移動)の列

    • 1人が複数の Plan を持ち、スコアが高いものが選ばれていく

ここまではただのデータ構造で、「どう動くか」のロジックは何も書いていません。

(2) シミュレーション中:MobsimAgent / MobsimDriverAgent

実際に qsim(交通流シミュレータ)が回っているときは、

  • Person に対応する「実行時エージェント」が MobsimAgent として生成されます。

  • 代表的には、MobsimDriverAgent というインタフェースを実装したクラスで、

    • 「次にどのリンクに進むか」

    • 「次の Activity にいつ切り替えるか」
      などを qsim から問いかけられ、それに応答する形で動きます。

この MobsimAgentJavaオブジェクトであって、スレッドではありません
シミュレータ本体(QSim)がループを回しながら、全エージェントの状態を順番に(または一部並列で)進めていきます。


2. Go の goroutine モデルとの違い

江端さんの Go 実装イメージ:

  • 人数分の goroutine を立てる

  • 各 goroutine の中にその人専用のメソッド・ロジックを書く

  • goroutine 同士はチャネルなどで通信

これに対して MATSim は:

  • 人数分のスレッドは作らない(基本は1スレッド+αのイベント駆動)

  • エージェント固有のロジックは「個別のソースコード」ではなく、
    **共通コード+個別パラメータ(属性・計画)**で表現

  • エージェント間の関係やイベント反応は、
    EventHandlerScoringFunction の実装として「横串」で書く

ざっくりいうと、MATSimは

「1人1スレッド+個別のプログラム」ではなく
「1つの交通流エンジンの中で、多数の状態マシンを回す」

タイプの設計です。


3. 「プログラマブルなエージェント」を実装する方法

とはいえ、「エージェントにメソッドを書き込めない」のではなく、
やり方が違うだけです。

3.1 共通ロジック+個別パラメータ

一番MATSimらしいやり方は:

  • 行動ルールは共通のクラスに書く(例:MyDynAgent

  • 各エージェントごとに

    • 属性(risk好き、社交的、インドアなど)

    • 特定の人物Id、行きたがる施設リスト
      を変えることで個性を出す

イメージ:

class MyDynAgent implements MobsimDriverAgent {
Person person;
PersonalityType type; // 外向型/内向型など
Set<Id<Person>> favoritePersons; // 「会いたい人」
// QSim から「次の行動は?」と聞かれるたびに、
// type や favoritePersons を参照して行動を決める
}

ここではコードは一種類ですが、
フィールド値によって**「人ごとの性格」が変わる**構造です。

3.2 DynAgent / within-day で「その場の意思決定」

前のやりとりで話したような、

  • 「特定の人に会った瞬間に進路を変える」

  • 「人混みが嫌いな人は混雑リンクを回避する」

といったその場での意思決定は、

  • DynAgent 系のエージェントクラスの中で実装

  • あるいは within-day replanning のロジックで、シミュレーション中に plan を書き換える

という形で書きます。


4. 「各エージェントに好き勝手メソッドを書く」ことはできるか?

技術的に言えば、Java なのでやろうと思えば何でもできますが、

  • 人数分のクラスや匿名クラスを作る

  • 人数分のスレッドを立てる

といった設計は、MATSimの思想とは真逆で、スケールもしません。

MATSimが想定しているのは、

  • 「エージェント全体を貫く共通ルールセット」

  • +「個体差は属性とパラメータで表現」

  • +「再計画(replanning)とスコアリングで行動パターンが進化」

というマクロな人口挙動モデルです。

江端さんの「1人1goroutine+個別メソッド」モデルは、
エージェント指向プログラミング / Actorモデルに近くて、それはそれで強力ですが、
MATSimはそこまでの自由度よりも「大量の人口を高速に回すこと」を優先して設計されています。


5. まとめ

  • MATSimのエージェントは:

    • 論理的には PersonPlan(データ)

    • 実行時には MobsimAgent / MobsimDriverAgent(状態マシン)

  • 人数分のスレッドや、人数分の別メソッドを持つ構造ではありません。

  • プログラマブルな振る舞いは、

    • 共通のエージェントクラス(DynAgentなど)にロジックを書く

    • 各人ごとの属性・パラメータで個性を出す

    • EventHandler / Scoring / within-day replanning で「接触」「出会い」への反応を定義する
      というスタイルになります。

もし「江端版MAS(Go + goroutine +性格つきエージェント)」を、
どこまでMATSim側にマッピングできるか、という視点で整理したいなら、

  • 性格・嗜好 → PersonAttributes

  • 台本(1日の予定) → Plan

  • その場の意思決定ロジック → DynAgent 実装

  • 接触に応じた行動変更 → EventHandler + DynAgent / within-day

という対応表を、もう少しきれいな図にしてお見せできます。必要ならそこまで書き起こします。

自律的判断を持てないエージェントは正直困ります。
しかし一方、「MAS本体をスクラッチから作れ」などと言う残酷なことを、私は、学生や後輩には言えません。
Go言語でエージェントを作って、それをMATSimで回す方法って、ないでしょうか?

正直に言うと、

「Go のエージェントをそのまま MATSim の中で走らせる」
という“公式なやり方”は ほぼ無い です。

MATSim はあくまで Java の中にすべてのエージェントを抱え込む設計 なので、
Go の goroutine をそのまま埋め込む、という発想とは構造が合いません。

ただ、完全に道がないわけではなくて、現実的な選択肢としてはこの3パターンかな、と思います。


選択肢①:Javaエージェント+Go「頭脳」の双方向連携(かなりヘビー)

イメージ

  • MATSim側

    • 各 Person に対応する DynAgent(Javaクラス)を作る

    • この DynAgent は、自分の「次の行動」を決めるときに
      → Go プロセスに状態を投げて、戻ってきた「行動案」に従う

  • Go側

    • いま江端さんが作っているような「自律判断エージェント」を goroutine で動かす

    • Java から送られてくる「現在位置・時刻・周辺状況」を入力にして
      「次にどこへ行くか」「どのリンクを選ぶか」を返す

技術的には

  • Java ↔ Go を

    • TCP/UDP socket

    • gRPC / HTTP
      などでつなぐ「コ・シミュレーション」になります。

メリット・デメリット

  • ✅ Go で書いた自律ロジックをそのまま活かせる

  • ✅ 交通流エンジンやネットワーク・経路計算は MATSim に任せられる

  • ❌ 実装はかなり大掛かり(Java 側と Go 側の両方にフレームワークが必要)

  • ❌ 性能・デバッグも重くなる(人数×ステップ分のRPCが飛ぶ)

「MAS本体をスクラッチで書くよりはマシ」かもしれないけど、
正直これは“自作フレームワーク級”の重さ
です。


選択肢②:MATSimは「シナリオ&ネットワーク生成器」と割り切り、Goで本番MASを回す

これは発想を逆にして、

  1. MATSimはオフライン専用に使う

    • ネットワーク(リンク・ノード)

    • 速度プロファイル・所要時間

    • 初期 OD・パーソントリップ的なプラン
      などを出すための「前処理ツール」と割り切る

  2. 本番のエージェントシミュレーションは Go で書く

    • すでにお持ちの goroutine ベースのエージェントを使う

    • 移動に関するコスト・時間は、MATSim からエクスポートした表(スキム)を参照

つまり、

ネットワーク・時空間コストの計算 → MATSim
エージェントの自律行動・出会い・心理 → Go の MAS

と役割を分けてしまうやり方です。

これは

  • ✅ 自律判断を全部 Go 側に寄せられる

  • ✅ MATSim の「一日分プラン+イテレーション」という思想に縛られない

  • ✅ 実装難易度は選択肢①よりずっと低い

  • ❌ 「MATSimの中で走っている」とは言えない(ただし実務上は問題ないことが多い)

**「江端版MAS+MATSim由来のネットワーク/コスト」**という構成なら、
今の Go コードを活かしつつ、MATSim を“いいとこ取り”できます。


選択肢③:自律性はJava側で書くと割り切り、MATSimの中で完結させる

もうひとつ現実的なのは、

「自律判断ロジックを Java に書き直す」
+「MATSimの DynAgent / within-day をフル活用する」

という割り切りです。

  • エージェントの「性格」「嗜好」「出会いへの反応」などを
    PersonAttributesDynAgentクラスに埋め込む

  • 「特定人物に接触したら plan をその場で変える」
    → DynAgent / within-day replanning で実装

これは

  • ✅ MATSimの設計に素直に乗っかれる

  • ✅ 既存のドキュメントやサンプルを流用しやすい

  • ❌ Go のコードは直接は使えない(考え方だけ移植)

「スクラッチでMAS全部書け」は避けつつ、
自律性をできるだけMATSimの中で実現する現実路線
です。


どれを選ぶのが良さそうか

江端さんの条件をざっくり整理すると:

  • 「エージェントは自律的判断をしてほしい」

  • しかし「MAS本体をイチから書け、とは言いにくい(言いたくない)」

  • かつ Go で既にエージェントモデルを持ちつつある

とすると、個人的なおすすめは順に:

  1. 選択肢③

    • まずは MATSim の中で DynAgent / within-day だけで
      「接触で行動が変わる」「性格で行動が変わる」をどこまで再現できるか試す

  2. それでも「やっぱり Go のコード資産を活かしたい」となったら、
    選択肢② で「ネットワーク・コストはMATSim、MASはGo」ときれいに役割分担

  3. 選択肢①(Java↔Goのオンライン連携)は、
    「研究テーマとしてコ・シミュレーションをやる」レベルの話にした方がよさそうです。


結論

  • MATSimの中に「Goのエージェントを直接入れて回す」ための素直な拡張ポイントは無い

  • やるとしたら、

    • Java側からGoにRPCするコ・シミュレーション(重い)か、

    • MATSimを“ネットワーク・前処理ツール”として使い、MAS本体はGoで回す構成か、

    • そもそも自律性をJava(DynAgent)側に移植するか、
      のどれかになる。


もし、「こういう感じで Go 側エージェントが state を持っている」というサンプル(擬似コードでも可)を1体ぶん見せて頂ければ、

  • それを MATSim DynAgent クラスにマッピングする案

  • もしくは MATSim↔Go コ・シミュレーションで最小構成のAPI設計案

をこちらで一回“たたき台”にして書いてみます。
「ここまでやるなら③でいいか…」という判断材料にもなると思います。

つまりJavaのクラスを使う方法なら、「まあ、なんとかなるかもしれない」という感じでしょうか?

そう、その理解でだいたい合ってる。

「Go でやっていることを、Java のクラスとして MATSim の枠の中に移植する」
という路線なら、“まあ、なんとかなる”現実的な射程に入っていると思う。

もちろん「楽勝」とまでは言わないけど、
「MAS本体をスクラッチで書け」という残酷さとは次元が違う、という感じ。


何をやれば済むのか(ざっくりスコープ)

Java クラスで頑張る場合に必要なのは、フルスクラッチではなくて、だいたいこの程度。

  1. 標準 MATSim はそのまま使う

    • ネットワーク構築、経路計算、qsim、反復(replanning)あたりは全部既製品に任せる。

  2. エージェントの性格・状態を PersonAttributes に持たせる

    • Go 側の「性格フラグ」「好み」「会いたい相手リスト」などを Java の属性に写す。

  3. 接触検出用の EventsHandler を 1クラス書く

    • LinkEnterEvent / ActivityStartEvent を拾って、

    • 「誰と誰が、どこで、いつ接触したか」を記録するクラス(前に書いた EncounterTracker)。

  4. 自律判断エージェントとしての DynAgent を 1クラス作る

    • Go の goroutine に相当する「頭脳」を Java クラスとして書くイメージ。

    • QSim から「次どこ行く?」と聞かれたときに、

      • 自分の属性

      • EncounterTracker が持っている「さっき誰に会ったか」
        を見て、経路変更や行動変更を決める。

  5. 必要なら within-day replanning で「残りの plan の書き換え」を入れる

    • 「会った瞬間から予定を変える」系の挙動をここで実現する。

やることは多いが、MATSim のコア部分には触らなくていいので、
「フレームワーク作者になる」必要まではない。


感覚的な対応づけ

江端版 MAS(Go) ↔ MATSim(Java) を、かなり雑に対応づけるとこうなる。

  • goroutine = 1人ぶんの自律エージェント
    MyDynAgent implements MobsimDriverAgent

  • goroutine 内の状態変数
    PersonAttributes + DynAgent のフィールド

  • goroutine 間の情報共有(チャネル/共有メモリ)
    → MATSim の Events + それを受ける EventsHandler

  • 「接触した瞬間に挙動を変える」
    → EventHandler でフラグ更新 → DynAgent/within-day が次の意思決定で参照


まとめ

  • 「Go エージェントをそのまま MATSim の中で動かす」は現実的ではない。

  • しかし Java クラス(DynAgent+EventsHandler+独自 Scoring)として書き直す路線なら、

    • MAS本体をスクラッチで書くほどの地獄ではなく、

    • 「いくつかの拡張クラスを追加する」レベルで収まる可能性が高い。

なので、「Java のクラスを使う方法なら『まあ、なんとかなるかもしれない』」という感触は、かなり現実に近いと思う。

もし次の一歩を決めるなら、
まずは「1人だけ特別扱いする DynAgent」の最小サンプル(接触したら家に帰る、くらい)を設計してみる、あたりから始めるのが良さそう。

正直に言うと「Javaって記述長いし、エラーメッセージ訳分からないし、ライブラリ煩いし、コンパイルも面倒」と思っているのですが、これって、単なる私の偏見でしょうか?

正直に言います。

いいえ、それは偏見ではなく、“かなり多くのエンジニアが抱えている実感”です。

江端さんが感じている
「Javaは記述が長い」「エラーが読みにくい」「ビルドがうるさい」「ライブラリが重い」
という感覚は、Java経験者の間でほぼ共通認識です。

以下、冷静に、しかし正直に整理します。


■ 1. Java は「記述が長い」 → 事実です

Java の根本思想が、

  • 明示的な型

  • 明示的なクラス構造

  • 例外宣言

  • インターフェースの多階層

なので、Go や Python に慣れていると異様に冗長に感じます。

例:Go の HTTP ハンドラは数行で書けるのに、
Java だとクラス+メソッド+アノテーション+例外+import で 10倍になります。

これは「言語として目指している方向が違う」ので、実際に短くはなりません。


■ 2. Java のエラーメッセージは「読みにくい」 → これも事実

Java の例外は階層が深く、スタックトレースも長い。

NullPointerException
at com.example.service.UserService.lambda$loadUser$17(UserService.java:123)
at java.base/java.util.Optional.orElseThrow(Optional.java:408)

Go の panic() や Python の ValueError の方がよほど読みやすい。

MATSim になるとさらに、

  • Guice(DIコンテナ)

  • config 読み込み

  • XML パース

  • イベントバス

など抽象レイヤが多く、“どこが本当の原因なのか”の特定に時間がかかりやすいです。

これは多くの開発者が「つらい」と感じています。


■ 3. Java のライブラリ管理は「煩い」 → ほぼ全員の共感ポイント

Maven / Gradle / Ivy などビルドツールの文化が複雑で、

  • 依存関係ツリーが長い

  • SNAPSHOT

  • transitive dependency

  • Java version mismatch

など、トラブルの原因が多い。

MATSim は依存関係が特に多いので、Java初心者が触ると「ライブラリ沼」にハマりがちです。


■ 4. コンパイルが面倒 → はい、Goと比べれば間違いなく面倒

Go:

go build

Java:

  • Maven/Gradle を叩く

  • 依存解決

  • テスト

  • JVM バージョン

  • ビルドパス

  • クラスパス

つまり、「作業が 1段多い」。

経験者でも「Javaはセットアップが面倒」と言うのが普通です。


■ では、Java の良いところは何か?

こういう “面倒なもの” が生き残っている理由は明確で、

✔ 大規模システムに強い(MATSimがJavaを採用した 最大理由)

✔ 型安全・例外安全が徹底している

✔ 並列・GC・JIT など実行性能が高い

✔ 20年以上の生態系と豊富なツール

✔ ユーザ数が世界的に多く、長期メンテ性が高い

簡単に言えば、

Java は「100万人の都市交通を動かすレベルの巨大なMAS」のために選ばれた言語
であって、
Go のような“軽量で気持ちよく書ける”言語とは目的が違う

ということです。


■ 結論:「あなたの感覚は正しい」+「MATSim的には Java は合理的」

江端さんが感じている不満は、
偏見ではなく、Javaを使う人のほぼ全員が抱く問題意識です。

そして、MATSimがJavaで書かれているのは、

「大規模・高性能・堅牢さ」が最優先のシミュレーションエンジンだから

という理由に基づいたものです。


■ もし「Javaがつらい」が続く場合の方針

1. Javaエージェントを最小限にとどめる

→ 行動ロジックは属性+簡単な条件分岐のみにする

2. Goで自律的判断を作り、それをMATSim側の「入力データ」に落とす

→ MATSim は「道路・ルーティング専用エンジン」と割り切る

3. DynAgent を使うが、Java部分はラッパー的にして実質ロジックは外出し

→ Java コードの負担を極力減らす


■ 最後に:江端さんの直感は、技術的にかなり妥当です

  • Java は冗長

  • エラーは読みにくい

  • コンパイルは面倒

  • ライブラリ管理も難しい

→ それは個人的な偏見ではなく、Javaが歴史的に背負っている宿命です。

ただし、

MATSimのように「巨大都市を1分単位で交通シミュレートする」世界では、
その“重厚さ”が必要だった

というだけです。


必要なら:

  • 「最小コードで DynAgent を書くテンプレ」

  • 「Go のエージェント実装を Java に写すためのマッピング表」

  • 「江端さんの MAS を MATSim と融合する最も軽い構成案」

なども作れます。

どういう方向に進むか、言っていただければ道筋をもっとクリアにします。

未分類

Posted by ebata