use crate::{
SubIntervalInPartition,
coords::{Coords1D, Coords1DInDomain},
intervals::{
Contains, GetLowerBoundValue, GetUpperBoundValue, Interval, IntervalBoundsRuntime,
IntervalClosed, IntervalFiniteLength, IntervalFinitePositiveLength,
IntervalFinitePositiveLengthTrait, IntervalLowerClosedUpperOpen,
IntervalLowerOpenUpperClosed, IntervalOpen, IntervalTrait, operations::IntervalOperations,
},
operations::refinement::{Grid1DNonUniformRefinement, Grid1DUniformRefinement},
scalars::{IntervalId, NumIntervals, PositiveNumPoints1D},
};
use duplicate::duplicate_item;
use more_asserts::debug_assert_lt;
use num_valid::{RealScalar, scalars::PositiveRealScalar};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, sync::Arc};
use try_create::TryNew;
pub trait HasDomain1D: Sized {
type Domain1D: IntervalTrait;
fn domain(&self) -> &Self::Domain1D;
}
pub trait HasCoords1D {
type Point1DType: RealScalar;
fn coords(&self) -> &Coords1D<Self::Point1DType>;
fn num_points(&self) -> PositiveNumPoints1D {
self.coords().num_points()
}
}
pub trait BuildIntervalInPartition: IntervalFinitePositiveLengthTrait {
type FirstIntervalInPartition: IntervalFinitePositiveLengthTrait<RealType = Self::RealType>
+ Into<Interval<Self::RealType>>
+ Into<IntervalFinitePositiveLength<Self::RealType>>;
type LastIntervalInPartition: IntervalFinitePositiveLengthTrait<RealType = Self::RealType>
+ Into<Interval<Self::RealType>>
+ Into<IntervalFinitePositiveLength<Self::RealType>>;
fn build_unique_interval(&self) -> Self {
self.clone()
}
fn build_first_interval(
lower_bound: Self::RealType,
upper_bound: Self::RealType,
) -> Self::FirstIntervalInPartition;
#[inline(always)]
fn build_middle_interval(
lower_bound: Self::RealType,
upper_bound: Self::RealType,
) -> IntervalLowerOpenUpperClosed<Self::RealType> {
IntervalLowerOpenUpperClosed::new(lower_bound, upper_bound)
}
fn build_last_interval(
lower_bound: Self::RealType,
upper_bound: Self::RealType,
) -> Self::LastIntervalInPartition;
}
#[duplicate_item(
I type_first_interval type_last_interval ;
[IntervalClosed] [IntervalClosed] [IntervalLowerOpenUpperClosed];
[IntervalOpen] [IntervalLowerOpenUpperClosed] [IntervalOpen] ;
[IntervalLowerClosedUpperOpen] [IntervalClosed] [IntervalOpen] ;
[IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed] [IntervalLowerOpenUpperClosed];
)]
impl<RealType: RealScalar> BuildIntervalInPartition for I<RealType> {
type FirstIntervalInPartition = type_first_interval<RealType>;
type LastIntervalInPartition = type_last_interval<RealType>;
fn build_first_interval(
lower_bound: Self::RealType,
upper_bound: Self::RealType,
) -> Self::FirstIntervalInPartition {
Self::FirstIntervalInPartition::new(lower_bound, upper_bound)
}
fn build_last_interval(
lower_bound: Self::RealType,
upper_bound: Self::RealType,
) -> Self::LastIntervalInPartition {
Self::LastIntervalInPartition::new(lower_bound, upper_bound)
}
}
pub trait IntervalPartition:
HasCoords1D
+ HasDomain1D<Domain1D: BuildIntervalInPartition<RealType = Self::Point1DType>>
+ Serialize
+ for<'a> Deserialize<'a>
{
fn num_intervals(&self) -> NumIntervals {
NumIntervals::try_new(self.num_points().as_ref() - 1).unwrap()
}
fn interval(&self, i: &IntervalId) -> SubIntervalInPartition<Self::Domain1D> {
more_asserts::debug_assert_lt!(i.as_ref(), self.num_intervals().as_ref());
let i = *i.as_ref();
let num_intervals = *self.num_intervals().as_ref();
if num_intervals == 1 {
let sub_interval = Self::Domain1D::build_unique_interval(self.domain());
return SubIntervalInPartition::Only(sub_interval);
}
let coords = self.coords();
let lower_bound = coords[i].clone();
let upper_bound = coords[i + 1].clone();
if i == 0 {
let sub_interval = Self::Domain1D::build_first_interval(lower_bound, upper_bound);
SubIntervalInPartition::First(sub_interval)
} else if i == num_intervals - 1 {
let sub_interval = Self::Domain1D::build_last_interval(lower_bound, upper_bound);
SubIntervalInPartition::Last(sub_interval)
} else {
SubIntervalInPartition::Middle(Self::Domain1D::build_middle_interval(
lower_bound,
upper_bound,
))
}
}
fn iter_intervals(
&self,
) -> impl Iterator<Item = (IntervalId, SubIntervalInPartition<Self::Domain1D>)> + '_ {
(0..*self.num_intervals().as_ref()).map(move |i| {
let id = IntervalId::new(i);
let interval = self.interval(&id);
(id, interval)
})
}
fn interval_length(&self, interval_id: &IntervalId) -> PositiveRealScalar<Self::Point1DType> {
let coords = self.coords();
let i = *interval_id.as_ref();
debug_assert_lt!(i, coords.len() - 1, "Interval ID out of bounds");
PositiveRealScalar::try_new(coords[i + 1].clone() - &coords[i])
.expect("Non-positive interval length!")
}
fn find_interval_id_of_point(&self, x: &Self::Point1DType) -> IntervalId {
debug_assert!(
self.domain().contains_point(x),
"The value {} is not in the interval {:?} spanned by the grid1d!",
x,
self.domain()
);
self.try_find_interval_id_of_point(x)
.expect("Precondition violated: point is outside the domain.")
}
fn try_find_interval_id_of_point(&self, x: &Self::Point1DType) -> Option<IntervalId> {
if !self.domain().contains_point(x) {
return None;
}
let idx = self.coords().find_insertion_index(x);
if idx == 0 {
return Some(IntervalId::new(0));
}
let final_id = idx - 1;
Some(IntervalId::new(final_id))
}
fn find_intervals_for_points(&self, points: &[Self::Point1DType]) -> Vec<Option<IntervalId>> {
points
.iter()
.map(|point| self.try_find_interval_id_of_point(point))
.collect()
}
fn find_intervals_for_points_parallel(
&self,
points: &[Self::Point1DType],
) -> Vec<Option<IntervalId>>
where
Self: Sync,
Self::Point1DType: Send + Sync,
{
points
.par_iter()
.map(|point| self.try_find_interval_id_of_point(point))
.collect()
}
fn max_interval_length(&self) -> PositiveRealScalar<Self::Point1DType> {
(0..*self.num_intervals().as_ref())
.map(|i| self.interval_length(&IntervalId::new(i)))
.max_by(|a, b| a.as_ref().partial_cmp(b.as_ref()).unwrap())
.unwrap()
}
fn min_interval_length(&self) -> PositiveRealScalar<Self::Point1DType> {
(0..*self.num_intervals().as_ref())
.map(|i| self.interval_length(&IntervalId::new(i)))
.min_by(|a, b| a.as_ref().partial_cmp(b.as_ref()).unwrap())
.unwrap()
}
fn uniformity_ratio(&self) -> PositiveRealScalar<Self::Point1DType> {
let max_len = self.max_interval_length();
let min_len = self.min_interval_length();
PositiveRealScalar::try_new(max_len.as_ref().clone() / min_len.as_ref())
.expect("Uniformity ratio must be positive")
}
fn intervals_in_intersection<
IntervalType: IntervalFinitePositiveLengthTrait<RealType = Self::Point1DType>,
>(
&self,
domain_in: &IntervalType,
) -> Vec<(IntervalId, IntervalFinitePositiveLength<Self::Point1DType>)> {
let overlap = match self.domain().intersection(domain_in) {
Some(Interval::FiniteLength(IntervalFiniteLength::PositiveLength(p))) => p,
_ => return Vec::new(),
};
let id_min = self
.try_find_interval_id_of_point(overlap.lower_bound_value())
.expect("Lower bound of overlap must be in domain");
let id_max = self
.try_find_interval_id_of_point(overlap.upper_bound_value())
.expect("Upper bound of overlap must be in domain");
let id_min_as_usize = *id_min.as_ref();
let id_max_as_usize = *id_max.as_ref();
let n_max_intersecting_intervals = id_max_as_usize - id_min_as_usize + 1;
let mut intersecting_intervals = Vec::with_capacity(n_max_intersecting_intervals);
for i in id_min_as_usize..=id_max_as_usize {
let current_id = IntervalId::new(i);
let grid_interval = self.interval(¤t_id);
if let Some(Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
intersection,
))) = grid_interval.intersection(&overlap)
{
intersecting_intervals.push((current_id, intersection));
}
}
debug_assert!(
!intersecting_intervals.is_empty(),
"Expected at least one interval with positive length in the intersection"
);
intersecting_intervals
}
type UniformlyRefinedGrid1DType: IntervalPartition<Point1DType = Self::Point1DType, Domain1D = Self::Domain1D>;
fn refine_uniform(
self,
num_extra_points_each_interval: &PositiveNumPoints1D,
) -> Grid1DUniformRefinement<Self>;
fn refine(
self,
intervals_to_refine: &BTreeMap<IntervalId, PositiveNumPoints1D>,
) -> Grid1DNonUniformRefinement<Self>;
}
pub trait TransformedPoints1D<ParamStorage, PhysStorage>
where
ParamStorage:
std::borrow::Borrow<Coords1D<<Self::ParamDomainType as IntervalBoundsRuntime>::RealType>>,
PhysStorage:
std::borrow::Borrow<Coords1D<<Self::PhysDomainType as IntervalBoundsRuntime>::RealType>>,
{
type ParamDomainType: IntervalTrait;
type PhysDomainType: IntervalTrait<
RealType = <Self::ParamDomainType as IntervalBoundsRuntime>::RealType,
>;
fn points_param_interval(&self) -> &Coords1DInDomain<Self::ParamDomainType, ParamStorage>;
fn points_phys_interval(&self) -> &Coords1DInDomain<Self::PhysDomainType, PhysStorage>;
}
pub type TransformedPoints1DOwned<ParamDomain, PhysDomain> = dyn TransformedPoints1D<
Coords1D<<ParamDomain as IntervalBoundsRuntime>::RealType>,
Coords1D<<PhysDomain as IntervalBoundsRuntime>::RealType>,
ParamDomainType = ParamDomain,
PhysDomainType = PhysDomain,
>;
pub type TransformedPoints1DBorrowed<'a, ParamDomain, PhysDomain> = dyn TransformedPoints1D<
&'a Coords1D<<ParamDomain as IntervalBoundsRuntime>::RealType>,
&'a Coords1D<<PhysDomain as IntervalBoundsRuntime>::RealType>,
ParamDomainType = ParamDomain,
PhysDomainType = PhysDomain,
> + 'a;
pub type TransformedPoints1DArc<ParamDomain, PhysDomain> = dyn TransformedPoints1D<
Arc<Coords1D<<ParamDomain as IntervalBoundsRuntime>::RealType>>,
Arc<Coords1D<<PhysDomain as IntervalBoundsRuntime>::RealType>>,
ParamDomainType = ParamDomain,
PhysDomainType = PhysDomain,
>;
pub type TransformedPoints1DRc<ParamDomain, PhysDomain> = dyn TransformedPoints1D<
std::rc::Rc<Coords1D<<ParamDomain as IntervalBoundsRuntime>::RealType>>,
std::rc::Rc<Coords1D<<PhysDomain as IntervalBoundsRuntime>::RealType>>,
ParamDomainType = ParamDomain,
PhysDomainType = PhysDomain,
>;
#[cfg(test)]
mod tests {
use super::*;
use crate::{Grid1D, Grid1DUniform};
use try_create::TryNew;
#[test]
fn test_has_domain_1d() {
let domain = IntervalClosed::new(0.0, 10.0);
let grid = Grid1DUniform::new(domain.clone(), NumIntervals::try_new(5).unwrap());
assert_eq!(grid.domain(), &domain);
}
#[test]
fn test_has_coords_1d() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 1.0),
NumIntervals::try_new(4).unwrap(),
);
assert_eq!(grid.num_points().as_ref(), &5);
assert_eq!(grid.coords().len(), 5);
}
#[test]
fn test_build_interval_in_partition_closed() {
let first = IntervalClosed::build_first_interval(0.0, 1.0);
let middle = IntervalClosed::build_middle_interval(1.0, 2.0);
let last = IntervalClosed::build_last_interval(2.0, 3.0);
assert!(first.contains_point(&0.0));
assert!(first.contains_point(&1.0));
assert!(!middle.contains_point(&1.0));
assert!(middle.contains_point(&2.0));
assert!(!last.contains_point(&2.0));
assert!(last.contains_point(&3.0));
}
#[test]
fn test_build_interval_in_partition_open() {
let first = IntervalOpen::build_first_interval(0.0, 1.0);
let last = IntervalOpen::build_last_interval(2.0, 3.0);
assert!(!first.contains_point(&0.0));
assert!(first.contains_point(&1.0));
assert!(!last.contains_point(&2.0));
assert!(!last.contains_point(&3.0));
}
#[test]
fn test_interval_partition_num_intervals() {
let grid = Grid1D::uniform(
IntervalClosed::new(0.0, 10.0),
NumIntervals::try_new(5).unwrap(),
);
assert_eq!(grid.num_intervals().as_ref(), &5);
assert_eq!(grid.num_points().as_ref(), &6);
}
#[test]
fn test_interval_partition_find_interval() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 4.0),
NumIntervals::try_new(4).unwrap(),
);
assert_eq!(grid.find_interval_id_of_point(&0.5), IntervalId::new(0));
assert_eq!(grid.find_interval_id_of_point(&1.5), IntervalId::new(1));
assert_eq!(grid.find_interval_id_of_point(&3.5), IntervalId::new(3));
assert_eq!(grid.find_interval_id_of_point(&1.0), IntervalId::new(0));
assert_eq!(grid.find_interval_id_of_point(&2.0), IntervalId::new(1));
assert_eq!(grid.find_interval_id_of_point(&0.0), IntervalId::new(0));
assert_eq!(grid.find_interval_id_of_point(&4.0), IntervalId::new(3));
}
#[test]
fn test_interval_partition_try_find() {
let grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 2.0),
NumIntervals::try_new(2).unwrap(),
);
assert_eq!(
grid.try_find_interval_id_of_point(&1.0),
Some(IntervalId::new(0))
);
assert_eq!(grid.try_find_interval_id_of_point(&-1.0), None);
assert_eq!(grid.try_find_interval_id_of_point(&3.0), None);
}
#[test]
fn test_interval_partition_uniformity() {
let uniform_grid = Grid1DUniform::new(
IntervalClosed::new(0.0, 1.0),
NumIntervals::try_new(10).unwrap(),
);
assert_eq!(uniform_grid.uniformity_ratio().as_ref(), &1.0);
assert_eq!(
uniform_grid.min_interval_length(),
uniform_grid.max_interval_length()
);
}
}