mirror of
https://github.com/L-yang-yang/cugenopt.git
synced 2026-04-25 12:16:21 +02:00
Initial commit: cuGenOpt GPU optimization solver
This commit is contained in:
commit
fc5a0ff4af
117 changed files with 25545 additions and 0 deletions
19
python/cugenopt/operator_packs/__init__.py
Normal file
19
python/cugenopt/operator_packs/__init__.py
Normal 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"]
|
||||
92
python/cugenopt/operator_packs/graph_color.py
Normal file
92
python/cugenopt/operator_packs/graph_color.py
Normal 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]
|
||||
71
python/cugenopt/operator_packs/knapsack.py
Normal file
71
python/cugenopt/operator_packs/knapsack.py
Normal 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]
|
||||
108
python/cugenopt/operator_packs/tsp.py
Normal file
108
python/cugenopt/operator_packs/tsp.py
Normal 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]
|
||||
Loading…
Add table
Add a link
Reference in a new issue