Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
표시
레이아웃
나란히 보기
합쳐 보기
실시간 편집
공백 변경 숨기기
변경 없는 행 숨기기
줄바꿈 비활성화
비교 단위
스마트
구문
없음
모양 변경
처리
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 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(()) }
비교하기