mirror of
https://github.com/tig-pool-nk/tig-monorepo.git
synced 2026-03-09 23:17:24 +08:00
Merge branches 'satisfiability/schnoing', 'satisfiability/walk_sat', 'satisfiability/fast_walk_sat', 'satisfiability/sprint_sat', 'satisfiability/inbound', 'satisfiability/sat_allocd', 'satisfiability/sat_optima', 'satisfiability/sat_global', 'satisfiability/sat_global_opt', 'satisfiability/sat_adaptive', 'satisfiability/sat_adaptive_opt_un', 'satisfiability/sat_separate', 'satisfiability/sat_separate_prob', 'satisfiability/sat_separate_opt', 'satisfiability/sat_separate_opt_p', 'satisfiability/sat_unified', 'satisfiability/sat_unified_opt', 'satisfiability/better_sat', 'satisfiability/sat_suma', 'vehicle_routing/clarke_wright', 'vehicle_routing/cw_heuristic', 'vehicle_routing/clarke_wright_super', 'vehicle_routing/advanced_routing', 'vehicle_routing/enhanced_routing', 'vehicle_routing/advanced_heuristics', 'vehicle_routing/enhanced_heuristics', 'vehicle_routing/advanced_cw_opt', 'vehicle_routing/advanced_cw_adp', 'vehicle_routing/enhanced_cw', 'vehicle_routing/new_enhanced_cw', 'vehicle_routing/new_enhanced_cw_opt', 'vehicle_routing/new_enhanced_cw_low', 'vehicle_routing/enhanced_solomon', 'vehicle_routing/better_routing', 'vehicle_routing/routing_redone', 'vehicle_routing/sausage', 'vehicle_routing/native_routing', 'vehicle_routing/simple_ls_zero', 'vehicle_routing/vrptw_ultimate', 'knapsack/dynamic', 'knapsack/knapmaxxing', 'knapsack/knapheudp', 'knapsack/classic_quadkp', 'knapsack/quadkp_improved', 'knapsack/knap_one', 'knapsack/quadkp_maximize', 'knapsack/relative_quad_fast', 'knapsack/new_relative_ultra', 'knapsack/relative_opt_fast', 'knapsack/relative_opt_mid', 'knapsack/relative_opt_optima', 'knapsack/relative_raw_ultra', 'knapsack/knapsack_redone', 'knapsack/native_knapsack', 'knapsack/fast_and_fun', 'knapsack/knapsplatt', 'vector_search/brute_force_bacalhau', 'vector_search/optimax_gpu', 'vector_search/invector', 'vector_search/invector_hybrid', 'vector_search/invector_hybrid_adp', 'vector_search/invector_revisited_s', 'vector_search/invector_adj', 'vector_search/invector_fast', 'vector_search/better_vector', 'vector_search/improved_search_adp', 'vector_search/is_adp_optimal', 'vector_search/improved_search_new', 'vector_search/cluster_improved', 'hypergraph/hyper_cluster' and 'hypergraph/hyper_improved'
This commit is contained in:
parent
ef0f6e3c15
319ddf3680
6a23286f14
b04db72c49
bc486fbbeb
1a449d070a
0fc6ce0639
31ea24ed72
c0977d089a
6edcf56304
6486dd9fe4
851742896e
4067360af6
a854780ae8
93ce1fe44c
bc7fc918ca
caa11a9d78
ea4013e439
e6a1b8d136
f4474963a8
9053973783
7973e295c9
6d98d1fcee
0660839d13
01c8081e5c
635718ad17
97836d72a0
1f182c82b0
f459d81fcf
1267857cf4
04ff7b950e
a9d24fa25e
0fc7ff7847
884778f64f
a0ec1933bb
6606afe53b
a7102f8ab7
a545b540e5
080829879d
5e89d917eb
a0ff0c476d
018e448ac3
df00079783
e291b7c14e
f0eedfb2c8
51f660d43d
9bdbb7f157
99b93ef361
98a4b6e6bc
cab2cefd2b
7f06ed40ee
76be8e683b
b909716976
fe93cd4850
a0f2142b84
e8ce44769e
a1912b34fd
d35133a766
4588812cb3
2d585be522
e8608c5c39
9b68e678bb
fef3eb8fb0
95f3bc7065
69e73dda8c
0b17a4fdd0
56cdf7e3be
a51683b3aa
99e0755811
5aa2b7f1a2
b2c3a6344a
commit
9b4234c5cf
BIN
tig-algorithms/lib/hypergraph/hyper_cluster.tar.gz
Normal file
BIN
tig-algorithms/lib/hypergraph/hyper_cluster.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/hypergraph/hyper_improved.tar.gz
Normal file
BIN
tig-algorithms/lib/hypergraph/hyper_improved.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/classic_quadkp.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/classic_quadkp.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/dynamic.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/dynamic.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/fast_and_fun.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/fast_and_fun.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/knap_one.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/knap_one.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/knapheudp.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/knapheudp.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/knapmaxxing.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/knapmaxxing.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/knapsack_redone.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/knapsack_redone.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/knapsplatt.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/knapsplatt.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/native_knapsack.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/native_knapsack.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/new_relative_ultra.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/new_relative_ultra.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/quadkp_improved.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/quadkp_improved.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/quadkp_maximize.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/quadkp_maximize.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/relative_opt_fast.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/relative_opt_fast.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/relative_opt_mid.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/relative_opt_mid.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/relative_opt_optima.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/relative_opt_optima.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/relative_quad_fast.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/relative_quad_fast.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/knapsack/relative_raw_ultra.tar.gz
Normal file
BIN
tig-algorithms/lib/knapsack/relative_raw_ultra.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/better_sat.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/better_sat.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/fast_walk_sat.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/fast_walk_sat.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/inbound.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/inbound.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_adaptive.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_adaptive.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_adaptive_opt_un.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_adaptive_opt_un.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_allocd.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_allocd.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_global.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_global.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_global_opt.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_global_opt.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_optima.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_optima.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_separate.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_separate.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_separate_opt.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_separate_opt.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_separate_opt_p.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_separate_opt_p.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_separate_prob.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_separate_prob.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_suma.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_suma.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_unified.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_unified.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sat_unified_opt.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sat_unified_opt.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/sprint_sat.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/sprint_sat.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/satisfiability/walk_sat.tar.gz
Normal file
BIN
tig-algorithms/lib/satisfiability/walk_sat.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/better_vector.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/better_vector.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/brute_force_bacalhau.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/brute_force_bacalhau.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/cluster_improved.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/cluster_improved.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/improved_search_adp.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/improved_search_adp.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/improved_search_new.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/improved_search_new.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/invector.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/invector.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/invector_adj.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/invector_adj.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/invector_fast.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/invector_fast.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/invector_hybrid.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/invector_hybrid.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/invector_hybrid_adp.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/invector_hybrid_adp.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/invector_revisited_s.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/invector_revisited_s.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/is_adp_optimal.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/is_adp_optimal.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vector_search/optimax_gpu.tar.gz
Normal file
BIN
tig-algorithms/lib/vector_search/optimax_gpu.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/advanced_cw_adp.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/advanced_cw_adp.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/advanced_cw_opt.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/advanced_cw_opt.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/advanced_heuristics.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/advanced_heuristics.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/advanced_routing.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/advanced_routing.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/better_routing.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/better_routing.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/clarke_wright.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/clarke_wright.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/clarke_wright_super.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/clarke_wright_super.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/cw_heuristic.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/cw_heuristic.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/enhanced_cw.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/enhanced_cw.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/enhanced_heuristics.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/enhanced_heuristics.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/enhanced_routing.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/enhanced_routing.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/enhanced_solomon.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/enhanced_solomon.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/native_routing.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/native_routing.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/new_enhanced_cw.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/new_enhanced_cw.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/new_enhanced_cw_low.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/new_enhanced_cw_low.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/new_enhanced_cw_opt.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/new_enhanced_cw_opt.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/sausage.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/sausage.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/simple_ls_zero.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/simple_ls_zero.tar.gz
Normal file
Binary file not shown.
BIN
tig-algorithms/lib/vehicle_routing/vrptw_ultimate.tar.gz
Normal file
BIN
tig-algorithms/lib/vehicle_routing/vrptw_ultimate.tar.gz
Normal file
Binary file not shown.
23
tig-algorithms/src/hypergraph/hyper_cluster/README.md
Normal file
23
tig-algorithms/src/hypergraph/hyper_cluster/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** hypergraph
|
||||
* **Algorithm Name:** hyper_cluster
|
||||
* **Copyright:** 2025 Rootz
|
||||
* **Identity of Submitter:** Rootz
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
343
tig-algorithms/src/hypergraph/hyper_cluster/kernels.cu
Normal file
343
tig-algorithms/src/hypergraph/hyper_cluster/kernels.cu
Normal file
@ -0,0 +1,343 @@
|
||||
/*!Copyright 2025 Rootz
|
||||
|
||||
Identity of Submitter Rootz
|
||||
|
||||
UAI null
|
||||
|
||||
Licensed under the TIG Inbound Game License v2.0 or (at your option) any later
|
||||
version (the "License"); you may not use this file except in compliance with the
|
||||
License. You may obtain a copy of the License at
|
||||
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
extern "C" __global__ void hyperedge_clustering(
|
||||
const int num_hyperedges,
|
||||
const int num_clusters,
|
||||
const int *hyperedge_nodes,
|
||||
const int *hyperedge_offsets,
|
||||
int *hyperedge_clusters
|
||||
) {
|
||||
int hedge = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (hedge < num_hyperedges) {
|
||||
int start = hyperedge_offsets[hedge];
|
||||
int end = hyperedge_offsets[hedge + 1];
|
||||
int hedge_size = end - start;
|
||||
|
||||
int quarter_clusters = num_clusters >> 2;
|
||||
int cluster_mask = quarter_clusters - 1;
|
||||
|
||||
int cluster;
|
||||
if (hedge_size <= 2) {
|
||||
cluster = hedge & cluster_mask;
|
||||
} else if (hedge_size <= 4) {
|
||||
cluster = quarter_clusters + (hedge & cluster_mask);
|
||||
} else if (hedge_size <= 8) {
|
||||
cluster = (quarter_clusters << 1) + (hedge & cluster_mask);
|
||||
} else {
|
||||
cluster = (quarter_clusters * 3) + (hedge & cluster_mask);
|
||||
}
|
||||
|
||||
hyperedge_clusters[hedge] = cluster;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void compute_node_preferences(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int num_hedge_clusters,
|
||||
const int *node_hyperedges,
|
||||
const int *node_offsets,
|
||||
const int *hyperedge_clusters,
|
||||
const int *hyperedge_offsets,
|
||||
int *pref_nodes,
|
||||
int *pref_parts,
|
||||
int *pref_gains,
|
||||
int *pref_priorities
|
||||
) {
|
||||
int node = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (node < num_nodes) {
|
||||
int start = node_offsets[node];
|
||||
int end = node_offsets[node + 1];
|
||||
int node_degree = end - start;
|
||||
|
||||
int cluster_votes[256];
|
||||
int max_clusters = min(num_hedge_clusters, 256);
|
||||
for (int i = 0; i < max_clusters; i++) {
|
||||
cluster_votes[i] = 0;
|
||||
}
|
||||
|
||||
int max_votes = 0;
|
||||
int best_cluster = 0;
|
||||
|
||||
for (int j = start; j < end; j++) {
|
||||
int hyperedge = node_hyperedges[j];
|
||||
int cluster = hyperedge_clusters[hyperedge];
|
||||
|
||||
if (cluster >= 0 && cluster < max_clusters) {
|
||||
int hedge_start = hyperedge_offsets[hyperedge];
|
||||
int hedge_end = hyperedge_offsets[hyperedge + 1];
|
||||
int hedge_size = hedge_end - hedge_start;
|
||||
int weight = (hedge_size <= 3) ? 3 : (hedge_size <= 6) ? 2 : 1;
|
||||
|
||||
cluster_votes[cluster] += weight;
|
||||
|
||||
if (cluster_votes[cluster] > max_votes ||
|
||||
(cluster_votes[cluster] == max_votes && cluster < best_cluster)) {
|
||||
max_votes = cluster_votes[cluster];
|
||||
best_cluster = cluster;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int target_partition;
|
||||
if (node_degree <= 3) {
|
||||
target_partition = (best_cluster + node) % num_parts;
|
||||
} else if (node_degree <= 8) {
|
||||
target_partition = (best_cluster + node_degree + node) % num_parts;
|
||||
} else {
|
||||
target_partition = (best_cluster * 2 + node_degree + node) % num_parts;
|
||||
}
|
||||
|
||||
pref_nodes[node] = node;
|
||||
pref_parts[node] = target_partition;
|
||||
pref_gains[node] = max_votes;
|
||||
pref_priorities[node] = (max_votes << 16) + (num_parts - (node % num_parts));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void execute_node_assignments(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int max_part_size,
|
||||
const int *sorted_nodes,
|
||||
const int *sorted_parts,
|
||||
int *partition,
|
||||
int *nodes_in_part
|
||||
) {
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (int i = 0; i < num_nodes; i++) {
|
||||
int node = sorted_nodes[i];
|
||||
int preferred_part = sorted_parts[i];
|
||||
|
||||
if (node >= 0 && node < num_nodes && preferred_part >= 0 && preferred_part < num_parts) {
|
||||
bool assigned = false;
|
||||
for (int attempt = 0; attempt < num_parts; attempt++) {
|
||||
int try_part = (preferred_part + attempt) % num_parts;
|
||||
if (nodes_in_part[try_part] < max_part_size) {
|
||||
partition[node] = try_part;
|
||||
nodes_in_part[try_part]++;
|
||||
assigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!assigned) {
|
||||
int fallback_part = node % num_parts;
|
||||
partition[node] = fallback_part;
|
||||
nodes_in_part[fallback_part]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void compute_refinement_moves(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int max_part_size,
|
||||
const int num_hyperedges,
|
||||
const int *node_hyperedges,
|
||||
const int *node_offsets,
|
||||
const int *hyperedge_nodes,
|
||||
const int *hyperedge_offsets,
|
||||
const int *partition,
|
||||
const int *nodes_in_part,
|
||||
int *move_nodes,
|
||||
int *move_parts,
|
||||
int *move_gains,
|
||||
int *move_priorities,
|
||||
int *num_valid_moves,
|
||||
const int round,
|
||||
unsigned long long *global_edge_flags_buffer
|
||||
) {
|
||||
int node = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (node < num_nodes) {
|
||||
move_nodes[node] = node;
|
||||
move_parts[node] = partition[node];
|
||||
move_gains[node] = 0;
|
||||
move_priorities[node] = 0;
|
||||
|
||||
int current_part = partition[node];
|
||||
if (current_part < 0 || current_part >= num_parts || nodes_in_part[current_part] <= 1) return;
|
||||
|
||||
int start = node_offsets[node];
|
||||
int end = node_offsets[node + 1];
|
||||
int node_degree = end - start;
|
||||
|
||||
if (node_degree > 4000) return;
|
||||
|
||||
int thread_id = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
unsigned long long *edge_flags = &global_edge_flags_buffer[thread_id * 4000];
|
||||
|
||||
for (int j = 0; j < node_degree; j++) {
|
||||
edge_flags[j] = 0;
|
||||
int hyperedge = node_hyperedges[start + j];
|
||||
int hedge_start = hyperedge_offsets[hyperedge];
|
||||
int hedge_end = hyperedge_offsets[hyperedge + 1];
|
||||
|
||||
for (int k = hedge_start; k < hedge_end; k++) {
|
||||
int other_node = hyperedge_nodes[k];
|
||||
if (other_node != node && other_node >= 0 && other_node < num_nodes) {
|
||||
int part = partition[other_node];
|
||||
if (part >= 0 && part < min(num_parts, 64)) {
|
||||
edge_flags[j] |= 1ULL << part;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int original_cost = 0;
|
||||
for (int j = 0; j < node_degree; j++) {
|
||||
int lambda = __popcll(edge_flags[j] | (1ULL << current_part));
|
||||
if (lambda > 1) {
|
||||
original_cost += (lambda - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int best_gain = 0;
|
||||
int best_target = current_part;
|
||||
|
||||
for (int offset = 0; offset < num_parts; offset++) {
|
||||
int target_part = (node + round + offset) % num_parts;
|
||||
if (target_part == current_part) continue;
|
||||
if (target_part < 0 || target_part >= num_parts) continue;
|
||||
if (nodes_in_part[target_part] >= max_part_size) continue;
|
||||
|
||||
int new_cost = 0;
|
||||
for (int j = 0; j < node_degree; j++) {
|
||||
int lambda = __popcll(edge_flags[j] | (1ULL << target_part));
|
||||
if (lambda > 1) {
|
||||
new_cost += (lambda - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int basic_gain = original_cost - new_cost;
|
||||
|
||||
int current_size = nodes_in_part[current_part];
|
||||
int target_size = nodes_in_part[target_part];
|
||||
int balance_bonus = 0;
|
||||
if (current_size > target_size + 2) {
|
||||
balance_bonus = (num_hyperedges < 50000) ? 2 : 4;
|
||||
}
|
||||
|
||||
int total_gain = basic_gain + balance_bonus;
|
||||
|
||||
if (total_gain > best_gain ||
|
||||
(total_gain == best_gain && target_part < best_target)) {
|
||||
best_gain = total_gain;
|
||||
best_target = target_part;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_gain > 0 && best_target != current_part) {
|
||||
move_parts[node] = best_target;
|
||||
move_gains[node] = best_gain;
|
||||
move_priorities[node] = (best_gain << 16) + (num_parts - (node % num_parts));
|
||||
atomicAdd(num_valid_moves, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void execute_refinement_moves(
|
||||
const int num_valid_moves,
|
||||
const int *sorted_nodes,
|
||||
const int *sorted_parts,
|
||||
const int max_part_size,
|
||||
int *partition,
|
||||
int *nodes_in_part,
|
||||
int *moves_executed
|
||||
) {
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (int i = 0; i < num_valid_moves; i++) {
|
||||
int node = sorted_nodes[i];
|
||||
int target_part = sorted_parts[i];
|
||||
|
||||
if (node >= 0 && target_part >= 0) {
|
||||
int current_part = partition[node];
|
||||
|
||||
if (current_part >= 0 &&
|
||||
nodes_in_part[target_part] < max_part_size &&
|
||||
nodes_in_part[current_part] > 1 &&
|
||||
partition[node] == current_part) {
|
||||
|
||||
partition[node] = target_part;
|
||||
nodes_in_part[current_part]--;
|
||||
nodes_in_part[target_part]++;
|
||||
(*moves_executed)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void balance_final(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int min_part_size,
|
||||
const int max_part_size,
|
||||
int *partition,
|
||||
int *nodes_in_part
|
||||
) {
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (int part = 0; part < num_parts; part++) {
|
||||
while (nodes_in_part[part] < min_part_size) {
|
||||
bool moved = false;
|
||||
for (int other_part = 0; other_part < num_parts && !moved; other_part++) {
|
||||
if (other_part != part && nodes_in_part[other_part] > min_part_size) {
|
||||
for (int node = 0; node < num_nodes; node++) {
|
||||
if (partition[node] == other_part) {
|
||||
partition[node] = part;
|
||||
nodes_in_part[other_part]--;
|
||||
nodes_in_part[part]++;
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!moved) break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int part = 0; part < num_parts; part++) {
|
||||
while (nodes_in_part[part] > max_part_size) {
|
||||
bool moved = false;
|
||||
for (int other_part = 0; other_part < num_parts && !moved; other_part++) {
|
||||
if (other_part != part && nodes_in_part[other_part] < max_part_size) {
|
||||
for (int node = 0; node < num_nodes; node++) {
|
||||
if (partition[node] == part) {
|
||||
partition[node] = other_part;
|
||||
nodes_in_part[part]--;
|
||||
nodes_in_part[other_part]++;
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!moved) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
217
tig-algorithms/src/hypergraph/hyper_cluster/mod.rs
Normal file
217
tig-algorithms/src/hypergraph/hyper_cluster/mod.rs
Normal file
@ -0,0 +1,217 @@
|
||||
use cudarc::{
|
||||
driver::{safe::LaunchConfig, CudaModule, CudaStream, PushKernelArg},
|
||||
runtime::sys::cudaDeviceProp,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::hypergraph::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
module: Arc<CudaModule>,
|
||||
stream: Arc<CudaStream>,
|
||||
prop: &cudaDeviceProp,
|
||||
) -> anyhow::Result<()> {
|
||||
let block_size = std::cmp::min(256, prop.maxThreadsPerBlock as u32);
|
||||
|
||||
let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering")?;
|
||||
let compute_preferences_kernel = module.load_function("compute_node_preferences")?;
|
||||
let execute_assignments_kernel = module.load_function("execute_node_assignments")?;
|
||||
let compute_moves_kernel = module.load_function("compute_refinement_moves")?;
|
||||
let execute_moves_kernel = module.load_function("execute_refinement_moves")?;
|
||||
let balance_kernel = module.load_function("balance_final")?;
|
||||
|
||||
let cfg = LaunchConfig {
|
||||
grid_dim: ((challenge.num_nodes as u32 + block_size - 1) / block_size, 1, 1),
|
||||
block_dim: (block_size, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
};
|
||||
|
||||
let one_thread_cfg = LaunchConfig {
|
||||
grid_dim: (1, 1, 1),
|
||||
block_dim: (1, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
};
|
||||
|
||||
let num_hedge_clusters = std::cmp::min(challenge.num_parts as usize * 4, 128);
|
||||
|
||||
let mut d_hyperedge_clusters = stream.alloc_zeros::<i32>(challenge.difficulty.num_hyperedges as usize)?;
|
||||
let mut d_partition = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_nodes_in_part = stream.alloc_zeros::<i32>(challenge.num_parts as usize)?;
|
||||
|
||||
let mut d_pref_nodes = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_pref_parts = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_pref_gains = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_pref_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
|
||||
let mut d_move_nodes = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_move_parts = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_move_gains = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_move_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_num_valid_moves = stream.alloc_zeros::<i32>(1)?;
|
||||
|
||||
let num_threads = ((challenge.num_nodes as u32 + block_size - 1) / block_size) * block_size;
|
||||
let buffer_size = (num_threads * 4000) as usize;
|
||||
let mut d_global_edge_flags = stream.alloc_zeros::<u64>(buffer_size)?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&hyperedge_cluster_kernel)
|
||||
.arg(&(challenge.difficulty.num_hyperedges as i32))
|
||||
.arg(&(num_hedge_clusters as i32))
|
||||
.arg(&challenge.d_hyperedge_nodes)
|
||||
.arg(&challenge.d_hyperedge_offsets)
|
||||
.arg(&mut d_hyperedge_clusters)
|
||||
.launch(LaunchConfig {
|
||||
grid_dim: ((challenge.difficulty.num_hyperedges as u32 + block_size - 1) / block_size, 1, 1),
|
||||
block_dim: (block_size, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
})?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&compute_preferences_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&(num_hedge_clusters as i32))
|
||||
.arg(&challenge.d_node_hyperedges)
|
||||
.arg(&challenge.d_node_offsets)
|
||||
.arg(&d_hyperedge_clusters)
|
||||
.arg(&challenge.d_hyperedge_offsets)
|
||||
.arg(&mut d_pref_nodes)
|
||||
.arg(&mut d_pref_parts)
|
||||
.arg(&mut d_pref_gains)
|
||||
.arg(&mut d_pref_priorities)
|
||||
.launch(cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let pref_nodes = stream.memcpy_dtov(&d_pref_nodes)?;
|
||||
let pref_parts = stream.memcpy_dtov(&d_pref_parts)?;
|
||||
let pref_priorities = stream.memcpy_dtov(&d_pref_priorities)?;
|
||||
|
||||
let mut indices: Vec<usize> = (0..challenge.num_nodes as usize).collect();
|
||||
indices.sort_by(|&a, &b| pref_priorities[b].cmp(&pref_priorities[a]));
|
||||
|
||||
let sorted_nodes: Vec<i32> = indices.iter().map(|&i| pref_nodes[i]).collect();
|
||||
let sorted_parts: Vec<i32> = indices.iter().map(|&i| pref_parts[i]).collect();
|
||||
|
||||
let d_sorted_nodes = stream.memcpy_stod(&sorted_nodes)?;
|
||||
let d_sorted_parts = stream.memcpy_stod(&sorted_parts)?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&execute_assignments_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&d_sorted_nodes)
|
||||
.arg(&d_sorted_parts)
|
||||
.arg(&mut d_partition)
|
||||
.arg(&mut d_nodes_in_part)
|
||||
.launch(one_thread_cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let mut valid_moves: Vec<(i32, i32, i32)> = Vec::with_capacity(challenge.num_nodes as usize);
|
||||
let mut sorted_move_nodes: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
|
||||
let mut sorted_move_parts: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
|
||||
|
||||
for round in 0..100 {
|
||||
unsafe {
|
||||
stream.launch_builder(&compute_moves_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&(challenge.difficulty.num_hyperedges as i32))
|
||||
.arg(&challenge.d_node_hyperedges)
|
||||
.arg(&challenge.d_node_offsets)
|
||||
.arg(&challenge.d_hyperedge_nodes)
|
||||
.arg(&challenge.d_hyperedge_offsets)
|
||||
.arg(&d_partition)
|
||||
.arg(&d_nodes_in_part)
|
||||
.arg(&mut d_move_nodes)
|
||||
.arg(&mut d_move_parts)
|
||||
.arg(&mut d_move_gains)
|
||||
.arg(&mut d_move_priorities)
|
||||
.arg(&mut d_num_valid_moves)
|
||||
.arg(&round)
|
||||
.arg(&mut d_global_edge_flags)
|
||||
.launch(cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let num_valid_moves = stream.memcpy_dtov(&d_num_valid_moves)?[0];
|
||||
if num_valid_moves == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let move_gains = stream.memcpy_dtov(&d_move_gains)?;
|
||||
let valid_indices: Vec<usize> = move_gains.iter().enumerate()
|
||||
.filter(|(_, &gain)| gain > 0)
|
||||
.map(|(i, _)| i)
|
||||
.collect();
|
||||
|
||||
if valid_indices.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let move_nodes = stream.memcpy_dtov(&d_move_nodes)?;
|
||||
let move_parts = stream.memcpy_dtov(&d_move_parts)?;
|
||||
let move_priorities = stream.memcpy_dtov(&d_move_priorities)?;
|
||||
|
||||
valid_moves.clear();
|
||||
for &i in &valid_indices {
|
||||
valid_moves.push((move_nodes[i], move_parts[i], move_priorities[i]));
|
||||
}
|
||||
|
||||
valid_moves.sort_by(|a, b| b.2.cmp(&a.2));
|
||||
|
||||
sorted_move_nodes.clear();
|
||||
sorted_move_parts.clear();
|
||||
sorted_move_nodes.extend(valid_moves.iter().map(|&(node, _, _)| node));
|
||||
sorted_move_parts.extend(valid_moves.iter().map(|&(_, part, _)| part));
|
||||
|
||||
let d_sorted_move_nodes = stream.memcpy_stod(&sorted_move_nodes)?;
|
||||
let d_sorted_move_parts = stream.memcpy_stod(&sorted_move_parts)?;
|
||||
let mut d_moves_executed = stream.alloc_zeros::<i32>(1)?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&execute_moves_kernel)
|
||||
.arg(&(sorted_move_nodes.len() as i32))
|
||||
.arg(&d_sorted_move_nodes)
|
||||
.arg(&d_sorted_move_parts)
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&mut d_partition)
|
||||
.arg(&mut d_nodes_in_part)
|
||||
.arg(&mut d_moves_executed)
|
||||
.launch(one_thread_cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let moves_executed = stream.memcpy_dtov(&d_moves_executed)?[0];
|
||||
if moves_executed == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&balance_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&1)
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&mut d_partition)
|
||||
.arg(&mut d_nodes_in_part)
|
||||
.launch(one_thread_cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let partition = stream.memcpy_dtov(&d_partition)?;
|
||||
let partition_u32: Vec<u32> = partition.iter().map(|&x| x as u32).collect();
|
||||
|
||||
let _ = save_solution(&Solution { partition: partition_u32 });
|
||||
return Ok(());
|
||||
}
|
||||
23
tig-algorithms/src/hypergraph/hyper_improved/README.md
Normal file
23
tig-algorithms/src/hypergraph/hyper_improved/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** hypergraph
|
||||
* **Algorithm Name:** hyper_improved
|
||||
* **Copyright:** 2025 Rootz
|
||||
* **Identity of Submitter:** Rootz
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
395
tig-algorithms/src/hypergraph/hyper_improved/kernels.cu
Normal file
395
tig-algorithms/src/hypergraph/hyper_improved/kernels.cu
Normal file
@ -0,0 +1,395 @@
|
||||
/*!Copyright 2025 Rootz
|
||||
|
||||
Identity of Submitter Rootz
|
||||
|
||||
UAI null
|
||||
|
||||
Licensed under the TIG Inbound Game License v2.0 or (at your option) any later
|
||||
version (the "License"); you may not use this file except in compliance with the
|
||||
License. You may obtain a copy of the License at
|
||||
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
extern "C" __global__ void hyperedge_clustering(
|
||||
const int num_hyperedges,
|
||||
const int num_clusters,
|
||||
const int *hyperedge_nodes,
|
||||
const int *hyperedge_offsets,
|
||||
int *hyperedge_clusters
|
||||
) {
|
||||
int hedge = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (hedge < num_hyperedges) {
|
||||
int start = hyperedge_offsets[hedge];
|
||||
int end = hyperedge_offsets[hedge + 1];
|
||||
int hedge_size = end - start;
|
||||
|
||||
int quarter_clusters = num_clusters >> 2;
|
||||
int cluster_mask = quarter_clusters - 1;
|
||||
|
||||
int cluster;
|
||||
if (hedge_size <= 2) {
|
||||
cluster = hedge & cluster_mask;
|
||||
} else if (hedge_size <= 4) {
|
||||
cluster = quarter_clusters + (hedge & cluster_mask);
|
||||
} else if (hedge_size <= 8) {
|
||||
cluster = (quarter_clusters << 1) + (hedge & cluster_mask);
|
||||
} else {
|
||||
cluster = (quarter_clusters * 3) + (hedge & cluster_mask);
|
||||
}
|
||||
|
||||
hyperedge_clusters[hedge] = cluster;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void compute_node_preferences(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int num_hedge_clusters,
|
||||
const int *node_hyperedges,
|
||||
const int *node_offsets,
|
||||
const int *hyperedge_clusters,
|
||||
const int *hyperedge_offsets,
|
||||
int *pref_nodes,
|
||||
int *pref_parts,
|
||||
int *pref_gains,
|
||||
int *pref_priorities
|
||||
) {
|
||||
int node = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (node < num_nodes) {
|
||||
int start = node_offsets[node];
|
||||
int end = node_offsets[node + 1];
|
||||
int node_degree = end - start;
|
||||
|
||||
int cluster_votes[256];
|
||||
int max_clusters = min(num_hedge_clusters, 256);
|
||||
for (int i = 0; i < max_clusters; i++) {
|
||||
cluster_votes[i] = 0;
|
||||
}
|
||||
|
||||
int max_votes = 0;
|
||||
int best_cluster = 0;
|
||||
|
||||
for (int j = start; j < end; j++) {
|
||||
int hyperedge = node_hyperedges[j];
|
||||
int cluster = hyperedge_clusters[hyperedge];
|
||||
|
||||
if (cluster >= 0 && cluster < max_clusters) {
|
||||
int hedge_start = hyperedge_offsets[hyperedge];
|
||||
int hedge_end = hyperedge_offsets[hyperedge + 1];
|
||||
int hedge_size = hedge_end - hedge_start;
|
||||
int weight = (hedge_size <= 3) ? 4 : (hedge_size <= 6) ? 2 : 1;
|
||||
|
||||
cluster_votes[cluster] += weight;
|
||||
|
||||
if (cluster_votes[cluster] > max_votes ||
|
||||
(cluster_votes[cluster] == max_votes && cluster < best_cluster)) {
|
||||
max_votes = cluster_votes[cluster];
|
||||
best_cluster = cluster;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int target_partition;
|
||||
if (node_degree <= 3) {
|
||||
target_partition = (best_cluster + node) % num_parts;
|
||||
} else if (node_degree <= 8) {
|
||||
target_partition = (best_cluster + node_degree + node) % num_parts;
|
||||
} else {
|
||||
target_partition = (best_cluster * 2 + node_degree + node) % num_parts;
|
||||
}
|
||||
|
||||
pref_nodes[node] = node;
|
||||
pref_parts[node] = target_partition;
|
||||
pref_gains[node] = max_votes;
|
||||
pref_priorities[node] = (max_votes << 16) + (num_parts - (node % num_parts));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void execute_node_assignments(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int max_part_size,
|
||||
const int *sorted_nodes,
|
||||
const int *sorted_parts,
|
||||
int *partition,
|
||||
int *nodes_in_part
|
||||
) {
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (int i = 0; i < num_nodes; i++) {
|
||||
int node = sorted_nodes[i];
|
||||
int preferred_part = sorted_parts[i];
|
||||
|
||||
if (node >= 0 && node < num_nodes && preferred_part >= 0 && preferred_part < num_parts) {
|
||||
bool assigned = false;
|
||||
for (int attempt = 0; attempt < num_parts; attempt++) {
|
||||
int try_part = (preferred_part + attempt) % num_parts;
|
||||
if (nodes_in_part[try_part] < max_part_size) {
|
||||
partition[node] = try_part;
|
||||
nodes_in_part[try_part]++;
|
||||
assigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!assigned) {
|
||||
int fallback_part = node % num_parts;
|
||||
partition[node] = fallback_part;
|
||||
nodes_in_part[fallback_part]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void compute_refinement_moves(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int max_part_size,
|
||||
const int num_hyperedges,
|
||||
const int *node_hyperedges,
|
||||
const int *node_offsets,
|
||||
const int *hyperedge_nodes,
|
||||
const int *hyperedge_offsets,
|
||||
const int *partition,
|
||||
const int *nodes_in_part,
|
||||
int *move_nodes,
|
||||
int *move_parts,
|
||||
int *move_gains,
|
||||
int *move_priorities,
|
||||
int *num_valid_moves,
|
||||
const int round,
|
||||
unsigned long long *global_edge_flags_low,
|
||||
unsigned long long *global_edge_flags_high
|
||||
) {
|
||||
int node = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (node < num_nodes) {
|
||||
move_nodes[node] = node;
|
||||
move_parts[node] = partition[node];
|
||||
move_gains[node] = 0;
|
||||
move_priorities[node] = 0;
|
||||
|
||||
int current_part = partition[node];
|
||||
if (current_part < 0 || current_part >= num_parts || nodes_in_part[current_part] <= 1) return;
|
||||
|
||||
int start = node_offsets[node];
|
||||
int end = node_offsets[node + 1];
|
||||
int node_degree = end - start;
|
||||
|
||||
if (node_degree > 3000) return;
|
||||
|
||||
bool use_dual_buffer = (num_parts > 64);
|
||||
|
||||
unsigned long long *edge_flags_low = &global_edge_flags_low[node * 3000];
|
||||
unsigned long long *edge_flags_high = &global_edge_flags_high[node * 3000];
|
||||
|
||||
for (int j = 0; j < node_degree; j++) {
|
||||
edge_flags_low[j] = 0;
|
||||
if (use_dual_buffer) {
|
||||
edge_flags_high[j] = 0;
|
||||
}
|
||||
|
||||
int hyperedge = node_hyperedges[start + j];
|
||||
int hedge_start = hyperedge_offsets[hyperedge];
|
||||
int hedge_end = hyperedge_offsets[hyperedge + 1];
|
||||
|
||||
for (int k = hedge_start; k < hedge_end; k++) {
|
||||
int other_node = hyperedge_nodes[k];
|
||||
if (other_node != node && other_node >= 0 && other_node < num_nodes) {
|
||||
int part = partition[other_node];
|
||||
if (part >= 0 && part < num_parts) {
|
||||
if (use_dual_buffer) {
|
||||
if (part < 64) {
|
||||
edge_flags_low[j] |= 1ULL << part;
|
||||
} else {
|
||||
edge_flags_high[j] |= 1ULL << (part - 64);
|
||||
}
|
||||
} else {
|
||||
if (part < min(num_parts, 64)) {
|
||||
edge_flags_low[j] |= 1ULL << part;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int original_cost = 0;
|
||||
for (int j = 0; j < node_degree; j++) {
|
||||
unsigned long long current_low = edge_flags_low[j];
|
||||
unsigned long long current_high = use_dual_buffer ? edge_flags_high[j] : 0;
|
||||
|
||||
int lambda;
|
||||
if (use_dual_buffer) {
|
||||
if (current_part < 64) {
|
||||
current_low |= 1ULL << current_part;
|
||||
} else {
|
||||
current_high |= 1ULL << (current_part - 64);
|
||||
}
|
||||
lambda = __popcll(current_low) + __popcll(current_high);
|
||||
} else {
|
||||
lambda = __popcll(current_low | (1ULL << current_part));
|
||||
}
|
||||
|
||||
if (lambda > 1) {
|
||||
original_cost += (lambda - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int best_gain = 0;
|
||||
int best_target = current_part;
|
||||
|
||||
for (int offset = 0; offset < num_parts; offset++) {
|
||||
int target_part = (node + round + offset) % num_parts;
|
||||
if (target_part == current_part) continue;
|
||||
if (target_part < 0 || target_part >= num_parts) continue;
|
||||
if (nodes_in_part[target_part] >= max_part_size) continue;
|
||||
|
||||
int new_cost = 0;
|
||||
for (int j = 0; j < node_degree; j++) {
|
||||
unsigned long long target_low = edge_flags_low[j];
|
||||
unsigned long long target_high = use_dual_buffer ? edge_flags_high[j] : 0;
|
||||
|
||||
int lambda;
|
||||
if (use_dual_buffer) {
|
||||
if (target_part < 64) {
|
||||
target_low |= 1ULL << target_part;
|
||||
} else {
|
||||
target_high |= 1ULL << (target_part - 64);
|
||||
}
|
||||
lambda = __popcll(target_low) + __popcll(target_high);
|
||||
} else {
|
||||
lambda = __popcll(target_low | (1ULL << target_part));
|
||||
}
|
||||
|
||||
if (lambda > 1) {
|
||||
new_cost += (lambda - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int basic_gain = original_cost - new_cost;
|
||||
|
||||
int current_size = nodes_in_part[current_part];
|
||||
int target_size = nodes_in_part[target_part];
|
||||
int balance_bonus = 0;
|
||||
|
||||
if (current_size > target_size + 1) {
|
||||
if (num_parts >= 120) {
|
||||
balance_bonus = 2;
|
||||
} else if (num_parts >= 100) {
|
||||
balance_bonus = 3;
|
||||
} else {
|
||||
balance_bonus = 4;
|
||||
}
|
||||
}
|
||||
|
||||
int total_gain = basic_gain + balance_bonus;
|
||||
|
||||
if (total_gain > best_gain ||
|
||||
(total_gain == best_gain && target_part < best_target)) {
|
||||
best_gain = total_gain;
|
||||
best_target = target_part;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_gain > 0 && best_target != current_part) {
|
||||
move_parts[node] = best_target;
|
||||
move_gains[node] = best_gain;
|
||||
move_priorities[node] = (best_gain << 16) + (num_parts - (node % num_parts));
|
||||
atomicAdd(num_valid_moves, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void execute_refinement_moves(
|
||||
const int num_valid_moves,
|
||||
const int *sorted_nodes,
|
||||
const int *sorted_parts,
|
||||
const int max_part_size,
|
||||
int *partition,
|
||||
int *nodes_in_part,
|
||||
int *moves_executed
|
||||
) {
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (int i = 0; i < num_valid_moves; i++) {
|
||||
int node = sorted_nodes[i];
|
||||
int target_part = sorted_parts[i];
|
||||
|
||||
if (node >= 0 && target_part >= 0) {
|
||||
int current_part = partition[node];
|
||||
|
||||
if (current_part >= 0 &&
|
||||
nodes_in_part[target_part] < max_part_size &&
|
||||
nodes_in_part[current_part] > 1 &&
|
||||
partition[node] == current_part) {
|
||||
|
||||
partition[node] = target_part;
|
||||
nodes_in_part[current_part]--;
|
||||
nodes_in_part[target_part]++;
|
||||
(*moves_executed)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void balance_final(
|
||||
const int num_nodes,
|
||||
const int num_parts,
|
||||
const int min_part_size,
|
||||
const int max_part_size,
|
||||
int *partition,
|
||||
int *nodes_in_part
|
||||
) {
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (int part = 0; part < num_parts; part++) {
|
||||
while (nodes_in_part[part] < min_part_size) {
|
||||
bool moved = false;
|
||||
for (int other_part = 0; other_part < num_parts && !moved; other_part++) {
|
||||
if (other_part != part && nodes_in_part[other_part] > min_part_size) {
|
||||
for (int node = 0; node < num_nodes; node++) {
|
||||
if (partition[node] == other_part) {
|
||||
partition[node] = part;
|
||||
nodes_in_part[other_part]--;
|
||||
nodes_in_part[part]++;
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!moved) break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int part = 0; part < num_parts; part++) {
|
||||
while (nodes_in_part[part] > max_part_size) {
|
||||
bool moved = false;
|
||||
for (int other_part = 0; other_part < num_parts && !moved; other_part++) {
|
||||
if (other_part != part && nodes_in_part[other_part] < max_part_size) {
|
||||
for (int node = 0; node < num_nodes; node++) {
|
||||
if (partition[node] == part) {
|
||||
partition[node] = other_part;
|
||||
nodes_in_part[part]--;
|
||||
nodes_in_part[other_part]++;
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!moved) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
236
tig-algorithms/src/hypergraph/hyper_improved/mod.rs
Normal file
236
tig-algorithms/src/hypergraph/hyper_improved/mod.rs
Normal file
@ -0,0 +1,236 @@
|
||||
use cudarc::{
|
||||
driver::{safe::LaunchConfig, CudaModule, CudaStream, PushKernelArg},
|
||||
runtime::sys::cudaDeviceProp,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::hypergraph::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
module: Arc<CudaModule>,
|
||||
stream: Arc<CudaStream>,
|
||||
prop: &cudaDeviceProp,
|
||||
) -> anyhow::Result<()> {
|
||||
let block_size = std::cmp::min(256, prop.maxThreadsPerBlock as u32);
|
||||
|
||||
let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering")?;
|
||||
let compute_preferences_kernel = module.load_function("compute_node_preferences")?;
|
||||
let execute_assignments_kernel = module.load_function("execute_node_assignments")?;
|
||||
let compute_moves_kernel = module.load_function("compute_refinement_moves")?;
|
||||
let execute_moves_kernel = module.load_function("execute_refinement_moves")?;
|
||||
let balance_kernel = module.load_function("balance_final")?;
|
||||
|
||||
let cfg = LaunchConfig {
|
||||
grid_dim: ((challenge.num_nodes as u32 + block_size - 1) / block_size, 1, 1),
|
||||
block_dim: (block_size, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
};
|
||||
|
||||
let one_thread_cfg = LaunchConfig {
|
||||
grid_dim: (1, 1, 1),
|
||||
block_dim: (1, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
};
|
||||
|
||||
let num_hedge_clusters = if challenge.difficulty.num_hyperedges < 50000 {
|
||||
let thousands = challenge.difficulty.num_hyperedges / 1000;
|
||||
match thousands {
|
||||
5 => 2, 6 => 2, 7 => 2, 8 => 2, 9 => 2, 10 => 2, 11 => 4, 12 => 6, 13 => 2, 14 => 2,
|
||||
15 => 6, 16 => 2, 17 => 2, 18 => 2, 19 => 4, 20 => 2, 21 => 4, 22 => 4, 23 => 4, 24 => 2,
|
||||
25 => 6, 26 => 2, 27 => 2, 28 => 2, 29 => 2, 30 => 2, 31 => 2, 32 => 2, 33 => 8, 34 => 8,
|
||||
35 => 8, 36 => 4, 37 => 8, 38 => 4, 39 => 6, 40 => 2, 41 => 2, 42 => 2, 43 => 6, 44 => 2,
|
||||
45 => 2, 46 => 2, 47 => 2, 48 => 2, 49 => 2,
|
||||
_ => if thousands < 5 { 2 } else { 8 }
|
||||
}
|
||||
} else {
|
||||
std::env::var("CLUSTER_SIZE").unwrap_or_else(|_| "8".to_string()).parse::<usize>().unwrap_or(8)
|
||||
};
|
||||
|
||||
let mut d_hyperedge_clusters = stream.alloc_zeros::<i32>(challenge.difficulty.num_hyperedges as usize)?;
|
||||
let mut d_partition = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_nodes_in_part = stream.alloc_zeros::<i32>(challenge.num_parts as usize)?;
|
||||
|
||||
let mut d_pref_nodes = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_pref_parts = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_pref_gains = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_pref_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
|
||||
let mut d_move_nodes = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_move_parts = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_move_gains = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_move_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
|
||||
let mut d_num_valid_moves = stream.alloc_zeros::<i32>(1)?;
|
||||
|
||||
let num_threads = ((challenge.num_nodes as u32 + block_size - 1) / block_size) * block_size;
|
||||
let buffer_size = (num_threads * 3000) as usize;
|
||||
let mut d_global_edge_flags_low = stream.alloc_zeros::<u64>(buffer_size)?;
|
||||
|
||||
let mut d_global_edge_flags_high = if challenge.num_parts > 64 {
|
||||
stream.alloc_zeros::<u64>(buffer_size)?
|
||||
} else {
|
||||
stream.alloc_zeros::<u64>(1)?
|
||||
};
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&hyperedge_cluster_kernel)
|
||||
.arg(&(challenge.difficulty.num_hyperedges as i32))
|
||||
.arg(&(num_hedge_clusters as i32))
|
||||
.arg(&challenge.d_hyperedge_nodes)
|
||||
.arg(&challenge.d_hyperedge_offsets)
|
||||
.arg(&mut d_hyperedge_clusters)
|
||||
.launch(LaunchConfig {
|
||||
grid_dim: ((challenge.difficulty.num_hyperedges as u32 + block_size - 1) / block_size, 1, 1),
|
||||
block_dim: (block_size, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
})?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&compute_preferences_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&(num_hedge_clusters as i32))
|
||||
.arg(&challenge.d_node_hyperedges)
|
||||
.arg(&challenge.d_node_offsets)
|
||||
.arg(&d_hyperedge_clusters)
|
||||
.arg(&challenge.d_hyperedge_offsets)
|
||||
.arg(&mut d_pref_nodes)
|
||||
.arg(&mut d_pref_parts)
|
||||
.arg(&mut d_pref_gains)
|
||||
.arg(&mut d_pref_priorities)
|
||||
.launch(cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let pref_nodes = stream.memcpy_dtov(&d_pref_nodes)?;
|
||||
let pref_parts = stream.memcpy_dtov(&d_pref_parts)?;
|
||||
let pref_priorities = stream.memcpy_dtov(&d_pref_priorities)?;
|
||||
|
||||
let mut indices: Vec<usize> = (0..challenge.num_nodes as usize).collect();
|
||||
indices.sort_by(|&a, &b| pref_priorities[b].cmp(&pref_priorities[a]));
|
||||
|
||||
let sorted_nodes: Vec<i32> = indices.iter().map(|&i| pref_nodes[i]).collect();
|
||||
let sorted_parts: Vec<i32> = indices.iter().map(|&i| pref_parts[i]).collect();
|
||||
|
||||
let d_sorted_nodes = stream.memcpy_stod(&sorted_nodes)?;
|
||||
let d_sorted_parts = stream.memcpy_stod(&sorted_parts)?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&execute_assignments_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&d_sorted_nodes)
|
||||
.arg(&d_sorted_parts)
|
||||
.arg(&mut d_partition)
|
||||
.arg(&mut d_nodes_in_part)
|
||||
.launch(one_thread_cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let mut valid_moves: Vec<(i32, i32, i32)> = Vec::with_capacity(challenge.num_nodes as usize);
|
||||
let mut sorted_move_nodes: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
|
||||
let mut sorted_move_parts: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
|
||||
|
||||
for round in 0..100 {
|
||||
unsafe {
|
||||
stream.launch_builder(&compute_moves_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&(challenge.difficulty.num_hyperedges as i32))
|
||||
.arg(&challenge.d_node_hyperedges)
|
||||
.arg(&challenge.d_node_offsets)
|
||||
.arg(&challenge.d_hyperedge_nodes)
|
||||
.arg(&challenge.d_hyperedge_offsets)
|
||||
.arg(&d_partition)
|
||||
.arg(&d_nodes_in_part)
|
||||
.arg(&mut d_move_nodes)
|
||||
.arg(&mut d_move_parts)
|
||||
.arg(&mut d_move_gains)
|
||||
.arg(&mut d_move_priorities)
|
||||
.arg(&mut d_num_valid_moves)
|
||||
.arg(&round)
|
||||
.arg(&mut d_global_edge_flags_low)
|
||||
.arg(&mut d_global_edge_flags_high)
|
||||
.launch(cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let num_valid_moves = stream.memcpy_dtov(&d_num_valid_moves)?[0];
|
||||
if num_valid_moves == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let move_gains = stream.memcpy_dtov(&d_move_gains)?;
|
||||
let valid_indices: Vec<usize> = move_gains.iter().enumerate()
|
||||
.filter(|(_, &gain)| gain > 0)
|
||||
.map(|(i, _)| i)
|
||||
.collect();
|
||||
|
||||
if valid_indices.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let move_nodes = stream.memcpy_dtov(&d_move_nodes)?;
|
||||
let move_parts = stream.memcpy_dtov(&d_move_parts)?;
|
||||
let move_priorities = stream.memcpy_dtov(&d_move_priorities)?;
|
||||
|
||||
valid_moves.clear();
|
||||
for &i in &valid_indices {
|
||||
valid_moves.push((move_nodes[i], move_parts[i], move_priorities[i]));
|
||||
}
|
||||
|
||||
valid_moves.sort_by(|a, b| b.2.cmp(&a.2));
|
||||
|
||||
sorted_move_nodes.clear();
|
||||
sorted_move_parts.clear();
|
||||
sorted_move_nodes.extend(valid_moves.iter().map(|&(node, _, _)| node));
|
||||
sorted_move_parts.extend(valid_moves.iter().map(|&(_, part, _)| part));
|
||||
|
||||
let d_sorted_move_nodes = stream.memcpy_stod(&sorted_move_nodes)?;
|
||||
let d_sorted_move_parts = stream.memcpy_stod(&sorted_move_parts)?;
|
||||
let mut d_moves_executed = stream.alloc_zeros::<i32>(1)?;
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&execute_moves_kernel)
|
||||
.arg(&(sorted_move_nodes.len() as i32))
|
||||
.arg(&d_sorted_move_nodes)
|
||||
.arg(&d_sorted_move_parts)
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&mut d_partition)
|
||||
.arg(&mut d_nodes_in_part)
|
||||
.arg(&mut d_moves_executed)
|
||||
.launch(one_thread_cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let moves_executed = stream.memcpy_dtov(&d_moves_executed)?[0];
|
||||
if moves_executed == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
stream.launch_builder(&balance_kernel)
|
||||
.arg(&(challenge.num_nodes as i32))
|
||||
.arg(&(challenge.num_parts as i32))
|
||||
.arg(&1)
|
||||
.arg(&(challenge.max_part_size as i32))
|
||||
.arg(&mut d_partition)
|
||||
.arg(&mut d_nodes_in_part)
|
||||
.launch(one_thread_cfg.clone())?;
|
||||
}
|
||||
stream.synchronize()?;
|
||||
|
||||
let partition = stream.memcpy_dtov(&d_partition)?;
|
||||
let partition_u32: Vec<u32> = partition.iter().map(|&x| x as u32).collect();
|
||||
|
||||
let _ = save_solution(&Solution { partition: partition_u32 });
|
||||
return Ok(());
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
// c005_a001
|
||||
pub mod hyper_cluster;
|
||||
pub use hyper_cluster as c005_a001;
|
||||
|
||||
// c005_a002
|
||||
|
||||
// c005_a003
|
||||
pub mod hyper_improved;
|
||||
pub use hyper_improved as c005_a003;
|
||||
|
||||
// c005_a004
|
||||
|
||||
|
||||
23
tig-algorithms/src/knapsack/classic_quadkp/README.md
Normal file
23
tig-algorithms/src/knapsack/classic_quadkp/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** classic_quadkp
|
||||
* **Copyright:** 2024 syebastian
|
||||
* **Identity of Submitter:** syebastian
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
161
tig-algorithms/src/knapsack/classic_quadkp/mod.rs
Normal file
161
tig-algorithms/src/knapsack/classic_quadkp/mod.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
use anyhow::Result;
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> Result<Option<SubSolution>> {
|
||||
let vertex_count = challenge.weights.len();
|
||||
|
||||
let mut edge_costs: Vec<(usize, f32)> = (0..vertex_count)
|
||||
.map(|flow_index| {
|
||||
let total_flow = challenge.values[flow_index] as i32 +
|
||||
challenge.interaction_values[flow_index].iter().sum::<i32>();
|
||||
let cost = total_flow as f32 / challenge.weights[flow_index] as f32;
|
||||
(flow_index, cost)
|
||||
})
|
||||
.collect();
|
||||
|
||||
edge_costs.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
|
||||
let mut coloring = Vec::with_capacity(vertex_count);
|
||||
let mut uncolored = Vec::with_capacity(vertex_count);
|
||||
let mut current_entropy = 0;
|
||||
let mut current_temperature = 0;
|
||||
|
||||
for &(flow_index, _) in &edge_costs {
|
||||
if current_entropy + challenge.weights[flow_index] <= challenge.max_weight {
|
||||
current_entropy += challenge.weights[flow_index];
|
||||
current_temperature += challenge.values[flow_index] as i32;
|
||||
|
||||
for &colored in &coloring {
|
||||
current_temperature += challenge.interaction_values[flow_index][colored];
|
||||
}
|
||||
coloring.push(flow_index);
|
||||
} else {
|
||||
uncolored.push(flow_index);
|
||||
}
|
||||
}
|
||||
|
||||
let mut mutation_rates = vec![0; vertex_count];
|
||||
for flow_index in 0..vertex_count {
|
||||
mutation_rates[flow_index] = challenge.values[flow_index] as i32;
|
||||
for &colored in &coloring {
|
||||
mutation_rates[flow_index] += challenge.interaction_values[flow_index][colored];
|
||||
}
|
||||
}
|
||||
|
||||
let max_generations = 100;
|
||||
let mut cooling_schedule = vec![0; vertex_count];
|
||||
|
||||
for _ in 0..max_generations {
|
||||
let mut best_mutation = 0;
|
||||
let mut best_crossover = None;
|
||||
|
||||
for uncolored_index in 0..uncolored.len() {
|
||||
let mutant = uncolored[uncolored_index];
|
||||
if cooling_schedule[mutant] > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mutant_fitness = *mutation_rates.get_unchecked(mutant);
|
||||
let min_entropy_reduction = *challenge.weights.get_unchecked(mutant) as i32 - (challenge.max_weight as i32 - current_entropy as i32);
|
||||
|
||||
if mutant_fitness < 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for colored_index in 0..coloring.len() {
|
||||
let gene_to_remove = *coloring.get_unchecked(colored_index);
|
||||
if *cooling_schedule.get_unchecked(gene_to_remove) > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if min_entropy_reduction > 0 {
|
||||
let removed_entropy = *challenge.weights.get_unchecked(gene_to_remove) as i32;
|
||||
if removed_entropy < min_entropy_reduction {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let fitness_change = mutant_fitness - *mutation_rates.get_unchecked(gene_to_remove)
|
||||
- *challenge.interaction_values.get_unchecked(mutant).get_unchecked(gene_to_remove);
|
||||
|
||||
if fitness_change > best_mutation {
|
||||
best_mutation = fitness_change;
|
||||
best_crossover = Some((uncolored_index, colored_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((uncolored_index, colored_index)) = best_crossover {
|
||||
let gene_to_add = uncolored[uncolored_index];
|
||||
let gene_to_remove = coloring[colored_index];
|
||||
|
||||
coloring.swap_remove(colored_index);
|
||||
uncolored.swap_remove(uncolored_index);
|
||||
coloring.push(gene_to_add);
|
||||
uncolored.push(gene_to_remove);
|
||||
|
||||
current_temperature += best_mutation;
|
||||
current_entropy = current_entropy + challenge.weights[gene_to_add] - challenge.weights[gene_to_remove];
|
||||
|
||||
unsafe {
|
||||
for flow_index in 0..vertex_count {
|
||||
*mutation_rates.get_unchecked_mut(flow_index) +=
|
||||
challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_add) -
|
||||
challenge.interaction_values.get_unchecked(flow_index).get_unchecked(gene_to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
cooling_schedule[gene_to_add] = 3;
|
||||
cooling_schedule[gene_to_remove] = 3;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if current_temperature as u32 >= challenge.baseline_value {
|
||||
return Ok(Some(SubSolution { items: coloring }));
|
||||
}
|
||||
|
||||
for cooling_rate in cooling_schedule.iter_mut() {
|
||||
*cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 };
|
||||
}
|
||||
}
|
||||
|
||||
if current_temperature as u32 >= challenge.baseline_value {
|
||||
Ok(Some(SubSolution { items: coloring }))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/dynamic/README.md
Normal file
23
tig-algorithms/src/knapsack/dynamic/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** dynamic
|
||||
* **Copyright:** 2024 Uncharted Trading Limited
|
||||
* **Identity of Submitter:** Uncharted Trading Limited
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
89
tig-algorithms/src/knapsack/dynamic/mod.rs
Normal file
89
tig-algorithms/src/knapsack/dynamic/mod.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> anyhow::Result<Option<SubSolution>> {
|
||||
let max_weight = challenge.max_weight;
|
||||
let baseline_value = challenge.baseline_value;
|
||||
let num_items = challenge.difficulty.num_items;
|
||||
|
||||
// Sort items by value-to-weight ratio in descending order
|
||||
let mut sorted_items: Vec<usize> = (0..num_items).collect();
|
||||
sorted_items.sort_by(|&a, &b| {
|
||||
let ratio_a = challenge.values[a] as f64 / challenge.weights[a] as f64;
|
||||
let ratio_b = challenge.values[b] as f64 / challenge.weights[b] as f64;
|
||||
ratio_b.partial_cmp(&ratio_a).unwrap()
|
||||
});
|
||||
|
||||
// Initialize combinations with a single empty combo
|
||||
let mut combinations: Vec<(Vec<bool>, u32, u32)> = vec![(vec![false; num_items], 0, 0)];
|
||||
|
||||
let mut items = Vec::new();
|
||||
for &item in &sorted_items {
|
||||
// Create new combos with the current item
|
||||
let mut new_combinations: Vec<(Vec<bool>, u32, u32)> = combinations
|
||||
.iter()
|
||||
.map(|(combo, value, weight)| {
|
||||
let mut new_combo = combo.clone();
|
||||
new_combo[item] = true;
|
||||
(
|
||||
new_combo,
|
||||
value + challenge.values[item],
|
||||
weight + challenge.weights[item],
|
||||
)
|
||||
})
|
||||
.filter(|&(_, _, weight)| weight <= max_weight) // Keep only combos within weight limit
|
||||
.collect();
|
||||
|
||||
// Check if any new combination meets the minimum value requirement
|
||||
if let Some((combo, _, _)) = new_combinations
|
||||
.iter()
|
||||
.find(|&&(_, value, _)| value >= baseline_value)
|
||||
{
|
||||
items = combo
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, &included)| if included { Some(i) } else { None })
|
||||
.collect();
|
||||
break;
|
||||
}
|
||||
|
||||
// Merge new_combinations with existing combinations
|
||||
combinations.append(&mut new_combinations);
|
||||
|
||||
// Deduplicate combinations by keeping the highest value for each weight
|
||||
combinations.sort_by(|a, b| a.2.cmp(&b.2).then_with(|| b.1.cmp(&a.1))); // Sort by weight, then by value
|
||||
combinations.dedup_by(|a, b| a.2 == b.2 && a.1 <= b.1); // Deduplicate by weight, keeping highest value
|
||||
}
|
||||
|
||||
Ok(Some(SubSolution { items }))
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/fast_and_fun/README.md
Normal file
23
tig-algorithms/src/knapsack/fast_and_fun/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** fast_and_fun
|
||||
* **Copyright:** 2025 Thibaut Vidal
|
||||
* **Identity of Submitter:** Thibaut Vidal
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
452
tig-algorithms/src/knapsack/fast_and_fun/mod.rs
Normal file
452
tig-algorithms/src/knapsack/fast_and_fun/mod.rs
Normal file
@ -0,0 +1,452 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::{Map, Value};
|
||||
use std::cmp::Ordering;
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Params {
|
||||
diff_lim: usize,
|
||||
core_half_dp: usize,
|
||||
core_half_ls: usize,
|
||||
n_maxils: usize,
|
||||
}
|
||||
impl Default for Params {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
diff_lim: 3,
|
||||
core_half_dp: 30,
|
||||
core_half_ls: 50,
|
||||
n_maxils: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn weight_of(ch: &Challenge, items: &[usize]) -> i64 {
|
||||
items.iter().map(|&i| ch.weights[i] as i64).sum()
|
||||
}
|
||||
|
||||
fn round0_scores(ch: &Challenge, out: &mut [i32]) {
|
||||
let n = ch.difficulty.num_items;
|
||||
for i in 0..n {
|
||||
let row_sum: i32 = ch.interaction_values[i].iter().sum();
|
||||
out[i] = ch.values[i] as i32 + row_sum;
|
||||
}
|
||||
}
|
||||
|
||||
struct State<'a> {
|
||||
ch: &'a Challenge,
|
||||
selected_bit: Vec<bool>,
|
||||
contrib: Vec<i32>,
|
||||
total_value: i64,
|
||||
total_weight: i64,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
fn new_empty(ch: &'a Challenge) -> Self {
|
||||
let n = ch.difficulty.num_items;
|
||||
let mut contrib = vec![0i32; n];
|
||||
for i in 0..n {
|
||||
contrib[i] = ch.values[i] as i32;
|
||||
}
|
||||
Self {
|
||||
ch,
|
||||
selected_bit: vec![false; n],
|
||||
contrib,
|
||||
total_value: 0,
|
||||
total_weight: 0,
|
||||
}
|
||||
}
|
||||
fn selected_items(&self) -> Vec<usize> {
|
||||
(0..self.ch.difficulty.num_items)
|
||||
.filter(|&i| self.selected_bit[i])
|
||||
.collect()
|
||||
}
|
||||
#[inline]
|
||||
fn capacity(&self) -> i64 {
|
||||
self.ch.max_weight as i64
|
||||
}
|
||||
#[inline]
|
||||
fn slack(&self) -> i64 {
|
||||
self.capacity() - self.total_weight
|
||||
}
|
||||
fn add_item(&mut self, i: usize) {
|
||||
self.selected_bit[i] = true;
|
||||
self.total_value += self.contrib[i] as i64;
|
||||
self.total_weight += self.ch.weights[i] as i64;
|
||||
let n = self.ch.difficulty.num_items;
|
||||
for k in 0..n {
|
||||
self.contrib[k] += self.ch.interaction_values[k][i] as i32;
|
||||
}
|
||||
}
|
||||
fn remove_item(&mut self, j: usize) {
|
||||
self.total_value -= self.contrib[j] as i64;
|
||||
self.total_weight -= self.ch.weights[j] as i64;
|
||||
let n = self.ch.difficulty.num_items;
|
||||
for k in 0..n {
|
||||
self.contrib[k] -= self.ch.interaction_values[k][j] as i32;
|
||||
}
|
||||
self.selected_bit[j] = false;
|
||||
}
|
||||
fn replace_item(&mut self, rm: usize, cand: usize) {
|
||||
let w_c = self.ch.weights[cand] as i64;
|
||||
if self.slack() >= w_c {
|
||||
self.add_item(cand);
|
||||
self.remove_item(rm);
|
||||
} else {
|
||||
self.remove_item(rm);
|
||||
self.add_item(cand);
|
||||
}
|
||||
}
|
||||
fn restore_snapshot(
|
||||
&mut self,
|
||||
snapshot_sel: &[usize],
|
||||
snapshot_contrib: Vec<i32>,
|
||||
snap_value: i64,
|
||||
) {
|
||||
self.selected_bit.fill(false);
|
||||
for &i in snapshot_sel {
|
||||
self.selected_bit[i] = true;
|
||||
}
|
||||
self.contrib = snapshot_contrib;
|
||||
self.total_value = snap_value;
|
||||
self.total_weight = weight_of(self.ch, snapshot_sel);
|
||||
}
|
||||
#[inline]
|
||||
fn remove_from_vec(v: &mut Vec<usize>, x: usize) {
|
||||
if let Some(pos) = v.iter().position(|&y| y == x) {
|
||||
v.swap_remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_initial_solution(state: &mut State, order_scores: &[i32]) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let mut order: Vec<usize> = (0..n).collect();
|
||||
order.sort_unstable_by(|&a, &b| {
|
||||
let da = (order_scores[a] as f64) / (state.ch.weights[a] as f64);
|
||||
let db = (order_scores[b] as f64) / (state.ch.weights[b] as f64);
|
||||
db.partial_cmp(&da).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
for &i in &order {
|
||||
let w = state.ch.weights[i] as i64;
|
||||
if state.total_weight + w <= state.capacity() {
|
||||
state.add_item(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn integer_core_target(ch: &Challenge, contrib: &[i32], core_half_dp: usize) -> Vec<usize> {
|
||||
let n = ch.difficulty.num_items;
|
||||
let mut order: Vec<usize> = (0..n).collect();
|
||||
order.sort_unstable_by(|&a, &b| {
|
||||
let da = (contrib[a] as f64) / (ch.weights[a] as f64);
|
||||
let db = (contrib[b] as f64) / (ch.weights[b] as f64);
|
||||
db.partial_cmp(&da).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
let mut pref_w: i64 = 0;
|
||||
let mut break_idx: usize = order.len().saturating_sub(1);
|
||||
for (pos, &i) in order.iter().enumerate() {
|
||||
let w = ch.weights[i] as i64;
|
||||
if pref_w + w > ch.max_weight as i64 {
|
||||
break_idx = pos;
|
||||
break;
|
||||
}
|
||||
pref_w += w;
|
||||
}
|
||||
let left = break_idx.saturating_sub(core_half_dp);
|
||||
let right = (break_idx + core_half_dp + 1).min(n);
|
||||
let locked = &order[..left];
|
||||
let core = &order[left..right];
|
||||
let used_locked: i64 = locked.iter().map(|&i| ch.weights[i] as i64).sum();
|
||||
let rem_cap = ((ch.max_weight as i64) - used_locked).max(0) as usize;
|
||||
let myw = rem_cap;
|
||||
let myk = core.len();
|
||||
let mut dp: Vec<i64> = vec![i64::MIN / 4; myw + 1];
|
||||
dp[0] = 0;
|
||||
let mut choose: Vec<u8> = vec![0u8; myk * (myw + 1)];
|
||||
let mut w_hi: usize = 0;
|
||||
for (t, &it) in core.iter().enumerate() {
|
||||
let wt = ch.weights[it] as usize;
|
||||
if wt > myw {
|
||||
continue;
|
||||
}
|
||||
let val = contrib[it] as i64;
|
||||
let new_hi = (w_hi + wt).min(myw);
|
||||
for w in (wt..=new_hi).rev() {
|
||||
let cand = dp[w - wt] + val;
|
||||
if cand > dp[w] {
|
||||
dp[w] = cand;
|
||||
choose[t * (myw + 1) + w] = 1;
|
||||
}
|
||||
}
|
||||
w_hi = new_hi;
|
||||
}
|
||||
let mut selected: Vec<usize> = locked.to_vec();
|
||||
let mut w_star = (0..=myw).max_by_key(|&w| dp[w]).unwrap_or(0);
|
||||
for t in (0..myk).rev() {
|
||||
let it = core[t];
|
||||
let wt = ch.weights[it] as usize;
|
||||
if wt <= w_star && choose[t * (myw + 1) + w_star] == 1 {
|
||||
selected.push(it);
|
||||
w_star -= wt;
|
||||
}
|
||||
}
|
||||
selected.sort_unstable();
|
||||
selected
|
||||
}
|
||||
|
||||
fn apply_dp_target_via_ops(state: &mut State, target_sel: &[usize]) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let mut in_target = vec![false; n];
|
||||
for &i in target_sel {
|
||||
in_target[i] = true;
|
||||
}
|
||||
let mut to_remove: Vec<usize> = Vec::new();
|
||||
for i in 0..n {
|
||||
if state.selected_bit[i] && !in_target[i] {
|
||||
to_remove.push(i);
|
||||
}
|
||||
}
|
||||
let mut to_add: Vec<usize> = Vec::new();
|
||||
for &i in target_sel {
|
||||
if !state.selected_bit[i] {
|
||||
to_add.push(i);
|
||||
}
|
||||
}
|
||||
for &r in &to_remove {
|
||||
state.remove_item(r);
|
||||
}
|
||||
for &a in &to_add {
|
||||
state.add_item(a);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_ls_windows(state: &State, core_half_ls: usize) -> (Vec<usize>, Vec<usize>) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let mut order: Vec<usize> = (0..n).collect();
|
||||
order.sort_unstable_by(|&a, &b| {
|
||||
let da = (state.contrib[a] as f64) / (state.ch.weights[a] as f64);
|
||||
let db = (state.contrib[b] as f64) / (state.ch.weights[b] as f64);
|
||||
db.partial_cmp(&da).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
let mut best_unused = Vec::with_capacity(core_half_ls);
|
||||
for &i in &order {
|
||||
if !state.selected_bit[i] {
|
||||
best_unused.push(i);
|
||||
if best_unused.len() >= core_half_ls {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut worst_used = Vec::with_capacity(core_half_ls);
|
||||
for &i in order.iter().rev() {
|
||||
if state.selected_bit[i] {
|
||||
worst_used.push(i);
|
||||
if worst_used.len() >= core_half_ls {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(best_unused, worst_used)
|
||||
}
|
||||
|
||||
fn apply_best_add_windowed(
|
||||
state: &mut State,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
let slack = state.slack();
|
||||
if slack <= 0 {
|
||||
return false;
|
||||
}
|
||||
let mut best: Option<(usize, i64)> = None;
|
||||
for &cand in &*best_unused {
|
||||
let w = state.ch.weights[cand] as i64;
|
||||
if w > slack {
|
||||
continue;
|
||||
}
|
||||
let delta = state.contrib[cand] as i64;
|
||||
if delta > 0 && best.map_or(true, |(_, bd)| delta > bd) {
|
||||
best = Some((cand, delta));
|
||||
}
|
||||
}
|
||||
if let Some((cand, _)) = best {
|
||||
state.add_item(cand);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_best_swap11_equal_windowed(
|
||||
state: &mut State,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
let mut best: Option<(usize, usize, i64)> = None;
|
||||
for &rm in &*worst_used {
|
||||
let w_rm = state.ch.weights[rm];
|
||||
for &cand in &*best_unused {
|
||||
if state.ch.weights[cand] != w_rm {
|
||||
continue;
|
||||
}
|
||||
let delta = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if delta > 0 && best.map_or(true, |(_, _, bd)| delta > bd) {
|
||||
best = Some((cand, rm, delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best {
|
||||
state.replace_item(rm, cand);
|
||||
State::remove_from_vec(worst_used, rm);
|
||||
best_unused.push(rm);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_best_swap_diff_reduce_windowed(
|
||||
state: &mut State,
|
||||
params: &Params,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
let mut best: Option<(usize, usize, i64)> = None;
|
||||
for &rm in &*worst_used {
|
||||
let w_rm = state.ch.weights[rm] as i64;
|
||||
for &cand in &*best_unused {
|
||||
let w_c = state.ch.weights[cand] as i64;
|
||||
if w_c >= w_rm {
|
||||
continue;
|
||||
}
|
||||
let dw = (w_rm - w_c) as usize;
|
||||
if dw == 0 || dw > params.diff_lim {
|
||||
continue;
|
||||
}
|
||||
let delta = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if delta > 0 && best.map_or(true, |(_, _, bd)| delta > bd) {
|
||||
best = Some((cand, rm, delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best {
|
||||
state.replace_item(rm, cand);
|
||||
State::remove_from_vec(worst_used, rm);
|
||||
best_unused.push(rm);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_best_swap_diff_increase_windowed(
|
||||
state: &mut State,
|
||||
params: &Params,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
if state.slack() <= 0 {
|
||||
return false;
|
||||
}
|
||||
let mut best: Option<(usize, usize, f64)> = None;
|
||||
for &rm in &*worst_used {
|
||||
let w_rm = state.ch.weights[rm] as i64;
|
||||
for &cand in &*best_unused {
|
||||
let w_c = state.ch.weights[cand] as i64;
|
||||
if w_c <= w_rm {
|
||||
continue;
|
||||
}
|
||||
let dw = (w_c - w_rm) as i64;
|
||||
if dw as usize > params.diff_lim {
|
||||
continue;
|
||||
}
|
||||
if state.slack() < dw {
|
||||
continue;
|
||||
}
|
||||
let delta = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if delta > 0 {
|
||||
let ratio = (delta as f64) / (dw as f64);
|
||||
if best.map_or(true, |(_, _, br)| ratio > br) {
|
||||
best = Some((cand, rm, ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best {
|
||||
state.replace_item(rm, cand);
|
||||
State::remove_from_vec(worst_used, rm);
|
||||
best_unused.push(rm);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn local_search_vnd(state: &mut State, params: &Params) {
|
||||
let (mut best_unused, mut worst_used) = build_ls_windows(state, params.core_half_ls);
|
||||
loop {
|
||||
if apply_best_add_windowed(state, &mut best_unused, &mut worst_used) {
|
||||
continue;
|
||||
}
|
||||
if apply_best_swap_diff_reduce_windowed(state, params, &mut best_unused, &mut worst_used) {
|
||||
continue;
|
||||
}
|
||||
if apply_best_swap11_equal_windowed(state, &mut best_unused, &mut worst_used) {
|
||||
continue;
|
||||
}
|
||||
if apply_best_swap_diff_increase_windowed(state, params, &mut best_unused, &mut worst_used)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let params = Params::default();
|
||||
let n = challenge.difficulty.num_items;
|
||||
let mut build_scores = vec![0i32; n];
|
||||
round0_scores(challenge, &mut build_scores);
|
||||
|
||||
let mut state = State::new_empty(challenge);
|
||||
build_initial_solution(&mut state, &build_scores);
|
||||
local_search_vnd(&mut state, ¶ms);
|
||||
|
||||
for _it in 0..params.n_maxils {
|
||||
let prev_sel = state.selected_items();
|
||||
let prev_val = state.total_value;
|
||||
let prev_contrib = state.contrib.clone();
|
||||
let target = integer_core_target(challenge, &state.contrib, params.core_half_dp);
|
||||
apply_dp_target_via_ops(&mut state, &target);
|
||||
local_search_vnd(&mut state, ¶ms);
|
||||
if state.total_value <= prev_val {
|
||||
state.restore_snapshot(&prev_sel, prev_contrib, prev_val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut items = state.selected_items();
|
||||
items.sort_unstable();
|
||||
let _ = save_solution(&Solution { items });
|
||||
Ok(())
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/knap_one/README.md
Normal file
23
tig-algorithms/src/knapsack/knap_one/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** knap_one
|
||||
* **Copyright:** 2024 VNX
|
||||
* **Identity of Submitter:** VNX
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
193
tig-algorithms/src/knapsack/knap_one/mod.rs
Normal file
193
tig-algorithms/src/knapsack/knap_one/mod.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
use anyhow::Result;
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> Result<Option<SubSolution>> {
|
||||
const WAIT_ITERATIONS: usize = 5;
|
||||
const MAX_STAGNANT_ITERATIONS: usize = 5;
|
||||
|
||||
let num_items = challenge.weights.len();
|
||||
let mut selected_items = vec![false; num_items];
|
||||
let mut total_value: i32 = 0;
|
||||
let mut total_weight: u32 = 0;
|
||||
let mut wait_map = vec![None; num_items];
|
||||
let values: Vec<i32> = challenge.values.iter().map(|&v| v as i32).collect();
|
||||
let weights: Vec<f64> = challenge.weights.iter().map(|&w| w as f64).collect();
|
||||
|
||||
let mut items_by_ratio: Vec<(usize, f64)> = (0..num_items)
|
||||
.map(|i| {
|
||||
let adjusted_value = values[i];
|
||||
let ratio = adjusted_value as f64 / weights[i];
|
||||
(i, ratio)
|
||||
})
|
||||
.collect();
|
||||
items_by_ratio.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
let mut interaction_gains = vec![0; num_items];
|
||||
|
||||
let mut iteration_count = 0;
|
||||
let mut stagnant_iterations = 0;
|
||||
let mut max_total_value = total_value;
|
||||
|
||||
loop {
|
||||
iteration_count += 1;
|
||||
|
||||
for entry in &mut wait_map {
|
||||
if let Some(iter) = entry {
|
||||
if *iter <= iteration_count {
|
||||
*entry = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut available_items: Vec<_> = items_by_ratio
|
||||
.iter()
|
||||
.filter(|(i, _)| !selected_items[*i] && wait_map[*i].is_none())
|
||||
.collect();
|
||||
|
||||
let mut improvement_found = false;
|
||||
let mut index = 0;
|
||||
|
||||
while index < available_items.len() {
|
||||
let (i, _) = available_items[index];
|
||||
let individual_value = values[*i];
|
||||
let interaction_gain = interaction_gains[*i];
|
||||
let gain = individual_value + interaction_gain;
|
||||
if gain >= individual_value {
|
||||
selected_items[*i] = true;
|
||||
total_value += gain;
|
||||
total_weight += challenge.weights[*i];
|
||||
|
||||
for j in 0..num_items {
|
||||
interaction_gains[j] += challenge.interaction_values[*i][j];
|
||||
}
|
||||
|
||||
improvement_found = true;
|
||||
available_items.remove(index);
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !improvement_found {
|
||||
for &(i, _) in &available_items {
|
||||
let new_item_value = values[*i] + interaction_gains[*i];
|
||||
let new_item_weight = challenge.weights[*i];
|
||||
|
||||
if new_item_value <= values[*i] {
|
||||
continue;
|
||||
}
|
||||
|
||||
for j in 0..num_items {
|
||||
if selected_items[j] {
|
||||
let removal_loss = values[j] + interaction_gains[j];
|
||||
if total_value + new_item_value - removal_loss > total_value {
|
||||
for k in 0..num_items {
|
||||
interaction_gains[k] -= challenge.interaction_values[j][k];
|
||||
}
|
||||
selected_items[j] = false;
|
||||
total_value -= removal_loss;
|
||||
total_weight -= challenge.weights[j];
|
||||
|
||||
selected_items[*i] = true;
|
||||
total_value += new_item_value;
|
||||
total_weight += new_item_weight;
|
||||
|
||||
for k in 0..num_items {
|
||||
interaction_gains[k] += challenge.interaction_values[*i][k];
|
||||
}
|
||||
|
||||
wait_map[j] = Some(iteration_count + WAIT_ITERATIONS);
|
||||
improvement_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if improvement_found {
|
||||
break;
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if total_weight > challenge.max_weight {
|
||||
let mut item_loss_ratios = Vec::new();
|
||||
for i in 0..num_items {
|
||||
if selected_items[i] {
|
||||
let loss = values[i] + interaction_gains[i];
|
||||
let ratio = weights[i] / (loss as f64).max(1.0);
|
||||
item_loss_ratios.push((ratio, i));
|
||||
}
|
||||
}
|
||||
item_loss_ratios.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
||||
|
||||
while total_weight > challenge.max_weight {
|
||||
if let Some((_, item)) = item_loss_ratios.pop() {
|
||||
for k in 0..num_items {
|
||||
interaction_gains[k] -= challenge.interaction_values[item][k];
|
||||
}
|
||||
selected_items[item] = false;
|
||||
total_weight -= challenge.weights[item];
|
||||
total_value -= values[item] + interaction_gains[item];
|
||||
wait_map[item] = Some(iteration_count + WAIT_ITERATIONS);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if total_value >= challenge.baseline_value as i32 && total_weight <= challenge.max_weight {
|
||||
let result_items: Vec<usize> = selected_items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, &is_selected)| is_selected)
|
||||
.map(|(i, _)| i)
|
||||
.collect();
|
||||
|
||||
return Ok(Some(SubSolution {
|
||||
items: result_items,
|
||||
}));
|
||||
}
|
||||
|
||||
if total_value > max_total_value {
|
||||
max_total_value = total_value;
|
||||
stagnant_iterations = 0;
|
||||
} else {
|
||||
stagnant_iterations += 1;
|
||||
}
|
||||
|
||||
if stagnant_iterations >= MAX_STAGNANT_ITERATIONS {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/knapheudp/README.md
Normal file
23
tig-algorithms/src/knapsack/knapheudp/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** knapheudp
|
||||
* **Copyright:** 2024 AllFather
|
||||
* **Identity of Submitter:** AllFather
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
104
tig-algorithms/src/knapsack/knapheudp/mod.rs
Normal file
104
tig-algorithms/src/knapsack/knapheudp/mod.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> anyhow::Result<Option<SubSolution>> {
|
||||
let max_weight = challenge.max_weight as usize;
|
||||
let baseline_value = challenge.baseline_value as usize;
|
||||
let num_items = challenge.difficulty.num_items;
|
||||
|
||||
let weights: Vec<usize> = challenge.weights.iter().map(|&w| w as usize).collect();
|
||||
let values: Vec<usize> = challenge.values.iter().map(|&v| v as usize).collect();
|
||||
|
||||
let mut sorted_items: Vec<(usize, f64)> = (0..num_items)
|
||||
.map(|i| (i, values[i] as f64 / weights[i] as f64))
|
||||
.collect();
|
||||
sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
|
||||
let mut upper_bound = 0;
|
||||
let mut remaining_weight = max_weight;
|
||||
for &(item_index, ratio) in &sorted_items {
|
||||
let item_weight = weights[item_index];
|
||||
let item_value = values[item_index];
|
||||
|
||||
if item_weight <= remaining_weight {
|
||||
upper_bound += item_value;
|
||||
remaining_weight -= item_weight;
|
||||
} else {
|
||||
upper_bound += (ratio * remaining_weight as f64).floor() as usize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if upper_bound < baseline_value {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut dp = vec![0; max_weight + 1];
|
||||
let mut selected = vec![vec![false; max_weight + 1]; num_items];
|
||||
|
||||
for (i, &(item_index, _)) in sorted_items.iter().enumerate() {
|
||||
let weight = weights[item_index];
|
||||
let value = values[item_index];
|
||||
|
||||
for w in (weight..=max_weight).rev() {
|
||||
let new_value = dp[w - weight] + value;
|
||||
if new_value > dp[w] {
|
||||
dp[w] = new_value;
|
||||
selected[i][w] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if dp[max_weight] >= baseline_value {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if dp[max_weight] < baseline_value {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut items = Vec::new();
|
||||
let mut w = max_weight;
|
||||
for i in (0..num_items).rev() {
|
||||
if selected[i][w] {
|
||||
let item_index = sorted_items[i].0;
|
||||
items.push(item_index);
|
||||
w -= weights[item_index];
|
||||
}
|
||||
if w == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(SubSolution { items }))
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/knapmaxxing/README.md
Normal file
23
tig-algorithms/src/knapsack/knapmaxxing/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** knapmaxxing
|
||||
* **Copyright:** 2024 Dominic Kennedy
|
||||
* **Identity of Submitter:** Dominic Kennedy
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
109
tig-algorithms/src/knapsack/knapmaxxing/mod.rs
Normal file
109
tig-algorithms/src/knapsack/knapmaxxing/mod.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> anyhow::Result<Option<SubSolution>> {
|
||||
let max_weight = challenge.max_weight as usize;
|
||||
let baseline_value = challenge.baseline_value as usize;
|
||||
let num_items = challenge.difficulty.num_items;
|
||||
|
||||
let max_weight_plus_one = max_weight + 1;
|
||||
|
||||
let weights: Vec<usize> = challenge.weights.iter().map(|weight| *weight as usize).collect();
|
||||
let values: Vec<usize> = challenge.values.iter().map(|value| *value as usize).collect();
|
||||
|
||||
let mut sorted_items: Vec<(usize, f64)> = (0..num_items)
|
||||
.map(|i| (i, values[i] as f64 / weights[i] as f64))
|
||||
.collect();
|
||||
sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
|
||||
let mut upper_bound = 0;
|
||||
let mut remaining_weight = max_weight;
|
||||
for &(item_index, ratio) in &sorted_items {
|
||||
let item_weight = weights[item_index];
|
||||
let item_value = values[item_index];
|
||||
|
||||
if item_weight <= remaining_weight {
|
||||
upper_bound += item_value;
|
||||
remaining_weight -= item_weight;
|
||||
} else {
|
||||
upper_bound += (ratio * remaining_weight as f64).floor() as usize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if upper_bound < baseline_value {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let num_states = (num_items + 1) * (max_weight_plus_one);
|
||||
let mut dp = vec![0; num_states];
|
||||
|
||||
for i in 1..=num_items {
|
||||
let (item_index, _) = sorted_items[i - 1];
|
||||
let item_weight = weights[item_index];
|
||||
let item_value = values[item_index];
|
||||
|
||||
let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one;
|
||||
let i_times_max_weight_plus_one = i * max_weight_plus_one;
|
||||
for w in (item_weight..=max_weight).rev() {
|
||||
let prev_state = i_minus_one_times_max_weight_plus_one + w;
|
||||
let curr_state = i_times_max_weight_plus_one + w;
|
||||
dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value);
|
||||
}
|
||||
}
|
||||
|
||||
let mut items = Vec::with_capacity(num_items);
|
||||
let mut i = num_items;
|
||||
let mut w = max_weight;
|
||||
let mut total_value = 0;
|
||||
while i > 0 && total_value < baseline_value {
|
||||
let (item_index, _) = sorted_items[i - 1];
|
||||
let item_weight = weights[item_index];
|
||||
let item_value = values[item_index];
|
||||
|
||||
let prev_state = (i - 1) * (max_weight_plus_one) + w;
|
||||
let curr_state = i * (max_weight_plus_one) + w;
|
||||
if dp[curr_state] != dp[prev_state] {
|
||||
items.push(item_index);
|
||||
w -= item_weight;
|
||||
total_value += item_value;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if total_value >= baseline_value {
|
||||
Ok(Some(SubSolution { items }))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/knapsack_redone/README.md
Normal file
23
tig-algorithms/src/knapsack/knapsack_redone/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** knapsack_redone
|
||||
* **Copyright:** 2025 frogmarch
|
||||
* **Identity of Submitter:** frogmarch
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
393
tig-algorithms/src/knapsack/knapsack_redone/mod.rs
Normal file
393
tig-algorithms/src/knapsack/knapsack_redone/mod.rs
Normal file
@ -0,0 +1,393 @@
|
||||
use anyhow::Result;
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
fn calculate_density_variance(item_densities: &[(usize, f32)]) -> f32 {
|
||||
if item_densities.len() < 2 {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
let mut sum = 0.0;
|
||||
for (_, density) in item_densities {
|
||||
sum += *density;
|
||||
}
|
||||
let mean = sum / item_densities.len() as f32;
|
||||
|
||||
let mut variance_sum = 0.0;
|
||||
for (_, density) in item_densities {
|
||||
let diff = *density - mean;
|
||||
variance_sum += diff * diff;
|
||||
}
|
||||
let variance = variance_sum / item_densities.len() as f32;
|
||||
|
||||
(variance.sqrt() / mean.abs()).clamp(0.1, 1.0)
|
||||
}
|
||||
|
||||
fn compute_solution(
|
||||
challenge: &Challenge,
|
||||
contribution_list: &mut [i32],
|
||||
unselected_items: &mut Vec<usize>,
|
||||
rng: &mut StdRng,
|
||||
) -> Result<Option<(Solution, i32)>> {
|
||||
let mut selected_items = Vec::new();
|
||||
let mut total_weight = 0;
|
||||
let mut total_value = 0;
|
||||
|
||||
let mut inv_weights: Vec<f32> = Vec::with_capacity(challenge.weights.len());
|
||||
for &w in &challenge.weights {
|
||||
inv_weights.push(1.0 / w as f32);
|
||||
}
|
||||
|
||||
let rcl_max = if challenge.difficulty.num_items <= 165 {
|
||||
9
|
||||
} else {
|
||||
10
|
||||
};
|
||||
|
||||
let mut item_densities: Vec<(usize, f32)> = Vec::with_capacity(unselected_items.len());
|
||||
for &idx in unselected_items.iter() {
|
||||
let ratio = contribution_list[idx] as f32 * inv_weights[idx];
|
||||
item_densities.push((idx, ratio));
|
||||
}
|
||||
|
||||
let density_variance = calculate_density_variance(&item_densities);
|
||||
let adaptive_exponent = 1.4 + (density_variance - 0.6).clamp(-0.4, 0.2);
|
||||
|
||||
let mut probs: Vec<f32> = Vec::with_capacity(rcl_max);
|
||||
for rank in 0..rcl_max {
|
||||
probs.push(1.0 / ((rank + 1) as f32).powf(adaptive_exponent));
|
||||
}
|
||||
|
||||
let mut acc_probs: Vec<f32> = Vec::with_capacity(rcl_max);
|
||||
let mut sum = 0.0;
|
||||
for &prob in &probs {
|
||||
sum += prob;
|
||||
acc_probs.push(sum);
|
||||
}
|
||||
let total_prob_max = sum;
|
||||
|
||||
let mut max_item_weight = 0;
|
||||
for &w in &challenge.weights {
|
||||
if w > max_item_weight {
|
||||
max_item_weight = w;
|
||||
}
|
||||
}
|
||||
|
||||
let list_size = 2;
|
||||
let mut top_ranks = vec![0; list_size];
|
||||
|
||||
while !item_densities.is_empty() {
|
||||
let num_candidates = item_densities.len();
|
||||
if num_candidates < 2 {
|
||||
break;
|
||||
}
|
||||
|
||||
let actual_rcl_size = num_candidates.min(rcl_max);
|
||||
let total_prob = if actual_rcl_size == rcl_max {
|
||||
total_prob_max
|
||||
} else {
|
||||
acc_probs[actual_rcl_size - 1]
|
||||
};
|
||||
|
||||
let random_threshold = rng.gen_range(0.0..total_prob);
|
||||
let mut selected_rank = match acc_probs[..actual_rcl_size]
|
||||
.binary_search_by(|prob| prob.partial_cmp(&random_threshold).unwrap())
|
||||
{
|
||||
Ok(i) | Err(i) => i,
|
||||
};
|
||||
if selected_rank >= actual_rcl_size {
|
||||
selected_rank = actual_rcl_size - 1;
|
||||
}
|
||||
|
||||
let selected_item;
|
||||
if selected_rank < list_size
|
||||
&& !selected_items.is_empty()
|
||||
&& top_ranks[selected_rank] < item_densities.len()
|
||||
{
|
||||
selected_rank = top_ranks[selected_rank];
|
||||
selected_item = item_densities[selected_rank].0;
|
||||
} else {
|
||||
item_densities
|
||||
.select_nth_unstable_by(selected_rank, |a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
selected_item = item_densities[selected_rank].0;
|
||||
}
|
||||
|
||||
selected_items.push(selected_item);
|
||||
total_weight += challenge.weights[selected_item];
|
||||
total_value += contribution_list[selected_item];
|
||||
|
||||
if total_weight + max_item_weight > challenge.max_weight {
|
||||
item_densities.retain(|(idx, _)| {
|
||||
total_weight + challenge.weights[*idx] <= challenge.max_weight
|
||||
&& *idx != selected_item
|
||||
});
|
||||
} else {
|
||||
item_densities.swap_remove(selected_rank);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) += *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(selected_item)
|
||||
.get_unchecked(x);
|
||||
}
|
||||
|
||||
let mut first_density = f32::MIN;
|
||||
let mut first_rank = 0;
|
||||
let mut second_density = f32::MIN;
|
||||
let mut second_rank = 0;
|
||||
|
||||
for (i, density) in item_densities.iter_mut().enumerate() {
|
||||
let interaction = *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(selected_item)
|
||||
.get_unchecked(density.0);
|
||||
density.1 += interaction as f32 * inv_weights[density.0];
|
||||
let current_density = density.1;
|
||||
|
||||
if current_density > first_density {
|
||||
second_density = first_density;
|
||||
second_rank = first_rank;
|
||||
first_density = current_density;
|
||||
first_rank = i;
|
||||
} else if current_density > second_density {
|
||||
second_density = current_density;
|
||||
second_rank = i;
|
||||
}
|
||||
}
|
||||
|
||||
top_ranks[0] = first_rank;
|
||||
top_ranks[1] = second_rank;
|
||||
}
|
||||
}
|
||||
unselected_items.clear();
|
||||
for i in 0..challenge.difficulty.num_items {
|
||||
unselected_items.push(i);
|
||||
}
|
||||
|
||||
let mut sorted_selected = selected_items.clone();
|
||||
sorted_selected.sort_unstable_by(|a, b| b.cmp(a));
|
||||
|
||||
for &selected in &sorted_selected {
|
||||
unselected_items.swap_remove(selected);
|
||||
}
|
||||
|
||||
let mut weight_item_pairs: Vec<(u32, usize)> = Vec::with_capacity(unselected_items.len());
|
||||
for &idx in unselected_items.iter() {
|
||||
weight_item_pairs.push((challenge.weights[idx], idx));
|
||||
}
|
||||
weight_item_pairs.sort_unstable_by_key(|&(weight, _)| weight);
|
||||
|
||||
unselected_items.clear();
|
||||
for (_, idx) in weight_item_pairs {
|
||||
unselected_items.push(idx);
|
||||
}
|
||||
|
||||
let local_search_iterations = if challenge.difficulty.num_items <= 165 {
|
||||
60
|
||||
} else {
|
||||
100
|
||||
};
|
||||
let mut feasible_adds = Vec::with_capacity(50);
|
||||
let mut feasible_swaps = Vec::with_capacity(100);
|
||||
|
||||
for _ in 0..local_search_iterations {
|
||||
let mut improved = false;
|
||||
|
||||
if total_weight < challenge.max_weight {
|
||||
for (i, &cand) in unselected_items.iter().enumerate() {
|
||||
let new_w = total_weight + challenge.weights[cand];
|
||||
if new_w > challenge.max_weight {
|
||||
break;
|
||||
}
|
||||
let new_val = total_value + contribution_list[cand];
|
||||
if new_val > total_value {
|
||||
feasible_adds.push(i);
|
||||
}
|
||||
}
|
||||
if !feasible_adds.is_empty() {
|
||||
let pick = rng.gen_range(0..feasible_adds.len());
|
||||
let add_idx = feasible_adds[pick];
|
||||
let new_item = unselected_items[add_idx];
|
||||
|
||||
unselected_items.remove(add_idx);
|
||||
selected_items.push(new_item);
|
||||
|
||||
total_weight += challenge.weights[new_item];
|
||||
total_value += contribution_list[new_item];
|
||||
improved = true;
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) += *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(x)
|
||||
.get_unchecked(new_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
feasible_adds.clear();
|
||||
}
|
||||
|
||||
let free_capacity = challenge.max_weight as i32 - total_weight as i32;
|
||||
for (j, &rem_item) in selected_items.iter().enumerate() {
|
||||
let rem_w = challenge.weights[rem_item] as i32;
|
||||
|
||||
for (i, &cand_item) in unselected_items.iter().enumerate() {
|
||||
let cand_w = challenge.weights[cand_item] as i32;
|
||||
if rem_w + free_capacity < cand_w {
|
||||
break;
|
||||
}
|
||||
|
||||
let val_diff = contribution_list[cand_item]
|
||||
- contribution_list[rem_item]
|
||||
- challenge.interaction_values[cand_item][rem_item];
|
||||
if val_diff > 0 {
|
||||
feasible_swaps.push((i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !feasible_swaps.is_empty() {
|
||||
let pick = rng.gen_range(0..feasible_swaps.len());
|
||||
let (unsel_idx, sel_idx) = feasible_swaps[pick];
|
||||
let new_item = unselected_items[unsel_idx];
|
||||
let remove_item = selected_items[sel_idx];
|
||||
|
||||
selected_items.swap_remove(sel_idx);
|
||||
selected_items.push(new_item);
|
||||
|
||||
let new_item_weight = challenge.weights[new_item];
|
||||
let remove_item_weight = challenge.weights[remove_item];
|
||||
|
||||
let current_pos = unsel_idx;
|
||||
let mut target_pos = current_pos;
|
||||
if new_item_weight != remove_item_weight {
|
||||
target_pos = unselected_items
|
||||
.binary_search_by(|&probe| challenge.weights[probe].cmp(&remove_item_weight))
|
||||
.unwrap_or_else(|e| e);
|
||||
}
|
||||
if current_pos != target_pos {
|
||||
unsafe {
|
||||
let ptr = unselected_items.as_mut_ptr();
|
||||
if target_pos < current_pos {
|
||||
std::ptr::copy(
|
||||
ptr.add(target_pos),
|
||||
ptr.add(target_pos + 1),
|
||||
current_pos - target_pos,
|
||||
);
|
||||
} else {
|
||||
target_pos = target_pos - 1;
|
||||
std::ptr::copy(
|
||||
ptr.add(current_pos + 1),
|
||||
ptr.add(current_pos),
|
||||
target_pos - current_pos,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
unselected_items[target_pos] = remove_item;
|
||||
|
||||
total_value += contribution_list[new_item]
|
||||
- contribution_list[remove_item]
|
||||
- challenge.interaction_values[new_item][remove_item];
|
||||
total_weight =
|
||||
total_weight + challenge.weights[new_item] - challenge.weights[remove_item];
|
||||
improved = true;
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) += *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(x)
|
||||
.get_unchecked(new_item)
|
||||
- *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(x)
|
||||
.get_unchecked(remove_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
feasible_swaps.clear();
|
||||
|
||||
if !improved {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if selected_items.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((
|
||||
Solution {
|
||||
items: selected_items,
|
||||
},
|
||||
total_value,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let num_iterations: i32 = 5;
|
||||
let mut rng =
|
||||
StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()));
|
||||
|
||||
let mut best_solution: Option<Solution> = None;
|
||||
let mut best_value = 0;
|
||||
|
||||
for _outer_iter in 0..num_iterations {
|
||||
let mut best_local_solution: Option<Solution> = None;
|
||||
let mut best_local_value = 0;
|
||||
|
||||
let k = 5;
|
||||
for _ in 0..k {
|
||||
let mut unselected_items: Vec<usize> =
|
||||
Vec::with_capacity(challenge.difficulty.num_items);
|
||||
for i in 0..challenge.difficulty.num_items {
|
||||
unselected_items.push(i);
|
||||
}
|
||||
|
||||
let mut contribution_list: Vec<i32> = Vec::with_capacity(challenge.values.len());
|
||||
for &v in &challenge.values {
|
||||
contribution_list.push(v as i32);
|
||||
}
|
||||
|
||||
let sol_result = compute_solution(
|
||||
challenge,
|
||||
&mut contribution_list,
|
||||
&mut unselected_items,
|
||||
&mut rng,
|
||||
)?;
|
||||
|
||||
let (solution, value) = match sol_result {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if value > best_local_value {
|
||||
best_local_value = value;
|
||||
best_local_solution = Some(Solution {
|
||||
items: solution.items.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(local_solution) = best_local_solution {
|
||||
if best_local_value > best_value {
|
||||
best_value = best_local_value;
|
||||
best_solution = Some(local_solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(solution) = best_solution {
|
||||
let _ = save_solution(&solution);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/knapsplatt/README.md
Normal file
23
tig-algorithms/src/knapsack/knapsplatt/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** knapsplatt
|
||||
* **Copyright:** 2025 Jeeperz
|
||||
* **Identity of Submitter:** Jeeperz
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
674
tig-algorithms/src/knapsack/knapsplatt/mod.rs
Normal file
674
tig-algorithms/src/knapsack/knapsplatt/mod.rs
Normal file
@ -0,0 +1,674 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::{Map, Value};
|
||||
use std::cmp::Ordering;
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Params {
|
||||
diff_lim: usize,
|
||||
core_half_dp: usize,
|
||||
core_half_ls: usize,
|
||||
n_maxils: usize,
|
||||
polish_k: usize,
|
||||
}
|
||||
impl Params {
|
||||
fn for_problem_size(num_items: usize) -> Self {
|
||||
let n_maxils = if num_items <= 600 {
|
||||
3
|
||||
} else if num_items <= 800 {
|
||||
4
|
||||
} else {
|
||||
5
|
||||
};
|
||||
|
||||
Self {
|
||||
diff_lim: 4,
|
||||
core_half_dp: 30,
|
||||
core_half_ls: 35,
|
||||
n_maxils,
|
||||
polish_k: 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for Params {
|
||||
fn default() -> Self {
|
||||
Self::for_problem_size(400)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn weight_of(ch: &Challenge, items: &[usize]) -> i64 {
|
||||
items.iter().map(|&i| ch.weights[i] as i64).sum()
|
||||
}
|
||||
|
||||
fn round0_scores(ch: &Challenge, out: &mut [i32]) {
|
||||
let n = ch.difficulty.num_items;
|
||||
for i in 0..n {
|
||||
let row_sum: i32 = ch.interaction_values[i].iter().sum();
|
||||
out[i] = ch.values[i] as i32 + row_sum;
|
||||
}
|
||||
}
|
||||
|
||||
struct State<'a> {
|
||||
ch: &'a Challenge,
|
||||
selected_bit: Vec<bool>,
|
||||
contrib: Vec<i32>,
|
||||
total_value: i64,
|
||||
total_weight: i64,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
fn new_empty(ch: &'a Challenge) -> Self {
|
||||
let n = ch.difficulty.num_items;
|
||||
let mut contrib = vec![0i32; n];
|
||||
for i in 0..n {
|
||||
contrib[i] = ch.values[i] as i32;
|
||||
}
|
||||
Self {
|
||||
ch,
|
||||
selected_bit: vec![false; n],
|
||||
contrib,
|
||||
total_value: 0,
|
||||
total_weight: 0,
|
||||
}
|
||||
}
|
||||
fn selected_items(&self) -> Vec<usize> {
|
||||
(0..self.ch.difficulty.num_items)
|
||||
.filter(|&i| self.selected_bit[i])
|
||||
.collect()
|
||||
}
|
||||
#[inline]
|
||||
fn capacity(&self) -> i64 {
|
||||
self.ch.max_weight as i64
|
||||
}
|
||||
#[inline]
|
||||
fn slack(&self) -> i64 {
|
||||
self.capacity() - self.total_weight
|
||||
}
|
||||
fn add_item(&mut self, i: usize) {
|
||||
self.selected_bit[i] = true;
|
||||
self.total_value += self.contrib[i] as i64;
|
||||
self.total_weight += self.ch.weights[i] as i64;
|
||||
let n = self.ch.difficulty.num_items;
|
||||
for k in 0..n {
|
||||
self.contrib[k] += self.ch.interaction_values[k][i] as i32;
|
||||
}
|
||||
}
|
||||
fn remove_item(&mut self, j: usize) {
|
||||
self.total_value -= self.contrib[j] as i64;
|
||||
self.total_weight -= self.ch.weights[j] as i64;
|
||||
let n = self.ch.difficulty.num_items;
|
||||
for k in 0..n {
|
||||
self.contrib[k] -= self.ch.interaction_values[k][j] as i32;
|
||||
}
|
||||
self.selected_bit[j] = false;
|
||||
}
|
||||
fn replace_item(&mut self, rm: usize, cand: usize) {
|
||||
let w_c = self.ch.weights[cand] as i64;
|
||||
if self.slack() >= w_c {
|
||||
self.add_item(cand);
|
||||
self.remove_item(rm);
|
||||
} else {
|
||||
self.remove_item(rm);
|
||||
self.add_item(cand);
|
||||
}
|
||||
}
|
||||
fn restore_snapshot(
|
||||
&mut self,
|
||||
snapshot_sel: &[usize],
|
||||
snapshot_contrib: Vec<i32>,
|
||||
snap_value: i64,
|
||||
) {
|
||||
self.selected_bit.fill(false);
|
||||
for &i in snapshot_sel {
|
||||
self.selected_bit[i] = true;
|
||||
}
|
||||
self.contrib = snapshot_contrib;
|
||||
self.total_value = snap_value;
|
||||
self.total_weight = weight_of(self.ch, snapshot_sel);
|
||||
}
|
||||
#[inline]
|
||||
fn remove_from_vec(v: &mut Vec<usize>, x: usize) {
|
||||
if let Some(pos) = v.iter().position(|&y| y == x) {
|
||||
v.swap_remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_initial_solution(state: &mut State, order_scores: &[i32]) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let mut order: Vec<usize> = (0..n).collect();
|
||||
order.sort_unstable_by(|&a, &b| {
|
||||
let da = (order_scores[a] as f64) / (state.ch.weights[a] as f64);
|
||||
let db = (order_scores[b] as f64) / (state.ch.weights[b] as f64);
|
||||
db.partial_cmp(&da).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
for &i in &order {
|
||||
let w = state.ch.weights[i] as i64;
|
||||
if state.total_weight + w <= state.capacity() {
|
||||
state.add_item(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn integer_core_target(ch: &Challenge, contrib: &[i32], core_half_dp: usize) -> Vec<usize> {
|
||||
let n = ch.difficulty.num_items;
|
||||
let mut order: Vec<usize> = (0..n).collect();
|
||||
order.sort_unstable_by(|&a, &b| {
|
||||
let da = (contrib[a] as f64) / (ch.weights[a] as f64);
|
||||
let db = (contrib[b] as f64) / (ch.weights[b] as f64);
|
||||
db.partial_cmp(&da).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
let mut pref_w: i64 = 0;
|
||||
let mut break_idx: usize = order.len().saturating_sub(1);
|
||||
for (pos, &i) in order.iter().enumerate() {
|
||||
let w = ch.weights[i] as i64;
|
||||
if pref_w + w > ch.max_weight as i64 {
|
||||
break_idx = pos;
|
||||
break;
|
||||
}
|
||||
pref_w += w;
|
||||
}
|
||||
let left = break_idx.saturating_sub(core_half_dp);
|
||||
let right = (break_idx + core_half_dp + 1).min(n);
|
||||
let locked = &order[..left];
|
||||
let core = &order[left..right];
|
||||
let used_locked: i64 = locked.iter().map(|&i| ch.weights[i] as i64).sum();
|
||||
let rem_cap = ((ch.max_weight as i64) - used_locked).max(0) as usize;
|
||||
let myw = rem_cap;
|
||||
let myk = core.len();
|
||||
let mut dp: Vec<i64> = vec![i64::MIN / 4; myw + 1];
|
||||
dp[0] = 0;
|
||||
let mut choose: Vec<u8> = vec![0u8; myk * (myw + 1)];
|
||||
let mut w_hi: usize = 0;
|
||||
for (t, &it) in core.iter().enumerate() {
|
||||
let wt = ch.weights[it] as usize;
|
||||
if wt > myw {
|
||||
continue;
|
||||
}
|
||||
let val = contrib[it] as i64;
|
||||
let new_hi = (w_hi + wt).min(myw);
|
||||
for w in (wt..=new_hi).rev() {
|
||||
let cand = dp[w - wt] + val;
|
||||
if cand > dp[w] {
|
||||
dp[w] = cand;
|
||||
choose[t * (myw + 1) + w] = 1;
|
||||
}
|
||||
}
|
||||
w_hi = new_hi;
|
||||
}
|
||||
let mut selected: Vec<usize> = locked.to_vec();
|
||||
let mut w_star = (0..=myw).max_by_key(|&w| dp[w]).unwrap_or(0);
|
||||
for t in (0..myk).rev() {
|
||||
let it = core[t];
|
||||
let wt = ch.weights[it] as usize;
|
||||
if wt <= w_star && choose[t * (myw + 1) + w_star] == 1 {
|
||||
selected.push(it);
|
||||
w_star -= wt;
|
||||
}
|
||||
}
|
||||
selected.sort_unstable();
|
||||
selected
|
||||
}
|
||||
|
||||
fn apply_dp_target_via_ops(state: &mut State, target_sel: &[usize]) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let mut in_target = vec![false; n];
|
||||
for &i in target_sel {
|
||||
in_target[i] = true;
|
||||
}
|
||||
let mut to_remove: Vec<usize> = Vec::new();
|
||||
for i in 0..n {
|
||||
if state.selected_bit[i] && !in_target[i] {
|
||||
to_remove.push(i);
|
||||
}
|
||||
}
|
||||
let mut to_add: Vec<usize> = Vec::new();
|
||||
for &i in target_sel {
|
||||
if !state.selected_bit[i] {
|
||||
to_add.push(i);
|
||||
}
|
||||
}
|
||||
for &r in &to_remove {
|
||||
state.remove_item(r);
|
||||
}
|
||||
for &a in &to_add {
|
||||
state.add_item(a);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_ls_windows(state: &State, core_half_ls: usize) -> (Vec<usize>, Vec<usize>) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let mut order: Vec<usize> = (0..n).collect();
|
||||
order.sort_unstable_by(|&a, &b| {
|
||||
let da = (state.contrib[a] as f64) / (state.ch.weights[a] as f64);
|
||||
let db = (state.contrib[b] as f64) / (state.ch.weights[b] as f64);
|
||||
db.partial_cmp(&da).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
let mut best_unused = Vec::with_capacity(core_half_ls);
|
||||
for &i in &order {
|
||||
if !state.selected_bit[i] {
|
||||
best_unused.push(i);
|
||||
if best_unused.len() >= core_half_ls {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut worst_used = Vec::with_capacity(core_half_ls);
|
||||
for &i in order.iter().rev() {
|
||||
if state.selected_bit[i] {
|
||||
worst_used.push(i);
|
||||
if worst_used.len() >= core_half_ls {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(best_unused, worst_used)
|
||||
}
|
||||
|
||||
fn apply_best_add_windowed(
|
||||
state: &mut State,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
let slack = state.slack();
|
||||
if slack <= 0 {
|
||||
return false;
|
||||
}
|
||||
let mut best: Option<(usize, i64)> = None;
|
||||
for &cand in &*best_unused {
|
||||
let w = state.ch.weights[cand] as i64;
|
||||
if w > slack {
|
||||
continue;
|
||||
}
|
||||
let delta = state.contrib[cand] as i64;
|
||||
if delta > 0 && best.map_or(true, |(_, bd)| delta > bd) {
|
||||
best = Some((cand, delta));
|
||||
}
|
||||
}
|
||||
if let Some((cand, _)) = best {
|
||||
state.add_item(cand);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_best_swap11_equal_windowed(
|
||||
state: &mut State,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
let mut best: Option<(usize, usize, i64)> = None;
|
||||
for &rm in &*worst_used {
|
||||
let w_rm = state.ch.weights[rm];
|
||||
for &cand in &*best_unused {
|
||||
if state.ch.weights[cand] != w_rm {
|
||||
continue;
|
||||
}
|
||||
let delta = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if delta > 0 && best.map_or(true, |(_, _, bd)| delta > bd) {
|
||||
best = Some((cand, rm, delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best {
|
||||
state.replace_item(rm, cand);
|
||||
State::remove_from_vec(worst_used, rm);
|
||||
best_unused.push(rm);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_best_swap_diff_reduce_windowed(
|
||||
state: &mut State,
|
||||
params: &Params,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
let mut best: Option<(usize, usize, i64)> = None;
|
||||
for &rm in &*worst_used {
|
||||
let w_rm = state.ch.weights[rm] as i64;
|
||||
for &cand in &*best_unused {
|
||||
let w_c = state.ch.weights[cand] as i64;
|
||||
if w_c >= w_rm {
|
||||
continue;
|
||||
}
|
||||
let dw = (w_rm - w_c) as usize;
|
||||
if dw == 0 || dw > params.diff_lim {
|
||||
continue;
|
||||
}
|
||||
let delta = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if delta > 0 && best.map_or(true, |(_, _, bd)| delta > bd) {
|
||||
best = Some((cand, rm, delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best {
|
||||
state.replace_item(rm, cand);
|
||||
State::remove_from_vec(worst_used, rm);
|
||||
best_unused.push(rm);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_best_swap_diff_increase_windowed(
|
||||
state: &mut State,
|
||||
params: &Params,
|
||||
best_unused: &mut Vec<usize>,
|
||||
worst_used: &mut Vec<usize>,
|
||||
) -> bool {
|
||||
if state.slack() <= 0 {
|
||||
return false;
|
||||
}
|
||||
let mut best: Option<(usize, usize, f64)> = None;
|
||||
for &rm in &*worst_used {
|
||||
let w_rm = state.ch.weights[rm] as i64;
|
||||
for &cand in &*best_unused {
|
||||
let w_c = state.ch.weights[cand] as i64;
|
||||
if w_c <= w_rm {
|
||||
continue;
|
||||
}
|
||||
let dw = (w_c - w_rm) as i64;
|
||||
if dw as usize > params.diff_lim {
|
||||
continue;
|
||||
}
|
||||
if state.slack() < dw {
|
||||
continue;
|
||||
}
|
||||
let delta = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if delta > 0 {
|
||||
let ratio = (delta as f64) / (dw as f64);
|
||||
if best.map_or(true, |(_, _, br)| ratio > br) {
|
||||
best = Some((cand, rm, ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best {
|
||||
state.replace_item(rm, cand);
|
||||
State::remove_from_vec(worst_used, rm);
|
||||
best_unused.push(rm);
|
||||
State::remove_from_vec(best_unused, cand);
|
||||
worst_used.push(cand);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn polish_once(state: &mut State, params: &Params) {
|
||||
let n = state.ch.difficulty.num_items;
|
||||
let k = params.polish_k.min(n).max(16);
|
||||
let mut idx: Vec<usize> = (0..n).collect();
|
||||
idx.sort_unstable_by(|&a, &b| {
|
||||
let ra = (state.contrib[a] as f64) / (state.ch.weights[a] as f64);
|
||||
let rb = (state.contrib[b] as f64) / (state.ch.weights[b] as f64);
|
||||
rb.partial_cmp(&ra).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
let mut unused_top: Vec<usize> = Vec::new();
|
||||
for &i in &idx {
|
||||
if !state.selected_bit[i] {
|
||||
unused_top.push(i);
|
||||
if unused_top.len() >= k {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut used_worst: Vec<usize> = Vec::new();
|
||||
for &i in idx.iter().rev() {
|
||||
if state.selected_bit[i] {
|
||||
used_worst.push(i);
|
||||
if used_worst.len() >= k {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut best_add: Option<(usize, i64)> = None;
|
||||
let slack0 = state.slack();
|
||||
if slack0 > 0 {
|
||||
for &i in &unused_top {
|
||||
let w = state.ch.weights[i] as i64;
|
||||
if w <= slack0 {
|
||||
let d = state.contrib[i] as i64;
|
||||
if d > 0 && best_add.map_or(true, |(_, bd)| d > bd) {
|
||||
best_add = Some((i, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((i, _)) = best_add {
|
||||
state.add_item(i);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut best_swap: Option<(usize, usize, i64)> = None;
|
||||
for &rm in &used_worst {
|
||||
for &cand in &unused_top {
|
||||
if state.ch.weights[cand] != state.ch.weights[rm] {
|
||||
continue;
|
||||
}
|
||||
let d = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if d > 0 && best_swap.map_or(true, |(_, _, bd)| d > bd) {
|
||||
best_swap = Some((cand, rm, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best_swap {
|
||||
state.replace_item(rm, cand);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut best_swap_red: Option<(usize, usize, i64)> = None;
|
||||
for &rm in &used_worst {
|
||||
let w_rm = state.ch.weights[rm] as i64;
|
||||
for &cand in &unused_top {
|
||||
let w_c = state.ch.weights[cand] as i64;
|
||||
if w_c >= w_rm {
|
||||
continue;
|
||||
}
|
||||
let dw = (w_rm - w_c) as usize;
|
||||
if dw == 0 || dw > params.diff_lim {
|
||||
continue;
|
||||
}
|
||||
let d = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if d > 0 && best_swap_red.map_or(true, |(_, _, bd)| d > bd) {
|
||||
best_swap_red = Some((cand, rm, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _)) = best_swap_red {
|
||||
state.replace_item(rm, cand);
|
||||
return;
|
||||
}
|
||||
|
||||
if state.slack() > 0 {
|
||||
let mut best_swap_inc: Option<(usize, usize, f64, i64)> = None;
|
||||
for &rm in &used_worst {
|
||||
let w_rm = state.ch.weights[rm] as i64;
|
||||
for &cand in &unused_top {
|
||||
let w_c = state.ch.weights[cand] as i64;
|
||||
if w_c <= w_rm {
|
||||
continue;
|
||||
}
|
||||
let dw = w_c - w_rm;
|
||||
if dw as usize > params.diff_lim {
|
||||
continue;
|
||||
}
|
||||
if state.slack() < dw {
|
||||
continue;
|
||||
}
|
||||
let d = (state.contrib[cand] as i64)
|
||||
- (state.contrib[rm] as i64)
|
||||
- (state.ch.interaction_values[cand][rm] as i64);
|
||||
if d > 0 {
|
||||
let r = (d as f64) / (dw as f64);
|
||||
if best_swap_inc.map_or(true, |(_, _, br, bd)| d > bd || (d == bd && r > br)) {
|
||||
best_swap_inc = Some((cand, rm, r, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((cand, rm, _, _)) = best_swap_inc {
|
||||
state.replace_item(rm, cand);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strategic_perturb_and_rebuild(state: &mut State, params: &Params) {
|
||||
let sel = state.selected_items();
|
||||
let m = sel.len();
|
||||
if m == 0 {
|
||||
return;
|
||||
}
|
||||
let mut bad = sel.clone();
|
||||
bad.sort_unstable_by(|&a, &b| {
|
||||
let ra = (state.contrib[a] as f64) / (state.ch.weights[a] as f64);
|
||||
let rb = (state.contrib[b] as f64) / (state.ch.weights[b] as f64);
|
||||
ra.partial_cmp(&rb).unwrap_or(Ordering::Equal)
|
||||
});
|
||||
let mut rem = (m / 10).max(1);
|
||||
if rem > 10 {
|
||||
rem = 10;
|
||||
}
|
||||
rem = rem.min(bad.len());
|
||||
for i in 0..rem {
|
||||
let r = bad[i];
|
||||
if state.selected_bit[r] {
|
||||
state.remove_item(r);
|
||||
}
|
||||
}
|
||||
let (best_unused, _) = build_ls_windows(state, params.core_half_ls);
|
||||
for &cand in &best_unused {
|
||||
let w = state.ch.weights[cand] as i64;
|
||||
if w <= state.slack() && (state.contrib[cand] as i64) > 0 {
|
||||
state.add_item(cand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn local_search_vnd(state: &mut State, params: &Params) {
|
||||
let (mut best_unused, mut worst_used) = build_ls_windows(state, params.core_half_ls);
|
||||
loop {
|
||||
if apply_best_add_windowed(state, &mut best_unused, &mut worst_used) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if state.slack() > 0 {
|
||||
let mut best_pair: Option<(usize, usize, i64)> = None;
|
||||
let slack = state.slack();
|
||||
let bu_len = best_unused.len();
|
||||
for a_i in 0..bu_len {
|
||||
let i = best_unused[a_i];
|
||||
let wi = state.ch.weights[i] as i64;
|
||||
if wi >= slack {
|
||||
continue;
|
||||
}
|
||||
let ci = state.contrib[i] as i64;
|
||||
for a_j in (a_i + 1)..bu_len {
|
||||
let j = best_unused[a_j];
|
||||
let wj = state.ch.weights[j] as i64;
|
||||
let wsum = wi + wj;
|
||||
if wsum > slack {
|
||||
continue;
|
||||
}
|
||||
let cj = state.contrib[j] as i64;
|
||||
let syn = state.ch.interaction_values[i][j] as i64;
|
||||
let delta = ci + cj + syn;
|
||||
if delta > 0 && best_pair.map_or(true, |(_, _, bd)| delta > bd) {
|
||||
best_pair = Some((i, j, delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((i, j, _)) = best_pair {
|
||||
state.add_item(i);
|
||||
state.add_item(j);
|
||||
State::remove_from_vec(&mut best_unused, i);
|
||||
State::remove_from_vec(&mut best_unused, j);
|
||||
worst_used.push(i);
|
||||
worst_used.push(j);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if apply_best_swap_diff_reduce_windowed(state, params, &mut best_unused, &mut worst_used) {
|
||||
continue;
|
||||
}
|
||||
if apply_best_swap11_equal_windowed(state, &mut best_unused, &mut worst_used) {
|
||||
continue;
|
||||
}
|
||||
if apply_best_swap_diff_increase_windowed(state, params, &mut best_unused, &mut worst_used)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let n = challenge.difficulty.num_items;
|
||||
let params = Params::for_problem_size(n);
|
||||
let mut build_scores = vec![0i32; n];
|
||||
round0_scores(challenge, &mut build_scores);
|
||||
|
||||
let mut state = State::new_empty(challenge);
|
||||
build_initial_solution(&mut state, &build_scores);
|
||||
local_search_vnd(&mut state, ¶ms);
|
||||
polish_once(&mut state, ¶ms);
|
||||
|
||||
for _it in 0..params.n_maxils {
|
||||
let prev_sel = state.selected_items();
|
||||
let prev_val = state.total_value;
|
||||
let prev_contrib = state.contrib.clone();
|
||||
|
||||
let target = integer_core_target(challenge, &state.contrib, params.core_half_dp);
|
||||
apply_dp_target_via_ops(&mut state, &target);
|
||||
local_search_vnd(&mut state, ¶ms);
|
||||
polish_once(&mut state, ¶ms);
|
||||
|
||||
if state.total_value > prev_val {
|
||||
continue;
|
||||
}
|
||||
|
||||
state.restore_snapshot(&prev_sel, prev_contrib.clone(), prev_val);
|
||||
strategic_perturb_and_rebuild(&mut state, ¶ms);
|
||||
local_search_vnd(&mut state, ¶ms);
|
||||
polish_once(&mut state, ¶ms);
|
||||
|
||||
if state.total_value <= prev_val {
|
||||
state.restore_snapshot(&prev_sel, prev_contrib, prev_val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut items = state.selected_items();
|
||||
items.sort_unstable();
|
||||
let _ = save_solution(&Solution { items });
|
||||
Ok(())
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
// c003_a001
|
||||
pub mod dynamic;
|
||||
pub use dynamic as c003_a001;
|
||||
|
||||
// c003_a002
|
||||
|
||||
@ -10,7 +11,8 @@
|
||||
|
||||
// c003_a006
|
||||
|
||||
// c003_a007
|
||||
pub mod knapmaxxing;
|
||||
pub use knapmaxxing as c003_a007;
|
||||
|
||||
// c003_a008
|
||||
|
||||
@ -34,7 +36,8 @@
|
||||
|
||||
// c003_a018
|
||||
|
||||
// c003_a019
|
||||
pub mod knapheudp;
|
||||
pub use knapheudp as c003_a019;
|
||||
|
||||
// c003_a020
|
||||
|
||||
@ -98,13 +101,15 @@
|
||||
|
||||
// c003_a050
|
||||
|
||||
// c003_a051
|
||||
pub mod classic_quadkp;
|
||||
pub use classic_quadkp as c003_a051;
|
||||
|
||||
// c003_a052
|
||||
|
||||
// c003_a053
|
||||
|
||||
// c003_a054
|
||||
pub mod quadkp_improved;
|
||||
pub use quadkp_improved as c003_a054;
|
||||
|
||||
// c003_a055
|
||||
|
||||
@ -116,7 +121,8 @@
|
||||
|
||||
// c003_a059
|
||||
|
||||
// c003_a060
|
||||
pub mod knap_one;
|
||||
pub use knap_one as c003_a060;
|
||||
|
||||
// c003_a061
|
||||
|
||||
@ -124,9 +130,11 @@
|
||||
|
||||
// c003_a063
|
||||
|
||||
// c003_a064
|
||||
pub mod quadkp_maximize;
|
||||
pub use quadkp_maximize as c003_a064;
|
||||
|
||||
// c003_a065
|
||||
pub mod relative_quad_fast;
|
||||
pub use relative_quad_fast as c003_a065;
|
||||
|
||||
// c003_a066
|
||||
|
||||
@ -134,7 +142,8 @@
|
||||
|
||||
// c003_a068
|
||||
|
||||
// c003_a069
|
||||
pub mod new_relative_ultra;
|
||||
pub use new_relative_ultra as c003_a069;
|
||||
|
||||
// c003_a070
|
||||
|
||||
@ -146,13 +155,16 @@
|
||||
|
||||
// c003_a074
|
||||
|
||||
// c003_a075
|
||||
pub mod relative_opt_fast;
|
||||
pub use relative_opt_fast as c003_a075;
|
||||
|
||||
// c003_a076
|
||||
pub mod relative_opt_mid;
|
||||
pub use relative_opt_mid as c003_a076;
|
||||
|
||||
// c003_a077
|
||||
|
||||
// c003_a078
|
||||
pub mod relative_opt_optima;
|
||||
pub use relative_opt_optima as c003_a078;
|
||||
|
||||
// c003_a079
|
||||
|
||||
@ -174,7 +186,8 @@
|
||||
|
||||
// c003_a088
|
||||
|
||||
// c003_a089
|
||||
pub mod relative_raw_ultra;
|
||||
pub use relative_raw_ultra as c003_a089;
|
||||
|
||||
// c003_a090
|
||||
|
||||
@ -182,17 +195,21 @@
|
||||
|
||||
// c003_a092
|
||||
|
||||
// c003_a093
|
||||
pub mod knapsack_redone;
|
||||
pub use knapsack_redone as c003_a093;
|
||||
|
||||
// c003_a094
|
||||
pub mod native_knapsack;
|
||||
pub use native_knapsack as c003_a094;
|
||||
|
||||
// c003_a095
|
||||
|
||||
// c003_a096
|
||||
|
||||
// c003_a097
|
||||
pub mod fast_and_fun;
|
||||
pub use fast_and_fun as c003_a097;
|
||||
|
||||
// c003_a098
|
||||
pub mod knapsplatt;
|
||||
pub use knapsplatt as c003_a098;
|
||||
|
||||
// c003_a099
|
||||
|
||||
|
||||
23
tig-algorithms/src/knapsack/native_knapsack/README.md
Normal file
23
tig-algorithms/src/knapsack/native_knapsack/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** native_knapsack
|
||||
* **Copyright:** 2025 Rootz
|
||||
* **Identity of Submitter:** Rootz
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
396
tig-algorithms/src/knapsack/native_knapsack/mod.rs
Normal file
396
tig-algorithms/src/knapsack/native_knapsack/mod.rs
Normal file
@ -0,0 +1,396 @@
|
||||
use anyhow::Result;
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
#[inline]
|
||||
fn calculate_density_variance(item_densities: &[(usize, f32)]) -> f32 {
|
||||
if item_densities.len() < 2 {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
let mut sum = 0.0;
|
||||
let mut variance_sum = 0.0;
|
||||
let len = item_densities.len() as f32;
|
||||
|
||||
for (_, density) in item_densities {
|
||||
sum += *density;
|
||||
}
|
||||
let mean = sum / len;
|
||||
|
||||
for (_, density) in item_densities {
|
||||
let diff = *density - mean;
|
||||
variance_sum += diff * diff;
|
||||
}
|
||||
let variance = variance_sum / len;
|
||||
|
||||
(variance.sqrt() / mean.abs()).clamp(0.1, 1.0)
|
||||
}
|
||||
|
||||
fn compute_solution(
|
||||
challenge: &Challenge,
|
||||
contribution_list: &mut [i32],
|
||||
unselected_items: &mut Vec<usize>,
|
||||
rng: &mut StdRng,
|
||||
) -> Result<Option<(Solution, i32)>> {
|
||||
let mut selected_items = Vec::new();
|
||||
let mut total_weight = 0;
|
||||
let mut total_value = 0;
|
||||
|
||||
let inv_weights: Vec<f32> = challenge.weights.iter().map(|&w| 1.0 / w as f32).collect();
|
||||
let rcl_max = 10;
|
||||
let max_item_weight = *challenge.weights.iter().max().unwrap();
|
||||
|
||||
let mut item_densities: Vec<(usize, f32)> = Vec::with_capacity(unselected_items.len());
|
||||
for &idx in unselected_items.iter() {
|
||||
let ratio = contribution_list[idx] as f32 * inv_weights[idx];
|
||||
item_densities.push((idx, ratio));
|
||||
}
|
||||
|
||||
let density_variance = calculate_density_variance(&item_densities);
|
||||
|
||||
let num_items = challenge.difficulty.num_items as f32;
|
||||
|
||||
let mut densities: Vec<f32> = item_densities.iter().map(|(_, d)| *d).collect();
|
||||
densities.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
|
||||
let adaptive_range = if num_items >= 500.0 {
|
||||
if densities.len() >= 10 {
|
||||
let p10_idx = densities.len() / 10;
|
||||
let p90_idx = densities.len() * 9 / 10;
|
||||
let p10 = densities[p10_idx];
|
||||
let p90 = densities[p90_idx];
|
||||
let density_range = (p90 - p10).abs();
|
||||
(density_range * 0.3).clamp(0.8, 2.2)
|
||||
} else {
|
||||
1.5
|
||||
}
|
||||
} else {
|
||||
1.5
|
||||
};
|
||||
|
||||
let base_exponent = if num_items >= 500.0 {
|
||||
1.4 - ((num_items - 500.0) / 200.0).sqrt() * 0.2
|
||||
} else {
|
||||
1.4
|
||||
};
|
||||
let adaptive_exponent =
|
||||
base_exponent + (density_variance - 0.6).clamp(-adaptive_range, adaptive_range);
|
||||
|
||||
let mut probs: Vec<f32> = Vec::with_capacity(rcl_max);
|
||||
for rank in 0..rcl_max {
|
||||
probs.push(1.0 / ((rank + 1) as f32).powf(adaptive_exponent));
|
||||
}
|
||||
|
||||
let mut acc_probs: Vec<f32> = Vec::with_capacity(rcl_max);
|
||||
let mut sum = 0.0;
|
||||
for &prob in &probs {
|
||||
sum += prob;
|
||||
acc_probs.push(sum);
|
||||
}
|
||||
let total_prob_max = sum;
|
||||
|
||||
let list_size = 2;
|
||||
let mut top_ranks = vec![0; list_size];
|
||||
|
||||
while !item_densities.is_empty() {
|
||||
let num_candidates = item_densities.len();
|
||||
if num_candidates < 2 {
|
||||
break;
|
||||
}
|
||||
|
||||
let actual_rcl_size = num_candidates.min(rcl_max);
|
||||
let total_prob = if actual_rcl_size == rcl_max {
|
||||
total_prob_max
|
||||
} else {
|
||||
acc_probs[actual_rcl_size - 1]
|
||||
};
|
||||
|
||||
let random_threshold = rng.gen_range(0.0..total_prob);
|
||||
let mut selected_rank = match acc_probs[..actual_rcl_size]
|
||||
.binary_search_by(|prob| prob.partial_cmp(&random_threshold).unwrap())
|
||||
{
|
||||
Ok(i) | Err(i) => i,
|
||||
};
|
||||
if selected_rank >= actual_rcl_size {
|
||||
selected_rank = actual_rcl_size - 1;
|
||||
}
|
||||
|
||||
let selected_item;
|
||||
if selected_rank < list_size
|
||||
&& !selected_items.is_empty()
|
||||
&& top_ranks[selected_rank] < item_densities.len()
|
||||
{
|
||||
selected_rank = top_ranks[selected_rank];
|
||||
selected_item = item_densities[selected_rank].0;
|
||||
} else {
|
||||
item_densities
|
||||
.select_nth_unstable_by(selected_rank, |a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
selected_item = item_densities[selected_rank].0;
|
||||
}
|
||||
|
||||
selected_items.push(selected_item);
|
||||
total_weight += challenge.weights[selected_item];
|
||||
total_value += contribution_list[selected_item];
|
||||
|
||||
if total_weight + max_item_weight > challenge.max_weight {
|
||||
item_densities.retain(|(idx, _)| {
|
||||
total_weight + challenge.weights[*idx] <= challenge.max_weight
|
||||
&& *idx != selected_item
|
||||
});
|
||||
} else {
|
||||
item_densities.swap_remove(selected_rank);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) += *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(selected_item)
|
||||
.get_unchecked(x);
|
||||
}
|
||||
|
||||
let mut first_density = f32::MIN;
|
||||
let mut first_rank = 0;
|
||||
let mut second_density = f32::MIN;
|
||||
let mut second_rank = 0;
|
||||
|
||||
for (i, density) in item_densities.iter_mut().enumerate() {
|
||||
let interaction = *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(selected_item)
|
||||
.get_unchecked(density.0);
|
||||
density.1 += interaction as f32 * inv_weights[density.0];
|
||||
let current_density = density.1;
|
||||
|
||||
if current_density > first_density {
|
||||
second_density = first_density;
|
||||
second_rank = first_rank;
|
||||
first_density = current_density;
|
||||
first_rank = i;
|
||||
} else if current_density > second_density {
|
||||
second_density = current_density;
|
||||
second_rank = i;
|
||||
}
|
||||
}
|
||||
|
||||
top_ranks[0] = first_rank;
|
||||
top_ranks[1] = second_rank;
|
||||
}
|
||||
}
|
||||
|
||||
unselected_items.clear();
|
||||
unselected_items.extend(0..challenge.difficulty.num_items);
|
||||
|
||||
selected_items.sort_unstable_by(|a, b| b.cmp(a));
|
||||
for &selected in &selected_items {
|
||||
unselected_items.swap_remove(selected);
|
||||
}
|
||||
|
||||
unselected_items.sort_unstable_by_key(|&idx| challenge.weights[idx]);
|
||||
|
||||
let mut feasible_adds = Vec::with_capacity(100);
|
||||
let mut feasible_swaps = Vec::with_capacity(200);
|
||||
|
||||
for _ in 0..50 {
|
||||
let mut improved = false;
|
||||
|
||||
if total_weight < challenge.max_weight {
|
||||
feasible_adds.clear();
|
||||
for (i, &cand) in unselected_items.iter().enumerate() {
|
||||
if total_weight + challenge.weights[cand] > challenge.max_weight {
|
||||
break;
|
||||
}
|
||||
let new_val = total_value + contribution_list[cand];
|
||||
if new_val > total_value {
|
||||
feasible_adds.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
if !feasible_adds.is_empty() {
|
||||
let add_idx = feasible_adds[rng.gen_range(0..feasible_adds.len())];
|
||||
let new_item = unselected_items[add_idx];
|
||||
|
||||
unselected_items.remove(add_idx);
|
||||
selected_items.push(new_item);
|
||||
total_weight += challenge.weights[new_item];
|
||||
total_value += contribution_list[new_item];
|
||||
improved = true;
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) += *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(x)
|
||||
.get_unchecked(new_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !improved {
|
||||
feasible_swaps.clear();
|
||||
let free_capacity = challenge.max_weight - total_weight;
|
||||
|
||||
'outer: for (j, &rem_item) in selected_items.iter().enumerate() {
|
||||
let rem_w = challenge.weights[rem_item];
|
||||
let available_weight = free_capacity + rem_w;
|
||||
|
||||
for (i, &cand_item) in unselected_items.iter().enumerate() {
|
||||
if challenge.weights[cand_item] > available_weight {
|
||||
break;
|
||||
}
|
||||
|
||||
let val_diff = contribution_list[cand_item]
|
||||
- contribution_list[rem_item]
|
||||
- challenge.interaction_values[cand_item][rem_item];
|
||||
if val_diff > 0 {
|
||||
feasible_swaps.push((i, j));
|
||||
if feasible_swaps.len() >= 50 {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !feasible_swaps.is_empty() {
|
||||
let (unsel_idx, sel_idx) = feasible_swaps[rng.gen_range(0..feasible_swaps.len())];
|
||||
let new_item = unselected_items[unsel_idx];
|
||||
let remove_item = selected_items[sel_idx];
|
||||
|
||||
selected_items.swap_remove(sel_idx);
|
||||
selected_items.push(new_item);
|
||||
unselected_items.remove(unsel_idx);
|
||||
|
||||
let insert_pos = unselected_items
|
||||
.binary_search_by_key(&challenge.weights[remove_item], |&idx| {
|
||||
challenge.weights[idx]
|
||||
})
|
||||
.unwrap_or_else(|e| e);
|
||||
unselected_items.insert(insert_pos, remove_item);
|
||||
|
||||
total_value += contribution_list[new_item]
|
||||
- contribution_list[remove_item]
|
||||
- challenge.interaction_values[new_item][remove_item];
|
||||
total_weight =
|
||||
total_weight + challenge.weights[new_item] - challenge.weights[remove_item];
|
||||
improved = true;
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) += *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(x)
|
||||
.get_unchecked(new_item)
|
||||
- *challenge
|
||||
.interaction_values
|
||||
.get_unchecked(x)
|
||||
.get_unchecked(remove_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !improved {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if selected_items.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((
|
||||
Solution {
|
||||
items: selected_items,
|
||||
},
|
||||
total_value,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let num_iterations = 11;
|
||||
let mut rng =
|
||||
StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()));
|
||||
|
||||
let mut best_solution: Option<Solution> = None;
|
||||
let mut best_value = 0;
|
||||
|
||||
let mut unselected_items: Vec<usize> = Vec::with_capacity(challenge.difficulty.num_items);
|
||||
let mut contribution_list: Vec<i32> = Vec::with_capacity(challenge.values.len());
|
||||
|
||||
for _outer_iter in 0..num_iterations {
|
||||
let mut best_local_solution: Option<Solution> = None;
|
||||
let mut best_local_value = 0;
|
||||
|
||||
let k = 2;
|
||||
for _ in 0..k {
|
||||
unselected_items.clear();
|
||||
unselected_items.reserve(challenge.difficulty.num_items);
|
||||
for i in 0..challenge.difficulty.num_items {
|
||||
unselected_items.push(i);
|
||||
}
|
||||
|
||||
contribution_list.clear();
|
||||
contribution_list.reserve(challenge.values.len());
|
||||
for &v in &challenge.values {
|
||||
contribution_list.push(v as i32);
|
||||
}
|
||||
|
||||
let sol_result = compute_solution(
|
||||
challenge,
|
||||
&mut contribution_list,
|
||||
&mut unselected_items,
|
||||
&mut rng,
|
||||
)?;
|
||||
|
||||
let (solution, value) = match sol_result {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if value > best_local_value {
|
||||
best_local_value = value;
|
||||
best_local_solution = Some(Solution {
|
||||
items: solution.items.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(local_solution) = best_local_solution {
|
||||
if best_local_value > best_value {
|
||||
best_value = best_local_value;
|
||||
best_solution = Some(local_solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(solution) = best_solution {
|
||||
let _ = save_solution(&solution);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
mod gpu_optimisation {
|
||||
use super::*;
|
||||
use cudarc::driver::*;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tig_challenges::CudaKernel;
|
||||
|
||||
pub const KERNEL: Option<CudaKernel> = None;
|
||||
|
||||
pub fn cuda_solve_challenge(
|
||||
challenge: &Challenge,
|
||||
dev: &Arc<CudaDevice>,
|
||||
mut funcs: HashMap<&'static str, CudaFunction>,
|
||||
) -> anyhow::Result<Option<Solution>> {
|
||||
solve_challenge(challenge)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cuda")]
|
||||
pub use gpu_optimisation::{cuda_solve_challenge, KERNEL};
|
||||
23
tig-algorithms/src/knapsack/new_relative_ultra/README.md
Normal file
23
tig-algorithms/src/knapsack/new_relative_ultra/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** new_relative_ultra
|
||||
* **Copyright:** 2025 syebastian
|
||||
* **Identity of Submitter:** syebastian
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
279
tig-algorithms/src/knapsack/new_relative_ultra/mod.rs
Normal file
279
tig-algorithms/src/knapsack/new_relative_ultra/mod.rs
Normal file
@ -0,0 +1,279 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
use anyhow::Result;
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
fn compute_solution(
|
||||
challenge: &SubInstance,
|
||||
contribution_list: &mut [i32],
|
||||
unselected_items: &mut Vec<usize>,
|
||||
rng: &mut StdRng,
|
||||
) -> Result<Option<(SubSolution, i32)>> {
|
||||
let mut selected_items = Vec::new();
|
||||
let mut total_weight = 0;
|
||||
let mut total_value = 0;
|
||||
|
||||
const RCL_MAX: usize = 10;
|
||||
|
||||
let probs: Vec<f32> = (0..RCL_MAX)
|
||||
.map(|rank| 1.0 / ((rank + 1) as f32).exp())
|
||||
.collect();
|
||||
|
||||
let mut acc_probs: Vec<f32> = Vec::with_capacity(RCL_MAX);
|
||||
let mut sum = 0.0;
|
||||
for &prob in &probs {
|
||||
sum += prob;
|
||||
acc_probs.push(sum);
|
||||
}
|
||||
let total_prob_max = sum;
|
||||
let max_item_weight = challenge.weights.iter().max().unwrap();
|
||||
|
||||
let mut item_densities: Vec<(usize, f32)> = unselected_items
|
||||
.iter()
|
||||
.map(|&idx| {
|
||||
let ratio = contribution_list[idx] as f32 / challenge.weights[idx] as f32;
|
||||
(idx, ratio)
|
||||
})
|
||||
.collect();
|
||||
|
||||
while !item_densities.is_empty() {
|
||||
let num_candidates = item_densities.len();
|
||||
if num_candidates < 2 {
|
||||
break;
|
||||
}
|
||||
|
||||
let actual_rcl_size = num_candidates.min(RCL_MAX);
|
||||
|
||||
let total_prob = if actual_rcl_size == RCL_MAX {
|
||||
total_prob_max
|
||||
} else {
|
||||
acc_probs[actual_rcl_size - 1]
|
||||
};
|
||||
|
||||
let random_threshold = rng.gen_range(0.0..total_prob);
|
||||
let mut selected_rank = match acc_probs[..actual_rcl_size].binary_search_by(|prob| {
|
||||
prob.partial_cmp(&random_threshold).unwrap()
|
||||
}) { Ok(i) | Err(i) => i };
|
||||
if selected_rank >= actual_rcl_size {
|
||||
selected_rank = actual_rcl_size - 1;
|
||||
}
|
||||
|
||||
item_densities.select_nth_unstable_by(selected_rank, |a, b| {
|
||||
b.1.partial_cmp(&a.1).unwrap()
|
||||
});
|
||||
let selected_item = item_densities[selected_rank].0;
|
||||
|
||||
selected_items.push(selected_item);
|
||||
total_weight += challenge.weights[selected_item];
|
||||
total_value += contribution_list[selected_item];
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) +=
|
||||
*challenge.interaction_values.get_unchecked(x).get_unchecked(selected_item);
|
||||
}
|
||||
}
|
||||
|
||||
if total_weight + max_item_weight > challenge.max_weight {
|
||||
item_densities.retain(|(idx, _)| {
|
||||
total_weight + challenge.weights[*idx] <= challenge.max_weight && *idx != selected_item
|
||||
});
|
||||
} else {
|
||||
item_densities.swap_remove(selected_rank);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for density in item_densities.iter_mut() {
|
||||
let interaction = *challenge.interaction_values.get_unchecked(selected_item).get_unchecked(density.0);
|
||||
let w = *challenge.weights.get_unchecked(density.0) as f32;
|
||||
density.1 += interaction as f32 / w;
|
||||
}
|
||||
}
|
||||
}
|
||||
unselected_items.clear();
|
||||
unselected_items.extend(0..challenge.difficulty.num_items);
|
||||
|
||||
let mut sorted_selected = selected_items.clone();
|
||||
sorted_selected.sort_unstable_by(|a, b| b.cmp(a));
|
||||
|
||||
for &selected in &sorted_selected {
|
||||
unselected_items.swap_remove(selected);
|
||||
}
|
||||
|
||||
let local_search_iterations = 150;
|
||||
for _ in 0..local_search_iterations {
|
||||
let mut improved = false;
|
||||
|
||||
let mut feasible_adds = Vec::new();
|
||||
for (i, &cand) in unselected_items.iter().enumerate() {
|
||||
let new_w = total_weight + challenge.weights[cand];
|
||||
let new_val = total_value + contribution_list[cand];
|
||||
if new_w <= challenge.max_weight && new_val >= total_value {
|
||||
feasible_adds.push(i);
|
||||
}
|
||||
}
|
||||
if !feasible_adds.is_empty() {
|
||||
let pick = rng.gen_range(0..feasible_adds.len());
|
||||
let add_idx = feasible_adds[pick];
|
||||
let new_item = unselected_items[add_idx];
|
||||
|
||||
unselected_items.swap_remove(add_idx);
|
||||
selected_items.push(new_item);
|
||||
|
||||
total_weight += challenge.weights[new_item];
|
||||
total_value += contribution_list[new_item];
|
||||
improved = true;
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) +=
|
||||
*challenge.interaction_values.get_unchecked(x).get_unchecked(new_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut feasible_swaps = Vec::new();
|
||||
for (i, &cand_item) in unselected_items.iter().enumerate() {
|
||||
let min_needed =
|
||||
challenge.weights[cand_item] as i32 - (challenge.max_weight as i32 - total_weight as i32);
|
||||
for (j, &rem_item) in selected_items.iter().enumerate() {
|
||||
let rem_w = challenge.weights[rem_item] as i32;
|
||||
if rem_w < min_needed {
|
||||
continue;
|
||||
}
|
||||
let val_diff = contribution_list[cand_item]
|
||||
- contribution_list[rem_item]
|
||||
- challenge.interaction_values[cand_item][rem_item];
|
||||
if val_diff >= 0 {
|
||||
feasible_swaps.push((i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !feasible_swaps.is_empty() {
|
||||
let pick = rng.gen_range(0..feasible_swaps.len());
|
||||
let (unsel_idx, sel_idx) = feasible_swaps[pick];
|
||||
let new_item = unselected_items[unsel_idx];
|
||||
let remove_item = selected_items[sel_idx];
|
||||
|
||||
selected_items.swap_remove(sel_idx);
|
||||
unselected_items.swap_remove(unsel_idx);
|
||||
selected_items.push(new_item);
|
||||
unselected_items.push(remove_item);
|
||||
|
||||
total_value += contribution_list[new_item]
|
||||
- contribution_list[remove_item]
|
||||
- challenge.interaction_values[new_item][remove_item];
|
||||
total_weight = total_weight + challenge.weights[new_item] - challenge.weights[remove_item];
|
||||
improved = true;
|
||||
|
||||
unsafe {
|
||||
for x in 0..challenge.difficulty.num_items {
|
||||
*contribution_list.get_unchecked_mut(x) +=
|
||||
*challenge.interaction_values.get_unchecked(x).get_unchecked(new_item) -
|
||||
*challenge.interaction_values.get_unchecked(x).get_unchecked(remove_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !improved {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if selected_items.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((SubSolution { items: selected_items }, total_value)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> Result<Option<SubSolution>> {
|
||||
let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(
|
||||
challenge.seed[..8].try_into().unwrap(),
|
||||
));
|
||||
|
||||
let mut best_solution: Option<SubSolution> = None;
|
||||
let mut best_value = 0;
|
||||
|
||||
for _outer_iter in 0..50 {
|
||||
let mut unselected_items: Vec<usize> = (0..challenge.difficulty.num_items).collect();
|
||||
let mut contribution_list = challenge
|
||||
.values
|
||||
.iter()
|
||||
.map(|&v| v as i32)
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
let sol_result =
|
||||
compute_solution(challenge, &mut contribution_list, &mut unselected_items, &mut rng)?;
|
||||
|
||||
let (solution, value) = match sol_result {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if value > best_value {
|
||||
best_value = value;
|
||||
best_solution = Some(SubSolution { items: solution.items.clone() });
|
||||
}
|
||||
|
||||
let threshold = lookup_threshold(challenge.difficulty.num_items);
|
||||
if (challenge.baseline_value as f32) * (1.0 - threshold * 0.008) >= best_value as f32 {
|
||||
return Ok(None);
|
||||
}
|
||||
else if challenge.baseline_value <= best_value as u32 {
|
||||
return Ok(best_solution);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(best_solution)
|
||||
}
|
||||
|
||||
fn lookup_threshold(num_items: usize) -> f32 {
|
||||
let points = vec![
|
||||
(100, 1.071), (105, 1.015), (110, 0.973), (120, 0.882),
|
||||
(125, 0.791), (130, 0.770), (135, 0.760), (140, 0.749),
|
||||
(145, 0.700), (150, 0.616), (155, 0.574), (160, 0.532),
|
||||
(165, 0.511), (170, 0.494), (175, 0.485), (180, 0.476),
|
||||
(190, 0.448), (195, 0.434), (200, 0.427), (205, 0.420),
|
||||
(210, 0.420), (215, 0.385), (220, 0.350), (225, 0.347),
|
||||
(230, 0.343), (235, 0.343), (240, 0.338), (245, 0.334),
|
||||
(250, 0.329)
|
||||
];
|
||||
|
||||
points.iter()
|
||||
.filter(|&&(x, _)| x <= num_items)
|
||||
.max_by_key(|&&(x, _)| x)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/quadkp_improved/README.md
Normal file
23
tig-algorithms/src/knapsack/quadkp_improved/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** quadkp_improved
|
||||
* **Copyright:** 2024 Rootz
|
||||
* **Identity of Submitter:** Rootz
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
184
tig-algorithms/src/knapsack/quadkp_improved/mod.rs
Normal file
184
tig-algorithms/src/knapsack/quadkp_improved/mod.rs
Normal file
@ -0,0 +1,184 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use serde_json::{Map, Value};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(
|
||||
challenge: &Challenge,
|
||||
save_solution: &dyn Fn(&Solution) -> Result<()>,
|
||||
hyperparameters: &Option<Map<String, Value>>,
|
||||
) -> Result<()> {
|
||||
Err(anyhow!("This algorithm is no longer compatible."))
|
||||
}
|
||||
|
||||
// Old code that is no longer compatible
|
||||
#[cfg(none)]
|
||||
mod dead_code {
|
||||
// TIG's UI uses the pattern `tig_challenges::<challenge_name>` to automatically detect your algorithm's challenge
|
||||
use anyhow::Result;
|
||||
use rand::{SeedableRng, Rng, rngs::StdRng};
|
||||
use tig_challenges::knapsack::*;
|
||||
|
||||
|
||||
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
|
||||
let mut solution = Solution {
|
||||
sub_solutions: Vec::new(),
|
||||
};
|
||||
for sub_instance in &challenge.sub_instances {
|
||||
match solve_sub_instance(sub_instance)? {
|
||||
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(Some(solution))
|
||||
}
|
||||
|
||||
pub fn solve_sub_instance(challenge: &SubInstance) -> Result<Option<SubSolution>> {
|
||||
let vertex_count = challenge.weights.len();
|
||||
|
||||
let mut item_scores: Vec<(usize, f32)> = (0..vertex_count)
|
||||
.map(|index| {
|
||||
let interaction_sum: i32 = challenge.interaction_values[index].iter().sum();
|
||||
let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32;
|
||||
let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1)
|
||||
/ challenge.weights[index] as f32;
|
||||
(index, combined_score)
|
||||
})
|
||||
.collect();
|
||||
|
||||
item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
|
||||
let mut selected_items = Vec::with_capacity(vertex_count);
|
||||
let mut unselected_items = Vec::with_capacity(vertex_count);
|
||||
let mut current_weight = 0;
|
||||
let mut current_value = 0;
|
||||
|
||||
for &(index, _) in &item_scores {
|
||||
if current_weight + challenge.weights[index] <= challenge.max_weight {
|
||||
current_weight += challenge.weights[index];
|
||||
current_value += challenge.values[index] as i32;
|
||||
|
||||
for &selected in &selected_items {
|
||||
current_value += challenge.interaction_values[index][selected];
|
||||
}
|
||||
selected_items.push(index);
|
||||
} else {
|
||||
unselected_items.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
let mut mutation_rates = vec![0; vertex_count];
|
||||
for index in 0..vertex_count {
|
||||
mutation_rates[index] = challenge.values[index] as i32;
|
||||
for &selected in &selected_items {
|
||||
mutation_rates[index] += challenge.interaction_values[index][selected];
|
||||
}
|
||||
}
|
||||
|
||||
let max_generations = 150;
|
||||
let mut cooling_schedule = vec![0; vertex_count];
|
||||
let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64);
|
||||
|
||||
for generation in 0..max_generations {
|
||||
let mut best_gain = 0;
|
||||
let mut best_swap = None;
|
||||
|
||||
for (u_index, &mutant) in unselected_items.iter().enumerate() {
|
||||
if cooling_schedule[mutant] > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mutant_fitness = mutation_rates[mutant];
|
||||
let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32);
|
||||
|
||||
if mutant_fitness < 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (c_index, &selected) in selected_items.iter().enumerate() {
|
||||
if cooling_schedule[selected] > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight {
|
||||
continue;
|
||||
}
|
||||
|
||||
let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32;
|
||||
let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty;
|
||||
|
||||
if fitness_gain > best_gain {
|
||||
best_gain = fitness_gain;
|
||||
best_swap = Some((u_index, c_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((u_index, c_index)) = best_swap {
|
||||
let added_item = unselected_items[u_index];
|
||||
let removed_item = selected_items[c_index];
|
||||
|
||||
selected_items.swap_remove(c_index);
|
||||
unselected_items.swap_remove(u_index);
|
||||
selected_items.push(added_item);
|
||||
unselected_items.push(removed_item);
|
||||
|
||||
current_value += best_gain;
|
||||
current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item];
|
||||
|
||||
if current_weight > challenge.max_weight {
|
||||
continue;
|
||||
}
|
||||
|
||||
for index in 0..vertex_count {
|
||||
mutation_rates[index] += challenge.interaction_values[index][added_item]
|
||||
- challenge.interaction_values[index][removed_item];
|
||||
}
|
||||
|
||||
cooling_schedule[added_item] = 3;
|
||||
cooling_schedule[removed_item] = 3;
|
||||
}
|
||||
|
||||
if current_value as u32 >= challenge.baseline_value {
|
||||
return Ok(Some(SubSolution { items: selected_items }));
|
||||
}
|
||||
|
||||
for cooling_rate in cooling_schedule.iter_mut() {
|
||||
*cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 };
|
||||
}
|
||||
|
||||
if current_value as u32 > (challenge.baseline_value * 9 / 10) {
|
||||
let high_potential_items: Vec<usize> = unselected_items
|
||||
.iter()
|
||||
.filter(|&&i| challenge.values[i] as i32 > (challenge.baseline_value as i32 / 4))
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
for &item in high_potential_items.iter().take(2) {
|
||||
if current_weight + challenge.weights[item] <= challenge.max_weight {
|
||||
selected_items.push(item);
|
||||
unselected_items.retain(|&x| x != item);
|
||||
current_weight += challenge.weights[item];
|
||||
current_value += challenge.values[item] as i32;
|
||||
|
||||
for &selected in &selected_items {
|
||||
if selected != item {
|
||||
current_value += challenge.interaction_values[item][selected];
|
||||
}
|
||||
}
|
||||
|
||||
if current_value as u32 >= challenge.baseline_value {
|
||||
return Ok(Some(SubSolution { items: selected_items }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if current_value as u32 >= challenge.baseline_value && current_weight <= challenge.max_weight {
|
||||
Ok(Some(SubSolution { items: selected_items }))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
23
tig-algorithms/src/knapsack/quadkp_maximize/README.md
Normal file
23
tig-algorithms/src/knapsack/quadkp_maximize/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# TIG Code Submission
|
||||
|
||||
## Submission Details
|
||||
|
||||
* **Challenge Name:** knapsack
|
||||
* **Algorithm Name:** quadkp_maximize
|
||||
* **Copyright:** 2024 codes_r_us
|
||||
* **Identity of Submitter:** codes_r_us
|
||||
* **Identity of Creator of Algorithmic Method:** null
|
||||
* **Unique Algorithm Identifier (UAI):** null
|
||||
|
||||
## License
|
||||
|
||||
The files in this folder are under the following licenses:
|
||||
* TIG Benchmarker Outbound License
|
||||
* TIG Commercial License
|
||||
* TIG Inbound Game License
|
||||
* TIG Innovator Outbound Game License
|
||||
* TIG Open Data License
|
||||
* TIG THV Game License
|
||||
|
||||
Copies of the licenses can be obtained at:
|
||||
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user