use std::collections::{HashMap, HashSet};
#[cfg(debug_assertions)]
use crate::tools::is_sorted_array;
use crate::{
constants::{DEEPEST_LEVEL, NSIBLINGS},
geometry::{PhysicalBox, Point},
morton::MortonKey,
parsort::parsort,
tools::{communicate_back, gather_to_all, global_inclusive_cumsum, redistribute, sort_to_bins},
};
use mpi::traits::{Equivalence, Root};
use itertools::{izip, Itertools};
use mpi::{collective::SystemOperation, traits::CommunicatorCollectives};
use rand::Rng;
use super::KeyType;
pub fn points_to_morton<C: CommunicatorCollectives>(
points: &[Point],
max_level: usize,
comm: &C,
) -> (Vec<MortonKey>, PhysicalBox) {
let max_level = if max_level > DEEPEST_LEVEL as usize {
DEEPEST_LEVEL as usize
} else {
max_level
};
let bounding_box = crate::octree::compute_global_bounding_box(points, comm);
let keys = points
.iter()
.map(|&point| MortonKey::from_physical_point(point, &bounding_box, max_level))
.collect_vec();
(keys, bounding_box)
}
pub fn compute_coarse_tree<C: CommunicatorCollectives>(
linear_keys: &[MortonKey],
comm: &C,
) -> Vec<MortonKey> {
let size = comm.size();
debug_assert!(is_linear_tree(linear_keys, comm));
if size == 1 {
return vec![MortonKey::root()];
}
let mut completed_region = linear_keys
.first()
.unwrap()
.fill_between_keys(*linear_keys.last().unwrap());
completed_region.insert(0, *linear_keys.first().unwrap());
completed_region.push(*linear_keys.last().unwrap());
let min_level = completed_region
.iter()
.map(|elem| elem.level())
.min()
.unwrap();
let largest_boxes = completed_region
.iter()
.filter(|elem| elem.level() == min_level)
.copied()
.collect_vec();
debug_assert!(is_linear_tree(&largest_boxes, comm));
complete_tree(&largest_boxes, comm)
}
pub fn compute_coarse_tree_weights<C: CommunicatorCollectives>(
linear_keys: &[MortonKey],
coarse_tree: &[MortonKey],
comm: &C,
) -> Vec<usize> {
let rank = comm.rank();
let global_coarse_tree = gather_to_all(coarse_tree, comm);
let coarse_tree_ranks = gather_to_all(&vec![rank as usize; coarse_tree.len()], comm);
let mut local_weight_contribution = vec![0; global_coarse_tree.len()];
let first_key = *linear_keys.first().unwrap();
let first_coarse_index = global_coarse_tree
.iter()
.take_while(|coarse_key| !coarse_key.is_ancestor(first_key))
.count();
let last_key = *linear_keys.last().unwrap();
let last_coarse_index = global_coarse_tree
.iter()
.take_while(|coarse_key| !coarse_key.is_ancestor(last_key))
.count();
for (w, &global_coarse_key) in izip!(
local_weight_contribution[first_coarse_index..=last_coarse_index].iter_mut(),
global_coarse_tree[first_coarse_index..=last_coarse_index].iter()
) {
*w += linear_keys
.iter()
.filter(|&&key| global_coarse_key.is_ancestor(key))
.count();
}
let mut global_weights = vec![0; global_coarse_tree.len()];
comm.all_reduce_into(
&local_weight_contribution,
&mut global_weights,
SystemOperation::sum(),
);
izip!(coarse_tree_ranks, global_weights)
.filter_map(|(r, weight)| {
if r == rank as usize {
Some(weight)
} else {
None
}
})
.collect_vec()
}
pub fn redistribute_with_respect_to_coarse_tree<C: CommunicatorCollectives>(
linear_keys: &[MortonKey],
coarse_tree: &[MortonKey],
comm: &C,
) -> Vec<MortonKey> {
let size = comm.size();
if size == 1 {
return linear_keys.to_vec();
}
let my_first = coarse_tree.first().unwrap();
let global_bins = gather_to_all(std::slice::from_ref(my_first), comm);
let rank_counts = sort_to_bins(linear_keys, &global_bins)
.iter()
.map(|&elem| elem as i32)
.collect_vec();
let result = redistribute(linear_keys, &rank_counts, comm);
#[cfg(debug_assertions)]
{
debug_assert!(is_sorted_array(&result, comm));
debug_assert!(coarse_tree.first().unwrap() <= result.first().unwrap());
debug_assert!(
result.last().unwrap() < coarse_tree.last().unwrap()
|| coarse_tree
.last()
.unwrap()
.is_ancestor(*result.last().unwrap())
);
}
result
}
pub fn create_local_tree(
sorted_fine_keys: &[MortonKey],
coarse_keys: &[MortonKey],
mut max_level: usize,
max_keys: usize,
) -> Vec<MortonKey> {
if max_level > DEEPEST_LEVEL as usize {
max_level = DEEPEST_LEVEL as usize;
}
let bins = coarse_keys.to_vec();
let counts = sort_to_bins(sorted_fine_keys, &bins);
let mut remainder = sorted_fine_keys;
let mut refined_keys = Vec::<MortonKey>::new();
for (&count, &coarse_key) in izip!(counts.iter(), coarse_keys.iter()) {
let current;
(current, remainder) = remainder.split_at(count);
if coarse_key.level() < max_level && current.len() > max_keys {
refined_keys.extend_from_slice(
create_local_tree(
current,
coarse_key.children().as_slice(),
max_level,
max_keys,
)
.as_slice(),
);
} else {
refined_keys.push(coarse_key)
}
}
refined_keys
}
pub fn linearize<R: Rng, C: CommunicatorCollectives>(
keys: &[MortonKey],
rng: &mut R,
comm: &C,
) -> Vec<MortonKey> {
let size = comm.size();
let rank = comm.rank();
if size == 1 {
return MortonKey::linearize(keys);
}
let sorted_keys = parsort(keys, comm, rng);
let mut result = Vec::<MortonKey>::new();
let next_key = communicate_back(&sorted_keys, comm);
for (&m1, &m2) in sorted_keys.iter().tuple_windows() {
if m1.is_ancestor(m2) {
continue;
} else {
result.push(m1);
}
}
if rank == size - 1 {
result.push(*sorted_keys.last().unwrap());
} else {
let last = *sorted_keys.last().unwrap();
if !last.is_ancestor(next_key.unwrap()) {
result.push(last);
}
}
debug_assert!(is_linear_tree(&result, comm));
result
}
pub fn load_balance<C: CommunicatorCollectives>(
sorted_keys: &[MortonKey],
weights: &[usize],
comm: &C,
) -> Vec<MortonKey> {
assert_eq!(sorted_keys.len(), weights.len());
let size = comm.size();
let rank = comm.rank();
if size == 1 {
return sorted_keys.to_vec();
}
let scan = global_inclusive_cumsum(weights, comm);
let mut total_weight = if rank == size - 1 {
*scan.last().unwrap()
} else {
0
};
comm.process_at_rank(size - 1)
.broadcast_into(&mut total_weight);
let w = total_weight / (size as usize);
let k = total_weight % (size as usize);
let mut bins = Vec::<usize>::with_capacity(size as usize);
for p in 1..=size as usize {
if p <= k {
bins.push((p - 1) * (1 + w));
} else {
bins.push((p - 1) * w + k);
}
}
let counts = sort_to_bins(&scan, &bins)
.iter()
.map(|elem| *elem as i32)
.collect_vec();
let mut recvbuffer = redistribute(sorted_keys, &counts, comm);
recvbuffer.sort_unstable();
recvbuffer
}
pub fn complete_tree<C: CommunicatorCollectives>(
linear_keys: &[MortonKey],
comm: &C,
) -> Vec<MortonKey> {
let mut linear_keys = linear_keys.to_vec();
debug_assert!(is_linear_tree(&linear_keys, comm));
let size = comm.size();
let rank = comm.rank();
if size == 1 {
return MortonKey::complete_tree(linear_keys.as_slice());
}
let next_key = communicate_back(&linear_keys, comm);
if rank < size - 1 {
linear_keys.push(next_key.unwrap());
}
if rank == 0 {
let first_key = linear_keys.first().unwrap();
let deepest_first = MortonKey::deepest_first();
if !first_key.is_ancestor(deepest_first) {
let ancestor = deepest_first.finest_common_ancestor(*first_key);
linear_keys.insert(0, ancestor.children()[0]);
}
}
if rank == size - 1 {
let last_key = linear_keys.last().unwrap();
let deepest_last = MortonKey::deepest_last();
if !last_key.is_ancestor(deepest_last) {
let ancestor = deepest_last.finest_common_ancestor(*last_key);
linear_keys.push(ancestor.children()[NSIBLINGS - 1]);
}
}
let mut result = Vec::<MortonKey>::new();
for (&key1, &key2) in linear_keys.iter().tuple_windows() {
result.push(key1);
result.extend_from_slice(key1.fill_between_keys(key2).as_slice());
}
if rank == size - 1 {
result.push(*linear_keys.last().unwrap());
}
debug_assert!(is_complete_linear_tree(&result, comm));
result
}
pub fn balance<R: Rng, C: CommunicatorCollectives>(
linear_keys: &[MortonKey],
rng: &mut R,
comm: &C,
) -> Vec<MortonKey> {
if linear_keys.len() == 1 && *linear_keys.first().unwrap() == MortonKey::root() {
return vec![MortonKey::root()];
}
let deepest_level = deepest_level(linear_keys, comm);
let mut work_list = linear_keys
.iter()
.copied()
.filter(|&key| key.level() == deepest_level)
.collect_vec();
let mut result = Vec::<MortonKey>::new();
for level in (1..=deepest_level).rev() {
let mut parents = HashSet::<MortonKey>::new();
let mut new_work_list = Vec::<MortonKey>::new();
for key in work_list.iter() {
let parent = key.parent();
if !parents.contains(&parent) {
parents.insert(parent);
result.extend_from_slice(key.siblings().as_slice());
new_work_list.extend_from_slice(
parent
.neighbours()
.iter()
.copied()
.filter(|&key| key.is_valid())
.collect_vec()
.as_slice(),
);
}
}
new_work_list.extend(
linear_keys
.iter()
.copied()
.filter(|&key| key.level() == level - 1),
);
work_list = new_work_list;
}
let result = linearize(&result, rng, comm);
debug_assert!(crate::octree::is_complete_linear_and_balanced(
&result, comm
));
result
}
pub fn is_linear_tree<C: CommunicatorCollectives>(arr: &[MortonKey], comm: &C) -> bool {
let mut is_linear = true;
for (&key1, &key2) in arr.iter().tuple_windows() {
if key1 >= key2 || key1.is_ancestor(key2) {
is_linear = false;
break;
}
}
if comm.size() == 1 {
return is_linear;
}
if let Some(next_key) = communicate_back(arr, comm) {
let last = *arr.last().unwrap();
if last >= next_key || last.is_ancestor(next_key) {
is_linear = false;
}
}
let mut global_is_linear = false;
comm.all_reduce_into(
&is_linear,
&mut global_is_linear,
SystemOperation::logical_and(),
);
global_is_linear
}
pub fn redistribute_points_with_respect_to_coarse_tree<C: CommunicatorCollectives>(
points: &[Point],
morton_keys_for_points: &[MortonKey],
coarse_tree: &[MortonKey],
comm: &C,
) -> (Vec<Point>, Vec<MortonKey>) {
if comm.size() == 1 {
return (points.to_vec(), morton_keys_for_points.to_vec());
}
pub fn argsort<T: Ord + Copy>(arr: &[T]) -> Vec<usize> {
let mut sort_indices = (0..arr.len()).collect_vec();
sort_indices.sort_unstable_by_key(|&index| arr[index]);
sort_indices
}
pub fn reorder<T: Copy>(arr: &[T], permutation: &[usize]) -> Vec<T> {
let mut reordered = Vec::<T>::with_capacity(arr.len());
for &index in permutation.iter() {
reordered.push(arr[index])
}
reordered
}
assert_eq!(points.len(), morton_keys_for_points.len());
let size = comm.size();
if size == 1 {
return (points.to_vec(), morton_keys_for_points.to_vec());
}
let sort_indices = argsort(morton_keys_for_points);
let sorted_keys = reorder(morton_keys_for_points, &sort_indices);
let sorted_points = reorder(points, &sort_indices);
let my_first = coarse_tree.first().unwrap();
let global_bins = gather_to_all(std::slice::from_ref(my_first), comm);
let counts = sort_to_bins(&sorted_keys, &global_bins)
.iter()
.map(|&elem| elem as i32)
.collect_vec();
let (distributed_points, distributed_keys) = (
redistribute(&sorted_points, &counts, comm),
redistribute(&sorted_keys, &counts, comm),
);
let sort_indices = argsort(&distributed_keys);
let sorted_keys = reorder(&distributed_keys, &sort_indices);
let sorted_points = reorder(&distributed_points, &sort_indices);
(sorted_points, sorted_keys)
}
pub fn is_complete_linear_tree<C: CommunicatorCollectives>(arr: &[MortonKey], comm: &C) -> bool {
let mut complete_linear = true;
for (key1, key2) in arr.iter().tuple_windows() {
if key1 >= key2 {
complete_linear = false;
break;
}
if let Some(expected_next) = key1.next_non_descendent_key() {
if !key2.is_ancestor(expected_next) {
complete_linear = false;
break;
}
} else {
complete_linear = false;
}
}
if let Some(next_first) = communicate_back(arr, comm) {
let last_key = arr.last().unwrap();
if *last_key >= next_first {
complete_linear = false;
}
if let Some(expected_next) = last_key.next_non_descendent_key() {
if !next_first.is_ancestor(expected_next) {
complete_linear = false;
}
} else {
complete_linear = false;
}
} else {
if !arr.last().unwrap().is_ancestor(MortonKey::deepest_last()) {
complete_linear = false;
}
}
if comm.rank() == 0 && !arr.first().unwrap().is_ancestor(MortonKey::deepest_first()) {
complete_linear = false;
}
let mut result = false;
comm.all_reduce_into(
&complete_linear,
&mut result,
SystemOperation::logical_and(),
);
result
}
pub fn deepest_level<C: CommunicatorCollectives>(keys: &[MortonKey], comm: &C) -> usize {
let local_deepest_level = keys.iter().map(|elem| elem.level()).max().unwrap();
if comm.size() == 1 {
return local_deepest_level;
}
let mut global_deepest_level: usize = 0;
comm.all_reduce_into(
&local_deepest_level,
&mut global_deepest_level,
SystemOperation::max(),
);
global_deepest_level
}
pub fn get_tree_bins<C: CommunicatorCollectives>(
complete_linear_tree: &[MortonKey],
comm: &C,
) -> Vec<MortonKey> {
gather_to_all(
std::slice::from_ref(complete_linear_tree.first().unwrap()),
comm,
)
}
pub fn get_key_index(arr: &[MortonKey], key: MortonKey) -> usize {
match arr.binary_search(&key) {
Ok(index) => index,
Err(index) => index - 1,
}
}
pub fn assign_points_to_leaf_keys(
point_keys: &[MortonKey],
leaf_keys: &[MortonKey],
) -> HashMap<MortonKey, Vec<usize>> {
let mut point_map = HashMap::<MortonKey, Vec<usize>>::new();
for (index, point_key) in point_keys.iter().enumerate() {
let leaf_key_index = get_key_index(leaf_keys, *point_key);
let leaf_key = leaf_keys[leaf_key_index];
debug_assert!(leaf_key.is_ancestor(*point_key));
point_map.entry(leaf_key).or_default().push(index);
}
point_map
}
pub fn generate_all_keys<C: CommunicatorCollectives>(
leaf_tree: &[MortonKey],
coarse_tree: &[MortonKey],
coarse_tree_bounds: &[MortonKey],
comm: &C,
) -> HashMap<MortonKey, KeyType> {
#[derive(Copy, Clone, Equivalence)]
struct KeyWithRank {
key: MortonKey,
rank: usize,
}
let rank = comm.rank() as usize;
let size = comm.size() as usize;
let mut all_keys = HashMap::<MortonKey, KeyType>::new();
let leaf_keys: HashSet<MortonKey> = HashSet::from_iter(leaf_tree.iter().copied());
if size > 1 {
let mut global_keys = HashSet::<MortonKey>::new();
for &key in coarse_tree {
let mut parent = key.parent();
while parent.level() > 0 && !all_keys.contains_key(&parent) {
global_keys.insert(parent);
parent = parent.parent();
}
}
let global_keys = gather_to_all(&global_keys.iter().copied().collect_vec(), comm);
for &key in &global_keys {
all_keys.entry(key).or_insert(KeyType::Global);
}
}
for leaf in leaf_keys {
debug_assert!(!all_keys.contains_key(&leaf));
all_keys.insert(leaf, KeyType::LocalLeaf);
let mut parent = leaf.parent();
while parent.level() > 0 && !all_keys.contains_key(&parent) {
all_keys.insert(parent, KeyType::LocalInterior);
parent = parent.parent();
}
}
all_keys.entry(MortonKey::root()).or_insert(KeyType::Global);
if size > 1 {
let mut rank_send_ghost = HashMap::<usize, Vec<KeyWithRank>>::new();
for index in 0..size {
rank_send_ghost.insert(index, Vec::<KeyWithRank>::new());
}
let mut send_to_all = Vec::<KeyWithRank>::new();
for (&key, &status) in all_keys.iter() {
if status == KeyType::Global {
continue;
}
for &neighbor in key.neighbours().iter().filter(|&&key| key.is_valid()) {
if all_keys
.get(&neighbor)
.is_some_and(|&value| value == KeyType::Global)
{
send_to_all.push(KeyWithRank { key, rank });
} else {
let neighbor_rank = get_key_index(coarse_tree_bounds, neighbor);
rank_send_ghost
.entry(neighbor_rank)
.and_modify(|keys| keys.push(KeyWithRank { key, rank }));
}
}
}
let send_ghost_to_all = gather_to_all(&send_to_all, comm);
let (arr, counts) = {
let mut arr = Vec::<KeyWithRank>::new();
let mut counts = Vec::<i32>::new();
for index in 0..size {
let keys = rank_send_ghost.get(&index).unwrap();
arr.extend(keys.iter());
counts.push(keys.len() as i32);
}
(arr, counts)
};
let mut ghost_keys = redistribute(&arr, &counts, comm);
ghost_keys.extend(send_ghost_to_all.iter());
for key in &ghost_keys {
if key.rank == rank {
continue;
}
all_keys.insert(key.key, KeyType::Ghost(key.rank));
}
}
all_keys
}
pub fn compute_neighbours(
all_keys: &HashMap<MortonKey, KeyType>,
) -> HashMap<MortonKey, Vec<MortonKey>> {
let mut neighbours = HashMap::<MortonKey, Vec<MortonKey>>::new();
for (key, key_type) in all_keys
.iter()
.filter(|(_, key_type)| !matches!(key_type, KeyType::Ghost(_)))
{
if *key_type == KeyType::LocalInterior || (*key_type == KeyType::Global) {
neighbours.insert(
*key,
key.neighbours()
.iter()
.copied()
.filter(|key| key.is_valid())
.collect_vec(),
);
continue;
}
if *key_type == KeyType::LocalLeaf {
for neighbour in key
.neighbours()
.iter()
.copied()
.filter(|key| key.is_valid())
{
if all_keys.contains_key(&neighbour) {
neighbours.entry(*key).or_default().push(neighbour);
} else if all_keys.contains_key(&neighbour.parent()) {
neighbours.entry(*key).or_default().push(neighbour.parent());
} else {
for neighbour_child in neighbour.children() {
for neighbour_child_neighbour in neighbour_child
.neighbours()
.iter()
.filter(|key| key.is_valid())
{
if neighbour_child_neighbour.parent() == *key {
debug_assert!(all_keys.contains_key(&neighbour_child));
neighbours.entry(*key).or_default().push(neighbour_child);
}
}
}
}
}
continue;
}
}
neighbours
}
#[cfg(test)]
mod test {
use crate::{
octree::get_key_index,
tools::{generate_random_keys, seeded_rng},
};
#[test]
fn test_get_key_rank() {
let mut rng = seeded_rng(0);
let mut keys = generate_random_keys(50, &mut rng);
keys.sort_unstable();
let mid = keys[25];
assert_eq!(25, get_key_index(&keys, mid));
keys.remove(25);
assert_eq!(24, get_key_index(&keys, mid));
}
}