#[cfg(feature = "mpi")]
use std::{
mem::transmute,
slice::{from_raw_parts, from_raw_parts_mut},
};
use rand::{seq::SliceRandom, thread_rng};
use rayon::prelude::*;
#[cfg(feature = "mpi")]
use mpi::collective::Root;
#[cfg(feature = "mpi")]
use mpi::topology::*;
#[cfg(feature = "mpi")]
use nalgebra::{Dyn, Matrix};
use crate::{computation_mode::*, datastructures::NAMatrix};
fn randomized_matching(vertices: &mut [usize]) {
vertices.shuffle(&mut thread_rng())
}
pub fn approx_min_cost_matching<const MODE: usize>(
graph: &NAMatrix,
mut vertices: Vec<usize>,
tries: usize,
) -> Vec<[usize; 2]> {
randomized_matching(&mut vertices);
let mut matching: Vec<[usize; 2]> = vertices
.chunks_exact(2)
.map(|chunk| [chunk[0], chunk[1]])
.collect();
match MODE {
SEQ_COMPUTATION => improve_matching(graph, matching.as_mut_slice(), tries),
PAR_COMPUTATION => par_improve_matching(graph, matching.as_mut_slice(), tries),
#[cfg(feature = "mpi")]
MPI_COMPUTATION => {
if let Some(universe) = mpi::initialize() {
let world = universe.world();
mpi_improve_matching(graph, matching.as_mut_slice(), tries, world)
} else {
let world = SystemCommunicator::world();
mpi_improve_matching(graph, matching.as_mut_slice(), tries, world)
}
}
_ => panic_on_invaid_mode::<MODE>(),
}
matching
}
fn improve_matching(graph: &NAMatrix, matching: &mut [[usize; 2]], tries: usize) {
if matching.len() <= 1 {
return;
}
let mut rng = thread_rng();
for _ in 0..tries {
matching.shuffle(&mut rng);
for chunk in matching.chunks_exact_mut(2) {
let edg01 = unsafe { chunk.as_mut_ptr().offset(0).as_mut().unwrap() };
let edg23 = unsafe { chunk.as_mut_ptr().offset(1).as_mut().unwrap() };
optimize_two_matching(edg01, edg23, graph);
}
}
}
#[inline]
fn optimize_two_matching(edg01: &mut [usize; 2], edg23: &mut [usize; 2], graph: &NAMatrix) {
let v0 = edg01[0];
let v1 = edg01[1];
let v2 = edg23[0];
let v3 = edg23[1];
let cost_edg01 = graph[(v0, v1)];
let cost_edg23 = graph[(v2, v3)];
let cost_0 = cost_edg01 + cost_edg23;
let cost_edg02 = graph[(v0, v2)];
let cost_edg13 = graph[(v1, v3)];
let cost_1 = cost_edg02 + cost_edg13;
let cost_edg03 = graph[(v0, v3)];
let cost_edg12 = graph[(v1, v2)];
let cost_2 = cost_edg03 + cost_edg12;
if cost_1 < cost_0 {
if cost_2 < cost_1 {
*edg01 = [v0, v2];
*edg23 = [v1, v2];
} else {
*edg01 = [v0, v2];
*edg23 = [v1, v3];
}
} else {
if cost_2 < cost_0 {
*edg01 = [v0, v3];
*edg23 = [v1, v2];
} }
}
fn par_improve_matching(graph: &NAMatrix, matching: &mut [[usize; 2]], tries: usize) {
let mut rng = thread_rng();
for _ in 0..tries {
matching.shuffle(&mut rng);
matching
.par_chunks_exact_mut(2)
.with_min_len(1000)
.for_each(|chunk| {
let edg01 = unsafe { chunk.as_mut_ptr().offset(0).as_mut().unwrap() };
let edg23 = unsafe { chunk.as_mut_ptr().offset(1).as_mut().unwrap() };
optimize_two_matching(edg01, edg23, graph);
});
}
}
#[cfg(feature = "mpi")]
pub fn mpi_improve_matching<C: Communicator>(
graph: &NAMatrix,
matching: &mut [[usize; 2]],
tries: usize,
world: C,
) {
use mpi::traits::*;
let size = world.size();
let rank = world.rank();
let root_process = world.process_at_rank(crate::ROOT_RANK);
const COST_TAG: mpi::Tag = 0;
const MATCHING_TAG: mpi::Tag = 1;
let mut tries_copy = tries;
if rank == crate::ROOT_RANK {
bootstrap_mpi_matching_calc(&root_process, matching, rank, &mut tries_copy, graph);
}
improve_matching(graph, matching, tries);
let cost: f64 = matching.iter().map(|&[i, j]| graph[(i, j)]).sum();
let mut min_cost = cost;
let mut best_matching_singletons: Vec<usize> = matching.iter().flat_map(|&edg| edg).collect();
if rank != crate::ROOT_RANK {
root_process.send_with_tag(&cost, COST_TAG);
let matching_singletons =
unsafe { from_raw_parts(&matching[0][0] as *const usize, matching.len() * 2) };
root_process.send_with_tag(matching_singletons, MATCHING_TAG);
} else {
for rk in 1..size {
let (other_cost, _status_cost) =
world.process_at_rank(rk).receive_with_tag::<f64>(COST_TAG);
let (msg, _status) = world
.process_at_rank(rk)
.receive_vec_with_tag::<usize>(MATCHING_TAG);
if other_cost < min_cost {
min_cost = other_cost;
best_matching_singletons = msg;
}
}
}
root_process.broadcast_into(&mut min_cost);
root_process.broadcast_into(&mut best_matching_singletons);
matching.copy_from_slice(unsafe {
from_raw_parts(
transmute::<*const usize, *const [usize; 2]>(best_matching_singletons.as_ptr()),
matching.len(),
)
});
}
#[cfg(feature = "mpi")]
pub fn bootstrap_mpi_matching_calc<C: Communicator>(
root_process: &Process<C>,
matching: &mut [[usize; 2]],
rank: Rank,
tries: &mut usize,
graph: &NAMatrix,
) -> (Vec<[usize; 2]>, NAMatrix) {
root_process.broadcast_into(tries);
let mut matching_size = matching.len();
broadcast_matching_size(root_process, &mut matching_size);
use crate::datastructures::AdjacencyMatrix;
let mut dim = graph.dim();
broadcast_dim(root_process, &mut dim);
let mut matching_vec = vec![[0usize; 2]; matching_size];
if rank == crate::ROOT_RANK {
matching_vec.copy_from_slice(matching);
}
broadcast_matching(root_process, &mut matching_vec);
let mut matrix_vec = vec![0.; dim * dim];
if rank == crate::ROOT_RANK {
matrix_vec.copy_from_slice(graph.data.as_vec())
}
broadcast_matrix_vec(root_process, &mut matrix_vec);
let matrix = NAMatrix(Matrix::from_vec_generic(Dyn(dim), Dyn(dim), matrix_vec));
(matching_vec, matrix)
}
#[cfg(feature = "mpi")]
fn broadcast_matching_size<C: Communicator>(root_process: &Process<C>, matching_size: &mut usize) {
root_process.broadcast_into(matching_size);
}
#[cfg(feature = "mpi")]
fn broadcast_dim<C: Communicator>(root_process: &Process<C>, dim: &mut usize) {
root_process.broadcast_into(dim);
}
#[cfg(feature = "mpi")]
fn broadcast_matching<C: Communicator>(root_process: &Process<C>, matching: &mut Vec<[usize; 2]>) {
let matching_singletons =
unsafe { from_raw_parts_mut(&mut matching[0][0] as *mut usize, matching.len() * 2) };
root_process.broadcast_into(matching_singletons);
}
#[cfg(feature = "mpi")]
fn broadcast_matrix_vec<C: Communicator>(root_process: &Process<C>, matrix_vec: &mut Vec<f64>) {
root_process.broadcast_into(matrix_vec);
}