cuGenOpt/python/cugenopt/operator_packs/tsp.py
2026-03-20 00:33:45 +08:00

108 lines
4 KiB
Python

"""
TSP-specific custom operators.
These operators exploit TSP structure (distance matrix, route continuity)
to perform more effective local search than generic permutation operators.
"""
from cugenopt.operators import CustomOperator
_TSP_2OPT_DELTA = CustomOperator(
name="tsp_2opt_delta",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 4) return false;
int n = prob->_n;
const float* dist = prob->d_dist;
int i = rand_int(rng, sz);
int j = rand_int(rng, sz - 2);
if (j >= i) j += 2; else if (j == i - 1) j = (i + sz - 1) % sz;
if (i > j) { int t = i; i = j; j = t; }
if (j - i < 2 || j - i >= sz - 1) return false;
int a = sol.data[row][i], b = sol.data[row][(i+1) % sz];
int c = sol.data[row][j], d = sol.data[row][(j+1) % sz];
float old_cost = dist[a * n + b] + dist[c * n + d];
float new_cost = dist[a * n + c] + dist[b * n + d];
if (new_cost >= old_cost) return false;
ops::perm_reverse(sol.data[row], i + 1, j);
return true;
""",
encoding="permutation",
initial_weight=1.5,
)
_TSP_OR_OPT_DELTA = CustomOperator(
name="tsp_or_opt_delta",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 5) return false;
int n = prob->_n;
const float* dist = prob->d_dist;
int seg_len = 1 + rand_int(rng, 3);
if (seg_len >= sz - 1) return false;
int from_pos = rand_int(rng, sz - seg_len);
int prev = sol.data[row][(from_pos + sz - 1) % sz];
int seg_start = sol.data[row][from_pos];
int seg_end = sol.data[row][from_pos + seg_len - 1];
int next = sol.data[row][(from_pos + seg_len) % sz];
float remove_cost = dist[prev * n + seg_start]
+ dist[seg_end * n + next]
- dist[prev * n + next];
int best_to = -1;
float best_delta = 0.0f;
for (int t = 0; t < 8; t++) {
int to = rand_int(rng, sz - seg_len);
if (to >= from_pos && to < from_pos + seg_len) continue;
int tp = sol.data[row][to];
int tn = sol.data[row][(to + 1) % sz];
float insert_cost = dist[tp * n + seg_start]
+ dist[seg_end * n + tn]
- dist[tp * n + tn];
float delta = insert_cost - remove_cost;
if (delta < best_delta) { best_delta = delta; best_to = to; }
}
if (best_to < 0) return false;
ops::perm_or_opt(sol.data[row], sz, from_pos, best_to, seg_len);
return true;
""",
encoding="permutation",
initial_weight=1.0,
)
_TSP_NODE_INSERT_DELTA = CustomOperator(
name="tsp_node_insert_delta",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 4) return false;
int n = prob->_n;
const float* dist = prob->d_dist;
int from_pos = rand_int(rng, sz);
int node = sol.data[row][from_pos];
int prev = sol.data[row][(from_pos + sz - 1) % sz];
int next = sol.data[row][(from_pos + 1) % sz];
float remove_save = dist[prev * n + node] + dist[node * n + next]
- dist[prev * n + next];
int best_to = -1;
float best_delta = 0.0f;
for (int t = 0; t < 8; t++) {
int to = rand_int(rng, sz - 1);
if (to >= from_pos) to++;
int tp = sol.data[row][to];
int tn = sol.data[row][(to + 1) % sz];
float insert_cost = dist[tp * n + node] + dist[node * n + tn]
- dist[tp * n + tn];
float delta = insert_cost - remove_save;
if (delta < best_delta) { best_delta = delta; best_to = to; }
}
if (best_to < 0) return false;
ops::perm_insert(sol.data[row], from_pos, best_to, sz);
return true;
""",
encoding="permutation",
initial_weight=1.0,
)
tsp_ops = [_TSP_2OPT_DELTA, _TSP_OR_OPT_DELTA, _TSP_NODE_INSERT_DELTA]