93 lines
4.2 KiB
Python
93 lines
4.2 KiB
Python
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]) |