use crate::mesh::attrib::*;
use hashbrown::HashMap;
use std::hash::Hash;
pub fn partition<'a, T: Hash + Eq + 'a>(iter: impl Iterator<Item = &'a T>) -> (Vec<usize>, usize) {
let mut partition = Vec::new();
let mut map: HashMap<&'a T, usize> = HashMap::default();
let mut part_counter = 0;
for val in iter {
let part = map.entry(val).or_insert_with(|| {
let part = part_counter;
part_counter += 1;
part
});
partition.push(*part);
}
(partition, part_counter)
}
pub fn partition_slice<T: PartialOrd>(slice: &[T]) -> (Vec<usize>, usize) {
use std::cmp::Ordering;
let mut permutation: Vec<_> = (0..slice.len()).collect();
permutation.sort_by(|&i, &j| unsafe {
slice
.get_unchecked(i)
.partial_cmp(slice.get_unchecked(j))
.unwrap_or(Ordering::Less)
});
let mut permutation_iter = permutation
.iter()
.map(|&i| (i, unsafe { slice.get_unchecked(i) }))
.peekable();
let mut partition = vec![0; slice.len()];
let mut part_counter = 0;
while let Some((pi, val)) = permutation_iter.next() {
unsafe { *partition.get_unchecked_mut(pi) = part_counter };
if permutation_iter.peek().map_or(true, |next| val != next.1) {
part_counter += 1;
}
}
(partition, part_counter)
}
pub trait Partition
where
Self: Sized,
{
fn partition_by_attrib<T: AttributeValueHash, Src: AttribIndex<Self>>(
&self,
attrib: &str,
) -> (Vec<usize>, usize);
fn partition_by_attrib_by_sort<T: PartialOrd + AttributeValue, Src: AttribIndex<Self>>(
&self,
attrib: &str,
) -> (Vec<usize>, usize);
}
impl<M> Partition for M
where
Self: Attrib + Sized,
{
#[inline]
fn partition_by_attrib<T: AttributeValueHash, Src: AttribIndex<Self>>(
&self,
attrib: &str,
) -> (Vec<usize>, usize) {
if let Ok(attrib_iter) = self.attrib_iter::<T, Src>(attrib) {
partition(attrib_iter)
} else {
(vec![0; self.attrib_size::<Src>()], 1)
}
}
#[inline]
fn partition_by_attrib_by_sort<T: PartialOrd + AttributeValue, Src: AttribIndex<Self>>(
&self,
attrib: &str,
) -> (Vec<usize>, usize) {
match self.attrib_as_slice::<T, Src>(attrib) {
Ok(attrib) => partition_slice(attrib),
Err(_) => (vec![0; self.attrib_size::<Src>()], 1),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh::topology::*;
use crate::mesh::{PointCloud, TetMeshExt};
#[test]
fn tetmesh_partition_by_attrib() {
let verts = vec![[0.0; 3]; 12];
let indices = vec![[0, 1, 2, 3], [1, 2, 3, 4], [5, 6, 7, 8], [8, 9, 10, 11]];
let mut tetmesh = TetMeshExt::new(verts, indices);
tetmesh
.add_attrib_data::<usize, VertexIndex>(
"attrib",
vec![1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1],
)
.unwrap();
assert_eq!(
tetmesh.partition_by_attrib::<usize, VertexIndex>("attrib"),
(vec![0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], 2)
);
}
fn partition_data(size: usize, nbins: usize) -> Vec<[usize; 3]> {
use rand::distributions::Uniform;
use rand::prelude::*;
let seed = [3u8; 32];
let mut rng = StdRng::from_seed(seed);
let index_bins = Uniform::from(0..nbins);
let bins: Vec<[usize; 3]> = (0..nbins)
.map(|_| [rng.gen(), rng.gen(), rng.gen()])
.collect();
(0..size)
.map(|_| bins[index_bins.sample(&mut rng)])
.collect()
}
#[test]
fn partition_by_attrib_complex() {
use rand::prelude::*;
let size = 10;
let seed = [3u8; 32];
let mut rng = StdRng::from_seed(seed);
let verts: Vec<[f64; 3]> = (0..size)
.map(|_| [rng.gen(), rng.gen(), rng.gen()])
.collect();
let mut ptcld = PointCloud::new(verts);
let data = partition_data(size, 10);
ptcld
.add_attrib_data::<_, VertexIndex>("attrib", data)
.unwrap();
let (_, num_parts1) = ptcld.partition_by_attrib::<[usize; 3], VertexIndex>("attrib");
let (_, num_parts2) =
ptcld.partition_by_attrib_by_sort::<[usize; 3], VertexIndex>("attrib");
assert_eq!(num_parts1, num_parts2);
}
#[test]
fn partition_by_attrib_by_hash() {
use rand::prelude::*;
let size = 100_000;
let seed = [3u8; 32];
let mut rng = StdRng::from_seed(seed);
let verts: Vec<[f64; 3]> = (0..size)
.map(|_| [rng.gen(), rng.gen(), rng.gen()])
.collect();
let mut ptcld = PointCloud::new(verts);
let data = partition_data(size, 100);
ptcld
.add_attrib_data::<_, VertexIndex>("attrib", data)
.unwrap();
let now = std::time::Instant::now();
let (_, num_parts) = ptcld.partition_by_attrib::<[usize; 3], VertexIndex>("attrib");
eprintln!("hash time = {}", now.elapsed().as_millis());
eprintln!("{}", num_parts);
}
#[test]
fn partition_by_attrib_by_sort() {
use rand::prelude::*;
let size = 100_000;
let seed = [3u8; 32];
let mut rng = StdRng::from_seed(seed);
let verts: Vec<[f64; 3]> = (0..size)
.map(|_| [rng.gen(), rng.gen(), rng.gen()])
.collect();
let mut ptcld = PointCloud::new(verts);
let data = partition_data(size, 100);
ptcld
.add_attrib_data::<_, VertexIndex>("attrib", data)
.unwrap();
let now = std::time::Instant::now();
let (_, num_parts) = ptcld.partition_by_attrib_by_sort::<[usize; 3], VertexIndex>("attrib");
eprintln!("sort time = {}", now.elapsed().as_millis());
eprintln!("{}", num_parts);
}
}