mesa/my_model.py

481 lines
23 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.resource_integration()
self.j_comp_consumed_produced()
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}")
# 赋予 产业的量
# 产业种类
data = pd.read_csv('input_data/测试 BomNodes.csv')
data['Code'] = data['Code'].astype('string')
self.type2 = data
# 设备c折旧比值
###
def initialize_firm_network(self):
# Read the firm data
firm = pd.read_csv("input_data/测试 Firm_amended 170.csv")
firm['Code'] = firm['Code'].astype('string')
firm.fillna(0, inplace=True)
firm_attr = firm.loc[:, ["Code", "Type_Region", "Revenue_Log", "原材料", "设备数量", "库存商品"]]
firm_industry_relation = pd.read_csv("input_data/firm_industry_relation.csv")
firm_industry_relation['Firm_Code'] = firm_industry_relation['Firm_Code'].astype('string')
firm_product = []
grouped = firm_industry_relation.groupby('Firm_Code')['Product_Code'].apply(list)
firm_product.append(grouped)
firm_attr['Product_Code'] = firm_attr['Code'].map(grouped)
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):
firm_industry_relation = pd.read_csv("input_data/firm_industry_relation.csv")
firm_industry_relation['Firm_Code'] = firm_industry_relation['Firm_Code'].astype('string')
# 将 'firm_prod' 表中的每一行作为图中的节点
self.G_FirmProd.add_nodes_from(firm_industry_relation.index)
# 为每个节点分配属性
grouped = firm_industry_relation.groupby('Firm_Code')
self.firm_prod_labels_dict = {code: group['Product_Code'].tolist() for code, group in grouped}
firm_prod_labels_dict = {code: firm_industry_relation.loc[code].to_dict() for code in
firm_industry_relation.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 = [firm_code for firm_code, product in self.firm_prod_labels_dict.items() if
pred_product_code in product]
# 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 = [firm_code for firm_code, product in self.firm_prod_labels_dict.items() if
succ_product_code in product]
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):
# 产业种类
# 利用 测试 BomNodes.csv 转换产业 和 id 前提是 一个产业一个产品id 且一一对应
product_id = self.type2.loc[self.type2['Code'] == ag_node, 'Index']
type2 = self.type2.loc[product_id, '产业种类'].values[0]
# depreciation ratio 折旧比值
product_id = product_id.iloc[0]
j_comp_data_consumed = self.data_consumed[product_id]
j_comp_data_produced = self.data_produced[product_id]
product = ProductAgent(ag_node, self, name=attr['Name'], type2=type2,
j_comp_data_consumed=j_comp_data_consumed,
j_comp_data_produced=j_comp_data_produced,
)
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_id = self.Firm['Code'] == ag_node
n_equip_c = self.Firm.loc[firm_id, '设备数量'].values[0]
demand_quantity = self.Firm.loc[firm_id, 'production_output'].values[0]
production_output = self.Firm.loc[firm_id, 'demand_quantity'].values[0]
# c购买价格 数据预处理
# c_price = self.Firm.loc[self.Firm['Code'] == ag_node, 'c_price'].values[0]
# 资源 资源库存信息 利用 firm_resource
R = self.firm_resource_R.loc[firm_id].to_list()[0]
P = self.firm_resource_P.loc[firm_id].to_list()[0]
C = self.firm_resource_C.loc[firm_id].to_list()[0]
firm_agent = FirmAgent(
ag_node, self,
type_region=attr['Type_Region'],
revenue_log=attr['Revenue_Log'],
n_equip_c=n_equip_c,
a_lst_product=a_lst_product,
demand_quantity=demand_quantity,
production_output=production_output,
# c_price=c_price,
R=R,
P=P,
C=C
)
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 resource_integration(self):
data_R = pd.read_csv("测试数据 companies_materials.csv")
data_C = pd.read_csv("测试数据 companies_devices.csv")
data_P = pd.read_csv("测试数据 companies_products.csv")
device_salvage_values = pd.read_csv('测试数据 device_salvage_values.csv')
self.device_salvage_values = device_salvage_values
data_merged_C = pd.merge(data_C, device_salvage_values, on='设备id', how='left')
firm_resource_R = (data_R.groupby('Firm_Code')[['材料id', '材料数量']]
.apply(lambda x: x.values.tolist()))
firm_resource_C = (data_merged_C.groupby('Firm_Code')[['设备id', '设备数量', '设备残值']]
.apply(lambda x: x.values.tolist()))
firm_resource_P = (data_P.groupby('Firm_Code')[['产品id', '产品数量']]
.apply(lambda x: x.values.tolist()))
self.firm_resource_R = firm_resource_R
self.firm_resource_C = firm_resource_C
self.firm_resource_P = firm_resource_P
def j_comp_consumed_produced(self):
data_consumed = pd.read_csv('测试数据 consumed_materials.csv')
data_produced = pd.read_csv('测试数据 produced_products.csv')
data_consumed = (data_consumed.groupby('产业id')[['消耗材料id', '消耗量']]
.apply(lambda x: x.values.tolist()))
data_produced = (data_produced.groupby('产业id')[['制造产品id', '制造量']]
.apply(lambda x: x.values.tolist()))
self.data_consumed = data_consumed
self.data_produced = data_produced
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()
# 3. 判断是否需要购买资源 判断是否需要购买机器
purchase_material_firms = {}
purchase_machinery_firms = {}
material_list = []
machinery_list = []
list_seek_material_firm = [] # 每一个收到请求的企业
list_seek_machinery_firm = [] # 每一个收到请求的企业
for firm in self.company_agents:
# 资源
for sub_list in firm.R:
if sub_list[1] <= firm.s_r:
required_material_quantity = firm.S_r - sub_list[1]
(material_list.append([sub_list[0], required_material_quantity]))
purchase_material_firms[firm] = material_list
# 设备
for sub_list in firm.C:
# 对于设备的required_machinery_quantity 要有所改变 根据残值而言! 每一个周期固定减少残值值 x firm 里面定义
sub_list[2] -= firm.x
if sub_list[2] <= 0: # 残值小于等于 0 时
sub_list[1] -= 1
required_machinery_quantity = 1 # 补回原来的量 也就是 1
(machinery_list
.append([sub_list[0], required_machinery_quantity]))
purchase_machinery_firms[firm] = machinery_list
# 寻源并发送请求 决定是否接受供应 并更新
for material_firm_key, sub_list_values in purchase_material_firms.items():
for mater_list in sub_list_values:
result = material_firm_key.seek_material_supply(mater_list[0])
# 如果 result 不等于 0才将其添加到 list_seek_material_firm 列表中
if result != -1:
list_seek_material_firm.append(result)
if len(list_seek_material_firm) != 0:
for seek_material_firm in list_seek_material_firm:
seek_material_firm.handle_material_request(mater_list) # 更新产品
for R_list in firm.R:
R_list[1] = firm.S_r
for machinery_firm, sub_list in purchase_machinery_firms.items():
for machi_list in sub_list:
# 执行一次调用 machinery_firm.seek_machinery_supply(machinery_list[0])
result = machinery_firm.seek_machinery_supply(machi_list[0])
# 如果 result 不等于 0才将其添加到 list_seek_machinery_firm 列表中
if result != -1:
list_seek_machinery_firm.append(result)
if len(list_seek_machinery_firm) != 0:
for seek_machinery_firm in list_seek_machinery_firm:
seek_machinery_firm.handle_machinery_request(machi_list)
for C_list, C0_list in zip(firm.C, firm.C0):
C_list[1] = C0_list[1] # 赋值回去
C_list[2] = C0_list[2]
# 消耗资源过程
consumed_material = []
for product in firm.indus_i:
for sub_list_data_consumed in product.j_comp_data_consumed:
consumed_material_id = sub_list_data_consumed[0]
consumed_material_num = sub_list_data_consumed[1]
consumed_material.append([consumed_material_id, consumed_material_num])
for sub_list_consumed_material in consumed_material:
for sub_list_material in firm.R:
if sub_list_material[0] == sub_list_consumed_material[0]:
sub_list_material[1] = sub_list_material[1] - sub_list_consumed_material[1]
# 生产产品过程
produced_products = []
for product in firm.indus_i:
for sub_list_produced_products in product.j_comp_data_consumed:
produced_products_id = sub_list_produced_products[0]
produced_products_num = sub_list_produced_products[1]
produced_products.append([produced_products_id, produced_products_num])
for sub_list_data_produced_products in produced_products:
for sub_list_products in firm.P:
if sub_list_products[0] == sub_list_data_produced_products[0]:
sub_list_products[1] = sub_list_products[1] - sub_list_data_produced_products[1]
# 刷新 R状态
firm.refresh_R()
# 刷新 C状态
firm.refresh_C()
# 刷新 P状态
firm.refresh_P()
# Increment the time step
self.t += 1