Hypergraph 10k

作成日 差分は期限切れになりません
2つのテキストは同一です
これら2つのテキストの間に違いはありません
0 削除
596
0 追加
596
use cudarc::{
use cudarc::{
driver::{safe::LaunchConfig, CudaModule, CudaStream, PushKernelArg},
driver::{safe::LaunchConfig, CudaModule, CudaStream, PushKernelArg},
runtime::sys::cudaDeviceProp,
runtime::sys::cudaDeviceProp,
};
};
use serde_json::{Map, Value};
use serde_json::{Map, Value};
use std::sync::Arc;
use std::sync::Arc;
use tig_challenges::hypergraph::*;
use tig_challenges::hypergraph::*;


pub fn solve(
pub fn solve(
challenge: &Challenge,
challenge: &Challenge,
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
hyperparameters: &Option<Map<String, Value>>,
hyperparameters: &Option<Map<String, Value>>,
module: Arc<CudaModule>,
module: Arc<CudaModule>,
stream: Arc<CudaStream>,
stream: Arc<CudaStream>,
prop: &cudaDeviceProp,
prop: &cudaDeviceProp,
) -> anyhow::Result<()> {
) -> anyhow::Result<()> {
let block_size = std::cmp::min(128, prop.maxThreadsPerBlock as u32);
let block_size = std::cmp::min(128, prop.maxThreadsPerBlock as u32);


let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering_10k")?;
let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering_10k")?;
let compute_preferences_kernel = module.load_function("compute_node_preferences_10k")?;
let compute_preferences_kernel = module.load_function("compute_node_preferences_10k")?;
let execute_assignments_kernel = module.load_function("execute_node_assignments_10k")?;
let execute_assignments_kernel = module.load_function("execute_node_assignments_10k")?;
let precompute_edge_flags_kernel = module.load_function("precompute_edge_flags_10k")?;
let precompute_edge_flags_kernel = module.load_function("precompute_edge_flags_10k")?;
let compute_moves_kernel = module.load_function("compute_refinement_moves_optimized_10k")?;
let compute_moves_kernel = module.load_function("compute_refinement_moves_optimized_10k")?;
let execute_moves_kernel = module.load_function("execute_refinement_moves_10k")?;
let execute_moves_kernel = module.load_function("execute_refinement_moves_10k")?;
let balance_kernel = module.load_function("balance_final_10k")?;
let balance_kernel = module.load_function("balance_final_10k")?;
let compute_connectivity_kernel = module.load_function("compute_connectivity_10k")?;
let compute_connectivity_kernel = module.load_function("compute_connectivity_10k")?;
let perturb_kernel = module.load_function("perturb_solution_10k")?;
let perturb_kernel = module.load_function("perturb_solution_10k")?;
let perturb_guided_kernel = module.load_function("perturb_guided_10k")?;
let perturb_guided_kernel = module.load_function("perturb_guided_10k")?;
let perturb_hubs_kernel = module.load_function("perturb_hubs_10k")?;
let perturb_hubs_kernel = module.load_function("perturb_hubs_10k")?;
let perturb_ruin_recreate_kernel = module.load_function("perturb_ruin_recreate_10k")?;
let perturb_ruin_recreate_kernel = module.load_function("perturb_ruin_recreate_10k")?;
let compute_swap_gains_kernel = module.load_function("compute_swap_gains_extended_10k")?;
let compute_swap_gains_kernel = module.load_function("compute_swap_gains_extended_10k")?;
let perturb_path_relink_kernel = module.load_function("perturb_path_relink_10k")?;
let perturb_path_relink_kernel = module.load_function("perturb_path_relink_10k")?;
let compute_he_moves_kernel = module.load_function("compute_hyperedge_centric_moves_10k")?;
let compute_he_moves_kernel = module.load_function("compute_hyperedge_centric_moves_10k")?;
let choose_elite_per_hyperedge_kernel = module.load_function("choose_elite_per_hyperedge_10k")?;
let choose_elite_per_hyperedge_kernel = module.load_function("choose_elite_per_hyperedge_10k")?;
let assign_from_elite_votes_kernel = module.load_function("assign_from_elite_votes_10k")?;
let assign_from_elite_votes_kernel = module.load_function("assign_from_elite_votes_10k")?;


let cfg = LaunchConfig {
let cfg = LaunchConfig {
grid_dim: (
grid_dim: (
(challenge.num_nodes as u32 + block_size - 1) / block_size,
(challenge.num_nodes as u32 + block_size - 1) / block_size,
1,
1,
1,
1,
),
),
block_dim: (block_size, 1, 1),
block_dim: (block_size, 1, 1),
shared_mem_bytes: 0,
shared_mem_bytes: 0,
};
};


let one_thread_cfg = LaunchConfig {
let one_thread_cfg = LaunchConfig {
grid_dim: (1, 1, 1),
grid_dim: (1, 1, 1),
block_dim: (1, 1, 1),
block_dim: (1, 1, 1),
shared_mem_bytes: 0,
shared_mem_bytes: 0,
};
};


let hedge_cfg = LaunchConfig {
let hedge_cfg = LaunchConfig {
grid_dim: (
grid_dim: (
(challenge.num_hyperedges as u32 + block_size - 1) / block_size,
(challenge.num_hyperedges as u32 + block_size - 1) / block_size,
1,
1,
1,
1,
),
),
block_dim: (block_size, 1, 1),
block_dim: (block_size, 1, 1),
shared_mem_bytes: 0,
shared_mem_bytes: 0,
};
};


let init_restart_id = hyperparameters
let init_restart_id = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("init_restart_id").and_then(|v| v.as_i64()))
.and_then(|p| p.get("init_restart_id").and_then(|v| v.as_i64()))
.map(|v| v.clamp(0, 16) as i32)
.map(|v| v.clamp(0, 16) as i32)
.unwrap_or(1);
.unwrap_or(1);
let init_random_seed = u32::from_le_bytes([
let init_random_seed = u32::from_le_bytes([
challenge.seed[0],
challenge.seed[0],
challenge.seed[1],
challenge.seed[1],
challenge.seed[2],
challenge.seed[2],
challenge.seed[3],
challenge.seed[3],
]);
]);


let mut num_hedge_clusters = if let Some(params) = hyperparameters {
let mut num_hedge_clusters = if let Some(params) = hyperparameters {
params
params
.get("clusters")
.get("clusters")
.and_then(|v| v.as_i64())
.and_then(|v| v.as_i64())
.map(|v| v.clamp(4, 256) as i32)
.map(|v| v.clamp(4, 256) as i32)
.unwrap_or(64)
.unwrap_or(64)
} else {
} else {
64
64
};
};


if num_hedge_clusters % 4 != 0 {
if num_hedge_clusters % 4 != 0 {
num_hedge_clusters += 4 - (num_hedge_clusters % 4);
num_hedge_clusters += 4 - (num_hedge_clusters % 4);
}
}


let mut d_hyperedge_clusters = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?;
let mut d_hyperedge_clusters = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?;
let mut d_partition = stream.alloc_zeros::<i32>(challenge.num_nodes 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_nodes_in_part = stream.alloc_zeros::<i32>(challenge.num_parts as usize)?;


let mut d_pref_parts = 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_priorities = 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_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut d_move_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;


let grid_x_calc = (challenge.num_nodes as u32 + block_size - 1) / block_size;
let grid_x_calc = (challenge.num_nodes as u32 + block_size - 1) / block_size;
let mut d_num_valid_moves = stream.alloc_zeros::<i32>(grid_x_calc as usize)?;
let mut d_num_valid_moves = stream.alloc_zeros::<i32>(grid_x_calc as usize)?;
let mut d_moves_executed = stream.alloc_zeros::<i32>(1)?;
let mut d_moves_executed = stream.alloc_zeros::<i32>(1)?;


let mut d_edge_flags_all = stream.alloc_zeros::<u64>(challenge.num_hyperedges as usize)?;
let mut d_edge_flags_all = stream.alloc_zeros::<u64>(challenge.num_hyperedges as usize)?;
let mut d_edge_flags_double = stream.alloc_zeros::<u64>(challenge.num_hyperedges as usize)?;
let mut d_edge_flags_double = stream.alloc_zeros::<u64>(challenge.num_hyperedges as usize)?;


let num_parts_usize = challenge.num_parts as usize;
let num_parts_usize = challenge.num_parts as usize;
let is_sparse = (challenge.num_nodes as usize) > 4 * (challenge.num_hyperedges as usize + 1);
let is_sparse = (challenge.num_nodes as usize) > 4 * (challenge.num_hyperedges as usize + 1);


let effort = hyperparameters
let effort = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("effort").and_then(|v| v.as_i64()))
.and_then(|p| p.get("effort").and_then(|v| v.as_i64()))
.unwrap_or(3);
.unwrap_or(3);


let (base_refine, base_ils, base_ils_quick, base_polish, base_post_balance) = match effort {
let (base_refine, base_ils, base_ils_quick, base_polish, base_post_balance) = match effort {
5 => (12000, 6, 70, 300, 0),
5 => (12000, 6, 70, 300, 0),
4 => (10000, 5, 60, 200, 0),
4 => (10000, 5, 60, 200, 0),
3 => (8000, 5, 60, 150, 0),
3 => (8000, 5, 60, 150, 0),
2 => (5000, 5, 60, 100, 0),
2 => (5000, 5, 60, 100, 0),
1 => (4000, 3, 25, 40, 0),
1 => (4000, 3, 25, 40, 0),
0 => (3000, 3, 20, 30, 0),
0 => (3000, 3, 20, 30, 0),
_ => (6000, 5, 50, 150, 0),
_ => (6000, 5, 50, 150, 0),
};
};


let refinement_rounds = hyperparameters
let refinement_rounds = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("refinement").and_then(|v| v.as_i64()))
.and_then(|p| p.get("refinement").and_then(|v| v.as_i64()))
.map(|v| v.clamp(50, 50_000) as usize)
.map(|v| v.clamp(50, 50_000) as usize)
.unwrap_or(base_refine);
.unwrap_or(base_refine);


let ils_iterations = hyperparameters
let ils_iterations = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("ils_iterations").and_then(|v| v.as_i64()))
.and_then(|p| p.get("ils_iterations").and_then(|v| v.as_i64()))
.map(|v| v.clamp(1, 500) as usize)
.map(|v| v.clamp(1, 500) as usize)
.unwrap_or(base_ils);
.unwrap_or(base_ils);


let ils_quick_refine = hyperparameters
let ils_quick_refine = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("ils_quick_refine").and_then(|v| v.as_i64()))
.and_then(|p| p.get("ils_quick_refine").and_then(|v| v.as_i64()))
.map(|v| v.clamp(10, 500) as usize)
.map(|v| v.clamp(10, 500) as usize)
.unwrap_or(base_ils_quick);
.unwrap_or(base_ils_quick);


let post_ils_polish = hyperparameters
let post_ils_polish = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("post_ils_polish").and_then(|v| v.as_i64()))
.and_then(|p| p.get("post_ils_polish").and_then(|v| v.as_i64()))
.map(|v| v.clamp(20, 500) as usize)
.map(|v| v.clamp(20, 500) as usize)
.unwrap_or(base_polish);
.unwrap_or(base_polish);


let tabu_tenure: usize = hyperparameters
let tabu_tenure: usize = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("tabu_tenure").and_then(|v| v.as_i64()))
.and_then(|p| p.get("tabu_tenure").and_then(|v| v.as_i64()))
.map(|v| v.clamp(1, 30) as usize)
.map(|v| v.clamp(1, 30) as usize)
.unwrap_or(10);
.unwrap_or(10);


let move_limit: usize = hyperparameters
let move_limit: usize = hyperparameters
.as_ref()
.as_ref()
.and_then(|p| p.get("move_limit").and_then(|v| v.as_i64()))
.and_then(|p| p.get("move_limit").and_then(|v| v.as_i64()))
.map(|v| v.clamp(256, 1_000_000) as usize)
.map(|v| v.clamp(256, 1_000_000) as usize)
.unwrap_or(if is_sparse {
.unwrap_or(if is_sparse {
262_144
262_144
} else if challenge.num_hyperedges as usize >= 150_000 || challenge.num_nodes as usize >= 250_000 {
} else if challenge.num_hyperedges as usize >= 150_000 || challenge.num_nodes as usize >= 250_000 {
131_072
131_072
} else {
} else {
200_000
200_000
});
});


let neg_gain_thresh: i32 = 3;
let neg_gain_thresh: i32 = 3;
let scan_limit_swap = 32usize;
let scan_limit_swap = 32usize;
let scan_limit_cycle = 8usize;
let scan_limit_cycle = 8usize;


let swap_buf_size = 4 * challenge.num_nodes as usize;
let swap_buf_size = 4 * challenge.num_nodes as usize;
let mut d_swap_gains = stream.alloc_zeros::<i32>(swap_buf_size)?;
let mut d_swap_gains = stream.alloc_zeros::<i32>(swap_buf_size)?;


let mut part_to_part: Vec<Vec<(usize, i32)>> = vec![vec![]; num_parts_usize * num_parts_usize];
let mut part_to_part: Vec<Vec<(usize, i32)>> = vec![vec![]; num_parts_usize * num_parts_usize];
let mut swap_gains_host: Vec<i32> = vec![0i32; swap_buf_size];
let mut swap_gains_host: Vec<i32> = vec![0i32; swap_buf_size];
let mut partition_host_swap: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut partition_host_swap: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut partition_mut_swap: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut partition_mut_swap: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut used_ba_buf: Vec<bool> = Vec::with_capacity(1024);
let mut used_ba_buf: Vec<bool> = Vec::with_capacity(1024);
let mut nodes_in_part_host: Vec<i32> = vec![0i32; num_parts_usize];
let mut nodes_in_part_host: Vec<i32> = vec![0i32; num_parts_usize];
let mut move_keys_host: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut move_keys_host: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let zero_counter_1 = [0i32; 1];
let zero_counter_1 = [0i32; 1];
let zero_counter_grid = vec![0i32; ((challenge.num_nodes as u32 + block_size - 1) / block_size) as usize];
let zero_counter_grid = vec![0i32; ((challenge.num_nodes as u32 + block_size - 1) / block_size) as usize];
let mut partition_host_mirror: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut partition_host_mirror: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut nodes_in_part_mirror: Vec<i32> = vec![0i32; num_parts_usize];
let mut nodes_in_part_mirror: Vec<i32> = vec![0i32; num_parts_usize];
let mut accepted_move_nodes: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
let mut accepted_move_nodes: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
let mut accepted_move_parts: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
let mut accepted_move_parts: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
let mut d_accepted_move_nodes = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut d_accepted_move_nodes = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut d_accepted_move_parts = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut d_accepted_move_parts = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut d_hedge_moves = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize * 4)?;
let mut d_hedge_moves = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize * 4)?;
let mut d_hedge_choice = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?;
let mut d_hedge_choice = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?;
let mut node_has_move = vec![false; challenge.num_nodes as usize];
let mut node_has_move = vec![false; challenge.num_nodes as usize];


let mut sorted_move_nodes: Vec<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);
let mut sorted_move_parts: Vec<i32> = Vec::with_capacity(challenge.num_nodes as usize);
let mut valid_moves: Vec<(usize, i32)> = Vec::with_capacity(challenge.num_nodes as usize);
let mut valid_moves: Vec<(usize, i32)> = Vec::with_capacity(challenge.num_nodes as usize);
let mut tgt_used: Vec<usize> = vec![0; num_parts_usize];
let mut tgt_used: Vec<usize> = vec![0; num_parts_usize];
let mut tgt_quota: Vec<usize> = vec![0; num_parts_usize];
let mut tgt_quota: Vec<usize> = vec![0; num_parts_usize];


let mut stagnant_rounds = 0;
let mut stagnant_rounds = 0;
let max_stagnant_rounds = 30;
let max_stagnant_rounds = 30;
let mut node_tabu_until: Vec<i32> = vec![0; challenge.num_nodes as usize];
let mut node_tabu_until: Vec<i32> = vec![0; challenge.num_nodes as usize];
let mut d_node_tabu_until = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut d_node_tabu_until = stream.alloc_zeros::<i32>(challenge.num_nodes as usize)?;
let mut global_round = 0i32;
let mut global_round = 0i32;


macro_rules! reset_move_counters {
macro_rules! reset_move_counters {
() => {{
() => {{
stream.memcpy_htod(&zero_counter_grid, &mut d_num_valid_moves)?;
stream.memcpy_htod(&zero_counter_grid, &mut d_num_valid_moves)?;
stream.memcpy_htod(&zero_counter_1, &mut d_moves_executed)?;
stream.memcpy_htod(&zero_counter_1, &mut d_moves_executed)?;
}};
}};
}
}


macro_rules! refresh_host_mirrors_from_device {
macro_rules! refresh_host_mirrors_from_device {
() => {{
() => {{
stream.memcpy_dtoh(&d_partition, &mut partition_host_mirror)?;
stream.memcpy_dtoh(&d_partition, &mut partition_host_mirror)?;
stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_mirror)?;
stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_mirror)?;
}};
}};
}
}


macro_rules! replay_initial_assignment_host {
macro_rules! replay_initial_assignment_host {
($sorted_nodes:expr, $sorted_parts:expr) => {{
($sorted_nodes:expr, $sorted_parts:expr) => {{
partition_host_mirror.fill(0);
partition_host_mirror.fill(0);
nodes_in_part_mirror.fill(0);
nodes_in_part_mirror.fill(0);
for i in 0..challenge.num_nodes as usize {
for i in 0..challenge.num_nodes as usize {
let node_i32 = $sorted_nodes[i];
let node_i32 = $sorted_nodes[i];
let preferred_part = $sorted_parts[i];
let preferred_part = $sorted_parts[i];
if node_i32 >= 0 && preferred_part >= 0 {
if node_i32 >= 0 && preferred_part >= 0 {
let node = node_i32 as usize;
let node = node_i32 as usize;
if node < challenge.num_nodes as usize && (preferred_part as usize) < num_parts_usize {
if node < challenge.num_nodes as usize && (preferred_part as usize) < num_parts_usize {
let start_part = if i < num_parts_usize {
let start_part = if i < num_parts_usize {
i as i32
i as i32
} else {
} else {
preferred_part
preferred_part
};
};


let mut assigned = false;
let mut assigned = false;
for attempt in 0..num_parts_usize {
for attempt in 0..num_parts_usize {
let try_part = ((start_part as usize) + attempt) % num_parts_usize;
let try_part = ((start_part as usize) + attempt) % num_parts_usize;
if nodes_in_part_mirror[try_part] < challenge.max_part_size as i32 {
if nodes_in_part_mirror[try_part] < challenge.max_part_size as i32 {
partition_host_mirror[node] = try_part as i32;
partition_host_mirror[node] = try_part as i32;
nodes_in_part_mirror[try_part] += 1;
nodes_in_part_mirror[try_part] += 1;
assigned = true;
assigned = true;
break;
break;
}
}
}
}


if !assigned {
if !assigned {
let fallback_part = node % num_parts_usize;
let fallback_part = node % num_parts_usize;
partition_host_mirror[node] = fallback_part as i32;
partition_host_mirror[node] = fallback_part as i32;
nodes_in_part_mirror[fallback_part] += 1;
nodes_in_part_mirror[fallback_part] += 1;
}
}
}
}
}
}
}
}
}};
}};
}
}


macro_rules! replay_execute_moves_host {
macro_rules! replay_execute_moves_host {
($sorted_nodes:expr, $sorted_parts:expr) => {{
($sorted_nodes:expr, $sorted_parts:expr) => {{
accepted_move_nodes.clear();
accepted_move_nodes.clear();
accepted_move_parts.clear();
accepted_move_parts.clear();


let mut host_moves_executed = 0i32;
let mut host_moves_executed = 0i32;
for i in 0..$sorted_nodes.len() {
for i in 0..$sorted_nodes.len() {
let node_i32 = $sorted_nodes[i];
let node_i32 = $sorted_nodes[i];
let target_part_i32 = $sorted_parts[i];
let target_part_i32 = $sorted_parts[i];


if node_i32 >= 0 && target_part_i32 >= 0 {
if node_i32 >= 0 && target_part_i32 >= 0 {
let node = node_i32 as usize;
let node = node_i32 as usize;
let target_part = target_part_i32 as usize;
let target_part = target_part_i32 as usize;


if node < partition_host_mirror.len() && target_part < num_parts_usize {
if node < partition_host_mirror.len() && target_part < num_parts_usize {
let current_part = partition_host_mirror[node];
let current_part = partition_host_mirror[node];
if current_part >= 0 {
if current_part >= 0 {
let current_part_usize = current_part as usize;
let current_part_usize = current_part as usize;
if current_part_usize < num_parts_usize
if current_part_usize < num_parts_usize
&& nodes_in_part_mirror[target_part] < challenge.max_part_size as i32
&& nodes_in_part_mirror[target_part] < challenge.max_part_size as i32
&& nodes_in_part_mirror[current_part_usize] > 1
&& nodes_in_part_mirror[current_part_usize] > 1
&& partition_host_mirror[node] == current_part
&& partition_host_mirror[node] == current_part
{
{
partition_host_mirror[node] = target_part as i32;
partition_host_mirror[node] = target_part as i32;
nodes_in_part_mirror[current_part_usize] -= 1;
nodes_in_part_mirror[current_part_usize] -= 1;
nodes_in_part_mirror[target_part] += 1;
nodes_in_part_mirror[target_part] += 1;
accepted_move_nodes.push(node_i32);
accepted_move_nodes.push(node_i32);
accepted_move_parts.push(target_part_i32);
accepted_move_parts.push(target_part_i32);
host_moves_executed += 1;
host_moves_executed += 1;
}
}
}
}
}
}
}
}
}
}


if host_moves_executed > 0 {
if host_moves_executed > 0 {
let accepted_len = accepted_move_nodes.len();
let accepted_len = accepted_move_nodes.len();
stream.memcpy_htod(&nodes_in_part_mirror, &mut d_nodes_in_part)?;
stream.memcpy_htod(&nodes_in_part_mirror, &mut d_nodes_in_part)?;
stream.memcpy_htod(accepted_move_nodes.as_slice(), &mut d_accepted_move_nodes)?;
stream.memcpy_htod(accepted_move_nodes.as_slice(), &mut d_accepted_move_nodes)?;
stream.memcpy_htod(accepted_move_parts.as_slice(), &mut d_accepted_move_parts)?;
stream.memcpy_htod(accepted_move_parts.as_slice(), &mut d_accepted_move_parts)?;
unsafe {
unsafe {
stream
stream
.launch_builder(&execute_moves_kernel)
.launch_builder(&execute_moves_kernel)
.arg(&(accepted_len as i32))
.arg(&(accepted_len as i32))
.arg(&d_accepted_move_nodes)
.arg(&d_accepted_move_nodes)
.arg(&d_accepted_move_parts)
.arg(&d_accepted_move_parts)
.arg(&(challenge.max_part_size as i32))
.arg(&(challenge.max_part_size as i32))
.arg(&mut d_partition)
.arg(&mut d_partition)
.arg(&mut d_nodes_in_part)
.arg(&mut d_nodes_in_part)
.arg(&mut d_moves_executed)
.arg(&mut d_moves_executed)
.launch(LaunchConfig {
.launch(LaunchConfig {
grid_dim: (
grid_dim: (
(accepted_len as u32 + block_size - 1) / block_size,
(accepted_len as u32 + block_size - 1) / block_size,
1,
1,
1,
1,
),
),
block_dim: (block_size, 1, 1),
block_dim: (block_size, 1, 1),
shared_mem_bytes: 0,
shared_mem_bytes: 0,
})?;
})?;
}
}
}
}


host_moves_executed
host_moves_executed
}};
}};
}
}


macro_rules! do_hyperedge_centric_phase {
macro_rules! do_hyperedge_centric_phase {
() => {{
() => {{
unsafe {
unsafe {
stream
stream
.launch_builder(&precompute_edge_flags_kernel)
.launch_builder(&precompute_edge_flags_kernel)
.arg(&(challenge.num_hyperedges as i32))
.arg(&(challenge.num_hyperedges as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_offsets)
.arg(&challenge.d_hyperedge_offsets)
.arg(&d_partition)
.arg(&d_partition)
.arg(&mut d_edge_flags_all)
.arg(&mut d_edge_flags_all)
.arg(&mut d_edge_flags_double)
.arg(&mut d_edge_flags_double)
.launch(hedge_cfg.clone())?;
.launch(hedge_cfg.clone())?;
}
}
unsafe {
unsafe {
stream
stream
.launch_builder(&compute_he_moves_kernel)
.launch_builder(&compute_he_moves_kernel)
.arg(&(challenge.num_hyperedges as i32))
.arg(&(challenge.num_hyperedges as i32))
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_offsets)
.arg(&challenge.d_hyperedge_offsets)
.arg(&d_partition)
.arg(&d_partition)
.arg(&d_edge_flags_all)
.arg(&d_edge_flags_all)
.arg(&d_edge_flags_double)
.arg(&d_edge_flags_double)
.arg(&challenge.d_node_offsets)
.arg(&challenge.d_node_offsets)
.arg(&mut d_hedge_moves)
.arg(&mut d_hedge_moves)
.launch(hedge_cfg.clone())?;
.launch(hedge_cfg.clone())?;
}
}
let hedge_moves_host = stream.memcpy_dtov(&d_hedge_moves)?;
let hedge_moves_host = stream.memcpy_dtov(&d_hedge_moves)?;
valid_moves.clear();
valid_moves.clear();
for (h_idx, chunk) in hedge_moves_host.chunks_exact(4).enumerate() {
for (h_idx, chunk) in hedge_moves_host.chunks_exact(4).enumerate() {
let mut block_size = 0;
let mut block_size = 0;
for &m in chunk {
for &m in chunk {
if m > 0 { block_size += 1; }
if m > 0 { block_size += 1; }
}
}
if block_size > 0 {
if block_size > 0 {
let prio = 32000 + block_size;
let prio = 32000 + block_size;
for &m in chunk {
for &m in chunk {
if m > 0 {
if m > 0 {
let node = ((m >> 6) - 1) as usize;
let node = ((m >> 6) - 1) as usize;
let tgt = (m & 63) as i32;
let tgt = (m & 63) as i32;
if !node_has_move[node] {
if !node_has_move[node] {
node_has_move[node] = true;
node_has_move[node] = true;
valid_moves.push((node, (prio << 16) | ((h_idx as i32 & 0x3FF) << 6) | tgt));
valid_moves.push((node, (prio << 16) | ((h_idx as i32 & 0x3FF) << 6) | tgt));
}
}
}
}
}
}
}
}
}
}
for &(node, _) in valid_moves.iter() {
for &(node, _) in valid_moves.iter() {
node_has_move[node] = false;
node_has_move[node] = false;
}
}
if !valid_moves.is_empty() {
if !valid_moves.is_empty() {
let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0));
let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0));
valid_moves.sort_unstable_by(cmp);
valid_moves.sort_unstable_by(cmp);
nodes_in_part_host.copy_from_slice(&nodes_in_part_mirror);
nodes_in_part_host.copy_from_slice(&nodes_in_part_mirror);
tgt_used.fill(0);
tgt_used.fill(0);
for p in 0..num_parts_usize {
for p in 0..num_parts_usize {
let free = (challenge.max_part_size as i32 - nodes_in_part_host[p]).max(0) as usize;
let free = (challenge.max_part_size as i32 - nodes_in_part_host[p]).max(0) as usize;
tgt_quota[p] = std::cmp::max(1, free + 4);
tgt_quota[p] = std::cmp::max(1, free + 4);
}
}
sorted_move_nodes.clear();
sorted_move_nodes.clear();
sorted_move_parts.clear();
sorted_move_parts.clear();
for &(node, key) in valid_moves.iter() {
for &(node, key) in valid_moves.iter() {
let tgt = (key & 63) as usize;
let tgt = (key & 63) as usize;
if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] {
if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] {
tgt_used[tgt] += 1;
tgt_used[tgt] += 1;
sorted_move_nodes.push(node as i32);
sorted_move_nodes.push(node as i32);
sorted_move_parts.push(tgt as i32);
sorted_move_parts.push(tgt as i32);
}
}
}
}
let me_he = replay_execute_moves_host!(sorted_move_nodes, sorted_move_parts);
let me_he = replay_execute_moves_host!(sorted_move_nodes, sorted_move_parts);
if me_he > 0 {
if me_he > 0 {
for &node in sorted_move_nodes.iter().take(me_he as usize) {
for &node in sorted_move_nodes.iter().take(me_he as usize) {
node_tabu_until[node as usize] = global_round + tabu_tenure as i32;
node_tabu_until[node as usize] = global_round + tabu_tenure as i32;
}
}
}
}
me_he
me_he
} else {
} else {
0i32
0i32
}
}
}};
}};
}
}


unsafe {
unsafe {
stream
stream
.launch_builder(&hyperedge_cluster_kernel)
.launch_builder(&hyperedge_cluster_kernel)
.arg(&(challenge.num_hyperedges as i32))
.arg(&(challenge.num_hyperedges as i32))
.arg(&(num_hedge_clusters as i32))
.arg(&(num_hedge_clusters as i32))
.arg(&challenge.d_hyperedge_offsets)
.arg(&challenge.d_hyperedge_offsets)
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_nodes)
.arg(&mut d_hyperedge_clusters)
.arg(&mut d_hyperedge_clusters)
.launch(LaunchConfig {
.launch(LaunchConfig {
grid_dim: (
grid_dim: (
(challenge.num_hyperedges as u32 + block_size - 1) / block_size,
(challenge.num_hyperedges as u32 + block_size - 1) / block_size,
1,
1,
1,
1,
),
),
block_dim: (block_size, 1, 1),
block_dim: (block_size, 1, 1),
shared_mem_bytes: 0,
shared_mem_bytes: 0,
})?;
})?;
}
}


unsafe {
unsafe {
stream
stream
.launch_builder(&compute_preferences_kernel)
.launch_builder(&compute_preferences_kernel)
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_parts as i32))
.arg(&(challenge.num_parts as i32))
.arg(&(num_hedge_clusters as i32))
.arg(&(num_hedge_clusters as i32))
.arg(&challenge.d_node_hyperedges)
.arg(&challenge.d_node_hyperedges)
.arg(&challenge.d_node_offsets)
.arg(&challenge.d_node_offsets)
.arg(&d_hyperedge_clusters)
.arg(&d_hyperedge_clusters)
.arg(&challenge.d_hyperedge_offsets)
.arg(&challenge.d_hyperedge_offsets)
.arg(&init_restart_id)
.arg(&init_restart_id)
.arg(&init_random_seed)
.arg(&init_random_seed)
.arg(&mut d_pref_parts)
.arg(&mut d_pref_parts)
.arg(&mut d_pref_priorities)
.arg(&mut d_pref_priorities)
.launch(cfg.clone())?;
.launch(cfg.clone())?;
}
}


let pref_parts = stream.memcpy_dtov(&d_pref_parts)?;
let pref_parts = stream.memcpy_dtov(&d_pref_parts)?;
let pref_priorities = stream.memcpy_dtov(&d_pref_priorities)?;
let pref_priorities = stream.memcpy_dtov(&d_pref_priorities)?;


let mut indices: Vec<usize> = (0..challenge.num_nodes as usize).collect();
let mut indices: Vec<usize> = (0..challenge.num_nodes as usize).collect();
indices.sort_unstable_by(|&a, &b| {
indices.sort_unstable_by(|&a, &b| {
pref_priorities[b].cmp(&pref_priorities[a]).then_with(|| a.cmp(&b))
pref_priorities[b].cmp(&pref_priorities[a]).then_with(|| a.cmp(&b))
});
});


let sorted_nodes: Vec<i32> = indices.iter().map(|&i| i as i32).collect();
let sorted_nodes: Vec<i32> = indices.iter().map(|&i| i as i32).collect();
let sorted_parts: Vec<i32> = indices.iter().map(|&i| pref_parts[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_nodes = stream.memcpy_stod(&sorted_nodes)?;


replay_initial_assignment_host!(sorted_nodes, sorted_parts);
replay_initial_assignment_host!(sorted_nodes, sorted_parts);
let assigned_parts: Vec<i32> = sorted_nodes
let assigned_parts: Vec<i32> = sorted_nodes
.iter()
.iter()
.map(|&node| partition_host_mirror[node as usize])
.map(|&node| partition_host_mirror[node as usize])
.collect();
.collect();
let d_assigned_parts = stream.memcpy_stod(&assigned_parts)?;
let d_assigned_parts = stream.memcpy_stod(&assigned_parts)?;
stream.memcpy_htod(&nodes_in_part_mirror, &mut d_nodes_in_part)?;
stream.memcpy_htod(&nodes_in_part_mirror, &mut d_nodes_in_part)?;


unsafe {
unsafe {
stream
stream
.launch_builder(&execute_assignments_kernel)
.launch_builder(&execute_assignments_kernel)
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_parts as i32))
.arg(&(challenge.num_parts as i32))
.arg(&(challenge.max_part_size as i32))
.arg(&(challenge.max_part_size as i32))
.arg(&d_sorted_nodes)
.arg(&d_sorted_nodes)
.arg(&d_assigned_parts)
.arg(&d_assigned_parts)
.arg(&mut d_partition)
.arg(&mut d_partition)
.arg(&mut d_nodes_in_part)
.arg(&mut d_nodes_in_part)
.launch(cfg.clone())?;
.launch(cfg.clone())?;
}
}


for round in 0..refinement_rounds {
for round in 0..refinement_rounds {
global_round += 1;
global_round += 1;
reset_move_counters!();
reset_move_counters!();


unsafe {
unsafe {
stream
stream
.launch_builder(&precompute_edge_flags_kernel)
.launch_builder(&precompute_edge_flags_kernel)
.arg(&(challenge.num_hyperedges as i32))
.arg(&(challenge.num_hyperedges as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_nodes)
.arg(&challenge.d_hyperedge_offsets)
.arg(&challenge.d_hyperedge_offsets)
.arg(&d_partition)
.arg(&d_partition)
.arg(&mut d_edge_flags_all)
.arg(&mut d_edge_flags_all)
.arg(&mut d_edge_flags_double)
.arg(&mut d_edge_flags_double)
.launch(hedge_cfg.clone())?;
.launch(hedge_cfg.clone())?;
}
}


unsafe {
unsafe {
stream
stream
.launch_builder(&compute_moves_kernel)
.launch_builder(&compute_moves_kernel)
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_nodes as i32))
.arg(&(challenge.num_parts as i32))
.arg(&(challenge.num_parts as i32))
.arg(&(challenge.max_part_size as i32))
.arg(&(challenge.max_part_size as i32))
.arg(&challenge.d_node_hyperedges)
.arg(&challenge.d_node_hyperedges)
.arg(&challenge.d_node_offsets)
.arg(&challenge.d_node_offsets)
.arg(&d_partition)
.arg(&d_partition)
.arg(&d_nodes_in_part)
.arg(&d_nodes_in_part)
.arg(&d_edge_flags_all)
.arg(&d_edge_flags_all)
.arg(&d_edge_flags_double)
.arg(&d_edge_flags_double)
.arg(&mut d_move_priorities)
.arg(&mut d_move_priorities)
.arg(&mut d_num_valid_moves)
.arg(&mut d_num_valid_moves)
.launch(cfg.clone())?;
.launch(cfg.clone())?;
}
}
let nvm_vec = stream.memcpy_dtov(&d_num_valid_moves)?;
let nvm_vec = stream.memcpy_dtov(&d_num_valid_moves)?;
let num_valid_moves: i32 = nvm_vec.iter().sum();
let num_valid_moves: i32 = nvm_vec.iter().sum();
if num_valid_moves == 0 {
if num_valid_moves == 0 {
break;
break;
}
}


stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?;
stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?;


valid_moves.clear();
valid_moves.clear();
let max_gain = move_keys_host.iter().filter(|&&k| k > 0).map(|&k| (k >> 16) - 1000).max().unwrap_or(0);
let max_gain = move_keys_host.iter().filter(|&&k| k > 0).map(|&k| (k >> 16) - 1000).max().unwrap_or(0);
let aspiration_threshold = std::cmp::max(1, (max_gain * 3) / 4);
let aspiration_threshold = std::cmp::max(1, (max_gain * 3) / 4);


for (node, &key) in move_keys_host.iter().enumerate() {
for (node, &key) in move_keys_host.iter().enumerate() {
if key > 0 {
if key > 0 {
let gain = (key >> 16) - 1000;
let gain = (key >> 16) - 1000;
if node_tabu_until[node] <= global_round || gain >= aspiration_threshold {
if node_tabu_until[node] <= global_round || gain >= aspiration_threshold {
valid_moves.push((node, key));
valid_moves.push((node, key));
}
}
}
}
}
}


if valid_moves.is_empty() {
if valid_moves.is_empty() {
break;
break;
}
}


let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0));
let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0));


let mut k_base = valid_moves.len();
let mut k_base = valid_moves.len();
let adaptive_limit = if round < 50 {
let adaptive_limit = if round < 50 {
move_limit / 2
move_limit / 2
} else if round < 200 {
} else if round < 200 {
move_limit
move_limit
} else {
} else {
move_limit / 3
move_limit / 3
};
};
if k_base > adaptive_limit {
if k_base > adaptive_limit {
k_base = adaptive_limit;
k_base = adaptive_limit;
}
}


let extra_window = 16384usize;
let extra_window = 16384usize;
let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(extra_window));
let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(extra_window));


if k_cand > 1 {
if k_cand > 1 {
valid_moves.select_nth_unstable_by(k_cand - 1, cmp);
valid_moves.select_nth_unstable_by(k_cand - 1, cmp);
valid_moves[..k_cand].sort_unstable_by(cmp);
valid_moves[..k_cand].sort_unstable_by(cmp);
} else {
} else {
valid_moves[..k_cand].sort_unstable_by(cmp);
valid_moves[..k_cand].sort_unstable_by(cmp);
}
}


nodes_in_part_host.copy_from_slice(&nodes_in_part_mirror);
nodes_in_part_host.copy_from_slice(&nodes_in_part_mirror);
let slack = if round < 64 { 8usize } else if round < 256 { 4usize } else { 2usize };
let slack = if round < 64 { 8usize } else if round < 256 { 4usize } else { 2usize };


tgt_used.fill(0);
tgt_used.fill(0);
for p in 0..num_parts_usize {
for p in 0..num_parts_usize {
let free = (challenge.max_part_size as i32 - nodes_in_part_host[p]).max(0) as usize;
let free = (challenge.max_part_size as i32 - nodes_in_part_host[p]).max(0) as usize;
tgt_quota[p] = std::cmp::max(1, free.saturating_add(slack));
tgt_quota[p] = std::cmp::max(1, free.saturating_add(slack));
}
}


sorted_move_nodes.clear();
sorted_move_nodes.clear();
sorted_move_parts.clear();
sorted_move_parts.clear();
for &(node, key) in valid_moves[..k_cand].iter() {
for &(node, key) in valid_moves[..k_cand].iter() {
if sorted_move_nodes.len() >= k_base {
if sorted_move_nodes.len() >= k_base {
break;
break;
}
}
let tgt = (key & 63) as usize;
let tgt = (key & 63) as usize;
if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] {
if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] {
tgt_used[tgt] += 1;
tgt_used[tgt] += 1;
sorted_move_nodes.push(node as i32);
sorted_move_nodes.push(node as i32);
sorted_move_parts.push(tgt as i32);
sorted_move_parts.push(tgt as i32);
}
}
}
}


if sorted_move_nodes.is_empty() {
if sorted_move_nodes.is_empty() {
let take = std::cmp::min(k_base, k_cand);
let take = std::cmp::min(k_base, k_cand);
sorted_move_nodes.extend(valid_moves[..take].iter().map(|(n, _)| *n as i32));
sorted_move_nodes.extend(valid_moves[..take].iter().map(|(n, _)| *n as i32));
sorted_move_parts.extend(valid_moves[..take].iter().map(|(_, key)| (key & 63) as i32));
sorted_move_parts.extend(valid_moves[..take].iter().map(|(_, key)| (key & 63) as i32));
}
}


let mut moves_executed = replay_execute_moves_host!(sorted_move_nodes, sorted_move_parts);
let mut moves_executed = replay_execute_moves_host!(sorted_move_nodes, sorted_move_parts);


if moves_executed == 0 && k_cand > k_base {
if moves_executed == 0 && k_cand > k_base {
sorted_move_nodes.clear();
sorted_move_nodes.clear();
sorted_move_parts.clear();
sorted_move_parts.clear();
let tail = &valid_moves[k_base..k_cand];
let tail = &valid_moves[k_base..k_cand];
let take = std::cmp::min(tail.len(), k_base);
let take = std::cmp::min(tail.len(), k_base);
sorted_move_nodes.extend(tail.iter().take(take).map(|(n, _)| *n as i32));
sorted_move_nodes.extend(tail.iter().take(take).map(|(n, _)| *n as i32));
sorted_move_parts.extend(tail.iter().take(take).map(|(_, key)| (key & 63) as i32
sorted_move_parts.extend(tail.iter().take(take).map(|(_, key)| (key & 63) as i32