1use std::collections::HashMap;
6use std::io::Write;
7use std::path::Path;
8
9use crate::face_record::FaceMatch;
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14pub enum WeightAggregate {
15 Sum,
16 Max,
17 Min,
18}
19
20#[derive(Clone, Debug)]
22pub struct BlockGraph {
23 pub adj_list: Vec<Vec<usize>>,
26 pub edge_weights: Vec<HashMap<usize, i64>>,
28}
29
30#[derive(Clone, Debug)]
32pub struct CsrGraph {
33 pub xadj: Vec<usize>,
34 pub adjncy: Vec<usize>,
35 pub eweights: Vec<i64>,
36}
37
38pub fn build_weighted_graph_from_face_matches(
44 face_matches: &[FaceMatch],
45 n_blocks: usize,
46 aggregate: WeightAggregate,
47 ignore_self_matches: bool,
48) -> BlockGraph {
49 let mut pair_weight: HashMap<(usize, usize), i64> = HashMap::new();
50
51 for m in face_matches {
52 let i = m.block1.block_index;
53 let j = m.block2.block_index;
54 if ignore_self_matches && i == j {
55 continue;
56 }
57
58 let di = (m.block1.ih as i64 - m.block1.il as i64)
59 .unsigned_abs()
60 .max(1);
61 let dj = (m.block1.jh as i64 - m.block1.jl as i64)
62 .unsigned_abs()
63 .max(1);
64 let dk = (m.block1.kh as i64 - m.block1.kl as i64)
65 .unsigned_abs()
66 .max(1);
67 let w = (di * dj * dk) as i64;
68
69 let (a, b) = if i < j { (i, j) } else { (j, i) };
70 let entry = pair_weight.entry((a, b)).or_insert(0);
71 match aggregate {
72 WeightAggregate::Sum => *entry += w,
73 WeightAggregate::Max => *entry = (*entry).max(w),
74 WeightAggregate::Min => {
75 if *entry == 0 {
76 *entry = w;
77 } else {
78 *entry = (*entry).min(w);
79 }
80 }
81 }
82 }
83
84 let mut adj_list: Vec<Vec<usize>> = vec![Vec::new(); n_blocks];
85 let mut edge_weights: Vec<HashMap<usize, i64>> = vec![HashMap::new(); n_blocks];
86
87 for (&(a, b), &w) in &pair_weight {
88 adj_list[a].push(b);
89 adj_list[b].push(a);
90 edge_weights[a].insert(b, w);
91 edge_weights[b].insert(a, w);
92 }
93
94 for list in &mut adj_list {
96 list.sort_unstable();
97 list.dedup();
98 }
99
100 BlockGraph {
101 adj_list,
102 edge_weights,
103 }
104}
105
106pub fn csr_from_block_graph(graph: &BlockGraph) -> CsrGraph {
108 let mut xadj: Vec<usize> = vec![0];
109 let mut adjncy: Vec<usize> = Vec::new();
110 let mut eweights: Vec<i64> = Vec::new();
111
112 let mut count: usize = 0;
113 for u in 0..graph.adj_list.len() {
114 for &v in &graph.adj_list[u] {
115 adjncy.push(v);
116 let w = graph.edge_weights[u].get(&v).copied().unwrap_or(1);
117 eweights.push(w);
118 count += 1;
119 }
120 xadj.push(count);
121 }
122
123 CsrGraph {
124 xadj,
125 adjncy,
126 eweights,
127 }
128}
129
130#[cfg(feature = "metis-partition")]
137pub fn partition_from_face_matches(
138 face_matches: &[FaceMatch],
139 block_sizes: &[usize],
140 nparts: usize,
141 favor_blocksize: bool,
142 aggregate: WeightAggregate,
143 ignore_self_matches: bool,
144) -> Result<(Vec<i32>, BlockGraph), String> {
145 let n_blocks = block_sizes.len();
146 let graph = build_weighted_graph_from_face_matches(
147 face_matches,
148 n_blocks,
149 aggregate,
150 ignore_self_matches,
151 );
152 let csr = csr_from_block_graph(&graph);
153
154 let mut g = metis_rs::Graph::new(n_blocks, csr.xadj, csr.adjncy).with_adjwgt(csr.eweights);
156
157 if favor_blocksize {
158 let vwgt: Vec<i64> = block_sizes.iter().map(|&s| s as i64).collect();
159 g = g.with_vwgt(vwgt);
160 }
161
162 let (_edge_cut, part) = metis_rs::partition(&g, nparts);
163
164 Ok((part.iter().map(|&p| p as i32).collect(), graph))
165}
166
167pub fn write_ddcmp(
171 parts: &[i32],
172 block_sizes: &[usize],
173 graph: &BlockGraph,
174 filename: &str,
175) -> std::io::Result<()> {
176 let n_blocks = parts.len();
177 let n_proc = parts.iter().copied().max().unwrap_or(-1) + 1;
178 let n_isp = n_proc;
179
180 let path = Path::new(filename);
182 if let Some(parent) = path.parent() {
183 if !parent.as_os_str().is_empty() {
184 std::fs::create_dir_all(parent)?;
185 }
186 }
187
188 let mut f = std::fs::File::create(path)?;
189 writeln!(f, "{}", n_proc)?;
190 writeln!(f, "{}", n_isp)?;
191 writeln!(f, "{}", n_blocks)?;
192 for (b_idx, part) in parts.iter().enumerate().take(n_blocks) {
193 writeln!(f, "{} {}", b_idx + 1, part + 1)?;
194 }
195 for isp in 0..n_isp {
196 writeln!(f, "{} {}", isp + 1, isp)?;
197 }
198
199 let np = n_proc as usize;
201 let mut communication_work = vec![0i64; np];
202 let mut partition_edge_weights = vec![0i64; np];
203 let mut volume_nodes = vec![0usize; np];
204
205 for (b, &bsize) in block_sizes.iter().enumerate() {
206 let pid = parts[b] as usize;
207 volume_nodes[pid] += bsize;
208 }
209
210 for b in 0..block_sizes.len() {
211 let pid = parts[b] as usize;
212 for &nbr in &graph.adj_list[b] {
213 let nbr_pid = parts[nbr] as usize;
214 if nbr_pid != pid {
215 communication_work[pid] += 1;
216 partition_edge_weights[pid] +=
217 graph.edge_weights[b].get(&nbr).copied().unwrap_or(1);
218 }
219 }
220 }
221
222 let info_path = path.with_file_name("ddcmp_info.txt");
224 let mut fi = std::fs::File::create(&info_path)?;
225 for i in 0..np {
226 let block_count = parts.iter().filter(|&&p| p == i as i32).count();
227 writeln!(fi, "Parition {} has {} blocks", i, block_count)?;
228 }
229 writeln!(fi, "Number of partitions/processors {}", n_proc)?;
230 for i in 0..np {
231 writeln!(
232 fi,
233 "Parition or processor {} has communication work {} edge_work {} volume_nodes {}",
234 i, communication_work[i], partition_edge_weights[i], volume_nodes[i]
235 )?;
236 }
237 let total_comm: i64 = communication_work.iter().sum();
238 let total_edge: i64 = partition_edge_weights.iter().sum();
239 writeln!(
240 fi,
241 "Total communication work {} Total edge_work {}",
242 total_comm, total_edge
243 )?;
244
245 Ok(())
246}