# HG changeset patch # User Tuomo Valkonen # Date 1729484896 18000 # Node ID daf0e3a70c79b4831093bdc61b88ae4c3d68584c # Parent dd144a9727229078ed820889e1a44ff5f5e68c5a New iteration interface, allowing for loops. diff -r dd144a972722 -r daf0e3a70c79 Cargo.lock --- a/Cargo.lock Sat Oct 19 10:39:54 2024 -0500 +++ b/Cargo.lock Sun Oct 20 23:28:16 2024 -0500 @@ -4,7 +4,7 @@ [[package]] name = "alg_tools" -version = "0.2.0-dev" +version = "0.3.0-dev" dependencies = [ "colored", "cpu-time", diff -r dd144a972722 -r daf0e3a70c79 Cargo.toml --- a/Cargo.toml Sat Oct 19 10:39:54 2024 -0500 +++ b/Cargo.toml Sun Oct 20 23:28:16 2024 -0500 @@ -1,6 +1,6 @@ [package] name = "alg_tools" -version = "0.2.0-dev" +version = "0.3.0-dev" edition = "2021" rust-version = "1.67" authors = ["Tuomo Valkonen "] diff -r dd144a972722 -r daf0e3a70c79 src/iterate.rs --- a/src/iterate.rs Sat Oct 19 10:39:54 2024 -0500 +++ b/src/iterate.rs Sun Oct 20 23:28:16 2024 -0500 @@ -52,6 +52,8 @@ use std::marker::PhantomData; use std::time::Duration; use std::error::Error; +use std::rc::Rc; +use std::cell::Cell; use crate::types::*; use crate::logger::*; @@ -114,7 +116,7 @@ /// /// This is the parameter obtained by the closure passed to [`AlgIterator::iterate`] or /// [`AlgIteratorFactory::iterate`]. -pub trait AlgIteratorState { +pub trait AlgIteratorState : Sized { /// Call `call_objective` if this is a verbose iteration. /// /// Verbosity depends on the [`AlgIterator`] that produced this state. @@ -122,7 +124,7 @@ /// 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(&self, calc_objective : impl FnMut() -> V) -> Step; + fn if_verbose(self, calc_objective : impl FnMut() -> V) -> Step; /// Returns the current iteration count. fn iteration(&self) -> usize; @@ -133,7 +135,7 @@ /// Result of a step of an [`AlgIterator`] #[derive(Debug, Serialize)] -pub enum Step { +pub enum Step { /// Iteration should be terminated Terminated, /// Iteration should be terminated due to failure @@ -141,14 +143,14 @@ /// No result this iteration (due to verbosity settings) Quiet, /// Result of this iteration (due to verbosity settings) - Result(V), + Result(V, S), } -impl Step { +impl Step { /// Maps the value contained within the `Step`, if any, by the closure `f`. - pub fn map(self, mut f : impl FnMut(V) -> U) -> Step { + pub fn map(self, mut f : impl FnMut(V) -> U) -> Step { match self { - Step::Result(v) => Step::Result(f(v)), + Step::Result(v, s) => Step::Result(f(v), s), Step::Failure(e) => Step::Failure(e), Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, @@ -156,6 +158,12 @@ } } +impl Default for Step { + fn default() -> Self { + Step::Quiet + } +} + /// An iterator for algorithms, produced by [`AlgIteratorFactory::prepare`]. /// /// Typically not accessed directly, but transparently produced by an [`AlgIteratorFactory`]. @@ -163,13 +171,26 @@ pub trait AlgIterator : Sized { /// The state type type State : AlgIteratorState; - /// The output type for [`Self::step`]. + /// The output type for [`Self::poststep`] and [`Self::step`]. type Output; - /// The error type for [`Self::step`] and [`Self::iterate`]. - type Err : Error; + /// The input type for [`Self::poststep`]. + type Input; - /// Advance the iterator. - fn step(&mut self) -> Step; + /// Advance the iterator, performing `step_fn` with the state + fn step(&mut self, step_fn : &mut F) -> Step + where F : FnMut(Self::State) -> Step, + 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; + + /// Handle step result + fn poststep(&mut self, result : Step) + -> Step + where E : Error; /// Return current iteration count. fn iteration(&self) -> usize { @@ -179,56 +200,29 @@ /// Return current state. fn state(&self) -> Self::State; - /// Iterate the `AlgIterator` until termination. + /// 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(&mut self) -> Result<(), Self::Err> { + fn iterate(&mut self, mut step_fn : F) -> Result<(), E> + where F : FnMut(Self::State) -> Step, + E : Error { loop { - match self.step() { + match self.step(&mut step_fn) { Step::Terminated => return Ok(()), Step::Failure(e) => return Err(e), _ => {}, } } } - - /// Converts the `AlgIterator` into a plain [`Iterator`]. - /// - /// [`Step::Quiet`] results are discarded, and [`Step::Failure`] results **panic**. - fn downcast(self) -> AlgIteratorI { - AlgIteratorI(self) - } -} - -/// Conversion of an `AlgIterator` into a plain [`Iterator`]. -/// -/// The conversion discards [`Step::Quiet`] and **panics** on [`Step::Failure`]. -pub struct AlgIteratorI(A); - -impl Iterator for AlgIteratorI -where A : AlgIterator { - type Item = A::Output; - - fn next(&mut self) -> Option { - loop { - match self.0.step() { - Step::Result(v) => return Some(v), - Step::Failure(e) => panic!("{e:?}"), - Step::Terminated => return None, - Step::Quiet => continue, - } - } - } } /// A factory for producing an [`AlgIterator`]. /// /// For usage instructions see the [module documentation][self]. pub trait AlgIteratorFactory : Sized { - type Iter : AlgIterator - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter : AlgIterator; + /// 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; @@ -238,12 +232,7 @@ type Output; /// Prepare an [`AlgIterator`], consuming the factory. - /// - /// The function `step_fn` should accept a `state` ([`AlgIteratorState`] parameter, and return - /// a [`Step`]. - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error; + fn prepare(self) -> Self::Iter; /// Iterate the the closure `step`. /// @@ -255,9 +244,9 @@ /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator::iterate`]. #[inline] fn iterate_fallible(self, step : F) -> Result<(), E> - where F : FnMut(&Self::State) -> Step, + where F : FnMut(Self::State) -> Step, E : Error { - self.prepare(step).iterate() + self.prepare().iterate(step) } /// Iterate the the closure `step`. @@ -272,7 +261,7 @@ /// with the error type `E=`[`std::convert::Infallible`]. #[inline] fn iterate(self, step : F) - where F : FnMut(&Self::State) -> Step { + where F : FnMut(Self::State) -> Step { self.iterate_fallible(step).unwrap_or_default() } @@ -288,13 +277,14 @@ /// /// For usage instructions see the [module documentation][self]. #[inline] - fn iterate_data_fallible(self, mut datasource : I, mut step : F) -> Result<(), E> - where F : FnMut(&Self::State, D) -> Step, + fn iterate_data_fallible(self, mut datasource : I, mut step : F) + -> Result<(), E> + where F : FnMut(Self::State, D) -> Step, I : Iterator, E : Error { - self.prepare(move |state| { + self.prepare().iterate(move |state| { datasource.next().map_or(Step::Terminated, |d| step(state, d)) - }).iterate() + }) } /// Iterate the closure `step` with data produced by `datasource`. @@ -309,7 +299,7 @@ /// For usage instructions see the [module documentation][self]. #[inline] fn iterate_data(self, datasource : I, step : F) - where F : FnMut(&Self::State, D) -> Step, + where F : FnMut(Self::State, D) -> Step, I : Iterator { self.iterate_data_fallible(datasource, step).unwrap_or_default() } @@ -398,6 +388,17 @@ /// 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 { + AlgIteratorIterator { + algi : self.prepare(), + result : Rc::new(AlgIteratorResponse{ + expected_iter : 0, + response : Cell::new(Step::Quiet) + }) + } + } } /// Options for [`BasicAlgIteratorFactory`]. @@ -483,11 +484,10 @@ /// The simplest [`AlgIterator`], created by [`BasicAlgIteratorFactory`] #[derive(Clone,Debug)] -pub struct BasicAlgIterator { +pub struct BasicAlgIterator { options : AlgIteratorOptions, iter : usize, - step_fn : F, - _phantoms : PhantomData<(V, E)>, + _phantoms : PhantomData, } impl AlgIteratorOptions { @@ -505,18 +505,13 @@ impl AlgIteratorFactory for AlgIteratorOptions where V : LogRepr { type State = BasicState; - type Iter = BasicAlgIterator - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = BasicAlgIterator; type Output = V; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { BasicAlgIterator{ options : self, iter : 0, - step_fn, _phantoms : PhantomData, } } @@ -530,18 +525,13 @@ impl AlgIteratorFactory for BasicAlgIteratorFactory where V : LogRepr { type State = BasicState; - type Iter = BasicAlgIterator - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = BasicAlgIterator; type Output = V; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { BasicAlgIterator { options : self.options, iter : 0, - step_fn, _phantoms : PhantomData } } @@ -552,33 +542,33 @@ } } -impl AlgIterator for BasicAlgIterator -where V : LogRepr, - E : Error, - F : FnMut(&BasicState) -> Step { +impl AlgIterator for BasicAlgIterator +where V : LogRepr { type State = BasicState; type Output = V; - type Err = E; + type Input = V; #[inline] - fn step(&mut self) -> Step { + fn prestep(&mut self) -> Option { if self.iter >= self.options.max_iter { - Step::Terminated + None } else { self.iter += 1; - let state = self.state(); - let res = (self.step_fn)(&state); - if let Step::Result(ref val) = res { - if state.verbose && !self.options.quiet { - println!("{}{}/{} {}{}", "".dimmed(), - state.iter, - self.options.max_iter, - val.logrepr(), - "".clear()); - } + Some(self.state()) + } + } + + fn poststep(&mut self, res : Step) -> Step { + 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 } + res } #[inline] @@ -601,9 +591,9 @@ impl AlgIteratorState for BasicState { #[inline] - fn if_verbose(&self, mut calc_objective : impl FnMut() -> V) -> Step { + fn if_verbose(self, mut calc_objective : impl FnMut() -> V) -> Step { if self.calc { - Step::Result(calc_objective()) + Step::Result(calc_objective(), self) } else { Step::Quiet } @@ -647,17 +637,13 @@ for StallIteratorFactory where BaseFactory : AlgIteratorFactory, U : SignedNum + PartialOrd { - type Iter = StallIterator> - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = StallIterator; type State = BaseFactory::State; type Output = BaseFactory::Output; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { StallIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), stall : self.stall, previous_value : None, } @@ -673,18 +659,24 @@ where BaseIterator : AlgIterator, U : SignedNum + PartialOrd { type State = BaseIterator::State; - type Output = BaseIterator::Output; - type Err = BaseIterator::Err; + type Output = U; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step { - match self.base_iterator.step() { - Step::Result(nv) => { + fn prestep(&mut self) -> Option { + self.base_iterator.prestep() + } + + #[inline] + fn poststep(&mut self, res : Step) -> Step + 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), + _ => Step::Result(nv, state), } }, val => val, @@ -722,17 +714,13 @@ for ValueIteratorFactory where BaseFactory : AlgIteratorFactory, U : SignedNum + PartialOrd { - type Iter = ValueIterator> - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = ValueIterator; type State = BaseFactory::State; type Output = BaseFactory::Output; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { ValueIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), target : self.target } } @@ -747,17 +735,22 @@ where BaseIterator : AlgIterator, U : SignedNum + PartialOrd { type State = BaseIterator::State; - type Output = BaseIterator::Output; - type Err = BaseIterator::Err; + type Output = U; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step { - match self.base_iterator.step() { - Step::Result(v) => { + fn prestep(&mut self) -> Option { + self.base_iterator.prestep() + } + + #[inline] + fn poststep(&mut self, res : Step) -> Step where E : Error{ + match self.base_iterator.poststep(res) { + Step::Result(v, state) => { if v <= self.target { Step::Terminated } else { - Step::Result(v) + Step::Result(v, state) } }, val => val, @@ -803,16 +796,12 @@ where BaseFactory : AlgIteratorFactory, BaseFactory::Output : 'log { type State = BaseFactory::State; - type Iter = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter> - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter>; type Output = (); - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { LoggingIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), logger : self.logger, } } @@ -829,12 +818,17 @@ BaseIterator::Output : 'log { type State = BaseIterator::State; type Output = (); - type Err = BaseIterator::Err; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step { - match self.base_iterator.step() { - Step::Result(v) => { + fn prestep(&mut self) -> Option { + self.base_iterator.prestep() + } + + #[inline] + fn poststep(&mut self, res : Step) -> Step<(), Self::State, E> where E : Error { + match self.base_iterator.poststep(res) { + Step::Result(v, _) => { self.logger.log(v); Step::Quiet }, @@ -856,7 +850,7 @@ } /// This [`AlgIteratorFactory`] allows output mapping. -/// +/// /// Typically produced with [`AlgIteratorFactory::mapped`]. #[derive(Debug)] pub struct MappingIteratorFactory { @@ -879,16 +873,12 @@ where BaseFactory : AlgIteratorFactory, G : Fn(usize, BaseFactory::Output) -> U { type State = BaseFactory::State; - type Iter = MappingIterator> - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = MappingIterator; type Output = U; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { MappingIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), map : self.map } } @@ -905,12 +895,17 @@ G : Fn(usize, BaseIterator::Output) -> U { type State = BaseIterator::State; type Output = U; - type Err = BaseIterator::Err; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step { - match self.base_iterator.step() { - Step::Result(v) => Step::Result((self.map)(self.iteration(), v)), + fn prestep(&mut self) -> Option { + self.base_iterator.prestep() + } + + #[inline] + fn poststep(&mut self, res : Step) -> Step 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), @@ -962,16 +957,12 @@ for TimingIteratorFactory where BaseFactory : AlgIteratorFactory { type State = BaseFactory::State; - type Iter = TimingIterator> - where F : FnMut(&Self::State) -> Step, - E : Error; + type Iter = TimingIterator; type Output = Timed; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step, - E : Error { + fn prepare(self) -> Self::Iter { TimingIterator { - base_iterator : self.0.prepare(step_fn), + base_iterator : self.0.prepare(), start_time : ProcessTime::now() } } @@ -987,16 +978,21 @@ where BaseIterator : AlgIterator { type State = BaseIterator::State; type Output = Timed; - type Err = BaseIterator::Err; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step { - match self.base_iterator.step() { - Step::Result(data) => { + fn prestep(&mut self) -> Option { + self.base_iterator.prestep() + } + + #[inline] + fn poststep(&mut self, res : Step) -> Step where E : Error { + match self.base_iterator.poststep(res) { + Step::Result(data, state) => { Step::Result(Timed{ cpu_time : self.start_time.elapsed(), data - }) + }, state) }, Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, @@ -1016,6 +1012,100 @@ } // +// New for-loop interface +// + +struct AlgIteratorResponse { + expected_iter : usize, + response : Cell>, +} +pub struct AlgIteratorIterator { + algi : I, + result : Rc>, +} + +pub struct AlgIteratorIteration { + state : I::State, + result : Rc>, +} + +impl AlgIteratorIterator { + fn poststep(&mut self, even_if_quiet : bool) { + let res = self.result.response.take(); + if !even_if_quiet { + if let Step::Quiet = res { + return + } + } + self.algi.poststep(res); + } +} + +impl std::iter::Iterator for AlgIteratorIterator { + type Item = AlgIteratorIteration; + + fn next(&mut self) -> Option { + self.poststep(true); + self.algi.prestep().map(|state| AlgIteratorIteration { + state, + result : self.result.clone(), + }) + } +} + +impl Drop for AlgIteratorIterator { + fn drop(&mut self) { + self.poststep(false) + } +} + +/// 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 AlgIteratorIteration { + /// 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 FnMut() -> 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, mut calc_objective : impl FnMut() -> I::Input) + -> Result<(), IterationError> { + if self.result.expected_iter != self.state.iteration() { + Err(IterationError::ReportingOrderingError) + } else { + let res = calc_objective(); + self.result.response.replace(Step::Result(res, self.state)); + 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 //