import matplotlib.pyplot as plt import numpy as np from chromosome_utils import ChromosomeUtils class ResultVisualizer: """结果可视化工具""" def __init__(self, utils: ChromosomeUtils): self.utils = utils self.figsize = (12, 8) self.supplier_names = ["RiskEnterprise"] + self.utils.supplier.names def plot_pareto_front(self, all_objectives: list[tuple[float, float]], non_dominated_objectives: list[tuple[float, float]]): """绘制帕累托前沿""" 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') plt.xlabel('Change Cost') plt.ylabel('Tardiness') plt.legend() plt.grid(True, alpha=0.5) plt.savefig('pareto_front.png', dpi=300, bbox_inches='tight') plt.show() def plot_convergence(self, convergence_history: list[tuple[float, float]]): """绘制收敛趋势图""" 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] plt.subplot(2, 1, 1) plt.plot(generations, costs, 'b-') plt.title('Convergence History') plt.ylabel('Average Change Cost') plt.grid(True, alpha=0.5) plt.subplot(2, 1, 2) plt.plot(generations, tardiness, 'r-') plt.xlabel('Generation') plt.ylabel('Average Tardiness') plt.grid(True, alpha=0.5) plt.tight_layout() plt.savefig('convergence_history.png', dpi=300, bbox_inches='tight') plt.show() def print_solution_details(self, solution: np.ndarray, objectives: tuple[float, float]): """打印单个解的详细信息(含数量校验)""" 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]:.2f}, 交付延期: {objectives[1]:.2f}") 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] q_segment = quantity_layer[start:end] demand_q = self.utils.order.Q[i] print(f"物料 {i} - 需求数量: {demand_q}, 分配总量: {np.sum(q_segment[e_segment==1]):.2f}") total_q_check.append(abs(np.sum(q_segment[e_segment==1]) - demand_q) < 1e-2) 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:.2f}, 分配数量: {qty:.2f}") start = end print("-" * 80) if all(total_q_check): print("✅ 所有物料数量满足需求约束") else: print("❌ 部分物料数量未满足需求约束") def print_pareto_solutions(self, population: np.ndarray, objectives: list[tuple[float, float]]): """打印帕累托前沿解的详细信息""" print("\n" + "=" * 100) print("帕累托前沿解详细方案") print("=" * 100) for i in range(min(5, len(population))): print(f"\n===== 最优解 {i+1} =====") self.print_solution_details(population[i], objectives[i])