Diff
checker
文本
文本
圖像
文檔
Excel
文件夾
Legal
Enterprise
桌面版
定價
登入
下載 Diffchecker 桌面版
比較文本
尋找兩個文字檔案之間的差異
工具
歷史
顯示
檢視
拆分
統一
即時編輯器
隱藏空白變更
摺疊未變更行
關閉換行
比對精度
智能
語法
無
變更外觀
處理
忽略
文字轉換
前往第一個差異
編輯輸入
Diffchecker Desktop
執行Diffchecker最安全的方式。取得Diffchecker桌面應用程式:您的差異永遠不會離開您的電腦!
取得桌面版
Hypergraph 200k
建立於
3 週前
差異永不過期
清除
匯出
分享
兩個文本完全相同
這兩個文本之間沒有差異
0 刪除
行
總計
刪除
字符
總計
刪除
要繼續使用此功能,請升級到
Diff
checker
Pro
查看價格
638 行
全部複製
0 新增
行
總計
新增
字符
總計
新增
要繼續使用此功能,請升級到
Diff
checker
Pro
查看價格
638 行
全部複製
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_200k")?;
let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering_200k")?;
let compute_preferences_kernel = module.load_function("compute_node_preferences_200k")?;
let compute_preferences_kernel = module.load_function("compute_node_preferences_200k")?;
let execute_assignments_kernel = module.load_function("execute_node_assignments_200k")?;
let execute_assignments_kernel = module.load_function("execute_node_assignments_200k")?;
let precompute_edge_flags_kernel = module.load_function("precompute_edge_flags_200k")?;
let precompute_edge_flags_kernel = module.load_function("precompute_edge_flags_200k")?;
let compute_moves_kernel = module.load_function("compute_refinement_moves_optimized_200k")?;
let compute_moves_kernel = module.load_function("compute_refinement_moves_optimized_200k")?;
let balance_kernel = module.load_function("balance_final_200k")?;
let balance_kernel = module.load_function("balance_final_200k")?;
let compute_connectivity_kernel = module.load_function("compute_connectivity_200k")?;
let compute_connectivity_kernel = module.load_function("compute_connectivity_200k")?;
let reduce_connectivity_sum_kernel = module.load_function("reduce_connectivity_sum_200k")?;
let reduce_connectivity_sum_kernel = module.load_function("reduce_connectivity_sum_200k")?;
let compute_swap_gains_kernel = module.load_function("compute_swap_gains_extended_200k")?;
let compute_swap_gains_kernel = module.load_function("compute_swap_gains_extended_200k")?;
let choose_elite_per_hyperedge_kernel = module.load_function("choose_elite_per_hyperedge_200k")?;
let choose_elite_per_hyperedge_kernel = module.load_function("choose_elite_per_hyperedge_200k")?;
let assign_from_elite_votes_kernel = module.load_function("assign_from_elite_votes_200k")?;
let assign_from_elite_votes_kernel = module.load_function("assign_from_elite_votes_200k")?;
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 connectivity_reduce_cfg = LaunchConfig {
let connectivity_reduce_cfg = LaunchConfig {
grid_dim: (
grid_dim: (
(challenge.num_hyperedges as u32 + block_size * 2 - 1) / (block_size * 2),
(challenge.num_hyperedges as u32 + block_size * 2 - 1) / (block_size * 2),
1,
1,
1,
1,
),
),
block_dim: (block_size, 1, 1),
block_dim: (block_size, 1, 1),
shared_mem_bytes: (block_size as usize * std::mem::size_of::<i32>()) as u32,
shared_mem_bytes: (block_size as usize * std::mem::size_of::<i32>()) as u32,
};
};
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 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 mut d_connectivity = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?;
let mut d_connectivity = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?;
let mut d_total_connectivity = stream.alloc_zeros::<i32>(4096)?;
let mut d_total_connectivity = stream.alloc_zeros::<i32>(4096)?;
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 swap_buf_size = 3 * challenge.num_nodes as usize;
let swap_buf_size = 3 * 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 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 => (9000, 6, 70, 300, 0),
5 => (9000, 6, 70, 300, 0),
4 => (7000, 5, 60, 200, 0),
4 => (7000, 5, 60, 200, 0),
3 => (5000, 5, 50, 150, 64),
3 => (5000, 5, 50, 150, 64),
2 => (2000, 5, 50, 100, 64),
2 => (2000, 5, 50, 100, 64),
1 => (1000, 5, 50, 50, 64),
1 => (1000, 5, 50, 50, 64),
0 => (500, 5, 50, 25, 64),
0 => (500, 5, 50, 25, 64),
_ => (5000, 5, 50, 200, 64),
_ => (5000, 5, 50, 200, 64),
};
};
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(14);
.unwrap_or(14);
let tabu_fail_tenure = 4usize;
let tabu_fail_tenure = 4usize;
let tabu_mark_base = 4096usize;
let tabu_mark_base = 4096usize;
let tabu_mark_mult = 16usize;
let tabu_mark_mult = 16usize;
let tabu_fail_mark_len = 4096usize;
let tabu_fail_mark_len = 4096usize;
let extra_window = 65536usize;
let extra_window = 65536usize;
let slack_early = 8usize;
let slack_early = 8usize;
let slack_mid = 4usize;
let slack_mid = 4usize;
let slack_late = 2usize;
let slack_late = 2usize;
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
} else if challenge.num_hyperedges as usize >= 150_000
|| challenge.num_nodes as usize >= 250_000
|| challenge.num_nodes as usize >= 250_000
{
{
131_072
131_072
} else {
} else {
200_000
200_000
});
});
let neg_gain_thresh: i32 = 5;
let neg_gain_thresh: i32 = 5;
let scan_limit_swap = 32usize;
let scan_limit_swap = 32usize;
let scan_limit_cycle = 8usize;
let scan_limit_cycle = 8usize;
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 partition_host_refine: Vec<i32> = vec![0i32; challenge.num_nodes as usize];
let mut partition_host_refine: 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 hedge_offsets_host = stream.memcpy_dtov(&challenge.d_hyperedge_offsets)?;
let hedge_offsets_host = stream.memcpy_dtov(&challenge.d_hyperedge_offsets)?;
let hyperedge_nodes_host = stream.memcpy_dtov(&challenge.d_hyperedge_nodes)?;
let hyperedge_nodes_host = stream.memcpy_dtov(&challenge.d_hyperedge_nodes)?;
let mut hedge_sizes_host: Vec<i32> = Vec::with_capacity(challenge.num_hyperedges as usize);
let mut hedge_sizes_host: Vec<i32> = Vec::with_capacity(challenge.num_hyperedges as usize);
for h in 0..(challenge.num_hyperedges as usize) {
for h in 0..(challenge.num_hyperedges as usize) {
let sz = hedge_offsets_host[h + 1] - hedge_offsets_host[h];
let sz = hedge_offsets_host[h + 1] - hedge_offsets_host[h];
hedge_sizes_host.push(sz);
hedge_sizes_host.push(sz);
}
}
let build_high_hedge_ids =
let build_high_hedge_ids =
|connectivity: &[i32], num_high_hedges: usize| -> Vec<i32> {
|connectivity: &[i32], num_high_hedges: usize| -> Vec<i32> {
let mut impact: Vec<(i32, i32, i32)> = connectivity
let mut impact: Vec<(i32, i32, i32)> = connectivity
.iter()
.iter()
.enumerate()
.enumerate()
.map(|(i, &c)| (c, hedge_sizes_host[i], i as i32))
.map(|(i, &c)| (c, hedge_sizes_host[i], i as i32))
.collect();
.collect();
impact.sort_unstable_by(|a, b| {
impact.sort_unstable_by(|a, b| {
b.0.cmp(&a.0)
b.0.cmp(&a.0)
.then_with(|| b.1.cmp(&a.1))
.then_with(|| b.1.cmp(&a.1))
.then_with(|| a.2.cmp(&b.2))
.then_with(|| a.2.cmp(&b.2))
});
});
impact
impact
.into_iter()
.into_iter()
.take(num_high_hedges)
.take(num_high_hedges)
.map(|t| t.2)
.map(|t| t.2)
.collect()
.collect()
};
};
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(&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)?;
let d_sorted_parts = stream.memcpy_stod(&sorted_parts)?;
let d_sorted_parts = stream.memcpy_stod(&sorted_parts)?;
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_sorted_parts)
.arg(&d_sorted_parts)
.arg(&mut d_partition)
.arg(&mut d_partition)
.arg(&mut d_nodes_in_part)
.arg(&mut d_nodes_in_part)
.launch(one_thread_cfg.clone())?;
.launch(one_thread_cfg.clone())?;
}
}
stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?;
stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?;
stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?;
stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?;
let simulate_execute_moves =
let simulate_execute_moves =
|partition_mirror: &mut [i32],
|partition_mirror: &mut [i32],
nodes_in_part_mirror: &mut [i32],
nodes_in_part_mirror: &mut [i32],
move_nodes: &[i32],
move_nodes: &[i32],
move_parts: &[i32]|
move_parts: &[i32]|
-> i32 {
-> i32 {
let mut moves_executed = 0i32;
let mut moves_executed = 0i32;
for i in 0..move_nodes.len() {
for i in 0..move_nodes.len() {
let node = move_nodes[i];
let node = move_nodes[i];
let target_part = move_parts[i];
let target_part = move_parts[i];
if node < 0 || target_part < 0 {
if node < 0 || target_part < 0 {
continue;
continue;
}
}
let node_usize = node as usize;
let node_usize = node as usize;
if node_usize >= partition_mirror.len() {
if node_usize >= partition_mirror.len() {
continue;
continue;
}
}
let current_part = partition_mirror[node_usize];
let current_part = partition_mirror[node_usize];
if current_part >= 0
if current_part >= 0
&& (current_part as usize) < nodes_in_part_mirror.len()
&& (current_part as usize) < nodes_in_part_mirror.len()
&& (target_part as usize) < nodes_in_part_mirror.len()
&& (target_part as usize) < nodes_in_part_mirror.len()
&& nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32
&& nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32
&& nodes_in_part_mirror[current_part as usize] > 1
&& nodes_in_part_mirror[current_part as usize] > 1
{
{
partition_mirror[node_usize] = target_part;
partition_mirror[node_usize] = target_part;
nodes_in_part_mirror[current_part as usize] -= 1;
nodes_in_part_mirror[current_part as usize] -= 1;
nodes_in_part_mirror[target_part as usize] += 1;
nodes_in_part_mirror[target_part as usize] += 1;
moves_executed += 1;
moves_executed += 1;
}
}
}
}
moves_executed
moves_executed
};
};
let perturb_on_host =
let perturb_on_host =
|partition_mirror: &mut [i32],
|partition_mirror: &mut [i32],
nodes_in_part_mirror: &mut [i32],
nodes_in_part_mirror: &mut [i32],
perturb_strength: i32,
perturb_strength: i32,
seed: u64| {
seed: u64| {
let mut state = seed;
let mut state = seed;
let mut moves_made = 0i32;
let mut moves_made = 0i32;
let target_moves = (challenge.num_nodes as i32 * perturb_strength) / 100;
let target_moves = (challenge.num_nodes as i32 * perturb_strength) / 100;
for _attempt in 0..(challenge.num_nodes as usize) {
for _attempt in 0..(challenge.num_nodes as usize) {
if moves_made >= target_moves {
if moves_made >= target_moves {
break;
break;
}
}
state = state
state = state
.wrapping_mul(6364136223846793005u64)
.wrapping_mul(6364136223846793005u64)
.wrapping_add(1442695040888963407u64);
.wrapping_add(1442695040888963407u64);
let node = (state % challenge.num_nodes as u64) as usize;
let node = (state % challenge.num_nodes as u64) as usize;
let current_part = partition_mirror[node];
let current_part = partition_mirror[node];
if current_part < 0 || current_part >= challenge.num_parts as i32 {
if current_part < 0 || current_part >= challenge.num_parts as i32 {
continue;
continue;
}
}
if nodes_in_part_mirror[current_part as usize] <= 1 {
if nodes_in_part_mirror[current_part as usize] <= 1 {
continue;
continue;
}
}
state = state
state = state
.wrapping_mul(6364136223846793005u64)
.wrapping_mul(6364136223846793005u64)
.wrapping_add(1442695040888963407u64);
.wrapping_add(1442695040888963407u64);
let target_part = (state % challenge.num_parts as u64) as i32;
let target_part = (state % challenge.num_parts as u64) as i32;
if target_part != current_part
if target_part != current_part
&& nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32
&& nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32
{
{
partition_mirror[node] = target_part;
partition_mirror[node] = target_part;
nodes_in_part_mirror[current_part as usize] -= 1;
nodes_in_part_mirror[current_part as usize] -= 1;
nodes_in_part_mirror[target_part as usize] += 1;
nodes_in_part_mirror[target_part as usize] += 1;
moves_made += 1;
moves_made += 1;
}
}
}
}
};
};
let perturb_guided_on_host =
let perturb_guided_on_host =
|partition_mirror: &mut [i32],
|partition_mirror: &mut [i32],
nodes_in_part_mirror: &mut [i32],
nodes_in_part_mirror: &mut [i32],
high_hedge_ids_host: &[i32]| {
high_hedge_ids_host: &[i32]| {
let np = std::cmp::min(num_parts_usize, 64usize);
let np = std::cmp::min(num_parts_usize, 64usize);
for &hedge_i32 in high_hedge_ids_host.iter() {
for &hedge_i32 in high_hedge_ids_host.iter() {
if hedge_i32 < 0 {
if hedge_i32 < 0 {
continue;
continue;
}
}
let hedge = hedge_i32 as usize;
let hedge = hedge_i32 as usize;
let start = hedge_offsets_host[hedge] as usize;
let start = hedge_offsets_host[hedge] as usize;
let end = hedge_offsets_host[hedge + 1] as usize;
let end = hedge_offsets_host[hedge + 1] as usize;
let hedge_size = end - start;
let hedge_size = end - start;
if hedge_size <= 1 {
if hedge_size <= 1 {
continue;
continue;
}
}
let mut part_count = [0i32; 64];
let mut part_count = [0i32; 64];
for k in start..end {
for k in start..end {
let node = hyperedge_nodes_host[k] as usize;
let node = hyperedge_nodes_host[k] as usize;
let part = partition_mirror[node];
let part = partition_mirror[node];
if part >= 0 && (part as usize) < np {
if part >= 0 && (part as usize) < np {
part_count[part as usize] += 1;
part_count[part as usize] += 1;
}
}
}
}
let mut majority_part = 0usize;
let mut majority_part = 0usize;
for p in 1..np {
for p in 1..np {
if part_count[p] > part_count[majority_part] {
if part_count[p] > part_count[majority_part] {
majority_part = p;
majority_part = p;
}
}
}
}
let mut parts_present = 0i32;
let mut parts_present = 0i32;
for p in 0..np {
for p in 0..np {
if part_count[p] > 0 {
if part_count[p] > 0 {
parts_present += 1;
parts_present += 1;
}
}
}
}
if parts_present <= 1 {
if parts_present <= 1 {
continue;
continue;
}
}
for _iter in 0..np {
for _iter in 0..np {
if parts_present <= 1 {
if parts_present <= 1 {
break;
break;
}
}
if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 {
if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 {
break;
break;
}
}
let mut min_part = usize::MAX;
let mut min_part = usize::MAX;
let mut min_cnt = 0i32;
let mut min_cnt = 0i32;
for p in 0..np {
for p in 0..np {
if p == majority_part {
if p == majority_part {
continue;
continue;
}
}
let cnt = part_count[p];
let cnt = part_count[p];
if cnt <= 0 {
if cnt <= 0 {
continue;
continue;
}
}
if min_part == usize::MAX || cnt < min_cnt || (cnt == min_cnt && p < min_part)
if min_part == usize::MAX || cnt < min_cnt || (cnt == min_cnt && p < min_part)
{
{
min_part = p;
min_part = p;
min_cnt = cnt;
min_cnt = cnt;
}
}
}
}
if min_part == usize::MAX {
if min_part == usize::MAX {
break;
break;
}
}
let mut moved_any = false;
let mut moved_any = false;
for k in start..end {
for k in start..end {
if part_count[min_part] <= 0 {
if part_count[min_part] <= 0 {
break;
break;
}
}
if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 {
if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 {
break;
break;
}
}
let node = hyperedge_nodes_host[k] as usize;
let node = hyperedge_nodes_host[k] as usize;
if partition_mirror[node] != min_part as i32 {
if partition_mirror[node] != min_part as i32 {
continue;
continue;
}
}
if nodes_in_part_mirror[min_part] <= 1 {
if nodes_in_part_mirror[min_part] <= 1 {
continue;
continue;
}
}
partition_mirror[node] = majority_part as i32;
partition_mirror[node] = majority_part as i32;
nodes_in_part_mirror[min_part] -= 1;
nodes_in_part_mirror[min_part] -= 1;
nodes_in_part_mirror[majority_part] += 1;
nodes_in_part_mirror[majority_part] += 1;
part_count[min_part] -= 1;
part_count[min_part] -= 1;
part_count[majority_part] += 1;
part_count[majority_part] += 1;
moved_any = true;
moved_any = true;
if part_count[min_part] == 0 {
if part_count[min_part] == 0 {
parts_present -= 1;
parts_present -= 1;
break;
break;
}
}
}
}
if !moved_any {
if !moved_any {
break;
break;
}
}
}
}
}
}
};
};
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 stagnant_rounds = 0usize;
let mut stagnant_rounds = 0usize;
let max_stagnant_rounds = 30usize;
let max_stagnant_rounds = 30usize;
let mut node_tabu_until: Vec<usize> = vec![0; challenge.num_nodes as usize];
let mut node_tabu_until: Vec<usize> = vec![0; 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 total_moves_executed = 0usize;
let mut total_moves_executed = 0usize;
let mut total_perturbations = 0usize;
let mut total_perturbations = 0usize;
for round in 0..refinement_rounds {
for round in 0..refinement_rounds {
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)
.launch(cfg.clone())?;
.launch(cfg.clone())?;
}
}
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 as u32 != 0x80000000).map(|&k| k >> 16).max().unwrap_or(0);
let max_gain = move_keys_host.iter().filter(|&&k| k as u32 != 0x80000000).map(|&k| k >> 16).max().unwrap_or(0);
let aspiration_threshold = (max_gain * 3) / 4;
let aspiration_threshold = (max_gain * 3) / 4;
let mut rng_state = 123456789u64.wrapping_add(round as u64);
let mut rng_state = 123456789u64.wrapping_add(round as u64);
for (node, &key) in move_keys_host.iter().enumerate() {
for (node, &key) in move_keys_host.iter().enumerate() {
if key as u32 != 0x80000000 {
if key as u32 != 0x80000000 {
let gain = key >> 16;
let gain = key >> 16;
let is_tabu = node_tabu_until[node] > round && gain < aspiration_threshold;
let is_tabu = node_tabu_until[node] > round && gain < aspiration_threshold;
if !is_tabu {
if !is_tabu {
if gain > 0 {
if gain > 0 {
valid_moves.push((node, key));
valid_moves.push((node, key));
} else if gain >= -3 {
} else if gain >= -3 {
let rounds_left = refinement_rounds.saturating_sub(round) as u64;
let rounds_left = refinement_rounds.saturating_sub(round) as u64;
let penalty = if gain < 0 { (-gain) as u64 } else { 0 };
let penalty = if gain < 0 { (-gain) as u64 } else { 0 };
let probability_num = rounds_left * rounds_left;
let probability_num = rounds_left * rounds_left;
let probability_den = (refinement_rounds as u64 * refinement_rounds as u64)
let probability_den = (refinement_rounds as u64 * refinement_rounds as u64)
.saturating_mul(1 + penalty * 5);
.saturating_mul(1 + penalty * 5);
rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1442695040888963407u64);
rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1442695040888963407u64);
if probability_den > 0 && (rng_state % probability_den) < probability_num {
if probability_den > 0 && (rng_state % probability_den) < probability_num {
let new_key = (0 << 16) | (key & 0xFFFF);
let new_key = (0 << 16) | (key & 0xFFFF);
valid_moves.push((node, new_key));
valid_moves.push((node, new_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 * 3) / 4
(move_limit * 3) / 4
} else {
} else {
move_limit / 4
move_limit / 4
};
};
if k_base > adaptive_limit {
if k_base > adaptive_limit {
k_base = adaptive_limit;
k_base = adaptive_limit;
}
}
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);
}
}
let slack = if round < 64 {
let slack = if round < 64 {
slack_early
slack_early
} else if round < 256 {
} else if round < 256 {
slack_mid
slack_mid
} else {
} else {
slack_late
slack_late
};
};
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 = simulate_execute_moves(
let mut moves_executed = simulate_execute_moves(
&mut partition_host_refine,
&mut partition_host_refine,
&mut nodes_in_part_host,
&mut nodes_in_part_host,
&sorted_move_nodes,
&sorted_move_nodes,
&sorted_move_parts,
&sorted_move_parts,
);
);
if moves_executed > 0 {
if moves_executed > 0 {
stream.memcpy_htod(&partition_host_refine, &mut d_partition)?;
stream.memcpy_htod(&partition_host_refine, &mut d_partition)?;
stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?;
stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?;
}
}
if moves_executed == 0 && k_cand > k_base {
if moves_executed == 0 && k_cand > k_base {
let fail_mark_len = std::cmp::min(sorted_move_nodes.len(), tabu_fail_mark_len);
let fail_mark_len = std::cmp::min(sorted_move_nodes.len(), tabu_fail_mark_len);
for &node in sorted_move_nodes.iter().take(fail_mark_len) {
for &node in sorted_move_nodes.iter().take(fail_mark_len) {
node_tabu_until[node as usize] = round + tabu_fail_tenure;
node_tabu_until[node as usize] = round + tabu_fail_tenure;
}
}
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));
if !sorted_move_nodes.is_empty() {
if !sorted_move_nodes.is_empty() {
moves_executed = simulate_execute_moves(
moves_executed = simulate_execute_moves(
&mut partition_host_refine,
&mut partition_host_refine,
&mut nodes_in_part_host,
&mut nodes_in_part_host,
&sorted_move_nodes,
&sorted_move_nodes,
&sorted_move_parts,
&sorted_move_parts,
);
);
if moves_executed > 0 {
if moves_executed > 0 {
stream.memcpy_htod(&partition_host_refine, &mut d_partition)?;
stream.memcpy_htod(&partition_host_refine, &mut d_partition)?;
stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?;
stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?;
}
}
}
}
}
}
total_moves_executed += moves_executed as usize;
total_moves_executed += moves_executed as usize;
if moves_executed > 0 {
if moves_executed > 0 {
let mark_len = std::cmp::min(
let mark_len = std::cmp::min(
sorted_move_nodes.len(),
sorted_move_nodes.len(),
std::cmp::max(
std::cmp::max(
tabu_mark_base,
tabu_mark_base,
(moves_executed as usize).saturating_mul(tabu_mark_mult),
(moves_executed as usize).saturating_mul(tabu_mark_mult),
已保存差異
原始文本
開啟檔案
use cudarc::{ driver::{safe::LaunchConfig, CudaModule, CudaStream, PushKernelArg}, runtime::sys::cudaDeviceProp, }; use serde_json::{Map, Value}; use std::sync::Arc; use tig_challenges::hypergraph::*; pub fn solve( 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(128, prop.maxThreadsPerBlock as u32); let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering_200k")?; let compute_preferences_kernel = module.load_function("compute_node_preferences_200k")?; let execute_assignments_kernel = module.load_function("execute_node_assignments_200k")?; let precompute_edge_flags_kernel = module.load_function("precompute_edge_flags_200k")?; let compute_moves_kernel = module.load_function("compute_refinement_moves_optimized_200k")?; let balance_kernel = module.load_function("balance_final_200k")?; let compute_connectivity_kernel = module.load_function("compute_connectivity_200k")?; let reduce_connectivity_sum_kernel = module.load_function("reduce_connectivity_sum_200k")?; let compute_swap_gains_kernel = module.load_function("compute_swap_gains_extended_200k")?; let choose_elite_per_hyperedge_kernel = module.load_function("choose_elite_per_hyperedge_200k")?; let assign_from_elite_votes_kernel = module.load_function("assign_from_elite_votes_200k")?; 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 hedge_cfg = LaunchConfig { grid_dim: ( (challenge.num_hyperedges as u32 + block_size - 1) / block_size, 1, 1, ), block_dim: (block_size, 1, 1), shared_mem_bytes: 0, }; let connectivity_reduce_cfg = LaunchConfig { grid_dim: ( (challenge.num_hyperedges as u32 + block_size * 2 - 1) / (block_size * 2), 1, 1, ), block_dim: (block_size, 1, 1), shared_mem_bytes: (block_size as usize * std::mem::size_of::<i32>()) as u32, }; let mut num_hedge_clusters = if let Some(params) = hyperparameters { params .get("clusters") .and_then(|v| v.as_i64()) .map(|v| v.clamp(4, 256) as i32) .unwrap_or(64) } else { 64 }; if num_hedge_clusters % 4 != 0 { 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_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_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_move_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes 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_connectivity = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?; let mut d_total_connectivity = stream.alloc_zeros::<i32>(4096)?; let mut d_hedge_choice = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?; let swap_buf_size = 3 * challenge.num_nodes as usize; let mut d_swap_gains = stream.alloc_zeros::<i32>(swap_buf_size)?; 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 effort = hyperparameters .as_ref() .and_then(|p| p.get("effort").and_then(|v| v.as_i64())) .unwrap_or(3); let (base_refine, base_ils, base_ils_quick, base_polish, base_post_balance) = match effort { 5 => (9000, 6, 70, 300, 0), 4 => (7000, 5, 60, 200, 0), 3 => (5000, 5, 50, 150, 64), 2 => (2000, 5, 50, 100, 64), 1 => (1000, 5, 50, 50, 64), 0 => (500, 5, 50, 25, 64), _ => (5000, 5, 50, 200, 64), }; let refinement_rounds = hyperparameters .as_ref() .and_then(|p| p.get("refinement").and_then(|v| v.as_i64())) .map(|v| v.clamp(50, 50_000) as usize) .unwrap_or(base_refine); let ils_iterations = hyperparameters .as_ref() .and_then(|p| p.get("ils_iterations").and_then(|v| v.as_i64())) .map(|v| v.clamp(1, 500) as usize) .unwrap_or(base_ils); let ils_quick_refine = hyperparameters .as_ref() .and_then(|p| p.get("ils_quick_refine").and_then(|v| v.as_i64())) .map(|v| v.clamp(10, 500) as usize) .unwrap_or(base_ils_quick); let post_ils_polish = hyperparameters .as_ref() .and_then(|p| p.get("post_ils_polish").and_then(|v| v.as_i64())) .map(|v| v.clamp(20, 500) as usize) .unwrap_or(base_polish); let tabu_tenure: usize = hyperparameters .as_ref() .and_then(|p| p.get("tabu_tenure").and_then(|v| v.as_i64())) .map(|v| v.clamp(1, 30) as usize) .unwrap_or(14); let tabu_fail_tenure = 4usize; let tabu_mark_base = 4096usize; let tabu_mark_mult = 16usize; let tabu_fail_mark_len = 4096usize; let extra_window = 65536usize; let slack_early = 8usize; let slack_mid = 4usize; let slack_late = 2usize; let move_limit: usize = hyperparameters .as_ref() .and_then(|p| p.get("move_limit").and_then(|v| v.as_i64())) .map(|v| v.clamp(256, 1_000_000) as usize) .unwrap_or(if is_sparse { 262_144 } else if challenge.num_hyperedges as usize >= 150_000 || challenge.num_nodes as usize >= 250_000 { 131_072 } else { 200_000 }); let neg_gain_thresh: i32 = 5; let scan_limit_swap = 32usize; let scan_limit_cycle = 8usize; 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 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_host_refine: Vec<i32> = vec![0i32; challenge.num_nodes as usize]; 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 move_keys_host: Vec<i32> = vec![0i32; challenge.num_nodes as usize]; let hedge_offsets_host = stream.memcpy_dtov(&challenge.d_hyperedge_offsets)?; let hyperedge_nodes_host = stream.memcpy_dtov(&challenge.d_hyperedge_nodes)?; let mut hedge_sizes_host: Vec<i32> = Vec::with_capacity(challenge.num_hyperedges as usize); for h in 0..(challenge.num_hyperedges as usize) { let sz = hedge_offsets_host[h + 1] - hedge_offsets_host[h]; hedge_sizes_host.push(sz); } let build_high_hedge_ids = |connectivity: &[i32], num_high_hedges: usize| -> Vec<i32> { let mut impact: Vec<(i32, i32, i32)> = connectivity .iter() .enumerate() .map(|(i, &c)| (c, hedge_sizes_host[i], i as i32)) .collect(); impact.sort_unstable_by(|a, b| { b.0.cmp(&a.0) .then_with(|| b.1.cmp(&a.1)) .then_with(|| a.2.cmp(&b.2)) }); impact .into_iter() .take(num_high_hedges) .map(|t| t.2) .collect() }; unsafe { stream .launch_builder(&hyperedge_cluster_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(num_hedge_clusters as i32)) .arg(&challenge.d_hyperedge_offsets) .arg(&challenge.d_hyperedge_nodes) .arg(&mut d_hyperedge_clusters) .launch(LaunchConfig { grid_dim: ( (challenge.num_hyperedges as u32 + block_size - 1) / block_size, 1, 1, ), block_dim: (block_size, 1, 1), shared_mem_bytes: 0, })?; } 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_parts) .arg(&mut d_pref_priorities) .launch(cfg.clone())?; } 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_unstable_by(|&a, &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_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.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?; let simulate_execute_moves = |partition_mirror: &mut [i32], nodes_in_part_mirror: &mut [i32], move_nodes: &[i32], move_parts: &[i32]| -> i32 { let mut moves_executed = 0i32; for i in 0..move_nodes.len() { let node = move_nodes[i]; let target_part = move_parts[i]; if node < 0 || target_part < 0 { continue; } let node_usize = node as usize; if node_usize >= partition_mirror.len() { continue; } let current_part = partition_mirror[node_usize]; if current_part >= 0 && (current_part as usize) < nodes_in_part_mirror.len() && (target_part as usize) < nodes_in_part_mirror.len() && nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32 && nodes_in_part_mirror[current_part as usize] > 1 { partition_mirror[node_usize] = target_part; nodes_in_part_mirror[current_part as usize] -= 1; nodes_in_part_mirror[target_part as usize] += 1; moves_executed += 1; } } moves_executed }; let perturb_on_host = |partition_mirror: &mut [i32], nodes_in_part_mirror: &mut [i32], perturb_strength: i32, seed: u64| { let mut state = seed; let mut moves_made = 0i32; let target_moves = (challenge.num_nodes as i32 * perturb_strength) / 100; for _attempt in 0..(challenge.num_nodes as usize) { if moves_made >= target_moves { break; } state = state .wrapping_mul(6364136223846793005u64) .wrapping_add(1442695040888963407u64); let node = (state % challenge.num_nodes as u64) as usize; let current_part = partition_mirror[node]; if current_part < 0 || current_part >= challenge.num_parts as i32 { continue; } if nodes_in_part_mirror[current_part as usize] <= 1 { continue; } state = state .wrapping_mul(6364136223846793005u64) .wrapping_add(1442695040888963407u64); let target_part = (state % challenge.num_parts as u64) as i32; if target_part != current_part && nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32 { partition_mirror[node] = target_part; nodes_in_part_mirror[current_part as usize] -= 1; nodes_in_part_mirror[target_part as usize] += 1; moves_made += 1; } } }; let perturb_guided_on_host = |partition_mirror: &mut [i32], nodes_in_part_mirror: &mut [i32], high_hedge_ids_host: &[i32]| { let np = std::cmp::min(num_parts_usize, 64usize); for &hedge_i32 in high_hedge_ids_host.iter() { if hedge_i32 < 0 { continue; } let hedge = hedge_i32 as usize; let start = hedge_offsets_host[hedge] as usize; let end = hedge_offsets_host[hedge + 1] as usize; let hedge_size = end - start; if hedge_size <= 1 { continue; } let mut part_count = [0i32; 64]; for k in start..end { let node = hyperedge_nodes_host[k] as usize; let part = partition_mirror[node]; if part >= 0 && (part as usize) < np { part_count[part as usize] += 1; } } let mut majority_part = 0usize; for p in 1..np { if part_count[p] > part_count[majority_part] { majority_part = p; } } let mut parts_present = 0i32; for p in 0..np { if part_count[p] > 0 { parts_present += 1; } } if parts_present <= 1 { continue; } for _iter in 0..np { if parts_present <= 1 { break; } if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 { break; } let mut min_part = usize::MAX; let mut min_cnt = 0i32; for p in 0..np { if p == majority_part { continue; } let cnt = part_count[p]; if cnt <= 0 { continue; } if min_part == usize::MAX || cnt < min_cnt || (cnt == min_cnt && p < min_part) { min_part = p; min_cnt = cnt; } } if min_part == usize::MAX { break; } let mut moved_any = false; for k in start..end { if part_count[min_part] <= 0 { break; } if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 { break; } let node = hyperedge_nodes_host[k] as usize; if partition_mirror[node] != min_part as i32 { continue; } if nodes_in_part_mirror[min_part] <= 1 { continue; } partition_mirror[node] = majority_part as i32; nodes_in_part_mirror[min_part] -= 1; nodes_in_part_mirror[majority_part] += 1; part_count[min_part] -= 1; part_count[majority_part] += 1; moved_any = true; if part_count[min_part] == 0 { parts_present -= 1; break; } } if !moved_any { break; } } } }; 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 valid_moves: Vec<(usize, i32)> = Vec::with_capacity(challenge.num_nodes as usize); let mut stagnant_rounds = 0usize; let max_stagnant_rounds = 30usize; let mut node_tabu_until: Vec<usize> = vec![0; challenge.num_nodes as 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 total_moves_executed = 0usize; let mut total_perturbations = 0usize; for round in 0..refinement_rounds { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let max_gain = move_keys_host.iter().filter(|&&k| k as u32 != 0x80000000).map(|&k| k >> 16).max().unwrap_or(0); let aspiration_threshold = (max_gain * 3) / 4; let mut rng_state = 123456789u64.wrapping_add(round as u64); for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; let is_tabu = node_tabu_until[node] > round && gain < aspiration_threshold; if !is_tabu { if gain > 0 { valid_moves.push((node, key)); } else if gain >= -3 { let rounds_left = refinement_rounds.saturating_sub(round) as u64; let penalty = if gain < 0 { (-gain) as u64 } else { 0 }; let probability_num = rounds_left * rounds_left; let probability_den = (refinement_rounds as u64 * refinement_rounds as u64) .saturating_mul(1 + penalty * 5); rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1442695040888963407u64); if probability_den > 0 && (rng_state % probability_den) < probability_num { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } } if valid_moves.is_empty() { break; } 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 adaptive_limit = if round < 50 { move_limit / 2 } else if round < 200 { (move_limit * 3) / 4 } else { move_limit / 4 }; if k_base > adaptive_limit { k_base = adaptive_limit; } let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(extra_window)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = if round < 64 { slack_early } else if round < 256 { slack_mid } else { slack_late }; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let mut moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 && k_cand > k_base { let fail_mark_len = std::cmp::min(sorted_move_nodes.len(), tabu_fail_mark_len); for &node in sorted_move_nodes.iter().take(fail_mark_len) { node_tabu_until[node as usize] = round + tabu_fail_tenure; } sorted_move_nodes.clear(); sorted_move_parts.clear(); let tail = &valid_moves[k_base..k_cand]; let take = std::cmp::min(tail.len(), k_base); 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)); if !sorted_move_nodes.is_empty() { moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } } } total_moves_executed += moves_executed as usize; if moves_executed > 0 { let mark_len = std::cmp::min( sorted_move_nodes.len(), std::cmp::max( tabu_mark_base, (moves_executed as usize).saturating_mul(tabu_mark_mult), ), ); let until = round + tabu_tenure; for &node in sorted_move_nodes.iter().take(mark_len) { node_tabu_until[node as usize] = until; } } if moves_executed == 0 { stagnant_rounds += 1; if stagnant_rounds >= 3 && round < refinement_rounds.saturating_sub(50) { let mini_seed = 987654321u64 + (round as u64) * 123456789u64; perturb_on_host( &mut partition_host_refine, &mut nodes_in_part_host, 3i32, mini_seed, ); stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; total_perturbations += 1; stagnant_rounds = 0; } else if stagnant_rounds > max_stagnant_rounds { break; } } else { stagnant_rounds = 0; } } macro_rules! do_swap_phase { ($d_partition:expr, $d_nodes_in_part:expr, $d_edge_flags_all:expr, $d_edge_flags_double:expr, $d_swap_gains:expr, $swap_gains_host:expr, $partition_host_swap:expr, $partition_mut_swap:expr, $part_to_part:expr, $used_ba_buf:expr, $max_rounds:expr, $ngt:expr, $scan_lim:expr, $scan_lim_cyc:expr) => {{ let num_nodes_i = challenge.num_nodes as i32; let num_parts_i = challenge.num_parts as i32; let np = num_parts_usize; let mut prev_swap_count = usize::MAX; let mut stagnant = 0usize; let mut total_swaps = 0usize; stream.memcpy_dtoh(&mut *$d_partition, $partition_host_swap)?; for _swap_round in 0..$max_rounds { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&num_nodes_i) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&mut *$d_partition) .arg(&mut *$d_edge_flags_all) .arg(&mut *$d_edge_flags_double) .launch(hedge_cfg.clone())?; } unsafe { stream .launch_builder(&compute_swap_gains_kernel) .arg(&num_nodes_i) .arg(&num_parts_i) .arg(&$ngt) .arg(&challenge.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&mut *$d_partition) .arg(&mut *$d_edge_flags_all) .arg(&mut *$d_edge_flags_double) .arg(&mut *$d_swap_gains) .launch(cfg.clone())?; } stream.memcpy_dtoh(&mut *$d_swap_gains, $swap_gains_host)?; let num_nodes = num_nodes_i as usize; for v in $part_to_part.iter_mut() { v.clear(); } for node in 0..num_nodes { let src = $partition_host_swap[node] as usize; if src >= np { continue; } for k in 0..3usize { let val = $swap_gains_host[node * 3 + k]; if val == 0 { continue; } let tgt = (val & 0xFFFF) as usize; let gain = ((val >> 16) as i16) as i32; if tgt < np && tgt != src { $part_to_part[src * np + tgt].push((node, gain)); } } } $partition_mut_swap.copy_from_slice($partition_host_swap); let mut swap_count = 0usize; for a in 0..np { for b in (a + 1)..np { let idx_ab = a * np + b; let idx_ba = b * np + a; if $part_to_part[idx_ab].is_empty() || $part_to_part[idx_ba].is_empty() { continue; } $part_to_part[idx_ab].sort_unstable_by(|x, y| y.1.cmp(&x.1)); $part_to_part[idx_ba].sort_unstable_by(|x, y| y.1.cmp(&x.1)); let lab_len = $part_to_part[idx_ab].len(); let lba_len = $part_to_part[idx_ba].len(); $used_ba_buf.clear(); $used_ba_buf.resize(lba_len, false); for i in 0..lab_len { let (node_a, gain_a) = $part_to_part[idx_ab][i]; if $partition_mut_swap[node_a] as usize != a { continue; } let mut best_combined = 0i32; let mut best_j = usize::MAX; let sl = std::cmp::min(lba_len, $scan_lim); for j in 0..sl { if $used_ba_buf[j] { continue; } let (node_b, gain_b) = $part_to_part[idx_ba][j]; if $partition_mut_swap[node_b] as usize != b { continue; } let combined = gain_a + gain_b; if combined > best_combined { best_combined = combined; best_j = j; } } if best_j < lba_len && best_combined > 0 { let (node_b, _) = $part_to_part[idx_ba][best_j]; $partition_mut_swap[node_a] = b as i32; $partition_mut_swap[node_b] = a as i32; $used_ba_buf[best_j] = true; swap_count += 1; } } } } let cyc_scan = $scan_lim_cyc; if cyc_scan > 0 { for a in 0..np { for b in 0..np { if b == a { continue; } let idx_ab = a * np + b; if $part_to_part[idx_ab].is_empty() { continue; } for c in 0..np { if c == a || c == b { continue; } let idx_bc = b * np + c; let idx_ca = c * np + a; if $part_to_part[idx_bc].is_empty() || $part_to_part[idx_ca].is_empty() { continue; } let sl_ab = std::cmp::min($part_to_part[idx_ab].len(), cyc_scan); let sl_bc = std::cmp::min($part_to_part[idx_bc].len(), cyc_scan); let sl_ca = std::cmp::min($part_to_part[idx_ca].len(), cyc_scan); 'outer: for i in 0..sl_ab { let (node_ab, gain_ab) = $part_to_part[idx_ab][i]; if $partition_mut_swap[node_ab] as usize != a { continue; } for j in 0..sl_bc { let (node_bc, gain_bc) = $part_to_part[idx_bc][j]; if $partition_mut_swap[node_bc] as usize != b { continue; } if node_bc == node_ab { continue; } if gain_ab + gain_bc <= 0 { break; } for k in 0..sl_ca { let (node_ca, gain_ca) = $part_to_part[idx_ca][k]; if $partition_mut_swap[node_ca] as usize != c { continue; } if node_ca == node_ab || node_ca == node_bc { continue; } if gain_ab + gain_bc + gain_ca > 0 { $partition_mut_swap[node_ab] = b as i32; $partition_mut_swap[node_bc] = c as i32; $partition_mut_swap[node_ca] = a as i32; swap_count += 1; break 'outer; } } } } } } } } if swap_count == 0 { break; } total_swaps += swap_count; if swap_count >= prev_swap_count { stagnant += 1; if stagnant >= 3 { break; } } else { stagnant = 0; } prev_swap_count = swap_count; stream.memcpy_htod($partition_mut_swap, &mut *$d_partition)?; $partition_host_swap.copy_from_slice($partition_mut_swap); } anyhow::Ok(total_swaps) }}; } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 100, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; partition_host_refine.copy_from_slice(&partition_host_swap); for _post_swap_round in 0..30 { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } } } if valid_moves.is_empty() { break; } let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0)); let k_base = std::cmp::min(valid_moves.len(), move_limit / 2); let k_cand = std::cmp::min(valid_moves.len(), k_base + extra_window / 2); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } tgt_used.fill(0); for p in 0..num_parts_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 + slack_mid); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let me = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if me > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if me == 0 { break; } } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 50, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; let perturb_strength = 3; unsafe { stream .launch_builder(&compute_connectivity_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_connectivity) .launch(hedge_cfg.clone())?; } let conn_vec = stream.memcpy_dtov(&d_connectivity)?; let mut best_connectivity: i32 = conn_vec.iter().sum(); let mut best_partition_host = stream.memcpy_dtov(&d_partition)?; let mut best_nodes_in_part_host = stream.memcpy_dtov(&d_nodes_in_part)?; let num_high_hedges = std::cmp::min(500usize, challenge.num_hyperedges as usize); let mut high_hedge_ids_host: Vec<i32> = build_high_hedge_ids(&conn_vec, num_high_hedges); let pool_size = ils_iterations; let mut elite_scores: Vec<i32> = vec![i32::MAX; pool_size]; let mut elite_flat_host: Vec<i32> = vec![0i32; pool_size * challenge.num_nodes as usize]; elite_scores[0] = best_connectivity; elite_flat_host[..challenge.num_nodes as usize].copy_from_slice(&best_partition_host); let mut elite_count: usize = 1; let mut d_elite_flat = stream.alloc_zeros::<i32>(pool_size * challenge.num_nodes as usize)?; let mut use_consensus_next = false; for ils_iter in 0..ils_iterations { let d_partition_restored = stream.memcpy_stod(&best_partition_host)?; let d_nodes_in_part_restored = stream.memcpy_stod(&best_nodes_in_part_host)?; d_partition = d_partition_restored; d_nodes_in_part = d_nodes_in_part_restored; partition_host_refine.copy_from_slice(&best_partition_host); nodes_in_part_host.copy_from_slice(&best_nodes_in_part_host); unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } let seed = 123456789u64 + (ils_iter as u64) * 987654321u64; let refreshed_host_from_device = if use_consensus_next && elite_count > 1 { stream.memcpy_htod(&elite_flat_host, &mut d_elite_flat)?; let mut elite_order_host: Vec<i32> = (0..elite_count as i32).collect(); elite_order_host.sort_unstable_by(|&a, &b| { elite_scores[a as usize] .cmp(&elite_scores[b as usize]) .then_with(|| a.cmp(&b)) }); let d_elite_order = stream.memcpy_stod(&elite_order_host)?; unsafe { stream .launch_builder(&choose_elite_per_hyperedge_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&(elite_count as i32)) .arg(&d_elite_flat) .arg(&d_elite_order) .arg(&challenge.d_hyperedge_offsets) .arg(&challenge.d_hyperedge_nodes) .arg(&mut d_hedge_choice) .launch(hedge_cfg.clone())?; } stream.memset_zeros(&mut d_nodes_in_part)?; unsafe { stream .launch_builder(&assign_from_elite_votes_kernel) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&(elite_count as i32)) .arg(&d_elite_flat) .arg(&d_hedge_choice) .arg(&challenge.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_edge_flags_all) .arg(&mut d_partition) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; nodes_in_part_host.fill(0); for &p in partition_host_refine.iter() { if p >= 0 && (p as usize) < num_parts_usize { nodes_in_part_host[p as usize] += 1; } } stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; unsafe { stream .launch_builder(&balance_kernel) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&1i32) .arg(&(challenge.max_part_size as i32)) .arg(&mut d_partition) .arg(&mut d_nodes_in_part) .launch(one_thread_cfg.clone())?; } true } else { if ils_iter % 2 == 0 { perturb_guided_on_host( &mut partition_host_refine, &mut nodes_in_part_host, &high_hedge_ids_host, ); } else { perturb_on_host( &mut partition_host_refine, &mut nodes_in_part_host, perturb_strength, seed, ); } stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; false }; if refreshed_host_from_device { stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?; } for _ in 0..ils_quick_refine { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let mut rng_state = 987654321u64; for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } else if gain == 0 { rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1); if (rng_state % 10) < 2 { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } if valid_moves.is_empty() { break; } 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(); if k_base > move_limit { k_base = move_limit; } let ils_extra = extra_window / 2; let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(ils_extra)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = slack_mid + 2; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 { break; } } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 25, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; unsafe { stream .launch_builder(&compute_connectivity_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_connectivity) .launch(hedge_cfg.clone())?; } unsafe { stream .launch_builder(&reduce_connectivity_sum_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&d_connectivity) .arg(&mut d_total_connectivity) .launch(connectivity_reduce_cfg.clone())?; } let block_sums = stream.memcpy_dtov(&d_total_connectivity)?; let num_blocks = ((challenge.num_hyperedges as u32 + block_size * 2 - 1) / (block_size * 2)) as usize; let new_connectivity: i32 = block_sums[..num_blocks].iter().sum(); let mut improved = false; if new_connectivity < best_connectivity { improved = true; best_connectivity = new_connectivity; best_partition_host = stream.memcpy_dtov(&d_partition)?; best_nodes_in_part_host = stream.memcpy_dtov(&d_nodes_in_part)?; let connectivity_vec = stream.memcpy_dtov(&d_connectivity)?; high_hedge_ids_host = build_high_hedge_ids(&connectivity_vec, num_high_hedges); } let slot_opt: Option<usize> = if elite_count < pool_size { let s = elite_count; elite_count += 1; Some(s) } else { let mut worst_idx = 0usize; let mut worst_score = elite_scores[0]; for i in 1..pool_size { if elite_scores[i] > worst_score { worst_score = elite_scores[i]; worst_idx = i; } } if new_connectivity < worst_score { Some(worst_idx) } else { None } }; if let Some(slot) = slot_opt { let src_part: Vec<i32>; let src_slice: &[i32] = if improved { &best_partition_host } else { src_part = stream.memcpy_dtov(&d_partition)?; &src_part }; let n = challenge.num_nodes as usize; elite_flat_host[slot * n..(slot + 1) * n].copy_from_slice(src_slice); elite_scores[slot] = new_connectivity; } use_consensus_next = !improved; } let d_partition_final = stream.memcpy_stod(&best_partition_host)?; let d_nodes_in_part_final = stream.memcpy_stod(&best_nodes_in_part_host)?; d_partition = d_partition_final; d_nodes_in_part = d_nodes_in_part_final; partition_host_refine.copy_from_slice(&best_partition_host); nodes_in_part_host.copy_from_slice(&best_nodes_in_part_host); for _ in 0..post_ils_polish { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let mut rng_state = 11223344u64; for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } else if gain == 0 { rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1); if (rng_state % 20) == 0 { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } if valid_moves.is_empty() { break; } let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0)); let polish_limit = 100000usize; let k_base = std::cmp::min(valid_moves.len(), polish_limit); let polish_extra = extra_window / 3; let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(polish_extra)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = slack_mid; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 { break; } } unsafe { stream .launch_builder(&balance_kernel) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&1i32) .arg(&(challenge.max_part_size as i32)) .arg(&mut d_partition) .arg(&mut d_nodes_in_part) .launch(one_thread_cfg.clone())?; } stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?; let post_balance_rounds = hyperparameters .as_ref() .and_then(|p| p.get("post_refinement").and_then(|v| v.as_i64())) .map(|v| v.clamp(0, 128) as usize) .unwrap_or(base_post_balance); for _ in 0..post_balance_rounds { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let mut rng_state = 55667788u64; for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } else if gain == 0 { rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1); if (rng_state % 20) == 0 { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } if valid_moves.is_empty() { break; } 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 adaptive_limit = move_limit / 2; if k_base > adaptive_limit { k_base = adaptive_limit; } let post_extra = extra_window / 3; let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(post_extra)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = slack_mid; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let mut moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 && k_cand > k_base { sorted_move_nodes.clear(); sorted_move_parts.clear(); let tail = &valid_moves[k_base..k_cand]; let take = std::cmp::min(tail.len(), k_base); 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)); if !sorted_move_nodes.is_empty() { moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } } } let _ = (moves_executed, total_moves_executed, total_perturbations); if moves_executed == 0 { break; } } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 10, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; let partition = stream.memcpy_dtov(&d_partition)?; let partition_u32: Vec<u32> = partition.iter().map(|&x| x as u32).collect(); save_solution(&Solution { partition: partition_u32, })?; Ok(()) }
更改後文本
開啟檔案
use cudarc::{ driver::{safe::LaunchConfig, CudaModule, CudaStream, PushKernelArg}, runtime::sys::cudaDeviceProp, }; use serde_json::{Map, Value}; use std::sync::Arc; use tig_challenges::hypergraph::*; pub fn solve( 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(128, prop.maxThreadsPerBlock as u32); let hyperedge_cluster_kernel = module.load_function("hyperedge_clustering_200k")?; let compute_preferences_kernel = module.load_function("compute_node_preferences_200k")?; let execute_assignments_kernel = module.load_function("execute_node_assignments_200k")?; let precompute_edge_flags_kernel = module.load_function("precompute_edge_flags_200k")?; let compute_moves_kernel = module.load_function("compute_refinement_moves_optimized_200k")?; let balance_kernel = module.load_function("balance_final_200k")?; let compute_connectivity_kernel = module.load_function("compute_connectivity_200k")?; let reduce_connectivity_sum_kernel = module.load_function("reduce_connectivity_sum_200k")?; let compute_swap_gains_kernel = module.load_function("compute_swap_gains_extended_200k")?; let choose_elite_per_hyperedge_kernel = module.load_function("choose_elite_per_hyperedge_200k")?; let assign_from_elite_votes_kernel = module.load_function("assign_from_elite_votes_200k")?; 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 hedge_cfg = LaunchConfig { grid_dim: ( (challenge.num_hyperedges as u32 + block_size - 1) / block_size, 1, 1, ), block_dim: (block_size, 1, 1), shared_mem_bytes: 0, }; let connectivity_reduce_cfg = LaunchConfig { grid_dim: ( (challenge.num_hyperedges as u32 + block_size * 2 - 1) / (block_size * 2), 1, 1, ), block_dim: (block_size, 1, 1), shared_mem_bytes: (block_size as usize * std::mem::size_of::<i32>()) as u32, }; let mut num_hedge_clusters = if let Some(params) = hyperparameters { params .get("clusters") .and_then(|v| v.as_i64()) .map(|v| v.clamp(4, 256) as i32) .unwrap_or(64) } else { 64 }; if num_hedge_clusters % 4 != 0 { 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_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_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_move_priorities = stream.alloc_zeros::<i32>(challenge.num_nodes 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_connectivity = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?; let mut d_total_connectivity = stream.alloc_zeros::<i32>(4096)?; let mut d_hedge_choice = stream.alloc_zeros::<i32>(challenge.num_hyperedges as usize)?; let swap_buf_size = 3 * challenge.num_nodes as usize; let mut d_swap_gains = stream.alloc_zeros::<i32>(swap_buf_size)?; 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 effort = hyperparameters .as_ref() .and_then(|p| p.get("effort").and_then(|v| v.as_i64())) .unwrap_or(3); let (base_refine, base_ils, base_ils_quick, base_polish, base_post_balance) = match effort { 5 => (9000, 6, 70, 300, 0), 4 => (7000, 5, 60, 200, 0), 3 => (5000, 5, 50, 150, 64), 2 => (2000, 5, 50, 100, 64), 1 => (1000, 5, 50, 50, 64), 0 => (500, 5, 50, 25, 64), _ => (5000, 5, 50, 200, 64), }; let refinement_rounds = hyperparameters .as_ref() .and_then(|p| p.get("refinement").and_then(|v| v.as_i64())) .map(|v| v.clamp(50, 50_000) as usize) .unwrap_or(base_refine); let ils_iterations = hyperparameters .as_ref() .and_then(|p| p.get("ils_iterations").and_then(|v| v.as_i64())) .map(|v| v.clamp(1, 500) as usize) .unwrap_or(base_ils); let ils_quick_refine = hyperparameters .as_ref() .and_then(|p| p.get("ils_quick_refine").and_then(|v| v.as_i64())) .map(|v| v.clamp(10, 500) as usize) .unwrap_or(base_ils_quick); let post_ils_polish = hyperparameters .as_ref() .and_then(|p| p.get("post_ils_polish").and_then(|v| v.as_i64())) .map(|v| v.clamp(20, 500) as usize) .unwrap_or(base_polish); let tabu_tenure: usize = hyperparameters .as_ref() .and_then(|p| p.get("tabu_tenure").and_then(|v| v.as_i64())) .map(|v| v.clamp(1, 30) as usize) .unwrap_or(14); let tabu_fail_tenure = 4usize; let tabu_mark_base = 4096usize; let tabu_mark_mult = 16usize; let tabu_fail_mark_len = 4096usize; let extra_window = 65536usize; let slack_early = 8usize; let slack_mid = 4usize; let slack_late = 2usize; let move_limit: usize = hyperparameters .as_ref() .and_then(|p| p.get("move_limit").and_then(|v| v.as_i64())) .map(|v| v.clamp(256, 1_000_000) as usize) .unwrap_or(if is_sparse { 262_144 } else if challenge.num_hyperedges as usize >= 150_000 || challenge.num_nodes as usize >= 250_000 { 131_072 } else { 200_000 }); let neg_gain_thresh: i32 = 5; let scan_limit_swap = 32usize; let scan_limit_cycle = 8usize; 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 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_host_refine: Vec<i32> = vec![0i32; challenge.num_nodes as usize]; 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 move_keys_host: Vec<i32> = vec![0i32; challenge.num_nodes as usize]; let hedge_offsets_host = stream.memcpy_dtov(&challenge.d_hyperedge_offsets)?; let hyperedge_nodes_host = stream.memcpy_dtov(&challenge.d_hyperedge_nodes)?; let mut hedge_sizes_host: Vec<i32> = Vec::with_capacity(challenge.num_hyperedges as usize); for h in 0..(challenge.num_hyperedges as usize) { let sz = hedge_offsets_host[h + 1] - hedge_offsets_host[h]; hedge_sizes_host.push(sz); } let build_high_hedge_ids = |connectivity: &[i32], num_high_hedges: usize| -> Vec<i32> { let mut impact: Vec<(i32, i32, i32)> = connectivity .iter() .enumerate() .map(|(i, &c)| (c, hedge_sizes_host[i], i as i32)) .collect(); impact.sort_unstable_by(|a, b| { b.0.cmp(&a.0) .then_with(|| b.1.cmp(&a.1)) .then_with(|| a.2.cmp(&b.2)) }); impact .into_iter() .take(num_high_hedges) .map(|t| t.2) .collect() }; unsafe { stream .launch_builder(&hyperedge_cluster_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(num_hedge_clusters as i32)) .arg(&challenge.d_hyperedge_offsets) .arg(&challenge.d_hyperedge_nodes) .arg(&mut d_hyperedge_clusters) .launch(LaunchConfig { grid_dim: ( (challenge.num_hyperedges as u32 + block_size - 1) / block_size, 1, 1, ), block_dim: (block_size, 1, 1), shared_mem_bytes: 0, })?; } 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_parts) .arg(&mut d_pref_priorities) .launch(cfg.clone())?; } 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_unstable_by(|&a, &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_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.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?; let simulate_execute_moves = |partition_mirror: &mut [i32], nodes_in_part_mirror: &mut [i32], move_nodes: &[i32], move_parts: &[i32]| -> i32 { let mut moves_executed = 0i32; for i in 0..move_nodes.len() { let node = move_nodes[i]; let target_part = move_parts[i]; if node < 0 || target_part < 0 { continue; } let node_usize = node as usize; if node_usize >= partition_mirror.len() { continue; } let current_part = partition_mirror[node_usize]; if current_part >= 0 && (current_part as usize) < nodes_in_part_mirror.len() && (target_part as usize) < nodes_in_part_mirror.len() && nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32 && nodes_in_part_mirror[current_part as usize] > 1 { partition_mirror[node_usize] = target_part; nodes_in_part_mirror[current_part as usize] -= 1; nodes_in_part_mirror[target_part as usize] += 1; moves_executed += 1; } } moves_executed }; let perturb_on_host = |partition_mirror: &mut [i32], nodes_in_part_mirror: &mut [i32], perturb_strength: i32, seed: u64| { let mut state = seed; let mut moves_made = 0i32; let target_moves = (challenge.num_nodes as i32 * perturb_strength) / 100; for _attempt in 0..(challenge.num_nodes as usize) { if moves_made >= target_moves { break; } state = state .wrapping_mul(6364136223846793005u64) .wrapping_add(1442695040888963407u64); let node = (state % challenge.num_nodes as u64) as usize; let current_part = partition_mirror[node]; if current_part < 0 || current_part >= challenge.num_parts as i32 { continue; } if nodes_in_part_mirror[current_part as usize] <= 1 { continue; } state = state .wrapping_mul(6364136223846793005u64) .wrapping_add(1442695040888963407u64); let target_part = (state % challenge.num_parts as u64) as i32; if target_part != current_part && nodes_in_part_mirror[target_part as usize] < challenge.max_part_size as i32 { partition_mirror[node] = target_part; nodes_in_part_mirror[current_part as usize] -= 1; nodes_in_part_mirror[target_part as usize] += 1; moves_made += 1; } } }; let perturb_guided_on_host = |partition_mirror: &mut [i32], nodes_in_part_mirror: &mut [i32], high_hedge_ids_host: &[i32]| { let np = std::cmp::min(num_parts_usize, 64usize); for &hedge_i32 in high_hedge_ids_host.iter() { if hedge_i32 < 0 { continue; } let hedge = hedge_i32 as usize; let start = hedge_offsets_host[hedge] as usize; let end = hedge_offsets_host[hedge + 1] as usize; let hedge_size = end - start; if hedge_size <= 1 { continue; } let mut part_count = [0i32; 64]; for k in start..end { let node = hyperedge_nodes_host[k] as usize; let part = partition_mirror[node]; if part >= 0 && (part as usize) < np { part_count[part as usize] += 1; } } let mut majority_part = 0usize; for p in 1..np { if part_count[p] > part_count[majority_part] { majority_part = p; } } let mut parts_present = 0i32; for p in 0..np { if part_count[p] > 0 { parts_present += 1; } } if parts_present <= 1 { continue; } for _iter in 0..np { if parts_present <= 1 { break; } if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 { break; } let mut min_part = usize::MAX; let mut min_cnt = 0i32; for p in 0..np { if p == majority_part { continue; } let cnt = part_count[p]; if cnt <= 0 { continue; } if min_part == usize::MAX || cnt < min_cnt || (cnt == min_cnt && p < min_part) { min_part = p; min_cnt = cnt; } } if min_part == usize::MAX { break; } let mut moved_any = false; for k in start..end { if part_count[min_part] <= 0 { break; } if nodes_in_part_mirror[majority_part] >= challenge.max_part_size as i32 { break; } let node = hyperedge_nodes_host[k] as usize; if partition_mirror[node] != min_part as i32 { continue; } if nodes_in_part_mirror[min_part] <= 1 { continue; } partition_mirror[node] = majority_part as i32; nodes_in_part_mirror[min_part] -= 1; nodes_in_part_mirror[majority_part] += 1; part_count[min_part] -= 1; part_count[majority_part] += 1; moved_any = true; if part_count[min_part] == 0 { parts_present -= 1; break; } } if !moved_any { break; } } } }; 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 valid_moves: Vec<(usize, i32)> = Vec::with_capacity(challenge.num_nodes as usize); let mut stagnant_rounds = 0usize; let max_stagnant_rounds = 30usize; let mut node_tabu_until: Vec<usize> = vec![0; challenge.num_nodes as 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 total_moves_executed = 0usize; let mut total_perturbations = 0usize; for round in 0..refinement_rounds { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let max_gain = move_keys_host.iter().filter(|&&k| k as u32 != 0x80000000).map(|&k| k >> 16).max().unwrap_or(0); let aspiration_threshold = (max_gain * 3) / 4; let mut rng_state = 123456789u64.wrapping_add(round as u64); for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; let is_tabu = node_tabu_until[node] > round && gain < aspiration_threshold; if !is_tabu { if gain > 0 { valid_moves.push((node, key)); } else if gain >= -3 { let rounds_left = refinement_rounds.saturating_sub(round) as u64; let penalty = if gain < 0 { (-gain) as u64 } else { 0 }; let probability_num = rounds_left * rounds_left; let probability_den = (refinement_rounds as u64 * refinement_rounds as u64) .saturating_mul(1 + penalty * 5); rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1442695040888963407u64); if probability_den > 0 && (rng_state % probability_den) < probability_num { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } } if valid_moves.is_empty() { break; } 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 adaptive_limit = if round < 50 { move_limit / 2 } else if round < 200 { (move_limit * 3) / 4 } else { move_limit / 4 }; if k_base > adaptive_limit { k_base = adaptive_limit; } let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(extra_window)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = if round < 64 { slack_early } else if round < 256 { slack_mid } else { slack_late }; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let mut moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 && k_cand > k_base { let fail_mark_len = std::cmp::min(sorted_move_nodes.len(), tabu_fail_mark_len); for &node in sorted_move_nodes.iter().take(fail_mark_len) { node_tabu_until[node as usize] = round + tabu_fail_tenure; } sorted_move_nodes.clear(); sorted_move_parts.clear(); let tail = &valid_moves[k_base..k_cand]; let take = std::cmp::min(tail.len(), k_base); 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)); if !sorted_move_nodes.is_empty() { moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } } } total_moves_executed += moves_executed as usize; if moves_executed > 0 { let mark_len = std::cmp::min( sorted_move_nodes.len(), std::cmp::max( tabu_mark_base, (moves_executed as usize).saturating_mul(tabu_mark_mult), ), ); let until = round + tabu_tenure; for &node in sorted_move_nodes.iter().take(mark_len) { node_tabu_until[node as usize] = until; } } if moves_executed == 0 { stagnant_rounds += 1; if stagnant_rounds >= 3 && round < refinement_rounds.saturating_sub(50) { let mini_seed = 987654321u64 + (round as u64) * 123456789u64; perturb_on_host( &mut partition_host_refine, &mut nodes_in_part_host, 3i32, mini_seed, ); stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; total_perturbations += 1; stagnant_rounds = 0; } else if stagnant_rounds > max_stagnant_rounds { break; } } else { stagnant_rounds = 0; } } macro_rules! do_swap_phase { ($d_partition:expr, $d_nodes_in_part:expr, $d_edge_flags_all:expr, $d_edge_flags_double:expr, $d_swap_gains:expr, $swap_gains_host:expr, $partition_host_swap:expr, $partition_mut_swap:expr, $part_to_part:expr, $used_ba_buf:expr, $max_rounds:expr, $ngt:expr, $scan_lim:expr, $scan_lim_cyc:expr) => {{ let num_nodes_i = challenge.num_nodes as i32; let num_parts_i = challenge.num_parts as i32; let np = num_parts_usize; let mut prev_swap_count = usize::MAX; let mut stagnant = 0usize; let mut total_swaps = 0usize; stream.memcpy_dtoh(&mut *$d_partition, $partition_host_swap)?; for _swap_round in 0..$max_rounds { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&num_nodes_i) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&mut *$d_partition) .arg(&mut *$d_edge_flags_all) .arg(&mut *$d_edge_flags_double) .launch(hedge_cfg.clone())?; } unsafe { stream .launch_builder(&compute_swap_gains_kernel) .arg(&num_nodes_i) .arg(&num_parts_i) .arg(&$ngt) .arg(&challenge.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&mut *$d_partition) .arg(&mut *$d_edge_flags_all) .arg(&mut *$d_edge_flags_double) .arg(&mut *$d_swap_gains) .launch(cfg.clone())?; } stream.memcpy_dtoh(&mut *$d_swap_gains, $swap_gains_host)?; let num_nodes = num_nodes_i as usize; for v in $part_to_part.iter_mut() { v.clear(); } for node in 0..num_nodes { let src = $partition_host_swap[node] as usize; if src >= np { continue; } for k in 0..3usize { let val = $swap_gains_host[node * 3 + k]; if val == 0 { continue; } let tgt = (val & 0xFFFF) as usize; let gain = ((val >> 16) as i16) as i32; if tgt < np && tgt != src { $part_to_part[src * np + tgt].push((node, gain)); } } } $partition_mut_swap.copy_from_slice($partition_host_swap); let mut swap_count = 0usize; for a in 0..np { for b in (a + 1)..np { let idx_ab = a * np + b; let idx_ba = b * np + a; if $part_to_part[idx_ab].is_empty() || $part_to_part[idx_ba].is_empty() { continue; } $part_to_part[idx_ab].sort_unstable_by(|x, y| y.1.cmp(&x.1)); $part_to_part[idx_ba].sort_unstable_by(|x, y| y.1.cmp(&x.1)); let lab_len = $part_to_part[idx_ab].len(); let lba_len = $part_to_part[idx_ba].len(); $used_ba_buf.clear(); $used_ba_buf.resize(lba_len, false); for i in 0..lab_len { let (node_a, gain_a) = $part_to_part[idx_ab][i]; if $partition_mut_swap[node_a] as usize != a { continue; } let mut best_combined = 0i32; let mut best_j = usize::MAX; let sl = std::cmp::min(lba_len, $scan_lim); for j in 0..sl { if $used_ba_buf[j] { continue; } let (node_b, gain_b) = $part_to_part[idx_ba][j]; if $partition_mut_swap[node_b] as usize != b { continue; } let combined = gain_a + gain_b; if combined > best_combined { best_combined = combined; best_j = j; } } if best_j < lba_len && best_combined > 0 { let (node_b, _) = $part_to_part[idx_ba][best_j]; $partition_mut_swap[node_a] = b as i32; $partition_mut_swap[node_b] = a as i32; $used_ba_buf[best_j] = true; swap_count += 1; } } } } let cyc_scan = $scan_lim_cyc; if cyc_scan > 0 { for a in 0..np { for b in 0..np { if b == a { continue; } let idx_ab = a * np + b; if $part_to_part[idx_ab].is_empty() { continue; } for c in 0..np { if c == a || c == b { continue; } let idx_bc = b * np + c; let idx_ca = c * np + a; if $part_to_part[idx_bc].is_empty() || $part_to_part[idx_ca].is_empty() { continue; } let sl_ab = std::cmp::min($part_to_part[idx_ab].len(), cyc_scan); let sl_bc = std::cmp::min($part_to_part[idx_bc].len(), cyc_scan); let sl_ca = std::cmp::min($part_to_part[idx_ca].len(), cyc_scan); 'outer: for i in 0..sl_ab { let (node_ab, gain_ab) = $part_to_part[idx_ab][i]; if $partition_mut_swap[node_ab] as usize != a { continue; } for j in 0..sl_bc { let (node_bc, gain_bc) = $part_to_part[idx_bc][j]; if $partition_mut_swap[node_bc] as usize != b { continue; } if node_bc == node_ab { continue; } if gain_ab + gain_bc <= 0 { break; } for k in 0..sl_ca { let (node_ca, gain_ca) = $part_to_part[idx_ca][k]; if $partition_mut_swap[node_ca] as usize != c { continue; } if node_ca == node_ab || node_ca == node_bc { continue; } if gain_ab + gain_bc + gain_ca > 0 { $partition_mut_swap[node_ab] = b as i32; $partition_mut_swap[node_bc] = c as i32; $partition_mut_swap[node_ca] = a as i32; swap_count += 1; break 'outer; } } } } } } } } if swap_count == 0 { break; } total_swaps += swap_count; if swap_count >= prev_swap_count { stagnant += 1; if stagnant >= 3 { break; } } else { stagnant = 0; } prev_swap_count = swap_count; stream.memcpy_htod($partition_mut_swap, &mut *$d_partition)?; $partition_host_swap.copy_from_slice($partition_mut_swap); } anyhow::Ok(total_swaps) }}; } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 100, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; partition_host_refine.copy_from_slice(&partition_host_swap); for _post_swap_round in 0..30 { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } } } if valid_moves.is_empty() { break; } let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0)); let k_base = std::cmp::min(valid_moves.len(), move_limit / 2); let k_cand = std::cmp::min(valid_moves.len(), k_base + extra_window / 2); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } tgt_used.fill(0); for p in 0..num_parts_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 + slack_mid); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let me = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if me > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if me == 0 { break; } } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 50, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; let perturb_strength = 3; unsafe { stream .launch_builder(&compute_connectivity_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_connectivity) .launch(hedge_cfg.clone())?; } let conn_vec = stream.memcpy_dtov(&d_connectivity)?; let mut best_connectivity: i32 = conn_vec.iter().sum(); let mut best_partition_host = stream.memcpy_dtov(&d_partition)?; let mut best_nodes_in_part_host = stream.memcpy_dtov(&d_nodes_in_part)?; let num_high_hedges = std::cmp::min(500usize, challenge.num_hyperedges as usize); let mut high_hedge_ids_host: Vec<i32> = build_high_hedge_ids(&conn_vec, num_high_hedges); let pool_size = ils_iterations; let mut elite_scores: Vec<i32> = vec![i32::MAX; pool_size]; let mut elite_flat_host: Vec<i32> = vec![0i32; pool_size * challenge.num_nodes as usize]; elite_scores[0] = best_connectivity; elite_flat_host[..challenge.num_nodes as usize].copy_from_slice(&best_partition_host); let mut elite_count: usize = 1; let mut d_elite_flat = stream.alloc_zeros::<i32>(pool_size * challenge.num_nodes as usize)?; let mut use_consensus_next = false; for ils_iter in 0..ils_iterations { let d_partition_restored = stream.memcpy_stod(&best_partition_host)?; let d_nodes_in_part_restored = stream.memcpy_stod(&best_nodes_in_part_host)?; d_partition = d_partition_restored; d_nodes_in_part = d_nodes_in_part_restored; partition_host_refine.copy_from_slice(&best_partition_host); nodes_in_part_host.copy_from_slice(&best_nodes_in_part_host); unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } let seed = 123456789u64 + (ils_iter as u64) * 987654321u64; let refreshed_host_from_device = if use_consensus_next && elite_count > 1 { stream.memcpy_htod(&elite_flat_host, &mut d_elite_flat)?; let mut elite_order_host: Vec<i32> = (0..elite_count as i32).collect(); elite_order_host.sort_unstable_by(|&a, &b| { elite_scores[a as usize] .cmp(&elite_scores[b as usize]) .then_with(|| a.cmp(&b)) }); let d_elite_order = stream.memcpy_stod(&elite_order_host)?; unsafe { stream .launch_builder(&choose_elite_per_hyperedge_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&(elite_count as i32)) .arg(&d_elite_flat) .arg(&d_elite_order) .arg(&challenge.d_hyperedge_offsets) .arg(&challenge.d_hyperedge_nodes) .arg(&mut d_hedge_choice) .launch(hedge_cfg.clone())?; } stream.memset_zeros(&mut d_nodes_in_part)?; unsafe { stream .launch_builder(&assign_from_elite_votes_kernel) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&(elite_count as i32)) .arg(&d_elite_flat) .arg(&d_hedge_choice) .arg(&challenge.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_edge_flags_all) .arg(&mut d_partition) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; nodes_in_part_host.fill(0); for &p in partition_host_refine.iter() { if p >= 0 && (p as usize) < num_parts_usize { nodes_in_part_host[p as usize] += 1; } } stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; unsafe { stream .launch_builder(&balance_kernel) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&1i32) .arg(&(challenge.max_part_size as i32)) .arg(&mut d_partition) .arg(&mut d_nodes_in_part) .launch(one_thread_cfg.clone())?; } true } else { if ils_iter % 2 == 0 { perturb_guided_on_host( &mut partition_host_refine, &mut nodes_in_part_host, &high_hedge_ids_host, ); } else { perturb_on_host( &mut partition_host_refine, &mut nodes_in_part_host, perturb_strength, seed, ); } stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; false }; if refreshed_host_from_device { stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?; } for _ in 0..ils_quick_refine { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let mut rng_state = 987654321u64; for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } else if gain == 0 { rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1); if (rng_state % 10) < 2 { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } if valid_moves.is_empty() { break; } 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(); if k_base > move_limit { k_base = move_limit; } let ils_extra = extra_window / 2; let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(ils_extra)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = slack_mid + 2; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 { break; } } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 25, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; unsafe { stream .launch_builder(&compute_connectivity_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_connectivity) .launch(hedge_cfg.clone())?; } unsafe { stream .launch_builder(&reduce_connectivity_sum_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&d_connectivity) .arg(&mut d_total_connectivity) .launch(connectivity_reduce_cfg.clone())?; } let block_sums = stream.memcpy_dtov(&d_total_connectivity)?; let num_blocks = ((challenge.num_hyperedges as u32 + block_size * 2 - 1) / (block_size * 2)) as usize; let new_connectivity: i32 = block_sums[..num_blocks].iter().sum(); let mut improved = false; if new_connectivity < best_connectivity { improved = true; best_connectivity = new_connectivity; best_partition_host = stream.memcpy_dtov(&d_partition)?; best_nodes_in_part_host = stream.memcpy_dtov(&d_nodes_in_part)?; let connectivity_vec = stream.memcpy_dtov(&d_connectivity)?; high_hedge_ids_host = build_high_hedge_ids(&connectivity_vec, num_high_hedges); } let slot_opt: Option<usize> = if elite_count < pool_size { let s = elite_count; elite_count += 1; Some(s) } else { let mut worst_idx = 0usize; let mut worst_score = elite_scores[0]; for i in 1..pool_size { if elite_scores[i] > worst_score { worst_score = elite_scores[i]; worst_idx = i; } } if new_connectivity < worst_score { Some(worst_idx) } else { None } }; if let Some(slot) = slot_opt { let src_part: Vec<i32>; let src_slice: &[i32] = if improved { &best_partition_host } else { src_part = stream.memcpy_dtov(&d_partition)?; &src_part }; let n = challenge.num_nodes as usize; elite_flat_host[slot * n..(slot + 1) * n].copy_from_slice(src_slice); elite_scores[slot] = new_connectivity; } use_consensus_next = !improved; } let d_partition_final = stream.memcpy_stod(&best_partition_host)?; let d_nodes_in_part_final = stream.memcpy_stod(&best_nodes_in_part_host)?; d_partition = d_partition_final; d_nodes_in_part = d_nodes_in_part_final; partition_host_refine.copy_from_slice(&best_partition_host); nodes_in_part_host.copy_from_slice(&best_nodes_in_part_host); for _ in 0..post_ils_polish { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let mut rng_state = 11223344u64; for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } else if gain == 0 { rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1); if (rng_state % 20) == 0 { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } if valid_moves.is_empty() { break; } let cmp = |a: &(usize, i32), b: &(usize, i32)| b.1.cmp(&a.1).then(a.0.cmp(&b.0)); let polish_limit = 100000usize; let k_base = std::cmp::min(valid_moves.len(), polish_limit); let polish_extra = extra_window / 3; let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(polish_extra)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = slack_mid; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 { break; } } unsafe { stream .launch_builder(&balance_kernel) .arg(&(challenge.num_nodes as i32)) .arg(&(challenge.num_parts as i32)) .arg(&1i32) .arg(&(challenge.max_part_size as i32)) .arg(&mut d_partition) .arg(&mut d_nodes_in_part) .launch(one_thread_cfg.clone())?; } stream.memcpy_dtoh(&d_partition, &mut partition_host_refine)?; stream.memcpy_dtoh(&d_nodes_in_part, &mut nodes_in_part_host)?; let post_balance_rounds = hyperparameters .as_ref() .and_then(|p| p.get("post_refinement").and_then(|v| v.as_i64())) .map(|v| v.clamp(0, 128) as usize) .unwrap_or(base_post_balance); for _ in 0..post_balance_rounds { unsafe { stream .launch_builder(&precompute_edge_flags_kernel) .arg(&(challenge.num_hyperedges as i32)) .arg(&(challenge.num_nodes as i32)) .arg(&challenge.d_hyperedge_nodes) .arg(&challenge.d_hyperedge_offsets) .arg(&d_partition) .arg(&mut d_edge_flags_all) .arg(&mut d_edge_flags_double) .launch(hedge_cfg.clone())?; } 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.d_node_hyperedges) .arg(&challenge.d_node_offsets) .arg(&d_partition) .arg(&d_nodes_in_part) .arg(&d_edge_flags_all) .arg(&d_edge_flags_double) .arg(&mut d_move_priorities) .launch(cfg.clone())?; } stream.memcpy_dtoh(&d_move_priorities, &mut move_keys_host)?; valid_moves.clear(); let mut rng_state = 55667788u64; for (node, &key) in move_keys_host.iter().enumerate() { if key as u32 != 0x80000000 { let gain = key >> 16; if gain > 0 { valid_moves.push((node, key)); } else if gain == 0 { rng_state = rng_state.wrapping_mul(6364136223846793005u64).wrapping_add(1); if (rng_state % 20) == 0 { let new_key = (0 << 16) | (key & 0xFFFF); valid_moves.push((node, new_key)); } } } } if valid_moves.is_empty() { break; } 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 adaptive_limit = move_limit / 2; if k_base > adaptive_limit { k_base = adaptive_limit; } let post_extra = extra_window / 3; let k_cand = std::cmp::min(valid_moves.len(), k_base.saturating_add(post_extra)); if k_cand > 1 { valid_moves.select_nth_unstable_by(k_cand - 1, cmp); valid_moves[..k_cand].sort_unstable_by(cmp); } else { valid_moves[..k_cand].sort_unstable_by(cmp); } let slack = slack_mid; tgt_used.fill(0); for p in 0..num_parts_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)); } sorted_move_nodes.clear(); sorted_move_parts.clear(); for &(node, key) in valid_moves[..k_cand].iter() { if sorted_move_nodes.len() >= k_base { break; } let tgt = (key & 63) as usize; if tgt < num_parts_usize && tgt_used[tgt] < tgt_quota[tgt] { tgt_used[tgt] += 1; sorted_move_nodes.push(node as i32); sorted_move_parts.push(tgt as i32); } } if sorted_move_nodes.is_empty() { let take = std::cmp::min(k_base, k_cand); 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)); } let mut moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } if moves_executed == 0 && k_cand > k_base { sorted_move_nodes.clear(); sorted_move_parts.clear(); let tail = &valid_moves[k_base..k_cand]; let take = std::cmp::min(tail.len(), k_base); 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)); if !sorted_move_nodes.is_empty() { moves_executed = simulate_execute_moves( &mut partition_host_refine, &mut nodes_in_part_host, &sorted_move_nodes, &sorted_move_parts, ); if moves_executed > 0 { stream.memcpy_htod(&partition_host_refine, &mut d_partition)?; stream.memcpy_htod(&nodes_in_part_host, &mut d_nodes_in_part)?; } } } let _ = (moves_executed, total_moves_executed, total_perturbations); if moves_executed == 0 { break; } } do_swap_phase!( &mut d_partition, &mut d_nodes_in_part, &mut d_edge_flags_all, &mut d_edge_flags_double, &mut d_swap_gains, &mut swap_gains_host, &mut partition_host_swap, &mut partition_mut_swap, &mut part_to_part, &mut used_ba_buf, 10, neg_gain_thresh, scan_limit_swap, scan_limit_cycle )?; let partition = stream.memcpy_dtov(&d_partition)?; let partition_u32: Vec<u32> = partition.iter().map(|&x| x as u32).collect(); save_solution(&Solution { partition: partition_u32, })?; Ok(()) }
尋找差異