IIabm/model.py

329 lines
14 KiB
Python

import agentpy as ap
import pandas as pd
import numpy as np
import random
import networkx as nx
from firm import FirmAgent
from product import ProductAgent
sample = 0
seed = 0
n_iter = 10
# dct_list_init_remove_firm_prod = {133: ['1.4.4.1'], 2: ['1.1.3']}
# dct_list_init_remove_firm_prod = {
# 135: ['1.3.2.1'],
# 133: ['1.4.4.1'],
# 2: ['1.1.3']
# }
dct_list_init_remove_firm_prod = {
140: ['1.4.5.1'],
135: ['1.3.2.1'],
133: ['1.4.4.1'],
2: ['1.1.3']
}
n_max_trial = 5
dct_sample_para = {
'sample': sample,
'seed': seed,
'n_iter': n_iter,
'n_max_trial': n_max_trial,
'dct_list_init_remove_firm_prod': dct_list_init_remove_firm_prod,
}
class Model(ap.Model):
def setup(self):
self.sample = self.p.sample
self.random = random.Random(self.p.seed)
self.nprandom = np.random.default_rng(self.p.seed)
self.int_n_iter = int(self.p.n_iter)
self.int_n_max_trial = int(self.p.n_max_trial)
self.dct_list_remove_firm_prod = self.p.dct_list_init_remove_firm_prod
# init graph bom
BomNodes = pd.read_csv('BomNodes.csv', index_col=0)
BomNodes.set_index('Code', inplace=True)
BomCateNet = pd.read_csv('BomCateNet.csv', index_col=0)
BomCateNet.fillna(0, inplace=True)
G_bom = nx.from_pandas_adjacency(BomCateNet.T,
create_using=nx.MultiDiGraph())
bom_labels_dict = {}
for code in G_bom.nodes:
bom_labels_dict[code] = BomNodes.loc[code].to_dict()
nx.set_node_attributes(G_bom, bom_labels_dict)
# init graph firm
Firm = pd.read_csv("Firm_amended.csv")
Firm.fillna(0, inplace=True)
Firm_attr = Firm.loc[:, ["Code", "Name", "Type_Region", "Revenue_Log"]]
firm_product = []
for _, row in Firm.loc[:, '1':].iterrows():
firm_product.append(row[row == 1].index.to_list())
Firm_attr.loc[:, 'Product_Code'] = firm_product
Firm_attr.set_index('Code')
G_Firm = nx.MultiDiGraph()
G_Firm.add_nodes_from(Firm["Code"])
firm_labels_dict = {}
for code in G_Firm.nodes:
firm_labels_dict[code] = Firm_attr.loc[code].to_dict()
nx.set_node_attributes(G_Firm, firm_labels_dict)
# add edge to G_firm according to G_bom
for node in nx.nodes(G_Firm):
# print(node, '-' * 20)
for product_code in G_Firm.nodes[node]['Product_Code']:
# print(product_code)
for succ_product_code in list(G_bom.successors(product_code)):
# print(succ_product_code)
list_succ_firms = Firm.index[Firm[succ_product_code] ==
1].to_list()
list_revenue_log = [
G_Firm.nodes[succ_firm]['Revenue_Log']
for succ_firm in list_succ_firms
]
# list_prob = [
# (v - min(list_revenue_log) + 1) /
# (max(list_revenue_log) - min(list_revenue_log) + 1)
# for v in list_revenue_log
# ]
# list_flag = [
# self.nprandom.choice([1, 0], p=[prob, 1 - prob])
# for prob in list_prob
# ]
# # print(list(zip(list_succ_firms,list_flag,list_prob)))
# list_added_edges = [(node, succ_firm, {
# 'Product': product_code
# }) for succ_firm, flag in zip(list_succ_firms, list_flag)
# if flag == 1]
list_prob = [
size / sum(list_revenue_log)
for size in list_revenue_log
]
succ_firm = self.nprandom.choice(list_succ_firms,
p=list_prob)
list_added_edges = [(node, succ_firm, {
'Product': product_code
})]
G_Firm.add_edges_from(list_added_edges)
# print('-' * 20)
self.firm_network = ap.Network(self, G_Firm)
self.product_network = ap.Network(self, G_bom)
# print([node.label for node in self.firm_network.nodes])
# print([list(self.firm_network.graph.predecessors(node))
# for node in self.firm_network.nodes])
# print([self.firm_network.graph.nodes[node.label]['Name']
# for node in self.firm_network.nodes])
# print([v for v in self.firm_network.graph.nodes(data=True)])
# init product
for ag_node, attr in self.product_network.graph.nodes(data=True):
product_agent = ProductAgent(self,
code=ag_node.label,
name=attr['Name'])
self.product_network.add_agents([product_agent], [ag_node])
self.a_list_total_products = ap.AgentList(self,
self.product_network.agents)
# init firm
for ag_node, attr in self.firm_network.graph.nodes(data=True):
firm_agent = FirmAgent(
self,
code=attr['Code'],
name=attr['Name'],
type_region=attr['Type_Region'],
revenue_log=attr['Revenue_Log'],
a_list_product=self.a_list_total_products.select([
code in attr['Product_Code']
for code in self.a_list_total_products.code
]))
# init capacity based on discrete uniform distribution
# list_out_edges = list(
# self.firm_network.graph.out_edges(ag_node,
# keys=True,
# data='Product'))
# for product in firm_agent.a_list_product:
# capacity = len([
# edge for edge in list_out_edges if edge[-1] ==
# product.code])
# firm_agent.dct_prod_capacity[product] = capacity
for product in firm_agent.a_list_product:
firm_agent.dct_prod_capacity[product] = self.nprandom.integers(
firm_agent.revenue_log / 5, firm_agent.revenue_log / 5 + 2)
# print(firm_agent.name, firm_agent.dct_prod_capacity)
self.firm_network.add_agents([firm_agent], [ag_node])
self.a_list_total_firms = ap.AgentList(self, self.firm_network.agents)
# print(list(zip(self.a_list_total_firms.code,
# self.a_list_total_firms.name,
# self.a_list_total_firms.capacity)))
# init dct_list_remove_firm_prod (from string to agent)
t_dct = {}
for firm_code, list_product in self.dct_list_remove_firm_prod.items():
firm = self.a_list_total_firms.select(
self.a_list_total_firms.code == firm_code)[0]
t_dct[firm] = self.a_list_total_products.select([
code in list_product
for code in self.a_list_total_products.code
])
self.dct_list_remove_firm_prod = t_dct
# set the initial firm product that are removed
for firm, a_list_product in self.dct_list_remove_firm_prod.items():
for product in a_list_product:
assert product in firm.a_list_product, \
f"product {product.code} not in firm {firm.code}"
firm.a_list_product_removed.append(product)
# draw network
self.draw_network()
def update(self):
self.a_list_total_firms.clean_before_time_step()
# stop simulation if reached terminal number of iteration
if self.t == self.int_n_iter or len(
self.dct_list_remove_firm_prod) == 0:
self.stop()
def step(self):
# shuffle self.dct_list_remove_firm_prod
# dct_key_list = list(self.dct_list_remove_firm_prod.keys())
# self.nprandom.shuffle(dct_key_list)
# self.dct_list_remove_firm_prod = {
# key: self.dct_list_remove_firm_prod[key].shuffle()
# for key in dct_key_list
# }
# print(self.dct_list_remove_firm_prod)
print('\n', '=' * 20, 'step', self.t, '=' * 20)
print(
'dct_list_remove_firm_prod', {
key.name: value.code
for key, value in self.dct_list_remove_firm_prod.items()
})
# remove_edge_to_cus_and_cus_up_prod
for firm, a_list_product in self.dct_list_remove_firm_prod.items():
for product in a_list_product:
firm.remove_edge_to_cus_remove_cus_up_prod(product)
for n_trial in range(self.int_n_max_trial):
print('=' * 10, 'trial', n_trial, '=' * 10)
# seek_alt_supply
# shuffle self.a_list_total_firms
self.a_list_total_firms = self.a_list_total_firms.shuffle()
for firm in self.a_list_total_firms:
if len(firm.a_list_up_product_removed) > 0:
# print(firm.name)
# print(firm.a_list_up_product_removed.code)
firm.seek_alt_supply()
# handle_request
# shuffle self.a_list_total_firms
self.a_list_total_firms = self.a_list_total_firms.shuffle()
for firm in self.a_list_total_firms:
if len(firm.dct_request_prod_from_firm) > 0:
firm.handle_request()
# reset dct_request_prod_from_firm
self.a_list_total_firms.clean_before_trial()
# do not use:
# self.a_list_total_firms.dct_request_prod_from_firm = {} why?
# based on a_list_up_product_removed,
# update a_list_product_disrupted / a_list_product_removed / dct_list_remove_firm_prod
self.dct_list_remove_firm_prod = {}
for firm in self.a_list_total_firms:
if len(firm.a_list_up_product_removed) > 0:
print(firm.name, 'a_list_up_product_removed', [product.code for product in firm.a_list_up_product_removed])
for product in firm.a_list_product:
n_up_product_removed = 0
for up_product_removed in firm.a_list_up_product_removed:
if product in up_product_removed.a_successors():
n_up_product_removed += 1
if n_up_product_removed == 0:
continue
else:
# update a_list_product_disrupted
if product not in firm.a_list_product_disrupted:
firm.a_list_product_disrupted.append(product)
# update a_list_product_removed / dct_list_remove_firm_prod
lost_percent = n_up_product_removed / len(
product.a_predecessors())
list_revenue_log = self.a_list_total_firms.revenue_log
std_size = (firm.revenue_log - min(list_revenue_log) +
1) / (max(list_revenue_log) -
min(list_revenue_log) + 1)
p_remove = 1 - std_size * (1 - lost_percent)
# flag = self.nprandom.choice([1, 0],
# p=[p_remove, 1 - p_remove])
flag = 1
if flag == 1:
firm.a_list_product_removed.append(product)
# if firm in
# self.dct_list_remove_firm_prod[firm] = firm.a_list_product_removed
if firm in self.dct_list_remove_firm_prod.keys():
self.dct_list_remove_firm_prod[firm].append(
product)
else:
self.dct_list_remove_firm_prod[
firm] = ap.AgentList(
self.model, [product])
# # update the firm that is removed
# self.dct_list_remove_firm_prod = {}
# for firm in self.a_list_total_firms:
# if len(firm.a_list_product_removed) > 0:
# self.dct_list_remove_firm_prod[
# firm] = firm.a_list_product_removed
# print(self.dct_list_remove_firm_prod)
print(
'dct_list_remove_firm_prod', {
key.name: value.code
for key, value in self.dct_list_remove_firm_prod.items()
})
def end(self):
pass
def draw_network(self):
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei'
pos = nx.nx_agraph.graphviz_layout(self.firm_network.graph,
prog="twopi",
args="")
node_label = nx.get_node_attributes(self.firm_network.graph, 'Name')
# print(node_label)
node_degree = dict(self.firm_network.graph.out_degree())
node_label = {
key: f"{node_label[key]} {node_degree[key]}"
for key in node_label.keys()
}
node_size = list(
nx.get_node_attributes(self.firm_network.graph,
'Revenue_Log').values())
node_size = list(map(lambda x: x**2, node_size))
edge_label = nx.get_edge_attributes(self.firm_network.graph, "Product")
# multi(di)graphs, the keys are 3-tuples
edge_label = {(n1, n2): label
for (n1, n2, _), label in edge_label.items()}
plt.figure(figsize=(12, 12), dpi=300)
nx.draw(self.firm_network.graph,
pos,
node_size=node_size,
labels=node_label,
font_size=6)
nx.draw_networkx_edge_labels(self.firm_network.graph,
pos,
edge_label,
font_size=4)
plt.savefig("network.png")
# model = Model(dct_sample_para)
# model.run()