Initial commit: cuGenOpt GPU optimization solver

This commit is contained in:
L-yang-yang 2026-03-20 00:33:45 +08:00
commit fc5a0ff4af
117 changed files with 25545 additions and 0 deletions

View file

@ -0,0 +1,19 @@
"""
Built-in operator packs for standard problems.
Each pack provides problem-aware custom operators that can significantly
improve solution quality for specific problem types. Users load them like:
from cugenopt.operator_packs import tsp_ops
result = cugenopt.solve_custom(..., custom_operators=tsp_ops)
Or with built-in solvers:
result = cugenopt.solve_tsp(dist, custom_operators=tsp_ops)
"""
from cugenopt.operator_packs.tsp import tsp_ops
from cugenopt.operator_packs.knapsack import knapsack_ops
from cugenopt.operator_packs.graph_color import graph_color_ops
__all__ = ["tsp_ops", "knapsack_ops", "graph_color_ops"]

View file

@ -0,0 +1,92 @@
"""
Graph Coloring specific custom operators.
These operators exploit graph structure (adjacency awareness) to
reduce conflicts more effectively than random integer mutations.
"""
from cugenopt.operators import CustomOperator
_GC_CONFLICT_RECOLOR = CustomOperator(
name="gc_conflict_recolor",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 2) return false;
int n = prob->_n;
const int* adj = prob->d_adj;
int node = rand_int(rng, sz);
int my_color = sol.data[row][node];
bool has_conflict = false;
for (int j = 0; j < sz; j++) {
if (j != node && adj[node * n + j] && sol.data[row][j] == my_color) {
has_conflict = true;
break;
}
}
if (!has_conflict) return false;
int range = val_ub - val_lb + 1;
int best_color = my_color;
int best_conflicts = sz;
for (int t = 0; t < 5; t++) {
int c = val_lb + rand_int(rng, range);
if (c == my_color) continue;
int conflicts = 0;
for (int j = 0; j < sz; j++)
if (j != node && adj[node * n + j] && sol.data[row][j] == c)
conflicts++;
if (conflicts < best_conflicts) {
best_conflicts = conflicts;
best_color = c;
}
}
if (best_color == my_color) return false;
sol.data[row][node] = best_color;
return true;
""",
encoding="integer",
initial_weight=1.5,
)
_GC_KEMPE_CHAIN = CustomOperator(
name="gc_kempe_swap",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 3) return false;
int n = prob->_n;
const int* adj = prob->d_adj;
int node = rand_int(rng, sz);
int c1 = sol.data[row][node];
int range = val_ub - val_lb + 1;
int c2 = val_lb + rand_int(rng, range - 1);
if (c2 >= c1) c2++;
int queue[64];
bool visited[512];
for (int i = 0; i < sz && i < 512; i++) visited[i] = false;
int head = 0, tail = 0;
queue[tail++] = node;
visited[node] = true;
int chain_len = 0;
while (head < tail && tail < 64 && chain_len < 16) {
int v = queue[head++];
int vc = sol.data[row][v];
int swap_c = (vc == c1) ? c2 : c1;
sol.data[row][v] = swap_c;
chain_len++;
for (int j = 0; j < sz; j++) {
if (!visited[j] && j < 512 && adj[v * n + j]
&& (sol.data[row][j] == c1 || sol.data[row][j] == c2)
&& tail < 64) {
visited[j] = true;
queue[tail++] = j;
}
}
}
return chain_len > 0;
""",
encoding="integer",
initial_weight=1.0,
)
graph_color_ops = [_GC_CONFLICT_RECOLOR, _GC_KEMPE_CHAIN]

View file

@ -0,0 +1,71 @@
"""
Knapsack-specific custom operators.
These operators exploit knapsack structure (value/weight ratio awareness)
to make more informed bit-flip decisions.
"""
from cugenopt.operators import CustomOperator
_KNAPSACK_GREEDY_FLIP = CustomOperator(
name="knapsack_greedy_flip",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 2) return false;
const float* weights = prob->d_weights;
const float* values = prob->d_values;
int pos = rand_int(rng, sz);
if (sol.data[row][pos] == 0) {
float ratio = (weights[pos] > 0.001f)
? values[pos] / weights[pos] : 0.0f;
if (ratio > 0.5f || curand_uniform(rng) < 0.3f) {
sol.data[row][pos] = 1;
return true;
}
} else {
float ratio = (weights[pos] > 0.001f)
? values[pos] / weights[pos] : 1e6f;
if (ratio < 0.5f || curand_uniform(rng) < 0.3f) {
sol.data[row][pos] = 0;
return true;
}
}
return false;
""",
encoding="binary",
initial_weight=0.8,
)
_KNAPSACK_SWAP_RATIO = CustomOperator(
name="knapsack_swap_ratio",
code=r"""
int row = 0;
int sz = sol.dim2_sizes[row];
if (sz < 2) return false;
const float* weights = prob->d_weights;
const float* values = prob->d_values;
int in_item = -1, out_item = -1;
for (int t = 0; t < 8; t++) {
int p = rand_int(rng, sz);
if (sol.data[row][p] == 1 && in_item < 0) in_item = p;
if (sol.data[row][p] == 0 && out_item < 0) out_item = p;
if (in_item >= 0 && out_item >= 0) break;
}
if (in_item < 0 || out_item < 0) return false;
float in_ratio = (weights[in_item] > 0.001f)
? values[in_item] / weights[in_item] : 1e6f;
float out_ratio = (weights[out_item] > 0.001f)
? values[out_item] / weights[out_item] : 0.0f;
if (out_ratio > in_ratio || curand_uniform(rng) < 0.2f) {
sol.data[row][in_item] = 0;
sol.data[row][out_item] = 1;
return true;
}
return false;
""",
encoding="binary",
initial_weight=0.8,
)
knapsack_ops = [_KNAPSACK_GREEDY_FLIP, _KNAPSACK_SWAP_RATIO]

View file

@ -0,0 +1,108 @@
"""
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]