Build the coarsening hierarchy for one bisection: `levels[0]` is the first contraction of `sub`, `levels.last()` is the coarsest graph (≈ [`COARSEST_TARGET`] vertices). The finest level (`sub` itself) is *not duplicated into the vector — it is supplied separately during uncoarsening. Returns an empty vector when `sub` is already small or can't be coarsened, in which case [`bisect`] partitions `sub
(sub: &SubHyper)
| 1013 | |
| 1014 | impl CoarseLevel { |
| 1015 | fn level(&self) -> Level<'_> { |
| 1016 | Level { vw: &self.vw, bw: &self.bw, nets: &self.nets, vnets: &self.vnets } |
| 1017 | } |
| 1018 | } |
| 1019 | |
| 1020 | /// Build the coarsening hierarchy for one bisection: `levels[0]` is the first |
| 1021 | /// contraction of `sub`, `levels.last()` is the coarsest graph (≈ |
| 1022 | /// [`COARSEST_TARGET`] vertices). The finest level (`sub` itself) is *not* |
| 1023 | /// duplicated into the vector — it is supplied separately during uncoarsening. |
| 1024 | /// Returns an empty vector when `sub` is already small or can't be coarsened, in |
| 1025 | /// which case [`bisect`] partitions `sub` directly. |
| 1026 | fn coarsen(sub: &SubHyper) -> Vec<CoarseLevel> { |
| 1027 | let total_bw: u64 = sub.bw.iter().sum(); |
| 1028 | // Cap a supervertex's balance weight so the coarsest graph keeps at least |
| 1029 | // ~COARSEST_TARGET pieces and stays balanceable; never merge past it. |
| 1030 | let max_cluster = (total_bw / COARSEST_TARGET as u64).max(1); |
| 1031 | let mut levels: Vec<CoarseLevel> = Vec::new(); |
| 1032 | loop { |
| 1033 | let cur: Level<'_> = match levels.last() { |
| 1034 | Some(l) => l.level(), |
| 1035 | None => sub.level(), |
| 1036 | }; |
| 1037 | let n = cur.num_vertices(); |
| 1038 | if n <= COARSEST_TARGET { |
| 1039 | break; |
| 1040 | } |
| 1041 | let (super_id, next) = match_vertices(cur, max_cluster); |
| 1042 | if (next as f64) > COARSEN_STALL_RATIO * n as f64 { |
| 1043 | break; // stalled: too few matchable pairs to make progress |
| 1044 | } |
no test coverage detected