Files
Enterprise/t_alns_rrd_reproduction/OPTIMIZATION_PLAN.md
huangfu 8ff9715fb7 Initial commit: T-ALNS-RRD paper reproduction project
- Paper: Optimizing urban last mile delivery efficiency (Liu & Wang, 2025)
- 5 algorithms: Static-VRPTW, TA-Greedy, ALNS-Base, T-ALNS, T-ALNS-RRD
- v1 baseline + v2 calibrated experiments with full results
- Tabu memory ablation study with convergence analysis
- Comprehensive final report (FINAL_REPORT.md)
2026-06-02 21:11:00 +08:00

12 KiB
Raw Permalink Blame History

T-ALNS-RRD 复现优化计划

版本管理策略

目录组织

t_alns_rrd_reproduction/
├── configs/
│   ├── default.yaml              # v1 原始配置(保留不动)
│   └── calibrated.yaml           # v2 校准配置(新)
├── src/                          # 核心代码(通过配置参数化,不复制)
│   └── ...                       # 只修改 bug不破坏 v1
├── results/
│   ├── v1_baseline/              # v1 运行结果(已生成 → 移入此处)
│   │   ├── tables/
│   │   ├── figures/
│   │   └── logs/
│   └── v2_calibrated/            # v2 运行结果(新生成)
│       ├── tables/
│       ├── figures/
│       └── logs/
└── CHANGELOG.md                  # 记录版本变更

运行方式:通过配置切换版本

# v1: 原始(已完成的实验)
python3 src/experiments/run_main_comparison.py --config default

# v2: 校准后
python3 src/experiments/run_main_comparison.py --config calibrated

输出自动路由到对应目录:

output_dir = f"results/{config_name}/"

修改清单6 项,按实施顺序)

修改 1修复 Static 基线公平性 【P0 | ~10 行】

问题static_vrptw.py:62 构建路径时只用 interval 0 的旅行时间,评估时却用时间依赖时间 → Static 被不公平地"惩罚"

修改文件: src/baselines/static_vrptw.py

改动内容:

# 原代码 (line ~60)
def _static_route_cost(self, nodes: list) -> float:
    depart_time = self.ctx.op_start
    for idx in range(1, len(nodes)):
        i, j = nodes[idx - 1], nodes[idx]
        tt = self.ctx.traffic.travel_time[i, j, 0]   # ❌ 永远 interval 0
        total += tt

# 新代码
def _static_route_cost(self, nodes: list) -> float:
    """使用所有时段的平均旅行时间,而非仅 interval 0"""
    depart_time = self.ctx.op_start
    for idx in range(1, len(nodes)):
        i, j = nodes[idx - 1], nodes[idx]
        # 对所有 12 个时段取平均 → 公平的"无交通感知"基线
        avg_tt = np.mean([self.ctx.traffic.travel_time[i, j, h] 
                          for h in range(self.ctx.n_intervals)
                          if not np.isinf(self.ctx.traffic.travel_time[i, j, h])])
        tt = avg_tt
        total += tt

同时需要Static 成本评估时不加 λ₁ 和 λ₂ 惩罚(只有纯旅行时间),让它和 TA-Greedy 在评估维度上可比。

cost.pyevaluate_solution 中加一个参数

def evaluate_solution(self, solution, problem_ctx, include_penalties=True):
    # Static: include_penalties=False → 只算旅行时间
    # 其他: include_penalties=True → 完整 Eq.1

预期效果Static 成本从 ~7580 → ~2500-3500TA-Greedy 改善从 70% → 15-25%


修改 2CES 缩放校准 【P0 | ~5 行】

问题data_generator.py:326ρ = θ × γ,θ=1.0,γ∈[0,1] → CES 只有 ~25。论文 CES ~1300-2850。

修改文件: src/data_generator.py

改动内容:

# 原代码 (line ~326)
rho = self.congestion_scale * gamma   # ρ ∈ [0, 1],太小

# 新代码
# ρ 应反映"拥堵带来的额外时间成本",量级应与 travel_time 可比
extra_time = base_time * (self.traffic_multipliers[h] - 1.0)  # 拥堵造成的额外分钟
rho = self.congestion_scale * extra_time * gamma
# γ 作为缩放因子:[0, 1] × extra_time → ρ ∈ [0, 额外时间]
# θ 控制在 config 中设置

修改文件: configs/calibrated.yaml

traffic:
  congestion_scale_theta: 50.0    # v1 用 1.0 → v2 用 50.0

预期效果CES 从 ~25 → ~300-800 范围(取决于 θ 值,可通过 config 微调)


修改 3数据集难度提升 【P1 | ~15 行配置 + 生成参数】

问题47 客户/4 车/120kg/宽时间窗 → OTDR 100%,无优化压力

修改文件: configs/calibrated.yaml

改动内容:

problem:
  n_customers: 60         # 47 → 60 (+28%)
  vehicle_capacity_kg: 100  # 120 → 100 (更紧)
  # 其余不变

customers:
  demand_min_kg: 4         # 3 → 4
  demand_max_kg: 15        # 12 → 15 (更多变异性)
  time_window_categories:
    morning:
      earliest: 540
      latest: 720
    afternoon:
      earliest: 780
      latest: 960
    evening:
      earliest: 1020
      latest: 1200
  # 每个客户窗口随机 30-90 分钟(而非 60-150
  window_length_min: 30    # 新增
  window_length_max: 90    # 新增

修改文件: src/data_generator.py(支持新配置参数)

# 在 _extract_params 中添加
self.tw_window_min = c.get("window_length_min", 60)
self.tw_window_max = c.get("window_length_max", 150)

# 在 generate_customers 中使用
window_len = self.rng.uniform(self.tw_window_min, self.tw_window_max)

预期效果OTDR 从 100% → 75-90%,各算法间出现明显差异


修改 4增加迭代数与事件频率 【P1 | 配置改动】

修改文件: configs/calibrated.yaml

alns:
  max_iterations: 1000     # 500 → 1000 (对齐论文)
  time_limit_sec: 600
  stall_limit: 400         # 200 → 400

rrd:
  rollout:
    horizon_min_min: 30
    horizon_max_min: 120
    n_sim_min: 5            # 2 → 5
    n_sim_max: 30           # 50 → 30
  events:
    urgency_threshold: 0.3  # 0.5 → 0.3 (更容易触发)
  event_probability: 0.5    # 新增在顶层
  event_check_interval: 5   # 10 → 5

同时修改: src/rrd/event_generator.py 让 E1 事件真正修改 travel_time 张量

def generate_traffic_incident(self, current_time, solution):
    arc = ...
    # 临时将受影响的弧的旅行时间乘以 3
    i, j = arc
    saved_times = self.ctx.traffic.travel_time[i, j, :].copy()
    self.ctx.traffic.travel_time[i, j, :] *= 3.0
    event._undo = lambda: setattr(self.ctx.traffic, 'travel_time', ...)  # rollback
    return event

修改 5实验参数 → 30 seeds + 统计 【P2 | ~20 行】

修改文件: src/experiments/run_main_comparison.py

改动内容:

# 1. 接受 --config 命令行参数
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--config", default="default", help="Config name")
args = parser.parse_args()

# 2. 加载对应 config
config_path = Path(f"configs/{args.config}.yaml")
with open(config_path) as f: cfg = yaml.safe_load(f)

# 3. 输出到版本目录
output_dir = Path(f"results/{args.config}/")

# 4. n_seeds 从 config 读取
n_seeds = cfg.get("experiments", {}).get("random_seeds", 30)

新增: 统计检验

from scipy import stats

# 对每对算法做 paired t-test
for i in range(len(algorithms)):
    for j in range(i+1, len(algorithms)):
        t_stat, p_val = stats.ttest_rel(costs_i, costs_j)
        print(f"  {names[i]} vs {names[j]}: t={t_stat:.2f}, p={p_val:.4f}")

修改文件: requirements.txt

scipy>=1.10     # 新增

修改 6实验结果版本隔离 【架构 | ~10 行】

修改文件: src/experiments/run_main_comparison.py

改动内容:

def run_experiment(config_name="default", ...):
    output_dir = Path(f"results/{config_name}/")
    output_dir.mkdir(parents=True, exist_ok=True)
    (output_dir / "tables").mkdir(exist_ok=True)
    (output_dir / "figures").mkdir(exist_ok=True)
    (output_dir / "logs").mkdir(exist_ok=True)
    
    # 保存一份 config 副本到结果目录
    import shutil
    shutil.copy(f"configs/{config_name}.yaml", output_dir / "config_used.yaml")
    
    # ... 后续所有输出路径都基于 output_dir

完整文件变更清单

# 文件 操作 内容
1 configs/calibrated.yaml 新建 v2 完整配置(复制 default + 修改 6 个参数组)
2 src/baselines/static_vrptw.py 修改 10 行 使用平均旅行时间 + 移除评估中的 λ 惩罚
3 src/cost.py 修改 5 行 evaluate_solution 加 include_penalties 参数
4 src/data_generator.py 修改 8 行 客户生成支持 window_length_min/maxρ 改用 extra_time
5 src/rrd/event_generator.py 修改 15 行 E1 事件真正修改 travel time 张量
6 src/experiments/run_main_comparison.py 修改 30 行 支持 --config版本输出目录统计检验
7 src/visualization/plot_results.py 修改 5 行 接受 output_dir 参数
8 requirements.txt 修改 1 行 添加 scipy
9 CHANGELOG.md 新建 记录 v1/v2 差异
10 results/v1_baseline/ 移动 将已有 v1 结果移入子目录

不动的文件v1 和 v2 共用):

src/problem.py         (无 bug不碰)
src/alns/*             (无 bug参数通过 config 控制)
src/tabu/*             (无 bug参数通过 config 控制)
src/rrd/dispatch.py    (无 bug)
src/rrd/rollout.py     (无 bug)
src/rrd/candidate_actions.py (无 bug)

实施步骤6 步,约 1-2 小时)

Step 0: 版本快照 + 目录准备

cd t_alns_rrd_reproduction

# 0.1 创建 CHANGELOG
echo "# Changelog\n\n## v1.0-baseline (default)\n- 47 customers, 4 vehicles, 120kg\n- θ=1.0, 200 iter, 5 seeds, complete graph\n- Known issues: inflated Static baseline, low CES, OTDR=100%\n" > CHANGELOG.md

# 0.2 隔离 v1 结果
mkdir -p results/v1_baseline
mv results/tables results/figures results/logs results/v1_baseline/ 2>/dev/null
mkdir -p results/v1_baseline/{tables,figures,logs}

Step 1: 创建 calibrated.yaml

复制 configs/default.yaml → 修改以上所有参数

Step 2: 修改 static_vrptw.py + cost.py

修复 Static 基线公平性(两个文件,共约 15 行改动)

Step 3: 修改 data_generator.py

支持新客户窗口参数 + 修复 ρ 计算(一个文件,约 10 行改动)

Step 4: 修改 event_generator.py

让 E1 事件真正影响 travel time一个文件约 15 行改动)

Step 5: 修改 run_main_comparison.py + 依赖

支持 --config、版本目录、统计检验、scipy一个文件 + requirements.txt

Step 6: 生成 v2 数据 + 跑实验

# 6.1 重新生成校准数据集
python3 -c "from src.data_generator import DataGenerator; DataGenerator(config_path='configs/calibrated.yaml', seed=42).generate_all()"

# 6.2 跑 v2 实验30 seeds × 1000 iter ~ 20-30 分钟)
python3 src/experiments/run_main_comparison.py --config calibrated

预期结果对比v1 vs v2

指标 v1 当前 v2 预期 变化原因
Static Cost 7580 2500-3500 用平均时间,移除评估偏差
TA-Greedy Cost 2250 2000-2500 更难的数据集
OTDR (all algos) 100% 75-95% 更紧时间窗 + 更多客户
CES (all algos) 22-30 300-800 θ=50 + ρ 改用 extra_time
Static→TA drop 70% 15-25% 基线公平后差距缩小
ALNS→T-ALNS drop 0.2% 2-5% 更多迭代让 Tabu 生效
T-ALNS→RRD drop 0.1% 1-3% 更多事件 + 事件真正改 travel time
n_seeds 5 30 对齐论文
p-values < 0.05 添加 t-test

风险控制

风险 缓解措施
v2 改动破坏 v1 所有改动通过 config 参数化,不改 v1 逻辑路径
数据集变难导致无可行解 先保守收紧n=55, Q=110不行再松
跑 30 seeds 太慢 先用 10 seeds 快速验证,确认趋势正确后再跑全量
CES 调到什么值合适 先设 θ=30 跑一轮看效果,再调

对比报告模板

完成后 results/ 目录结构:

results/
├── v1_baseline/
│   ├── tables/main_comparison.csv
│   ├── figures/fig1-7.png
│   ├── logs/convergence.npz
│   └── config_used.yaml
├── v2_calibrated/
│   ├── tables/main_comparison.csv
│   ├── figures/fig1-7.png
│   ├── logs/convergence.npz
│   └── config_used.yaml
└── comparison_report.md    # 自动生成的 v1 vs v2 对比

对比报告包含:

  • 两张表并排v1 数值 vs v2 数值)
  • 趋势一致性检查(每个算法的递进方向)
  • 统计显著性v2 的 t-test 结果)
  • 结论v2 更接近论文报告的质量标准