use std::{collections::HashMap, rc::Rc};
use itertools::izip;
use mpi::traits::{Communicator, CommunicatorCollectives};
use super::IndexLayout;
pub struct IndexEmbedding<'a, C: Communicator> {
global_layout: Rc<IndexLayout<'a, C>>,
embedded_index_subset: Vec<usize>,
embedded_layout: Rc<IndexLayout<'a, C>>,
local_to_embedded_index: HashMap<usize, usize>,
}
impl<'a, C: Communicator> IndexEmbedding<'a, C> {
pub fn new(
global_layout: Rc<IndexLayout<'a, C>>,
embedded_index_subset: &[usize],
comm: &'a C,
) -> Self
where
C: CommunicatorCollectives,
{
let local_valid = embedded_index_subset
.iter()
.all(|&index| index < global_layout.number_of_local_indices())
&& {
let mut unique_subset = embedded_index_subset.to_vec();
unique_subset.sort_unstable();
unique_subset.dedup();
unique_subset.len() == embedded_index_subset.len()
};
let mut valid = false;
comm.all_reduce_into(
&local_valid,
&mut valid,
mpi::collective::SystemOperation::logical_and(),
);
assert!(
valid,
"embedded indices must be unique and within the local layout"
);
let embedded_layout = Rc::new(IndexLayout::from_local_counts(
embedded_index_subset.len(),
comm,
));
let local_to_embedded_index: HashMap<usize, usize> = embedded_index_subset
.iter()
.enumerate()
.map(|(i, &x)| (x, i))
.collect();
Self {
global_layout,
embedded_index_subset: embedded_index_subset.to_vec(),
embedded_layout,
local_to_embedded_index,
}
}
pub fn embedded_layout(&self) -> Rc<IndexLayout<'a, C>> {
self.embedded_layout.clone()
}
pub fn global_layout(&self) -> Rc<IndexLayout<'a, C>> {
self.global_layout.clone()
}
pub fn embedded_index_to_local_index(&self, embedded_index: usize) -> usize {
self.embedded_index_subset[embedded_index]
}
pub fn local_index_to_embedded_index(&self, local_index: usize) -> Option<usize> {
self.local_to_embedded_index.get(&local_index).copied()
}
pub fn embedded_index_to_global_index(&self, embedded_index: usize) -> usize {
self.global_layout
.local2global(self.embedded_index_to_local_index(embedded_index))
.unwrap()
}
pub fn global_index_to_embedded_index(&self, global_index: usize) -> Option<usize> {
let rank = self.global_layout.comm().rank() as usize;
self.local_index_to_embedded_index(self.global_layout.global2local(rank, global_index)?)
}
pub fn embed_data<T: Default + Copy>(
&self,
data: &[T],
out_vector: &mut [T],
chunk_size: usize,
) {
assert!(chunk_size > 0, "embedding chunk size must be positive");
assert_eq!(data.len(), self.embedded_index_subset.len() * chunk_size);
assert_eq!(
out_vector.len(),
self.global_layout.number_of_local_indices() * chunk_size
);
out_vector.fill(T::default());
for (&local_index, chunk) in
izip!(self.embedded_index_subset.iter(), data.chunks(chunk_size))
{
out_vector[local_index * chunk_size..(local_index + 1) * chunk_size]
.copy_from_slice(chunk);
}
}
pub fn extract_embedded_data<T: Default + Copy>(
&self,
data: &[T],
chunk_size: usize,
) -> Vec<T> {
assert!(chunk_size > 0, "embedding chunk size must be positive");
assert_eq!(
data.len(),
self.global_layout.number_of_local_indices() * chunk_size
);
let mut extracted_data =
vec![T::default(); self.embedded_layout.number_of_local_indices() * chunk_size];
for (local_index, chunk) in izip!(
self.embedded_index_subset.iter(),
extracted_data.chunks_mut(chunk_size)
) {
chunk.copy_from_slice(&data[local_index * chunk_size..(1 + local_index) * chunk_size]);
}
extracted_data
}
}