walky/solvers/approximate/
matching.rs1#[cfg(feature = "mpi")]
6use std::{
7 mem::transmute,
8 slice::{from_raw_parts, from_raw_parts_mut},
9};
10
11use rand::{seq::SliceRandom, thread_rng};
12use rayon::prelude::*;
13
14#[cfg(feature = "mpi")]
15use mpi::collective::Root;
16#[cfg(feature = "mpi")]
17use mpi::topology::*;
18#[cfg(feature = "mpi")]
19use nalgebra::{Dyn, Matrix};
20
21use crate::{computation_mode::*, datastructures::NAMatrix};
22
23fn randomized_matching(vertices: &mut [usize]) {
24 vertices.shuffle(&mut thread_rng())
25}
26
27pub fn approx_min_cost_matching<const MODE: usize>(
31 graph: &NAMatrix,
32 mut vertices: Vec<usize>,
33 tries: usize,
34) -> Vec<[usize; 2]> {
35 randomized_matching(&mut vertices);
36 let mut matching: Vec<[usize; 2]> = vertices
37 .chunks_exact(2)
38 .map(|chunk| [chunk[0], chunk[1]])
39 .collect();
40
41 match MODE {
42 SEQ_COMPUTATION => improve_matching(graph, matching.as_mut_slice(), tries),
43 PAR_COMPUTATION => par_improve_matching(graph, matching.as_mut_slice(), tries),
44 #[cfg(feature = "mpi")]
45 MPI_COMPUTATION => {
46 if let Some(universe) = mpi::initialize() {
47 let world = universe.world();
49 mpi_improve_matching(graph, matching.as_mut_slice(), tries, world)
50 } else {
51 let world = SystemCommunicator::world();
52 mpi_improve_matching(graph, matching.as_mut_slice(), tries, world)
53 }
54 }
55 _ => panic_on_invaid_mode::<MODE>(),
56 }
57 matching
58}
59
60fn improve_matching(graph: &NAMatrix, matching: &mut [[usize; 2]], tries: usize) {
90 if matching.len() <= 1 {
92 return;
93 }
94
95 let mut rng = thread_rng();
96
97 for _ in 0..tries {
98 matching.shuffle(&mut rng);
99
100 for chunk in matching.chunks_exact_mut(2) {
103 let edg01 = unsafe { chunk.as_mut_ptr().offset(0).as_mut().unwrap() };
108 let edg23 = unsafe { chunk.as_mut_ptr().offset(1).as_mut().unwrap() };
109
110 optimize_two_matching(edg01, edg23, graph);
111 }
112 }
113}
114
115#[inline]
137fn optimize_two_matching(edg01: &mut [usize; 2], edg23: &mut [usize; 2], graph: &NAMatrix) {
138 let v0 = edg01[0];
139 let v1 = edg01[1];
140 let v2 = edg23[0];
141 let v3 = edg23[1];
142
143 let cost_edg01 = graph[(v0, v1)];
145 let cost_edg23 = graph[(v2, v3)];
146 let cost_0 = cost_edg01 + cost_edg23;
147
148 let cost_edg02 = graph[(v0, v2)];
150 let cost_edg13 = graph[(v1, v3)];
151 let cost_1 = cost_edg02 + cost_edg13;
152
153 let cost_edg03 = graph[(v0, v3)];
155 let cost_edg12 = graph[(v1, v2)];
156 let cost_2 = cost_edg03 + cost_edg12;
157
158 if cost_1 < cost_0 {
162 if cost_2 < cost_1 {
163 *edg01 = [v0, v2];
165 *edg23 = [v1, v2];
166 } else {
167 *edg01 = [v0, v2];
169 *edg23 = [v1, v3];
170 }
171 } else {
172 if cost_2 < cost_0 {
174 *edg01 = [v0, v3];
176 *edg23 = [v1, v2];
177 } }
179}
180
181fn par_improve_matching(graph: &NAMatrix, matching: &mut [[usize; 2]], tries: usize) {
183 let mut rng = thread_rng();
186
187 for _ in 0..tries {
188 matching.shuffle(&mut rng);
189
190 matching
193 .par_chunks_exact_mut(2)
194 .with_min_len(1000)
195 .for_each(|chunk| {
196 let edg01 = unsafe { chunk.as_mut_ptr().offset(0).as_mut().unwrap() };
201 let edg23 = unsafe { chunk.as_mut_ptr().offset(1).as_mut().unwrap() };
202
203 optimize_two_matching(edg01, edg23, graph);
204 });
205 }
206}
207
208#[cfg(feature = "mpi")]
209pub fn mpi_improve_matching<C: Communicator>(
210 graph: &NAMatrix,
211 matching: &mut [[usize; 2]],
212 tries: usize,
213 world: C,
214) {
215 use mpi::traits::*;
216
217 let size = world.size();
218 let rank = world.rank();
219 let root_process = world.process_at_rank(crate::ROOT_RANK);
220 const COST_TAG: mpi::Tag = 0;
221 const MATCHING_TAG: mpi::Tag = 1;
222
223 let mut tries_copy = tries;
224 if rank == crate::ROOT_RANK {
225 bootstrap_mpi_matching_calc(&root_process, matching, rank, &mut tries_copy, graph);
226 }
227
228 improve_matching(graph, matching, tries);
232 let cost: f64 = matching.iter().map(|&[i, j]| graph[(i, j)]).sum();
233 let mut min_cost = cost;
234
235 let mut best_matching_singletons: Vec<usize> = matching.iter().flat_map(|&edg| edg).collect();
236
237 if rank != crate::ROOT_RANK {
238 root_process.send_with_tag(&cost, COST_TAG);
239 let matching_singletons =
244 unsafe { from_raw_parts(&matching[0][0] as *const usize, matching.len() * 2) };
245 root_process.send_with_tag(matching_singletons, MATCHING_TAG);
246 } else {
247 for rk in 1..size {
254 let (other_cost, _status_cost) =
255 world.process_at_rank(rk).receive_with_tag::<f64>(COST_TAG);
256 let (msg, _status) = world
257 .process_at_rank(rk)
258 .receive_vec_with_tag::<usize>(MATCHING_TAG);
259 if other_cost < min_cost {
260 min_cost = other_cost;
261 best_matching_singletons = msg;
262 }
263 }
264 }
265
266 root_process.broadcast_into(&mut min_cost);
268 root_process.broadcast_into(&mut best_matching_singletons);
269
270 matching.copy_from_slice(unsafe {
276 from_raw_parts(
277 transmute::<*const usize, *const [usize; 2]>(best_matching_singletons.as_ptr()),
278 matching.len(),
279 )
280 });
281}
282
283#[cfg(feature = "mpi")]
301pub fn bootstrap_mpi_matching_calc<C: Communicator>(
302 root_process: &Process<C>,
303 matching: &mut [[usize; 2]],
304 rank: Rank,
305 tries: &mut usize,
306 graph: &NAMatrix,
307) -> (Vec<[usize; 2]>, NAMatrix) {
308 root_process.broadcast_into(tries);
311
312 let mut matching_size = matching.len();
313 broadcast_matching_size(root_process, &mut matching_size);
314
315 use crate::datastructures::AdjacencyMatrix;
317 let mut dim = graph.dim();
318 broadcast_dim(root_process, &mut dim);
319
320 let mut matching_vec = vec![[0usize; 2]; matching_size];
322 if rank == crate::ROOT_RANK {
323 matching_vec.copy_from_slice(matching);
324 }
325 broadcast_matching(root_process, &mut matching_vec);
326
327 let mut matrix_vec = vec![0.; dim * dim];
329 if rank == crate::ROOT_RANK {
330 matrix_vec.copy_from_slice(graph.data.as_vec())
331 }
332 broadcast_matrix_vec(root_process, &mut matrix_vec);
333 let matrix = NAMatrix(Matrix::from_vec_generic(Dyn(dim), Dyn(dim), matrix_vec));
334
335 (matching_vec, matrix)
336}
337
338#[cfg(feature = "mpi")]
340fn broadcast_matching_size<C: Communicator>(root_process: &Process<C>, matching_size: &mut usize) {
341 root_process.broadcast_into(matching_size);
342}
343
344#[cfg(feature = "mpi")]
345fn broadcast_dim<C: Communicator>(root_process: &Process<C>, dim: &mut usize) {
346 root_process.broadcast_into(dim);
347}
348
349#[cfg(feature = "mpi")]
350fn broadcast_matching<C: Communicator>(root_process: &Process<C>, matching: &mut Vec<[usize; 2]>) {
351 let matching_singletons =
359 unsafe { from_raw_parts_mut(&mut matching[0][0] as *mut usize, matching.len() * 2) };
360 root_process.broadcast_into(matching_singletons);
362}
363
364#[cfg(feature = "mpi")]
365fn broadcast_matrix_vec<C: Communicator>(root_process: &Process<C>, matrix_vec: &mut Vec<f64>) {
366 root_process.broadcast_into(matrix_vec);
367}