fix ALNS rewards and tabu diversification

This commit is contained in:
皇甫其逊
2026-06-02 22:11:54 +08:00
parent 6d829fe7d9
commit 5da471b67f
5 changed files with 43 additions and 5 deletions

View File

@@ -41,3 +41,21 @@ python src/experiments/run_main_comparison.py --config paper --seeds 30 --iterat
### Interpretation rule
- Check whether Static > TA-Greedy > ALNS-Base > T-ALNS > T-ALNS-RRD in total cost, CES decreases, and OTDR improves.
- If the paper trend is not reproduced, keep the generated tables and record the failed metrics honestly instead of tuning results by hand.
## 2026-06-02 follow-up algorithm fidelity fix
### Findings addressed
- ALNS, T-ALNS, and T-ALNS-RRD did not distinguish `sigma_2` current-solution improvements from `sigma_3` simulated-annealing accepted non-improving moves.
- T-ALNS diversification intensity used adaptive Tabu tenure instead of the paper's actual move-memory occupancy `|T_move| / |T_move|max`.
### Changes made
- Reward assignment now follows the paper's four levels:
- `reward_global_best` for new global best.
- `reward_improvement` for accepted moves that improve the previous current solution.
- `reward_accepted` for accepted non-improving moves.
- `reward_rejected` for rejected moves.
- Added `MoveTabu.utilization` and changed Eq.27 diversification intensity to use actual move Tabu list occupancy.
### Local validation
- Per user instruction, no local Python tests, smoke tests, or full experiments were run.
- Static checks only: source inspection and `git diff --check`.

View File

@@ -207,6 +207,7 @@ class ALNSBase:
new_cost = self.cost_calc.compute_total_cost(S_new, self.ctx)
# Acceptance decision (Eq.20)
previous_cost = current_cost
if sa.accept(current_cost, new_cost):
S_current = S_new
current_cost = new_cost
@@ -216,9 +217,12 @@ class ALNSBase:
best_cost = new_cost
stall_counter = 0
reward = self.cfg["reward_global_best"]
else:
elif new_cost < previous_cost:
stall_counter += 1
reward = self.cfg["reward_improvement"]
else:
stall_counter += 1
reward = self.cfg["reward_accepted"]
else:
reward = self.cfg["reward_rejected"]

View File

@@ -285,6 +285,7 @@ class TALNSRRD:
new_cost = self.cost_calc.compute_total_cost(S_new, self.ctx)
# Acceptance
previous_cost = current_cost
if sa.accept(current_cost, new_cost):
S_current = S_new
current_cost = new_cost
@@ -300,10 +301,14 @@ class TALNSRRD:
last_best_iter = iter_count
self.talns.move_tabu.update_tenure(found_improvement=True)
reward = self.cfg["reward_global_best"]
elif new_cost < previous_cost:
stall_counter += 1
self.talns.move_tabu.update_tenure(found_improvement=True)
reward = self.cfg["reward_improvement"]
else:
stall_counter += 1
self.talns.move_tabu.update_tenure(found_improvement=False)
reward = self.cfg["reward_improvement"]
reward = self.cfg["reward_accepted"]
accepted = True
else:
reward = self.cfg["reward_rejected"]

View File

@@ -75,6 +75,11 @@ class MoveTabu:
while len(self._entries) > self.tenure_max:
self._entries.popleft()
@property
def utilization(self) -> float:
"""Normalized |T_move| / |T_move|max for Eq.27 diversification."""
return len(self._entries) / max(self.tenure_max, 1)
def update_tenure(self, found_improvement: bool):
"""Adapt tenure based on improvement (Eq.32).

View File

@@ -160,8 +160,9 @@ class TALNS:
# Time since last improvement (normalized)
time_factor = (current_iter - last_best_iter) / max(t_max, 1)
# Move Tabu utilization
move_factor = self.move_tabu.tenure / self.cfg["move_tabu_tenure_max"]
# Move Tabu utilization: Eq.27 uses actual memory occupancy |T_move|,
# not the adaptive tenure parameter from Eq.32.
move_factor = self.move_tabu.utilization
# Frequency std
freq_std = self.freq_mem.get_std_assignment_freq()
@@ -379,6 +380,7 @@ class TALNS:
new_cost = self.cost_calc.compute_total_cost(S_new, self.ctx)
# Acceptance decision
previous_cost = current_cost
if sa.accept(current_cost, new_cost):
S_current = S_new
current_cost = new_cost
@@ -398,10 +400,14 @@ class TALNS:
last_best_iter = iter_count
self.move_tabu.update_tenure(found_improvement=True)
reward = self.cfg["reward_global_best"]
elif new_cost < previous_cost:
stall_counter += 1
self.move_tabu.update_tenure(found_improvement=True)
reward = self.cfg["reward_improvement"]
else:
stall_counter += 1
self.move_tabu.update_tenure(found_improvement=False)
reward = self.cfg["reward_improvement"]
reward = self.cfg["reward_accepted"]
accepted = True
else:
reward = self.cfg["reward_rejected"]