use crate::{
ContourPiece, IntegrationOutput, SegmentHeap,
core::{QuadratureSamples, Segment},
storage::SegmentHeapError,
};
use num_traits::Float;
use trellis_runner::{Progress, ProgressDiagnostics, TrellisFloat, UserState};
#[derive(Clone, Default, Debug)]
pub(crate) struct IntegrationState<P, O, F>
where
F: PartialOrd + PartialEq,
P: ContourPiece<Float = F>,
{
segments: SegmentHeap<P, O, F>,
evaluations: usize,
refinements: usize,
}
pub(crate) struct IntegrationSummary<I, O, F> {
pub(crate) integral: O,
pub(crate) error: F,
pub(crate) evaluations: usize,
pub(crate) refinements: usize,
pub(crate) samples: Option<QuadratureSamples<I, O>>,
}
impl<P, O, F> IntegrationState<P, O, F>
where
F: Float,
O: IntegrationOutput<P::Input, Float = F>,
P: ContourPiece<Float = F>,
{
pub(crate) fn new() -> Self {
Self {
segments: SegmentHeap::empty(),
evaluations: 0,
refinements: 0,
}
}
pub fn push_segments(
&mut self,
segments: Vec<Segment<P, O, F>>,
) -> Result<(), SegmentHeapError> {
for each in segments {
self.push(each)?;
}
Ok(())
}
pub fn push(&mut self, segment: Segment<P, O, F>) -> Result<(), SegmentHeapError> {
self.segments.push(segment)
}
pub fn pop_worst(&mut self) -> Option<Segment<P, O, F>> {
self.segments.pop_worst()
}
pub(crate) fn integral(&self) -> Option<O> {
self.segments.result()
}
pub(crate) fn error(&self) -> F {
self.segments.error()
}
pub(crate) fn evaluations(&self) -> usize {
self.evaluations
}
pub(crate) fn refinements(&self) -> usize {
self.refinements
}
pub(crate) fn record_evaluations(&mut self, n: usize) {
self.evaluations += n;
}
pub(crate) fn record_refinement(&mut self) {
self.refinements += 1;
}
pub(crate) fn record_refinements(&mut self, n: usize) {
self.refinements += n;
}
pub(crate) fn summary(&self) -> Option<IntegrationSummary<P::Input, O, F>> {
let integral = self.integral()?;
Some(IntegrationSummary {
integral,
error: self.error(),
refinements: self.refinements,
evaluations: self.evaluations,
samples: self.segments.samples(),
})
}
}
impl<P, O, F> UserState for IntegrationState<P, O, F>
where
F: Float + TrellisFloat,
O: IntegrationOutput<P::Input, Float = F>,
P: ContourPiece<Float = F>,
{
type Float = F;
fn is_initialised(&self) -> bool {
!self.segments.is_empty()
}
fn progress(&self) -> Option<Progress<Self::Float>> {
let Some(integral) = self.integral() else {
unreachable!("IntegrationState is always initialised if segments is empty");
};
let measure = integral.mean_component();
let absolute_error = self.error();
let scale = Float::max(measure, F::one());
let relative_error = absolute_error / scale;
Some(Progress::Report {
measure,
diagnostics: ProgressDiagnostics {
absolute_error: Some(absolute_error),
relative_error: Some(relative_error),
gradient_norm: None,
step_size: None,
},
})
}
}