fix ALNS rewards and tabu diversification
This commit is contained in:
18
PROGRESS.md
18
PROGRESS.md
@@ -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`.
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user