OrderReallocation-HeavyTruc.../visualizer.py

123 lines
6.2 KiB
Python
Raw Permalink 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 matplotlib.pyplot as plt
import numpy as np
from chromosome_utils import ChromosomeUtils
class ResultVisualizer:
"""结果可视化工具类:绘制帕累托前沿、收敛曲线,打印最优解详情(适配整数化)"""
def __init__(self, utils: ChromosomeUtils):
"""
初始化可视化工具
:param utils: 染色体工具类实例(用于解析染色体)
"""
self.utils = utils
self.figsize = (12, 8) # 图表大小
# 企业名称列表(风险企业+供应商)
self.supplier_names = ["RiskEnterprise"] + self.utils.supplier.names
def plot_pareto_front(self, all_objectives: list[tuple[int, int]],
non_dominated_objectives: list[tuple[int, int]]):
"""
绘制帕累托前沿(所有解 vs 帕累托最优解)
:param all_objectives: 所有解的目标值(整数)
:param non_dominated_objectives: 帕累托最优解的目标值(整数)
"""
plt.figure(figsize=self.figsize)
# 绘制所有解(蓝色点)
if all_objectives:
c_all = [obj[0] for obj in all_objectives] # 成本(整数)
t_all = [obj[1] for obj in all_objectives] # 延期(整数)
plt.scatter(c_all, t_all, color='blue', alpha=0.3, label='All Solutions', zorder=1)
# 绘制帕累托前沿(红色点)
if non_dominated_objectives:
c_nd = [obj[0] for obj in non_dominated_objectives]
t_nd = [obj[1] for obj in non_dominated_objectives]
plt.scatter(c_nd, t_nd, color='red', s=50, label='Pareto Front', zorder=5)
# 图表设置
plt.title('Pareto Front: Change Cost vs Tardiness (Integerized)')
plt.xlabel('Change Cost (Integer)')
plt.ylabel('Tardiness (Integer)')
plt.legend()
plt.grid(True, alpha=0.5)
plt.savefig('pareto_front_integerized.png', dpi=300, bbox_inches='tight') # 保存图片
plt.show()
def plot_convergence(self, convergence_history: list[tuple[int, int]]):
"""
绘制收敛趋势图(每代最优前沿的平均成本和延期,整数化)
:param convergence_history: 收敛历史数据((平均成本, 平均延期),整数)
"""
if len(convergence_history) == 0: # 无数据时不绘制
return
plt.figure(figsize=self.figsize)
generations = list(range(len(convergence_history))) # 代数
costs = [h[0] for h in convergence_history] # 平均成本(整数)
tardiness = [h[1] for h in convergence_history] # 平均延期(整数)
# 子图1平均成本趋势
plt.subplot(2, 1, 1)
plt.plot(generations, costs, 'b-')
plt.title('Convergence History (Integerized)')
plt.ylabel('Average Change Cost (Integer)')
plt.grid(True, alpha=0.5)
# 子图2平均延期趋势
plt.subplot(2, 1, 2)
plt.plot(generations, tardiness, 'r-')
plt.xlabel('Generation')
plt.ylabel('Average Tardiness (Integer)')
plt.grid(True, alpha=0.5)
plt.tight_layout() # 调整布局
plt.savefig('convergence_history_integerized.png', dpi=300, bbox_inches='tight') # 保存图片
plt.show()
def print_solution_details(self, solution: np.ndarray, objectives: tuple[int, int]):
"""
打印单个解的详细信息(企业选择、产能、数量等,整数化)
:param solution: 染色体(解,整数)
:param objectives: 该解的目标值(成本, 延期,整数)
"""
# 拆分染色体为三层
enterprise_layer, capacity_layer, quantity_layer = self.utils._split_chromosome(solution)
split_points = np.cumsum(self.utils.material_enterprise_count) # 分割点
print(f"\n变更成本: {objectives[0]}, 交付延期: {objectives[1]}") # 整数输出
print("=" * 80)
total_q_check = [] # 检查各物料的总数量是否满足需求(整数)
start = 0
for i in range(self.utils.I): # 遍历每种物料
end = split_points[i]
ents = self.utils.material_optional_enterprises[i] # 可选企业
e_segment = enterprise_layer[start:end] # 企业选择状态
c_segment = capacity_layer[start:end].astype(int) # 产能(整数)
q_segment = quantity_layer[start:end].astype(int) # 数量(整数)
demand_q = self.utils.order.Q[i] # 需求数量(整数)
allocated_q = np.sum(q_segment[e_segment == 1]) # 分配的总数量(整数)
print(f"物料 {i+1} - 需求数量: {demand_q}, 分配总量: {allocated_q}")
total_q_check.append(allocated_q == demand_q) # 整数相等检查
print(f" 选择的企业及其分配:")
for idx, ent in enumerate(ents):
if e_segment[idx] == 1: # 仅打印被选中的企业
ent_name = self.supplier_names[ent] # 企业名称
cap = c_segment[idx] # 产能(整数)
qty = q_segment[idx] # 数量(整数)
print(f" 企业: {ent_name}, 产能: {cap}, 分配数量: {qty}")
start = end
print("-" * 80)
# 验证数量约束是否满足
if all(total_q_check):
print("✅ 所有物料数量满足需求约束")
else:
print("❌ 部分物料数量未满足需求约束")
def print_pareto_solutions(self, population: np.ndarray, objectives: list[tuple[int, int]]):
"""
打印排序后的帕累托前沿解详细信息(按评价指标排序)
:param population: 排序后的帕累托前沿解种群
:param objectives: 对应的目标值列表
"""
print("\n" + "=" * 100)
print("按评价指标排序的帕累托前沿解详细方案(整数化)")
print("=" * 100)
# 打印所有选定的解
for i in range(len(population)):
print(f"\n===== 排序解 {i + 1} =====")
self.print_solution_details(population[i], objectives[i])