diff --git a/src/cellgrid/iters.rs b/src/cellgrid/iters.rs index b89ae50..0a45d14 100644 --- a/src/cellgrid/iters.rs +++ b/src/cellgrid/iters.rs @@ -16,11 +16,24 @@ mod private { /// "Marker" trait indicating a type being a valid neighborhood configuration. pub trait SpaceConfig { + /// Default implementation suitable for `Full`. #[doc(hidden)] #[inline] fn neighbors_as_slice(neighbors: &[i32], _: private::Token) -> &[i32] { neighbors } + + /// Default implementation suitable for `Half`. + #[doc(hidden)] + #[inline] + fn intra_cell_pairs

( + iter: Iter<'_, P>, + _: private::Token, + ) -> impl FusedIterator + Clone { + iter.clone() + .enumerate() + .flat_map(move |(n, i)| iter.clone().skip(n + 1).map(move |j| (i, j))) + } } /// _Full-space_ neighborhood. @@ -28,7 +41,20 @@ pub struct Full; /// _Half-space_ neighborhood. pub struct Half; -impl SpaceConfig for Full {} +impl SpaceConfig for Full { + #[inline] + fn intra_cell_pairs

( + iter: Iter<'_, P>, + _: private::Token, + ) -> impl FusedIterator + Clone { + let rev = iter.clone().rev(); + rev.clone() + .enumerate() + .flat_map(move |(n, i)| rev.clone().skip(n + 1).map(move |j| (i, j))) + .chain(Half::intra_cell_pairs(iter.clone(), private::Token)) + } +} + impl SpaceConfig for Half { #[inline] fn neighbors_as_slice(neighbors: &[i32], _: private::Token) -> &[i32] { @@ -189,20 +215,19 @@ where /// Returns an iterator over all unique pairs of points in this `GridCell`. #[inline] - fn intra_cell_pairs(self) -> impl FusedIterator + Clone { + fn intra_cell_pairs(self) -> impl FusedIterator + Clone { // this is equivalent to // self.iter().copied().tuple_combinations::<(P, P)>() // but faster for our specific case (pairs from slice of `Copy` values) - self.iter() - .enumerate() - .flat_map(move |(n, i)| self.iter().skip(n + 1).map(move |j| (i, j))) + // TODO: expose type parameter `S: SpaceConfig` on `{CellGrid, GridCell}::particle_pairs()` + S::intra_cell_pairs(self.iter(), private::Token) } /// Returns an iterator over all unique pairs of points in this `GridCell` with points of the neighboring cells. #[inline] - fn inter_cell_pairs(self) -> impl FusedIterator + Clone { + fn inter_cell_pairs(self) -> impl FusedIterator + Clone { self.iter() - .cartesian_product(self.neighbors::().flat_map(|cell| cell.iter())) + .cartesian_product(self.neighbors::().flat_map(|cell| cell.iter())) } /// Returns an iterator over all _relevant_ pairs of particles within in the neighborhood of this `GridCell`. @@ -210,10 +235,9 @@ where /// _Relevant_ means the distance between paired particles might be less than the `cutoff` but /// this cannot be guaranteed.\ /// This method consumes `self` but `GridCell` implements [`Copy`]. - //TODO: handle full-space as well - //TODO: document that we're relying on GridCell impl'ing Copy here (so we can safely consume `self`) pub fn particle_pairs(self) -> impl FusedIterator + Clone { - self.intra_cell_pairs().chain(self.inter_cell_pairs()) + self.intra_cell_pairs::() + .chain(self.inter_cell_pairs::()) } } @@ -315,7 +339,7 @@ mod tests { assert_eq!( cell_grid .iter() - .map(|cell| cell.intra_cell_pairs().count()) + .map(|cell| cell.intra_cell_pairs::().count()) .sum::(), 4, "testing intra_cell_pairs()" @@ -324,10 +348,41 @@ mod tests { assert_eq!( cell_grid .iter() - .map(|cell| cell.inter_cell_pairs().count()) + .map(|cell| cell.inter_cell_pairs::().count()) .sum::(), 24, "testing inter_cell_pairs()" ); } + + #[test] + fn test_half_full_space_particle_pairs() { + // Using 0-origin to avoid floating point errors + let points = generate_pointcloud([2, 2, 2], 1.0, [0.0, 0.0, 0.0]); + let cell_grid = CellGrid::new(points.iter().copied(), 1.0); + + assert_eq!( + 2 * cell_grid + .iter() + .map(|cell| cell.intra_cell_pairs::().count()) + .sum::(), + cell_grid + .iter() + .map(|cell| cell.intra_cell_pairs::().count()) + .sum::(), + "testing intra_cell_pairs() half-/full-space" + ); + + assert_eq!( + 2 * cell_grid + .iter() + .map(|cell| cell.inter_cell_pairs::().count()) + .sum::(), + cell_grid + .iter() + .map(|cell| cell.inter_cell_pairs::().count()) + .sum::(), + "testing inter_cell_pairs() half-/full-space" + ); + } } diff --git a/surface-sampling/examples/cli.rs b/surface-sampling/examples/cli.rs index 4f0a4fb..de29869 100644 --- a/surface-sampling/examples/cli.rs +++ b/surface-sampling/examples/cli.rs @@ -48,7 +48,6 @@ enum Commands { }, /// Evaluate SDF of a PDB file for a specified grid of q=l³ query points - /// in a gr Eval { /// Protein structure file to generate SDF from. pdb: PathBuf,