MCPcopy Create free account
hub / github.com/argumentcomputer/ix / initial_partition

Function initial_partition

crates/kernel/src/shard.rs:1165–1213  ·  view source on GitHub ↗

Decide the bisection on the coarsest graph: greedy graph-growing for a locality-aware initial cut, then FM to (capped) convergence. The graph is tiny (≈[`COARSEST_TARGET`] vertices), so this is where we spend on quality — the *global* cut is decided here and only locally refined while uncoarsening. We restart from several diverse seeds (see [`INITIAL_RESTARTS`]) and keep the lowest-cut partition,

(lv: Level<'_>, wmin: u64, wmax: u64)

Source from the content-addressed store, hash-verified

1163 vnets[v as usize].push(i as u32);
1164 }
1165 }
1166 CoarseLevel { vw, bw, nets, vnets, match_map: super_id.to_vec() }
1167}
1168
1169/// Decide the bisection on the coarsest graph: greedy graph-growing for a
1170/// locality-aware initial cut, then FM to (capped) convergence. The graph is
1171/// tiny (≈[`COARSEST_TARGET`] vertices), so this is where we spend on quality —
1172/// the *global* cut is decided here and only locally refined while uncoarsening.
1173/// We restart from several diverse seeds (see [`INITIAL_RESTARTS`]) and keep the
1174/// lowest-cut partition, which makes graph-growing robust to "leaking" across a
1175/// thin bridge between two clusters.
1176fn initial_partition(lv: Level<'_>, wmin: u64, wmax: u64) -> Vec<u8> {
1177 let n = lv.num_vertices();
1178 if n == 0 {
1179 return Vec::new();
1180 }
1181 if n == 1 {
1182 return vec![0];
1183 }
1184 let total_bw: u64 = lv.bw.iter().sum();
1185 // Diverse deterministic seeds: the heaviest vertex plus points spread evenly
1186 // across the id range (one tends to start far from any inter-cluster bridge).
1187 let heaviest = (0..n)
1188 .max_by(|&a, &b| lv.vw[a].cmp(&lv.vw[b]).then(Reverse(a).cmp(&Reverse(b))))
1189 .unwrap();
1190 let mut seeds = vec![heaviest];
1191 for i in 0..INITIAL_RESTARTS {
1192 let s = (i * n) / INITIAL_RESTARTS;
1193 if !seeds.contains(&s) {
1194 seeds.push(s);
1195 }
1196 }
1197 // Select the lowest-cut candidate, but *only* among non-degenerate splits
1198 // (both sides non-empty) and preferring those within the balance window. A
1199 // graph-growing run seeded at a light vertex can sweep an entire sub onto one
1200 // side (cut 0) when a single atomic block holds more than half the balance
1201 // weight; such a split is both unbalanced and degenerate, and accepting it
1202 // would leave a shard empty downstream. Key: (unbalanced?, cut), minimized.
1203 let mut best: Option<((u8, u128), Vec<u8>)> = None;
1204 for &seed in &seeds {
1205 let mut side = greedy_grow(lv, total_bw, seed);
1206 fm_refine(lv, &mut side, wmin, wmax, MAX_FM_PASSES);
1207 let s0 = (0..n).filter(|&v| side[v] == 0).count();
1208 if s0 == 0 || s0 == n {
1209 continue; // degenerate (one side empty) — never select
1210 }
1211 let side0_bw: u64 =
1212 (0..n).filter(|&v| side[v] == 0).map(|v| lv.bw[v]).sum();
1213 let unbalanced = u8::from(side0_bw < wmin || side0_bw > wmax);
1214 let (_, cut) = NetState::new(lv, &side);
1215 let key = (unbalanced, cut);
1216 if best.as_ref().is_none_or(|(bk, _)| key < *bk) {

Callers 1

bisectFunction · 0.85

Calls 8

greedy_growFunction · 0.85
fm_refineFunction · 0.85
num_verticesMethod · 0.80
pushMethod · 0.80
iterMethod · 0.45
cmpMethod · 0.45
containsMethod · 0.45
countMethod · 0.45

Tested by

no test coverage detected