mesa/my_model.py

308 lines
14 KiB
Python

import json
from random import shuffle
import networkx as nx
import pandas as pd
from mesa import Model
from mesa.space import MultiGrid, NetworkGrid
from mesa.datacollection import DataCollector
import numpy as np
from firm import FirmAgent
from product import ProductAgent
class MyModel(Model):
def __init__(self, params):
# 属性
self.is_prf_size = params['prf_size']
self.prf_conn = params['prf_conn']
self.cap_limit_prob_type = params['cap_limit_prob_type']
self.cap_limit_level = params['cap_limit_level']
self.diff_new_conn = params['diff_new_conn']
self.firm_network = nx.MultiDiGraph() # 有向多重图
self.firm_prod_network = nx.MultiDiGraph()
self.product_network = nx.MultiDiGraph() # 有向多重图
# NetworkGrid 用于管理网格
# NetworkX 图对象
self.t = 0
self.network_graph = nx.MultiDiGraph()
self.grid = NetworkGrid(self.network_graph)
self.data_collector = DataCollector(
agent_reporters={"Product": "name"}
)
# initialize graph bom
self.G_bom = nx.adjacency_graph(json.loads(params['g_bom']))
# Create the firm-product network graph
self.G_FirmProd = nx.MultiDiGraph()
# Create the firm network graph
self.G_Firm = nx.MultiDiGraph()
self.company_agents = []
self.product_agents = []
self.nprandom = np.random.default_rng(params['seed'])
# Initialize parameters from `params`
self.sample = params['sample']
self.int_stop_ts = 0
self.int_n_iter = int(params['n_iter'])
self.dct_lst_init_disrupt_firm_prod = params['dct_lst_init_disrupt_firm_prod']
# external variable
self.int_n_max_trial = int(params['n_max_trial'])
self.is_prf_size = bool(params['prf_size'])
self.remove_t = int(params['remove_t'])
self.int_netw_prf_n = int(params['netw_prf_n'])
# 方法执行
self.initialize_product_network(params)
self.initialize_firm_network()
self.initialize_firm_product_network()
self.add_edges_to_firm_network()
self.connect_unconnected_nodes()
self.initialize_agents()
self.initialize_disruptions()
def initialize_product_network(self, params):
try:
self.product_network = nx.adjacency_graph(json.loads(params['g_bom']))
except Exception as e:
print(f"Failed to initialize product network: {e}")
def initialize_firm_network(self):
# Read the firm data
firm = pd.read_csv("input_data/Firm_amended.csv")
firm['Code'] = firm['Code'].astype('string')
firm.fillna(0, inplace=True)
firm_attr = firm.loc[:, ["Code", "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', inplace=True)
self.G_Firm.add_nodes_from(firm["Code"])
# Assign attributes to the firm nodes
firm_labels_dict = {code: firm_attr.loc[code].to_dict() for code in self.G_Firm.nodes}
nx.set_node_attributes(self.G_Firm, firm_labels_dict)
self.Firm = firm
def initialize_firm_product_network(self):
""" Initialize the firm-product network """
# Read the firm-product data
Firm_Prod = pd.read_csv("input_data/Firm_amended.csv")
Firm_Prod.fillna(0, inplace=True)
# Stack the firm-product relationships into a DataFrame
firm_prod = pd.DataFrame({'bool': Firm_Prod.loc[:, '1':].stack()})
firm_prod = firm_prod[firm_prod['bool'] == 1].reset_index()
firm_prod.drop('bool', axis=1, inplace=True)
firm_prod.rename({'level_0': 'Firm_Code', 'level_1': 'Product_Code'}, axis=1, inplace=True)
firm_prod['Firm_Code'] = firm_prod['Firm_Code'].astype('string')
self.G_FirmProd.add_nodes_from(firm_prod.index)
# Assign attributes to the firm-product nodes
firm_prod_labels_dict = {code: firm_prod.loc[code].to_dict() for code in firm_prod.index}
nx.set_node_attributes(self.G_FirmProd, firm_prod_labels_dict)
def add_edges_to_firm_network(self):
""" Add edges between firms based on the product BOM relationships """
# Add edges to G_Firm according to G_bom
for node in nx.nodes(self.G_Firm):
lst_pred_product_code = []
for product_code in self.G_Firm.nodes[node]['Product_Code']:
lst_pred_product_code += list(self.G_bom.predecessors(product_code))
lst_pred_product_code = list(set(lst_pred_product_code))
lst_pred_product_code = list(sorted(lst_pred_product_code)) # Ensure consistency
for pred_product_code in lst_pred_product_code:
# Get a list of firms producing the component (pred_product_code)
lst_pred_firm = self.Firm['Code'][self.Firm[pred_product_code] == 1].to_list()
# Select multiple suppliers (multi-sourcing)
n_pred_firm = self.int_netw_prf_n
if n_pred_firm > len(lst_pred_firm):
n_pred_firm = len(lst_pred_firm)
if self.is_prf_size:
lst_pred_firm_size = [self.G_Firm.nodes[pred_firm]['Revenue_Log'] for pred_firm in
lst_pred_firm]
lst_prob = [size / sum(lst_pred_firm_size) for size in lst_pred_firm_size]
lst_choose_firm = self.nprandom.choice(lst_pred_firm, n_pred_firm, replace=False, p=lst_prob)
else:
lst_choose_firm = self.nprandom.choice(lst_pred_firm, n_pred_firm, replace=False)
# Add edges from predecessor firms to current node (firm)
lst_add_edge = [(pred_firm, node, {'Product': pred_product_code}) for pred_firm in lst_choose_firm]
self.G_Firm.add_edges_from(lst_add_edge)
# Add edges to firm-product network
self.add_edges_to_firm_product_network(node, pred_product_code, lst_choose_firm)
def add_edges_to_firm_product_network(self, node, pred_product_code, lst_choose_firm):
""" Helper function to add edges to the firm-product network """
set_node_prod_code = set(self.G_Firm.nodes[node]['Product_Code'])
set_pred_succ_code = set(self.G_bom.successors(pred_product_code))
lst_use_pred_prod_code = list(set_node_prod_code & set_pred_succ_code)
for pred_firm in lst_choose_firm:
pred_node = [n for n, v in self.G_FirmProd.nodes(data=True) if
v['Firm_Code'] == pred_firm and v['Product_Code'] == pred_product_code][0]
for use_pred_prod_code in lst_use_pred_prod_code:
current_node = [n for n, v in self.G_FirmProd.nodes(data=True) if
v['Firm_Code'] == node and v['Product_Code'] == use_pred_prod_code][0]
self.G_FirmProd.add_edge(pred_node, current_node)
def connect_unconnected_nodes(self):
""" Connect unconnected nodes in the firm network """
for node in nx.nodes(self.G_Firm):
if self.G_Firm.degree(node) == 0:
for product_code in self.G_Firm.nodes[node]['Product_Code']:
current_node = [n for n, v in self.G_FirmProd.nodes(data=True) if
v['Firm_Code'] == node and v['Product_Code'] == product_code][0]
lst_succ_product_code = list(self.G_bom.successors(product_code))
for succ_product_code in lst_succ_product_code:
lst_succ_firm = self.Firm['Code'][self.Firm[succ_product_code] == 1].to_list()
n_succ_firm = self.int_netw_prf_n
if n_succ_firm > len(lst_succ_firm):
n_succ_firm = len(lst_succ_firm)
if self.is_prf_size:
lst_succ_firm_size = [self.G_Firm.nodes[succ_firm]['Revenue_Log'] for succ_firm in
lst_succ_firm]
lst_prob = [size / sum(lst_succ_firm_size) for size in lst_succ_firm_size]
lst_choose_firm = self.nprandom.choice(lst_succ_firm, n_succ_firm, replace=False,
p=lst_prob)
else:
lst_choose_firm = self.nprandom.choice(lst_succ_firm, n_succ_firm, replace=False)
lst_add_edge = [(node, succ_firm, {'Product': product_code}) for succ_firm in
lst_choose_firm]
self.G_Firm.add_edges_from(lst_add_edge)
# Add edges to firm-product network
for succ_firm in lst_choose_firm:
succ_node = [n for n, v in self.G_FirmProd.nodes(data=True) if
v['Firm_Code'] == succ_firm and v['Product_Code'] == succ_product_code][0]
self.G_FirmProd.add_edge(current_node, succ_node)
self.sample.g_firm = json.dumps(nx.adjacency_data(self.G_Firm))
self.firm_network = self.G_Firm # 直接使用 networkx 图对象
self.firm_prod_network = self.G_FirmProd # 直接使用 networkx 图对象
def initialize_agents(self):
""" Initialize agents and add them to the model. """
for ag_node, attr in self.product_network.nodes(data=True):
product = ProductAgent(ag_node, self, name=attr['Name'])
self.add_agent(product)
# self.grid.place_agent(product, ag_node)
##print(f"Product agent created: {product.name}, ID: {product.unique_id}")
for ag_node, attr in self.firm_network.nodes(data=True):
a_lst_product = [agent for agent in self.product_agents if agent.unique_id in attr['Product_Code']]
firm_agent = FirmAgent(
ag_node, self,
type_region=attr['Type_Region'],
revenue_log=attr['Revenue_Log'],
a_lst_product=a_lst_product,
)
self.add_agent(firm_agent)
##print(f"Firm agent created: {firm_agent.unique_id}, Products: {[p.name for p in a_lst_product]}")
# self.grid.place_agent(firm_agent, ag_node)
def initialize_disruptions(self):
# 初始化一部字典,用于存储每个公司及其对应的受干扰产品列表
t_dct = {}
# 遍历初始公司-产品干扰数据,将其转化为基于公司和产品的映射
for firm_code, lst_product in self.dct_lst_init_disrupt_firm_prod.items():
# 从 company_agents 列表中选择指定公司
firms = [firm for firm in self.company_agents if firm.unique_id == firm_code]
firm = firms[0] if firms else None
# 从总产品列表中选择该公司受干扰的产品
disrupted_products = [product for product in self.product_agents if product.unique_id in lst_product]
# 将公司与其受干扰的产品映射到字典中
t_dct[firm] = disrupted_products
# 更新 self.dct_lst_init_disrupt_firm_prod 字典,存储公司及其受干扰的产品
self.dct_lst_init_disrupt_firm_prod = t_dct
# 设置初始受干扰的公司产品状态
for firm, a_lst_product in self.dct_lst_init_disrupt_firm_prod.items():
for product in a_lst_product:
# 确保产品存在于公司的生产状态字典中
assert product in firm.dct_prod_up_prod_stat.keys(), \
f"Product {product.code} not in firm {firm.code}"
# 将产品状态更新为干扰状态,并记录干扰时间
firm.dct_prod_up_prod_stat[product]['p_stat'].append(('D', self.t))
def add_agent(self, agent):
if isinstance(agent, FirmAgent):
self.company_agents.append(agent)
elif isinstance(agent, ProductAgent):
self.product_agents.append(agent)
def step(self):
# 1. Remove edge to customer and disrupt customer up product
for firm in self.company_agents:
for prod in firm.dct_prod_up_prod_stat.keys():
status, ts = firm.dct_prod_up_prod_stat[prod]['p_stat'][-1]
if status == 'D' and ts == self.t - 1:
firm.remove_edge_to_cus(prod)
for firm in self.company_agents:
for prod in firm.dct_prod_up_prod_stat.keys():
for up_prod in firm.dct_prod_up_prod_stat[prod]['s_stat'].keys():
if firm.dct_prod_up_prod_stat[prod]['s_stat'][up_prod]['set_disrupt_firm']:
firm.disrupt_cus_prod(prod, up_prod)
# 2. Trial Process
for n_trial in range(self.int_n_max_trial):
shuffle(self.company_agents) # 手动打乱代理顺序
is_stop_trial = True
for firm in self.company_agents:
lst_seek_prod = []
for prod in firm.dct_prod_up_prod_stat.keys():
status = firm.dct_prod_up_prod_stat[prod]['p_stat'][-1][0]
if status == 'D':
for supply in firm.dct_prod_up_prod_stat[prod]['s_stat'].keys():
if not firm.dct_prod_up_prod_stat[prod]['s_stat'][supply]['stat']:
lst_seek_prod.append(supply)
lst_seek_prod = list(set(lst_seek_prod))
if len(lst_seek_prod) > 0:
is_stop_trial = False
for supply in lst_seek_prod:
firm.seek_alt_supply(supply)
if is_stop_trial:
break
# Handle requests
shuffle(self.company_agents) # 手动打乱代理顺序
for firm in self.company_agents:
if len(firm.dct_request_prod_from_firm) > 0:
firm.handle_request()
# Reset dct_request_prod_from_firm
for firm in self.company_agents:
firm.clean_before_trial()
# Increment the time step
self.t += 1