Wed, 22 Apr 2026 23:41:59 -0500
Fix internal links in documentation
/*! Tools for separating the computational steps of an iterative algorithm from stopping rules and reporting. The computational step is to be implemented as a closure. That closure gets passed a `state` parameter that implements an [`if_verbose`][AlgIteratorState::if_verbose] method that is to be called to determine whether function values or other potentially costly things need to be calculated on that iteration. The parameter of [`if_verbose`][AlgIteratorState::if_verbose] is another closure that does the necessary computation. ## Simple example ```rust # use alg_tools::types::*; # use alg_tools::iterate::*; let mut iter = AlgIteratorOptions{ max_iter : 100, verbose_iter : Verbose::Every(10), .. Default::default() }; let mut x = 1 as float; # let mut iter_clone = iter.clone(); iter.iterate(|state|{ // This is our computational step x = x + x.sqrt(); state.if_verbose(||{ // return current value when requested return x }) }); // or alternatively (avoiding problems with moves) # iter = iter_clone; for state in iter.iter() { // This is our computational step x = x + x.sqrt(); state.if_verbose(||{ // return current value when requested return x }) } ``` There is no colon after `state.if_verbose`, because we need to return its value. If you do something after the step, you need to store the result in a variable and return it from the main computational step afterwards. This will print out ```output 10/100 J=31.051164 20/100 J=108.493699 30/100 J=234.690039 40/100 J=410.056327 50/100 J=634.799262 60/100 J=909.042928 70/100 J=1232.870172 80/100 J=1606.340254 90/100 J=2029.497673 100/100 J=2502.377071 ``` */ use crate::logger::*; use crate::types::*; use colored::{ColoredString, Colorize}; use core::fmt::Debug; use cpu_time::ProcessTime; use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::error::Error; use std::marker::PhantomData; use std::rc::Rc; use std::time::Duration; /// Create the displayed presentation for log items. pub trait LogRepr: Debug { fn logrepr(&self) -> ColoredString { format!("« {self:?} »").as_str().into() } } impl LogRepr for str { fn logrepr(&self) -> ColoredString { self.into() } } impl LogRepr for String { fn logrepr(&self) -> ColoredString { self.as_str().into() } } impl<T> LogRepr for T where T: Num, { fn logrepr(&self) -> ColoredString { format!("J={self}").as_str().into() } } impl<V> LogRepr for Option<V> where V: LogRepr, { fn logrepr(&self) -> ColoredString { match self { None => "===missing value===".red(), Some(v) => v.logrepr(), } } } /// Helper struct for returning results annotated with an additional string to /// [`if_verbose`][AlgIteratorState::if_verbose]. The [`LogRepr`] implementation will /// display that string when so decided by the specific [`AlgIterator`] in use. #[derive(Debug, Clone)] pub struct Annotated<F>(pub F, pub String); impl<V> LogRepr for Annotated<V> where V: LogRepr, { fn logrepr(&self) -> ColoredString { format!("{}\t| {}", self.0.logrepr(), self.1) .as_str() .into() } } /// Basic log item. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] pub struct LogItem<V> { pub iter: usize, // This causes [`csv`] to crash. //#[serde(flatten)] pub data: V, } impl<V> LogItem<V> { /// Creates a new log item fn new(iter: usize, data: V) -> Self { LogItem { iter, data } } } /// State of an [`AlgIterator`]. /// /// This is the parameter obtained by the closure passed to [`AlgIterator::iterate`] or /// [`AlgIteratorFactory::iterate`]. pub trait AlgIteratorState: Sized { /// Call `call_objective` if this is a verbose iteration. /// /// Verbosity depends on the [`AlgIterator`] that produced this state. /// /// The closure `calc_objective` should return an arbitrary value of type `V`, to be inserted /// into the log, or whatever is deemed by the [`AlgIterator`]. For usage instructions see the /// [module documentation][self]. fn if_verbose<V, E: Error>(self, calc_objective: impl FnOnce() -> V) -> Step<V, Self, E>; /// Returns the current iteration count. fn iteration(&self) -> usize; /// Indicates whether the iterator is quiet fn is_quiet(&self) -> bool; } /// Result of a step of an [`AlgIterator`] #[derive(Debug, Serialize)] pub enum Step<V, S, Fail: Error = std::convert::Infallible> { /// Iteration should be terminated Terminated, /// Iteration should be terminated due to failure Failure(Fail), /// No result this iteration (due to verbosity settings) Quiet, /// Result of this iteration (due to verbosity settings) Result(V, S), } impl<V, S, E: Error> Step<V, S, E> { /// Maps the value contained within the `Step`, if any, by the closure `f`. pub fn map<U>(self, mut f: impl FnMut(V) -> U) -> Step<U, S, E> { match self { Step::Result(v, s) => Step::Result(f(v), s), Step::Failure(e) => Step::Failure(e), Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, } } } impl<V, S, E: Error> Default for Step<V, S, E> { fn default() -> Self { Step::Quiet } } /// An iterator for algorithms, produced by [`AlgIteratorFactory::prepare`]. /// /// Typically not accessed directly, but transparently produced by an [`AlgIteratorFactory`]. /// Every [`AlgIteratorFactory`] has to implement a corresponding `AlgIterator`. pub trait AlgIterator: Sized { /// The state type type State: AlgIteratorState; /// The output type for [`Self::poststep`] and [`Self::step`]. type Output; /// The input type for [`Self::poststep`]. type Input; /// Advance the iterator, performing `step_fn` with the state fn step<F, E>(&mut self, step_fn: &mut F) -> Step<Self::Output, Self::State, E> where F: FnMut(Self::State) -> Step<Self::Input, Self::State, E>, E: Error, { self.prestep() .map_or(Step::Terminated, |state| self.poststep(step_fn(state))) } /// Initial stage of advancing the iterator, before the actual step fn prestep(&mut self) -> Option<Self::State>; /// Handle step result fn poststep<E>( &mut self, result: Step<Self::Input, Self::State, E>, ) -> Step<Self::Output, Self::State, E> where E: Error; /// Return current iteration count. fn iteration(&self) -> usize { self.state().iteration() } /// Return current state. fn state(&self) -> Self::State; /// Iterate the `AlgIterator` until termination, erforming `step_fn` on each step. /// /// Returns either `()` or an error if the step closure terminated in [`Step::Failure`]. #[inline] fn iterate<F, E>(&mut self, mut step_fn: F) -> Result<(), E> where F: FnMut(Self::State) -> Step<Self::Input, Self::State, E>, E: Error, { loop { match self.step(&mut step_fn) { Step::Terminated => return Ok(()), Step::Failure(e) => return Err(e), _ => {} } } } } /// A factory for producing an [`AlgIterator`]. /// /// For usage instructions see the [module documentation][self]. pub trait AlgIteratorFactory<V>: Sized { type Iter: AlgIterator<State = Self::State, Input = V, Output = Self::Output>; /// The state type of the corresponding [`AlgIterator`]. /// A reference to this is passed to the closures passed to methods such as [`Self::iterate`]. type State: AlgIteratorState; /// The output type of the corresponding [`AlgIterator`]. /// This is the output of the closures passed to methods such as [`Self::iterate`] after /// mappings performed by each [`AlgIterator`] implementation. type Output; /// Prepare an [`AlgIterator`], consuming the factory. fn prepare(self) -> Self::Iter; /// Iterate the the closure `step`. /// /// The closure should accept a `state` parameter (satisfying the trait [`AlgIteratorState`]). /// It should return the output of /// `state.`[`if_verbose`][AlgIteratorState::if_verbose], [`Step::Terminated`] to indicate /// completion for other reason, or [`Step::Failure`] for termination for failure. /// /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator::iterate`]. #[inline] fn iterate_fallible<F, E>(self, step: F) -> Result<(), E> where F: FnMut(Self::State) -> Step<V, Self::State, E>, E: Error, { self.prepare().iterate(step) } /// Iterate the closure `step`. /// /// The closure should accept a `state` parameter (satisfying the trait [`AlgIteratorState`]), /// It should return the output of /// `state.`[`if_verbose`][AlgIteratorState::if_verbose]. /// /// For usage instructions see the [module documentation][self]. /// /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator::iterate`] /// with the error type `E=`[`std::convert::Infallible`]. #[inline] fn iterate<F>(self, step: F) where F: FnMut(Self::State) -> Step<V, Self::State>, { self.iterate_fallible(step).unwrap_or_default() } /// Iterate the closure `step` with data produced by `datasource`. /// /// The closure should accept a `state` parameter (satisfying the trait [`AlgIteratorState`]), /// and a data parameter taken from `datasource`. It should return the output of /// `state.`[`if_verbose`][AlgIteratorState::if_verbose], [`Step::Terminated`] to indicate /// completion for other reason, or [`Step::Failure`] for termination for failure. /// /// If the `datasource` runs out of data, the iterator is considered having terminated /// successsfully. /// /// For usage instructions see the [module documentation][self]. #[inline] fn iterate_data_fallible<F, D, I, E>(self, mut datasource: I, mut step: F) -> Result<(), E> where F: FnMut(Self::State, D) -> Step<V, Self::State, E>, I: Iterator<Item = D>, E: Error, { self.prepare().iterate(move |state| { datasource .next() .map_or(Step::Terminated, |d| step(state, d)) }) } /// Iterate the closure `step` with data produced by `datasource`. /// /// The closure should accept a `state` parameter (satisfying the trait [`AlgIteratorState`]), /// and a data parameter taken from `datasource`. It should return the output of /// `state.`[`if_verbose`][AlgIteratorState::if_verbose]. /// /// If the `datasource` runs out of data, the iterator is considered having terminated /// successsfully. /// /// For usage instructions see the [module documentation][self]. #[inline] fn iterate_data<F, D, I>(self, datasource: I, step: F) where F: FnMut(Self::State, D) -> Step<V, Self::State>, I: Iterator<Item = D>, { self.iterate_data_fallible(datasource, step) .unwrap_or_default() } // fn make_iterate<'own>(self) // -> Box<dyn (FnMut(dyn FnMut(&Self::State) -> Option<V>) -> ()) + 'own> { // Box::new(move |step| self.iterate(step)) // } // fn make_iterate_data<'own, D, I>(self, datasource : I) // -> Box<dyn (FnMut(dyn FnMut(&Self::State, D) -> Option<V>) -> ()) + 'own> // where I : Iterator<Item = D> + 'own { // Box::new(move |step| self.iterate_data(step, datasource)) // } /// Add logging to the iterator produced by the factory. /// /// Returns a new factory whose corresponding [`AlgIterator`] only inserts that data into the /// log without passing it onwards. /// /// Use as: /// ```rust /// # use alg_tools::iterate::*; /// # use alg_tools::logger::*; /// let iter = AlgIteratorOptions::default(); /// let mut log = Logger::new(); /// iter.into_log(&mut log).iterate(|state|{ /// // perform iterations /// state.if_verbose(||{ /// // calculate and return function value or other displayed data v /// return 0 /// }) /// }) /// ``` fn into_log<'log>( self, logger: &'log mut Logger<Self::Output>, ) -> LoggingIteratorFactory<'log, Self::Output, Self> where Self: Sized, { LoggingIteratorFactory { base_options: self, logger } } /// Map the output of the iterator produced by the factory. /// /// Returns a new factory. fn mapped<U, G>(self, map: G) -> MappingIteratorFactory<G, Self> where Self: Sized, G: Fn(usize, Self::Output) -> U, { MappingIteratorFactory { base_options: self, map } } /// Adds iteration number to the output. /// /// Returns a new factory. /// Typically followed by [`Self::into_log`]. fn with_iteration_number( self, ) -> MappingIteratorFactory<fn(usize, Self::Output) -> LogItem<Self::Output>, Self> where Self: Sized, { self.mapped(LogItem::new) } /// Add timing to the iterator produced by the factory. fn timed(self) -> TimingIteratorFactory<Self> where Self: Sized, { TimingIteratorFactory(self) } /// Add value stopping threshold to the iterator produce by the factory fn stop_target(self, target: Self::Output) -> ValueIteratorFactory<Self::Output, Self> where Self: Sized, Self::Output: Num, { ValueIteratorFactory { base_options: self, target: target } } /// Add stall stopping to the iterator produce by the factory fn stop_stall(self, stall: Self::Output) -> StallIteratorFactory<Self::Output, Self> where Self: Sized, Self::Output: Num, { StallIteratorFactory { base_options: self, stall: stall } } /// Is the iterator quiet, i.e., on-verbose? fn is_quiet(&self) -> bool { false } /// Returns an an [`std::iter::Iterator`] that can be used in a `for`-loop. fn iter(self) -> AlgIteratorIterator<Self::Iter> { AlgIteratorIterator { algi: Rc::new(RefCell::new(self.prepare())) } } /// Returns an an [`std::iter::Iterator`] that can be used in a `for`-loop, /// also inputting an initial iteration status calculated by `f` if needed. fn iter_init( self, f: impl FnOnce() -> <Self::Iter as AlgIterator>::Input, ) -> AlgIteratorIterator<Self::Iter> { let mut i = self.prepare(); let st = i.state(); let step: Step<<Self::Iter as AlgIterator>::Input, Self::State> = st.if_verbose(f); i.poststep(step); AlgIteratorIterator { algi: Rc::new(RefCell::new(i)) } } } /// Options for [`BasicAlgIteratorFactory`]. /// /// Use as: /// ``` /// # use alg_tools::iterate::*; /// let iter = AlgIteratorOptions{ max_iter : 10000, .. Default::default() }; /// ``` #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)] pub struct AlgIteratorOptions { /// Maximum number of iterations pub max_iter: usize, /// Number of iterations between verbose iterations that display state. pub verbose_iter: Verbose, /// Whether verbose iterations are displayed, or just passed onwards to a containing /// `AlgIterator`. pub quiet: bool, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)] pub enum Verbose { /// Be verbose every $n$ iterations. Every(usize), /// Be verbose every $n$ iterations and initial $m$ iterations. EveryAndInitial { every: usize, initial: usize }, /// Be verbose if iteration number $n$ divides by $b^{\text{floor}(\log_b(n))}$, where /// $b$ is indicated logarithmic base. So, with $b=10$, /// * every iteration for first 10 iterations, /// * every 10 iterations from there on until 100 iterations, /// * every 100 iteartinos frmo there on until 1000 iterations, etc. Logarithmic(usize), /// Same as `Logarithmic`, but $\log_b(n)$ is replaced by $min\{c, \log_b(n)\}$ where $c$ /// is the given `cap`. For example, with `base=10` and `cap=2`, the first ten iterations /// will be output, then every tenth iteration, and after 100 iterations, every 100th iteration, /// without further logarithmic progression. LogarithmicCap { base: usize, cap: u32 }, } impl Verbose { /// Indicates whether given iteration number is verbose pub fn is_verbose(&self, iter: usize) -> bool { match self { &Verbose::Every(every) => every != 0 && iter % every == 0, &Verbose::EveryAndInitial { every, initial } => { iter <= initial || (every != 0 && iter % every == 0) } &Verbose::Logarithmic(base) => { let every = base.pow((iter as float).log(base as float).floor() as u32); iter % every == 0 } &Verbose::LogarithmicCap { base, cap } => { let every = base.pow(((iter as float).log(base as float).floor() as u32).min(cap)); iter % every == 0 } } } } impl Default for AlgIteratorOptions { fn default() -> AlgIteratorOptions { AlgIteratorOptions { max_iter: 1000, verbose_iter: Verbose::EveryAndInitial { every: 100, initial: 10 }, quiet: false, } } } /// State of a `BasicAlgIterator` #[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)] pub struct BasicState { /// Current iteration iter: usize, /// Whether the iteration is verbose, i.e., results should be displayed. /// Requires `calc` to be `true`. verbose: bool, /// Whether results should be calculated. calc: bool, /// Indicates whether the iteration is quiet quiet: bool, } /// [`AlgIteratorFactory`] for [`BasicAlgIterator`] #[derive(Clone, Debug)] pub struct BasicAlgIteratorFactory<V> { options: AlgIteratorOptions, _phantoms: PhantomData<V>, } /// The simplest [`AlgIterator`], created by [`BasicAlgIteratorFactory`] #[derive(Clone, Debug)] pub struct BasicAlgIterator<V> { options: AlgIteratorOptions, iter: usize, _phantoms: PhantomData<V>, } impl AlgIteratorOptions { /// [`AlgIteratorOptions`] is directly a factory for [`BasicAlgIterator`], /// however, due to type inference issues, it may become convenient to instantiate /// it to a specific return type for the inner step function. This method does that. pub fn instantiate<V>(&self) -> BasicAlgIteratorFactory<V> { BasicAlgIteratorFactory { options: self.clone(), _phantoms: PhantomData } } } impl<V> AlgIteratorFactory<V> for AlgIteratorOptions where V: LogRepr, { type State = BasicState; type Iter = BasicAlgIterator<V>; type Output = V; fn prepare(self) -> Self::Iter { BasicAlgIterator { options: self, iter: 0, _phantoms: PhantomData } } #[inline] fn is_quiet(&self) -> bool { self.quiet } } impl<V> AlgIteratorFactory<V> for BasicAlgIteratorFactory<V> where V: LogRepr, { type State = BasicState; type Iter = BasicAlgIterator<V>; type Output = V; fn prepare(self) -> Self::Iter { BasicAlgIterator { options: self.options, iter: 0, _phantoms: PhantomData } } #[inline] fn is_quiet(&self) -> bool { self.options.quiet } } impl<V> AlgIterator for BasicAlgIterator<V> where V: LogRepr, { type State = BasicState; type Output = V; type Input = V; #[inline] fn prestep(&mut self) -> Option<Self::State> { if self.iter >= self.options.max_iter { None } else { self.iter += 1; Some(self.state()) } } fn poststep<E: Error>(&mut self, res: Step<V, Self::State, E>) -> Step<V, Self::State, E> { if let Step::Result(ref val, ref state) = res { if state.verbose && !self.options.quiet { println!( "{}{}/{} {}{}", "".dimmed(), state.iter, self.options.max_iter, val.logrepr(), "".clear() ); } } res } #[inline] fn iteration(&self) -> usize { self.iter } #[inline] fn state(&self) -> BasicState { let iter = self.iter; let verbose = self.options.verbose_iter.is_verbose(iter); BasicState { iter: iter, verbose: verbose, calc: verbose, quiet: self.options.quiet } } } impl AlgIteratorState for BasicState { #[inline] fn if_verbose<V, E: Error>(self, calc_objective: impl FnOnce() -> V) -> Step<V, Self, E> { if self.calc { Step::Result(calc_objective(), self) } else { Step::Quiet } } #[inline] fn iteration(&self) -> usize { self.iter } #[inline] fn is_quiet(&self) -> bool { self.quiet } } // // Stall detecting iteration function. // /// An [`AlgIteratorFactory`] for an [`AlgIterator`] that detects “stall”. /// /// We define stall as $(v_{k+n}-v_k)/v_k ≤ θ$, where $n$ the distance between /// [`Step::Result`] iterations, and $θ$ is the provided `stall` parameter. #[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)] pub struct StallIteratorFactory<U: Num, BaseFactory> { /// An [`AlgIteratorFactory`] on which to build on pub base_options: BaseFactory, /// Stalling threshold $θ$. pub stall: U, } /// Iterator produced by [`StallIteratorFactory`]. pub struct StallIterator<U: Num, BaseIterator> { base_iterator: BaseIterator, stall: U, previous_value: Option<U>, } impl<V, U, BaseFactory> AlgIteratorFactory<V> for StallIteratorFactory<U, BaseFactory> where BaseFactory: AlgIteratorFactory<V, Output = U>, U: SignedNum + PartialOrd, { type Iter = StallIterator<U, BaseFactory::Iter>; type State = BaseFactory::State; type Output = BaseFactory::Output; fn prepare(self) -> Self::Iter { StallIterator { base_iterator: self.base_options.prepare(), stall: self.stall, previous_value: None, } } fn is_quiet(&self) -> bool { self.base_options.is_quiet() } } impl<U, BaseIterator> AlgIterator for StallIterator<U, BaseIterator> where BaseIterator: AlgIterator<Output = U>, U: SignedNum + PartialOrd, { type State = BaseIterator::State; type Output = U; type Input = BaseIterator::Input; #[inline] fn prestep(&mut self) -> Option<Self::State> { self.base_iterator.prestep() } #[inline] fn poststep<E>(&mut self, res: Step<Self::Input, Self::State, E>) -> Step<U, Self::State, E> where E: Error, { match self.base_iterator.poststep(res) { Step::Result(nv, state) => { let previous_v = self.previous_value; self.previous_value = Some(nv); match previous_v { Some(pv) if (nv - pv).abs() <= self.stall * pv.abs() => Step::Terminated, _ => Step::Result(nv, state), } } val => val, } } #[inline] fn iteration(&self) -> usize { self.base_iterator.iteration() } #[inline] fn state(&self) -> Self::State { self.base_iterator.state() } } /// An [`AlgIteratorFactory`] for an [`AlgIterator`] that detect whether step function /// return value is less than `target`, and terminates if it is. #[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)] pub struct ValueIteratorFactory<U: Num, BaseFactory> { /// An [`AlgIteratorFactory`] on which to build on pub base_options: BaseFactory, /// Target value pub target: U, } /// Iterator produced by [`ValueIteratorFactory`]. pub struct ValueIterator<U: Num, BaseIterator> { base_iterator: BaseIterator, target: U, } impl<V, U, BaseFactory> AlgIteratorFactory<V> for ValueIteratorFactory<U, BaseFactory> where BaseFactory: AlgIteratorFactory<V, Output = U>, U: SignedNum + PartialOrd, { type Iter = ValueIterator<U, BaseFactory::Iter>; type State = BaseFactory::State; type Output = BaseFactory::Output; fn prepare(self) -> Self::Iter { ValueIterator { base_iterator: self.base_options.prepare(), target: self.target } } fn is_quiet(&self) -> bool { self.base_options.is_quiet() } } impl<U, BaseIterator> AlgIterator for ValueIterator<U, BaseIterator> where BaseIterator: AlgIterator<Output = U>, U: SignedNum + PartialOrd, { type State = BaseIterator::State; type Output = U; type Input = BaseIterator::Input; #[inline] fn prestep(&mut self) -> Option<Self::State> { self.base_iterator.prestep() } #[inline] fn poststep<E>(&mut self, res: Step<Self::Input, Self::State, E>) -> Step<U, Self::State, E> where E: Error, { match self.base_iterator.poststep(res) { Step::Result(v, state) => { if v <= self.target { Step::Terminated } else { Step::Result(v, state) } } val => val, } } #[inline] fn iteration(&self) -> usize { self.base_iterator.iteration() } #[inline] fn state(&self) -> Self::State { self.base_iterator.state() } } // // Logging iterator // /// [`AlgIteratorFactory`] for a logging [`AlgIterator`]. /// /// Typically produced with [`AlgIteratorFactory::into_log`]. /// The `Output` of the corresponding [`LoggingIterator`] is `()`: #[derive(Debug)] pub struct LoggingIteratorFactory<'log, U, BaseFactory> { /// Base [`AlgIteratorFactory`] on which to build base_options: BaseFactory, /// The `Logger` to use. logger: &'log mut Logger<U>, } /// Iterator produced by `LoggingIteratorFactory`. pub struct LoggingIterator<'log, U, BaseIterator> { base_iterator: BaseIterator, logger: &'log mut Logger<U>, } impl<'log, V, BaseFactory> AlgIteratorFactory<V> for LoggingIteratorFactory<'log, BaseFactory::Output, BaseFactory> where BaseFactory: AlgIteratorFactory<V>, BaseFactory::Output: 'log, { type State = BaseFactory::State; type Iter = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter>; type Output = (); fn prepare(self) -> Self::Iter { LoggingIterator { base_iterator: self.base_options.prepare(), logger: self.logger } } #[inline] fn is_quiet(&self) -> bool { self.base_options.is_quiet() } } impl<'log, BaseIterator> AlgIterator for LoggingIterator<'log, BaseIterator::Output, BaseIterator> where BaseIterator: AlgIterator, BaseIterator::Output: 'log, { type State = BaseIterator::State; type Output = (); type Input = BaseIterator::Input; #[inline] fn prestep(&mut self) -> Option<Self::State> { self.base_iterator.prestep() } #[inline] fn poststep<E>(&mut self, res: Step<Self::Input, Self::State, E>) -> Step<(), Self::State, E> where E: Error, { match self.base_iterator.poststep(res) { Step::Result(v, _) => { self.logger.log(v); Step::Quiet } Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, Step::Failure(e) => Step::Failure(e), } } #[inline] fn iteration(&self) -> usize { self.base_iterator.iteration() } #[inline] fn state(&self) -> Self::State { self.base_iterator.state() } } /// This [`AlgIteratorFactory`] allows output mapping. /// /// Typically produced with [`AlgIteratorFactory::mapped`]. #[derive(Debug)] pub struct MappingIteratorFactory<G, BaseFactory> { /// Base [`AlgIteratorFactory`] on which to build base_options: BaseFactory, /// A closure `G : Fn(usize, BaseFactory::Output) -> U` that gets the current iteration /// and the output of the base factory as input, and produces a new output. map: G, } /// [`AlgIterator`] produced by [`MappingIteratorFactory`]. pub struct MappingIterator<G, BaseIterator> { base_iterator: BaseIterator, map: G, } impl<V, U, G, BaseFactory> AlgIteratorFactory<V> for MappingIteratorFactory<G, BaseFactory> where BaseFactory: AlgIteratorFactory<V>, G: Fn(usize, BaseFactory::Output) -> U, { type State = BaseFactory::State; type Iter = MappingIterator<G, BaseFactory::Iter>; type Output = U; fn prepare(self) -> Self::Iter { MappingIterator { base_iterator: self.base_options.prepare(), map: self.map } } #[inline] fn is_quiet(&self) -> bool { self.base_options.is_quiet() } } impl<U, G, BaseIterator> AlgIterator for MappingIterator<G, BaseIterator> where BaseIterator: AlgIterator, G: Fn(usize, BaseIterator::Output) -> U, { type State = BaseIterator::State; type Output = U; type Input = BaseIterator::Input; #[inline] fn prestep(&mut self) -> Option<Self::State> { self.base_iterator.prestep() } #[inline] fn poststep<E>( &mut self, res: Step<Self::Input, Self::State, E>, ) -> Step<Self::Output, Self::State, E> where E: Error, { match self.base_iterator.poststep(res) { Step::Result(v, state) => Step::Result((self.map)(self.iteration(), v), state), Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, Step::Failure(e) => Step::Failure(e), } } #[inline] fn iteration(&self) -> usize { self.base_iterator.iteration() } #[inline] fn state(&self) -> Self::State { self.base_iterator.state() } } // // Timing iterator // /// An [`AlgIteratorFactory`] for an [`AlgIterator`] that adds spent CPU time to verbose events. #[derive(Debug)] pub struct TimingIteratorFactory<BaseFactory>(pub BaseFactory); /// Iterator produced by [`TimingIteratorFactory`] #[derive(Debug)] pub struct TimingIterator<BaseIterator> { base_iterator: BaseIterator, start_time: ProcessTime, } /// Data `U` with production time attached #[derive(Copy, Clone, Debug, Serialize)] pub struct Timed<U> { /// CPU time taken pub cpu_time: Duration, /// Iteration number pub iter: usize, /// User data //#[serde(flatten)] pub data: U, } impl<T> LogRepr for Timed<T> where T: LogRepr, { fn logrepr(&self) -> ColoredString { format!( "[{:.3}s] {}", self.cpu_time.as_secs_f64(), self.data.logrepr() ) .as_str() .into() } } impl<V, BaseFactory> AlgIteratorFactory<V> for TimingIteratorFactory<BaseFactory> where BaseFactory: AlgIteratorFactory<V>, { type State = BaseFactory::State; type Iter = TimingIterator<BaseFactory::Iter>; type Output = Timed<BaseFactory::Output>; fn prepare(self) -> Self::Iter { TimingIterator { base_iterator: self.0.prepare(), start_time: ProcessTime::now() } } #[inline] fn is_quiet(&self) -> bool { self.0.is_quiet() } } impl<BaseIterator> AlgIterator for TimingIterator<BaseIterator> where BaseIterator: AlgIterator, { type State = BaseIterator::State; type Output = Timed<BaseIterator::Output>; type Input = BaseIterator::Input; #[inline] fn prestep(&mut self) -> Option<Self::State> { self.base_iterator.prestep() } #[inline] fn poststep<E>( &mut self, res: Step<Self::Input, Self::State, E>, ) -> Step<Self::Output, Self::State, E> where E: Error, { match self.base_iterator.poststep(res) { Step::Result(data, state) => Step::Result( Timed { cpu_time: self.start_time.elapsed(), iter: self.iteration(), data }, state, ), Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, Step::Failure(e) => Step::Failure(e), } } #[inline] fn iteration(&self) -> usize { self.base_iterator.iteration() } #[inline] fn state(&self) -> Self::State { self.base_iterator.state() } } // // New for-loop interface // pub struct AlgIteratorIterator<I: AlgIterator> { algi: Rc<RefCell<I>>, } pub struct AlgIteratorIteration<I: AlgIterator> { state: I::State, algi: Rc<RefCell<I>>, } impl<I: AlgIterator> std::iter::Iterator for AlgIteratorIterator<I> { type Item = AlgIteratorIteration<I>; fn next(&mut self) -> Option<Self::Item> { let algi = self.algi.clone(); RefCell::borrow_mut(&self.algi) .prestep() .map(|state| AlgIteratorIteration { state, algi }) } } /// Types of errors that may occur #[derive(Debug, PartialEq, Eq)] pub enum IterationError { /// [`AlgIteratorIteration::if_verbose_check`] is not called in iteration order. ReportingOrderingError, } impl<I: AlgIterator> AlgIteratorIteration<I> { /// Call `call_objective` if this is a verbose iteration. /// /// Verbosity depends on the [`AlgIterator`] that produced this state. /// /// The closure `calc_objective` should return an arbitrary value of type `V`, to be inserted /// into the log, or whatever is deemed by the [`AlgIterator`]. For usage instructions see the /// [module documentation][self]. /// /// This function may panic if result reporting is not ordered correctly (an unlikely mistake /// if using this facility correctly). For a version that propagates errors, see /// [`Self::if_verbose_check`]. pub fn if_verbose(self, calc_objective: impl FnOnce() -> I::Input) { self.if_verbose_check(calc_objective).unwrap() } /// Version of [`Self::if_verbose`] that propagates errors instead of panicking. pub fn if_verbose_check( self, calc_objective: impl FnOnce() -> I::Input, ) -> Result<(), IterationError> { let mut algi = match RefCell::try_borrow_mut(&self.algi) { Err(_) => return Err(IterationError::ReportingOrderingError), Ok(algi) => algi, }; if self.state.iteration() != algi.iteration() { Err(IterationError::ReportingOrderingError) } else { let res: Step<I::Input, I::State, std::convert::Infallible> = self.state.if_verbose(calc_objective); algi.poststep(res); Ok(()) } } /// Returns the current iteration count. pub fn iteration(&self) -> usize { self.state.iteration() } /// Indicates whether the iterator is quiet pub fn is_quiet(&self) -> bool { self.state.is_quiet() } } // // Tests // #[cfg(test)] mod tests { use super::*; use crate::logger::Logger; #[test] fn iteration() { let options = AlgIteratorOptions { max_iter: 10, verbose_iter: Verbose::Every(3), ..Default::default() }; { let mut start = 1 as int; options.iterate(|state| { start = start * 2; state.if_verbose(|| start) }); assert_eq!(start, (2 as int).pow(10)); } { let mut start = 1 as int; let mut log = Logger::new(); let factory = options .instantiate() .with_iteration_number() .into_log(&mut log); factory.iterate(|state| { start = start * 2; state.if_verbose(|| start) }); assert_eq!(start, (2 as int).pow(10)); assert_eq!( log.data() .iter() .map(|LogItem { data: v, iter: _ }| v.clone()) .collect::<Vec<int>>(), (1..10) .map(|i| (2 as int).pow(i)) .skip(2) .step_by(3) .collect::<Vec<int>>() ) } } #[test] fn iteration_for_loop() { let options = AlgIteratorOptions { max_iter: 10, verbose_iter: Verbose::Every(3), ..Default::default() }; { let mut start = 1 as int; for state in options.iter() { start = start * 2; state.if_verbose(|| start) } assert_eq!(start, (2 as int).pow(10)); } { let mut start = 1 as int; let mut log = Logger::new(); let factory = options .instantiate() .with_iteration_number() .into_log(&mut log); for state in factory.iter() { start = start * 2; state.if_verbose(|| start) } assert_eq!(start, (2 as int).pow(10)); assert_eq!( log.data() .iter() .map(|LogItem { data: v, iter: _ }| v.clone()) .collect::<Vec<int>>(), (1..10) .map(|i| (2 as int).pow(i)) .skip(2) .step_by(3) .collect::<Vec<int>>() ) } } }