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 from mesa.time import RandomActivation from firm import FirmAgent from product import ProductAgent class MyModel(Model): def __init__(self, params): # self.num_agents = N self.is_prf_size = params['is_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'] # NetworkX 图对象 self.t = 0 self.network_graph = nx.MultiDiGraph() # NetworkGrid 用于管理网格 self.grid = NetworkGrid(self.network_graph) self.data_collector = DataCollector( agent_reporters={"Product": "name"} ) self.company_agents = [] self.product_agents = [] # 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.product_network = None self.firm_network = None self.firm_prod_network = None self.initialize_product_network(params) self.initialize_firm_network() self.initialize_firm_product_network() self.initialize_agents() self.initialize_disruptions() def initialize_product_network(self, params): """ Initialize the product network and add it to the model. """ self.product_network = nx.adjacency_graph(json.loads(params['g_bom'])) self.network_graph.add_edges_from(self.product_network.edges) def initialize_firm_network(self): """ Initialize the firm network and add it to the model. """ 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 = [row[row == 1].index.to_list() for _, row in Firm.loc[:, '1':].iterrows()] Firm_attr.loc[:, 'Product_Code'] = firm_product Firm_attr.set_index('Code', inplace=True) self.firm_network = nx.MultiDiGraph() self.firm_network.add_nodes_from(Firm["Code"]) firm_labels_dict = {code: Firm_attr.loc[code].to_dict() for code in self.firm_network.nodes} nx.set_node_attributes(self.firm_network, firm_labels_dict) def initialize_firm_product_network(self): """ Initialize the firm-product network and add it to the model. """ Firm_Prod = pd.read_csv("input_data/Firm_amended.csv") Firm_Prod.fillna(0, inplace=True) 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.firm_prod_network = nx.MultiDiGraph() self.firm_prod_network.add_nodes_from(firm_prod.index) firm_prod_labels_dict = {code: firm_prod.loc[code].to_dict() for code in firm_prod.index} nx.set_node_attributes(self.firm_prod_network, firm_prod_labels_dict) self.add_edges_to_firm_network() self.connect_unconnected_nodes() def add_edges_to_firm_network(self): """ Add edges to the firm network based on product BOM. """ Firm = pd.read_csv("input_data/Firm_amended.csv") Firm['Code'] = Firm['Code'].astype('string') Firm.fillna(0, inplace=True) for node in nx.nodes(self.firm_network): lst_pred_product_code = [] for product_code in self.firm_network.nodes[node]['Product_Code']: lst_pred_product_code += list(self.product_network.predecessors(product_code)) lst_pred_product_code = list(set(lst_pred_product_code)) lst_pred_product_code = list(sorted(lst_pred_product_code)) for pred_product_code in lst_pred_product_code: lst_pred_firm = Firm['Code'][Firm[pred_product_code] == 1].to_list() 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.firm_network.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.random.choices(lst_pred_firm, k=n_pred_firm, weights=lst_prob) else: lst_choose_firm = self.random.choices(lst_pred_firm, k=n_pred_firm) lst_add_edge = [(pred_firm, node, {'Product': pred_product_code}) for pred_firm in lst_choose_firm] self.firm_network.add_edges_from(lst_add_edge) # Add edges to firm-prod network set_node_prod_code = set(self.firm_network.nodes[node]['Product_Code']) set_pred_succ_code = set(self.product_network.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.firm_prod_network.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.firm_prod_network.nodes(data=True) if v['Firm_Code'] == node and v['Product_Code'] == use_pred_prod_code][0] self.firm_prod_network.add_edge(pred_node, current_node) def connect_unconnected_nodes(self): """ Connect unconnected nodes in the firm network. """ Firm = pd.read_csv("input_data/Firm_amended.csv") Firm['Code'] = Firm['Code'].astype('string') Firm.fillna(0, inplace=True) for node in nx.nodes(self.firm_network): if self.firm_network.degree(node) == 0: for product_code in self.firm_network.nodes[node]['Product_Code']: current_node = [n for n, v in self.firm_prod_network.nodes(data=True) if v['Firm_Code'] == node and v['Product_Code'] == product_code][0] lst_succ_product_code = list(self.product_network.successors(product_code)) for succ_product_code in lst_succ_product_code: lst_succ_firm = Firm['Code'][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.firm_network.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.random.choices(lst_succ_firm, k=n_succ_firm, weights=lst_prob) else: lst_choose_firm = self.random.choices(lst_succ_firm, k=n_succ_firm) lst_add_edge = [(node, succ_firm, {'Product': product_code}) for succ_firm in lst_choose_firm] self.firm_network.add_edges_from(lst_add_edge) for succ_firm in lst_choose_firm: succ_node = [n for n, v in self.firm_prod_network.nodes(data=True) if v['Firm_Code'] == succ_firm and v['Product_Code'] == succ_product_code][0] self.firm_prod_network.add_edge(current_node, succ_node) 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) 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) # self.grid.place_agent(firm_agent, ag_node) def initialize_disruptions(self): """ Initialize disruptions in the network. """ for firm_code, lst_product in self.dct_lst_init_disrupt_firm_prod.items(): for product_code in lst_product: self.firm_network.nodes[firm_code]['Product_Code'].remove(product_code) # Log disruptions for visualization self.dct_lst_init_disrupt_firm_prod[firm_code].append(product_code) 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): print(f"Running step {self.t}") # 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