Mesa3.xによる経済循環モデル（２）

# はじめに
[前回](https://qiita.com/shirokedamaneko/items/ddff6f70ebe6f917c88c)に続きMeaa3.xでプログラム。
お題は、「貨幣の複雑性」(安富歩）の物々交換モデル(本書P59-61)です。
jupyter notebook上で動きます。
虫取りは不完全ですが、とりあえず動く状態なので、忘れないうちにアップします。

# 補助関数等
ロジックで共通なものを抜き出しました。

```python
from mesa import Agent, Model
import numpy as np

import random
def random_n_except_i(n,i):
    r = random.randint(1,n-1)
    if r >= i:
        r += 1
    return r

# 配列hの数の少ない順に、配列位置を配列rに返す。
# 但し、個数の数だけ重複登録し、同数グループの順番はシャッフルする。
# 最後に配列の頭からm個を切り出す。

def select_k(h,m):
    r = []
    for i in range(1,max(h)+1):         # i = 1..配列hの最大個数
        t = []                          # t 同じ個数のグループ
        for j in range(len(h)):
            if h[j] == i:
                for k in range(1,i+1):  # 個数分だけ複製
                    t.append(j)
        random.shuffle(t)
        print(f"{i} = {t}")
        r.extend(t)
    return r[:m]

def exchange(AH,AD,BH,BD,DB):
    ks = select_k(BH,DB)
    print(ks)
    for k in ks:
        if BH[k] > 0:
            AH[k] += 1
            AD[k] -= 1
            BH[k] -= 1                  
                
        for j in range(len(BD)):
            BH[j] += BD[j]
            BD[j] = 0
    
    return ks
```
# MoneyAgentを作成

```python
class MoneyAgent(Agent):
    def __init__(self, model):
        super().__init__(model)
        self.w = 0      # 要求する財
        self.p = 0      # 生産する財
        self.h = []     # 所有商品
        self.d = []     # 需要
        self.u = 0.0    # 得点
        self.s = 0
    
    def step(self):
        # 1. 交換相手Bを選択する
        max_w = 0
        for b in self.model.agents:
            if b.unique_id != self.unique_id:
                hw = b.h[self.w - 1]        # bが持っている商品wの数量
                if hw > 0:
                    if hw > max_w:          # 所有数の最も大きい相手を選ぶ
                        B = b
                        max_w = hw

        if max_w > 0:           # 2. 互いの需要量を決定する
            A=self
            AW = A.w - 1        # Aの需要する商品index
            BW = B.w - 1        # Bの需要する商品index
            A.d[AW] = B.h[AW]   # Aの需要数量       
            B.d[BW] = A.h[BW]   # Bの需要数量

            DA = np.sum(A.d)    # Aの需要数量
            DB = np.sum(B.d)    # Bの需要数量

            # 交換するかどうかの判定して交換する
            if DA == DB and DB > 0:    
                for i in range(len(A.h)):
                    CA = A.d[AW] - A.d[AW]
                    CB = B.d[BW] - B.d[BW]
                    A.h[AW] += CA
                    B.h[AW] -= CA
                    B.h[AW] += CB
                    A.h[AW] -= CB           
                             
            elif DA > DB and DB > 0:
                exchange(AH,AD,BH,BD,DB)

            elif DB > DA and DA > 0:
                exchange(BH,BD,AH,AD,DA)   

            if DA > 0 and DB > 0:
                self.s += 1
                print(f"AW={AW+1} BW={BW+1} DA={DA} DB={DB}")
                print(f"A:Agent{A.unique_id} has h={A.h} d={A.d} w={A.w} and p={A.p}  u={A.u} sumD={np.sum(A.d)}")
                print(f"B:Agent{B.unique_id} has h={B.h} d={B.d} w={B.w} and p={B.p}  u={B.u} sumD={np.sum(B.d)}")   
                                 
            #　交換後、需要量を初期化
            A.d = np.zeros(len(A.h),dtype=int)
            B.d = np.zeros(len(B.h),dtype=int)         


```

# MoneyModelの作成
```python
class MoneyModel(Model):
    def __init__(self, n, seed=None):
        super().__init__(seed=seed)
        self.num_agents = n
        self.count = 0
        self.s = 0

        # Create n agents
        MoneyAgent.create_agents(model=self,n=n)

    def initalize(self):
        n = self.num_agents
        for a in self.agents:
            i = a.unique_id
            a.w = random_n_except_i(n,i)
            a.p = i
            a.h = np.zeros(n,dtype=int)
            a.d = np.zeros(n,dtype=int)
            a.h[i-1] = 1
            a.u = 0.0
            a.s = 0     # 取引回数
            
    def chenge_w(self):
        n = self.num_agents
        for a in self.agents:
            i = a.unique_id
            w = a.w
            a.w = random_n_except_i(n,i)
            ## print(f"Agent{i} change W from {w} to {a.w}")

    def step(self):
        self.initalize()
        self.agents.do("step")
        self.s = 0
        for a in self.agents:
            self.s += a.s
        if self.s > 0:
            print(f"success = {self.s}")
            print("***********************")
```

# 実行結果
最後は、取引回数と、取引成功の平均値です。

```python
model = MoneyModel(5)
ss = []
for _ in range(5):
    model.step()
    ss.append(model.s)
    model.chenge_w()
print(ss)
print(np.mean(ss))

```

AW=4 BW=2 DA=1 DB=1
A:Agent2 has h=[0 1 0 0 0] d=[0 0 0 1 0] w=4 and p=2  u=0.0 sumD=1
B:Agent4 has h=[0 0 0 1 0] d=[0 1 0 0 0] w=2 and p=4  u=0.0 sumD=1
AW=2 BW=4 DA=1 DB=1
A:Agent4 has h=[0 0 0 1 0] d=[0 1 0 0 0] w=2 and p=4  u=0.0 sumD=1
B:Agent2 has h=[0 1 0 0 0] d=[0 0 0 1 0] w=4 and p=2  u=0.0 sumD=1
success = 2
***********************
AW=4 BW=1 DA=1 DB=1
A:Agent1 has h=[1 0 0 0 0] d=[0 0 0 1 0] w=4 and p=1  u=0.0 sumD=1
B:Agent4 has h=[0 0 0 1 0] d=[1 0 0 0 0] w=1 and p=4  u=0.0 sumD=1
AW=5 BW=2 DA=1 DB=1
A:Agent2 has h=[0 1 0 0 0] d=[0 0 0 0 1] w=5 and p=2  u=0.0 sumD=1
B:Agent5 has h=[0 0 0 0 1] d=[0 1 0 0 0] w=2 and p=5  u=0.0 sumD=1
AW=1 BW=4 DA=1 DB=1
A:Agent4 has h=[0 0 0 1 0] d=[1 0 0 0 0] w=1 and p=4  u=0.0 sumD=1
B:Agent1 has h=[1 0 0 0 0] d=[0 0 0 1 0] w=4 and p=1  u=0.0 sumD=1
AW=2 BW=5 DA=1 DB=1
A:Agent5 has h=[0 0 0 0 1] d=[0 1 0 0 0] w=2 and p=5  u=0.0 sumD=1
B:Agent2 has h=[0 1 0 0 0] d=[0 0 0 0 1] w=5 and p=2  u=0.0 sumD=1
success = 4
***********************
[2, 0, 0, 4, 0]
1.2

# おわりに
力不足なので、まだ実装してない部分があります。
とりあえずは忘れない内に。