From 3f104a43958d5e54e5d5cd45bfcc218d27dddc15 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Thu, 14 May 2026 06:28:25 -0600 Subject: [PATCH] perf(operators): make ERX neighbor removal O(degree) per step (397K -> 140K instr) Edge Recombination Crossover scrubbed `current` from every one of the n adjacency lists on each step of the walk -- an O(n^2) pass. The parent-tour adjacency relation is symmetric (b in adj[a] iff a in adj[b]), so `current` only ever appears in the lists of its own neighbors. Taking adj[current] out with mem::take and retaining only over those lists is O(degree). edge_recombination_crossover_vary n=100: 397_214 -> 140_403 (-65%, 2.83x); n=30: 62_708 -> 39_987 (-36%). Output bit-identical -- all 606 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/operators/permutation.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/operators/permutation.rs b/src/operators/permutation.rs index 1353197..8c11db4 100644 --- a/src/operators/permutation.rs +++ b/src/operators/permutation.rs @@ -642,14 +642,19 @@ fn erx_child(p1: &[usize], p2: &[usize], start: usize, rng: &mut Rng) -> Vec = adj[current] + let neighbors: Vec = current_adj .iter() .copied() .filter(|&c| !visited[c])