OrderReallocation-HeavyTruc.../chromosome_utils.py

206 lines
10 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 random
import numpy as np
from data_structures import OrderData, RiskEnterpriseData, SupplierData
class ChromosomeUtils:
"""染色体编码、解码、修复工具类"""
def __init__(self, order_data: OrderData, risk_data: RiskEnterpriseData, supplier_data: SupplierData):
self.order = order_data
self.risk = risk_data
self.supplier = supplier_data
self.I = order_data.I
self.material_optional_enterprises = self._get_material_optional_enterprises()
self.material_enterprise_count = [len(ents) for ents in self.material_optional_enterprises]
self.chromosome_length = 3 * sum(self.material_enterprise_count)
def _get_material_optional_enterprises(self) -> list[list[int]]:
"""获取每个物料的可选企业列表0=风险企业1~supplier_count=供应商"""
optional = []
for i in range(self.I):
ents = [0] # 先加入风险企业
for j in range(self.supplier.supplier_count):
if self.supplier.can_produce[j][i] == 1:
ents.append(j + 1)
optional.append(ents)
return optional
def _split_chromosome(self, chromosome: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""拆分染色体为三层:企业编码层、能力编码层、数量编码层"""
total_ent_count = sum(self.material_enterprise_count)
enterprise_layer = chromosome[:total_ent_count]
capacity_layer = chromosome[total_ent_count:2*total_ent_count]
quantity_layer = chromosome[2*total_ent_count:]
return enterprise_layer, capacity_layer, quantity_layer
def _merge_chromosome(self, enterprise_layer: np.ndarray, capacity_layer: np.ndarray,
quantity_layer: np.ndarray) -> np.ndarray:
"""合并三层为完整染色体"""
return np.hstack([enterprise_layer, capacity_layer, quantity_layer])
def repair_enterprise_layer(self, enterprise_layer: np.ndarray) -> np.ndarray:
"""修复企业编码层:确保每种物料至少有一个企业生产"""
repaired = enterprise_layer.copy()
split_points = np.cumsum(self.material_enterprise_count)
start = 0
for i in range(self.I):
end = split_points[i]
segment = repaired[start:end]
if np.sum(segment) == 0:
select_idx = random.randint(0, len(segment)-1)
segment[select_idx] = 1
repaired[start:end] = segment
start = end
return repaired
def repair_capacity_layer(self, enterprise_layer: np.ndarray, capacity_layer: np.ndarray) -> np.ndarray:
"""修复能力编码层:满足单物料产能和总产能约束"""
repaired = capacity_layer.copy()
split_points = np.cumsum(self.material_enterprise_count)
start = 0
# 单物料产能约束
for i in range(self.I):
end = split_points[i]
ents = self.material_optional_enterprises[i]
segment = repaired[start:end]
enterprise_segment = enterprise_layer[start:end]
for idx, ent in enumerate(ents):
if enterprise_segment[idx] == 1:
if ent == 0:
max_cap = self.risk.C0_i_std[i]
else:
supplier_id = ent - 1
max_cap = self.supplier.Cj_i_std[supplier_id][i]
if segment[idx] <= 0 or segment[idx] > max_cap:
segment[idx] = random.uniform(1, max_cap)
repaired[start:end] = segment
start = end
# 总产能约束
enterprise_total_capacity = {}
start = 0
for i in range(self.I):
end = split_points[i]
ents = self.material_optional_enterprises[i]
enterprise_segment = enterprise_layer[start:end]
capacity_segment = repaired[start:end]
for idx, ent in enumerate(ents):
if enterprise_segment[idx] == 1:
if ent not in enterprise_total_capacity:
enterprise_total_capacity[ent] = 0
enterprise_total_capacity[ent] += capacity_segment[idx]
start = end
# 调整超出总产能的企业
for ent, total_cap in enterprise_total_capacity.items():
if ent == 0:
max_total_cap = self.risk.C0_total_max
else:
supplier_id = ent - 1
max_total_cap = self.supplier.Cj_total_max[supplier_id]
if total_cap > max_total_cap:
scale = max_total_cap / total_cap
start = 0
for i in range(self.I):
end = split_points[i]
ents = self.material_optional_enterprises[i]
enterprise_segment = enterprise_layer[start:end]
capacity_segment = repaired[start:end]
for idx, e in enumerate(ents):
if e == ent and enterprise_segment[idx] == 1:
capacity_segment[idx] *= scale
repaired[start:end] = capacity_segment
start = end
return repaired
def repair_quantity_layer(self, enterprise_layer: np.ndarray, quantity_layer: np.ndarray) -> np.ndarray:
"""修复数量编码层:满足总量需求、起订量和最大供应量约束"""
repaired = quantity_layer.copy()
split_points = np.cumsum(self.material_enterprise_count)
start = 0
for i in range(self.I):
qi = self.order.Q[i]
end = split_points[i]
ents = self.material_optional_enterprises[i]
segment = repaired[start:end]
enterprise_segment = enterprise_layer[start:end]
selected_indices = np.where(enterprise_segment == 1)[0]
if len(selected_indices) == 0:
start = end
continue
selected_ents = [ents[idx] for idx in selected_indices]
# 步骤1初始化合法范围起订量~最大供应量)
for idx, ent in zip(selected_indices, selected_ents):
if ent == 0:
min_q = 0
max_q = qi
else:
supplier_id = ent - 1
min_q = self.supplier.MinOrder[supplier_id][i]
max_q = self.supplier.MaxOrder[supplier_id][i]
if segment[idx] < min_q:
segment[idx] = min_q
elif segment[idx] > max_q:
segment[idx] = max_q
# 步骤2强制总量等于需求数量核心修复
current_total = np.sum(segment[selected_indices])
if current_total <= 0:
weights = np.random.rand(len(selected_indices))
weights /= np.sum(weights)
segment[selected_indices] = qi * weights
else:
scale = qi / current_total
segment[selected_indices] *= scale
# 步骤3处理超出最大供应量的情况修复中文变量名+逻辑)
need_adjust = True
while need_adjust:
need_adjust = False
total_excess = 0
for idx, ent in zip(selected_indices, selected_ents):
if ent == 0:
max_q = qi
else:
supplier_id = ent - 1
max_q = self.supplier.MaxOrder[supplier_id][i]
if segment[idx] > max_q:
excess = segment[idx] - max_q
total_excess += excess
segment[idx] = max_q
need_adjust = True
if total_excess > 0:
available_indices = []
available_max = []
for idx, ent in zip(selected_indices, selected_ents):
if ent == 0:
max_q = qi
else:
supplier_id = ent - 1
max_q = self.supplier.MaxOrder[supplier_id][i]
remaining = max_q - segment[idx]
if remaining > 0:
available_indices.append(idx)
available_max.append(remaining)
if len(available_indices) > 0:
available_total = sum(available_max)
if available_total > 0:
alloc_ratio = np.array(available_max) / available_total
alloc_excess = total_excess * alloc_ratio
for idx, alloc in zip(available_indices, alloc_excess):
segment[idx] += alloc
else:
segment[selected_indices] = np.minimum(segment[selected_indices],
[self.supplier.MaxOrder[ent-1][i] if ent !=0 else qi for ent in selected_ents])
current_total = np.sum(segment[selected_indices])
if abs(current_total - qi) > 1e-6:
scale = qi / current_total
segment[selected_indices] *= scale
need_adjust = True
repaired[start:end] = segment
start = end
return repaired
def repair_chromosome(self, chromosome: np.ndarray) -> np.ndarray:
"""完整修复染色体:企业层→能力层→数量层"""
enterprise_layer, capacity_layer, quantity_layer = self._split_chromosome(chromosome)
enterprise_layer = self.repair_enterprise_layer(enterprise_layer)
capacity_layer = self.repair_capacity_layer(enterprise_layer, capacity_layer)
quantity_layer = self.repair_quantity_layer(enterprise_layer, quantity_layer)
return self._merge_chromosome(enterprise_layer, capacity_layer, quantity_layer)