Sat, 22 Oct 2022 22:28:04 +0300
Convert iteration utilities to GATs
| 0 | 1 | /*! |
| 2 | ||
| 3 | This module provides tools for separating the computational steps of iterative | |
| 4 | algorithms from visualisation and other reporting routines. The approach is heavily | |
| 5 | indebted to functional programming. The computational step is to be implemented as a | |
| 6 | closure. That closure gets passed a `state` parameter that implements an | |
| 7 | [`if_verbose`][AlgIteratorState::if_verbose] method that is to be called to determine | |
| 8 | whether function values or other potentially costly things need to be calculated on that | |
| 9 | iteration. The parameter of [`if_verbose`][AlgIteratorState::if_verbose] is another | |
| 10 | closure that does the necessary computation. | |
| 11 | ||
| 12 | ## Simple example | |
| 13 | ||
| 14 | ```rust | |
| 15 | use alg_tools::types::*; | |
| 16 | use alg_tools::iterate::*; | |
| 17 | let mut iter = AlgIteratorOptions{ max_iter : 100, | |
| 18 | verbose_iter : Verbose::Every(10), | |
| 19 | .. Default::default() }; | |
| 20 | let mut x = 1 as float; | |
| 21 | iter.iterate(|state|{ | |
| 22 | // This is our computational step | |
| 23 | x = x + x.sqrt(); | |
| 24 | state.if_verbose(||{ | |
| 25 | // return current value when requested | |
| 26 | return x | |
| 27 | }) | |
| 28 | }) | |
| 29 | ``` | |
| 30 | 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. | |
| 31 | ||
| 32 | This will print out | |
| 33 | ```output | |
| 34 | 10/100 J=31.051164 | |
| 35 | 20/100 J=108.493699 | |
| 36 | 30/100 J=234.690039 | |
| 37 | 40/100 J=410.056327 | |
| 38 | 50/100 J=634.799262 | |
| 39 | 60/100 J=909.042928 | |
| 40 | 70/100 J=1232.870172 | |
| 41 | 80/100 J=1606.340254 | |
| 42 | 90/100 J=2029.497673 | |
| 43 | 100/100 J=2502.377071 | |
| 44 | ``` | |
| 45 | */ | |
| 46 | ||
| 47 | use colored::{Colorize,ColoredString}; | |
| 48 | //use num_traits::Num; | |
| 49 | use core::fmt::Debug; | |
| 50 | //use core::fmt::Display; | |
| 51 | //use std::sync::mpsc::Receiver; | |
| 52 | use serde::{Serialize, Deserialize}; | |
| 53 | use cpu_time::ProcessTime; | |
| 54 | use std::marker::PhantomData; | |
| 55 | use std::time::Duration; | |
| 56 | use crate::types::*; | |
| 57 | use crate::logger::*; | |
| 58 | ||
| 59 | /// Create the displayed presentation for log items. Override for nice (condensed) presentation of rich log items, or define `show` if it is not defined for your type. | |
| 60 | pub trait LogRepr : Debug { | |
| 61 | fn logrepr(&self) -> ColoredString { format!("« {:?} »", self).as_str().into() } | |
| 62 | } | |
| 63 | ||
| 64 | impl LogRepr for str { | |
| 65 | fn logrepr(&self) -> ColoredString { self.into() } | |
| 66 | } | |
| 67 | ||
| 68 | impl LogRepr for String { | |
| 69 | fn logrepr(&self) -> ColoredString { self.as_str().into() } | |
| 70 | } | |
| 71 | ||
| 72 | impl<T> LogRepr for T where T : Num { | |
| 73 | fn logrepr(&self) -> ColoredString { format!("J={}", self).as_str().into() } | |
| 74 | } | |
| 75 | ||
| 76 | impl<V> LogRepr for Option<V> where V : LogRepr { | |
| 77 | fn logrepr(&self) -> ColoredString { | |
| 78 | match self { | |
| 79 | None => { "===missing value===".red() } | |
| 80 | Some(v) => { v.logrepr() } | |
| 81 | } | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | #[derive(Debug,Clone)] | |
| 86 | pub struct Annotated<F>(pub F, pub String); | |
| 87 | ||
| 88 | impl<V> LogRepr for Annotated<V> where V : LogRepr { | |
| 89 | fn logrepr(&self) -> ColoredString { | |
| 90 | format!("{}\t| {}", self.0.logrepr(), self.1).as_str().into() | |
| 91 | } | |
| 92 | } | |
| 93 | ||
| 94 | ||
| 95 | /// Basic log item. | |
| 96 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] | |
| 97 | pub struct LogItem<V> { | |
| 98 | pub iter : usize, | |
| 99 | // This causes [`csv`] to crash. | |
| 100 | //#[serde(flatten)] | |
| 101 | pub data : V | |
| 102 | } | |
| 103 | ||
| 104 | fn make_logitem<V>(iter : usize, data : V) -> LogItem<V> { | |
| 105 | LogItem{ iter, data } | |
| 106 | } | |
| 107 | ||
| 108 | /// State of an [`AlgIterator`] | |
| 109 | pub trait AlgIteratorState { | |
| 110 | fn if_verbose<A>(&self, calc_objective : impl FnMut() -> A) -> Option<A>; | |
| 111 | ||
| 112 | fn iteration(&self) -> usize; | |
| 113 | } | |
| 114 | ||
| 115 | /// Result of a step of an [`AlgIterator`] | |
| 116 | #[derive(Debug, Serialize)] | |
| 117 | pub enum Step<V> { | |
| 118 | /// Iteration should be terminated | |
| 119 | Terminated, | |
| 120 | /// No result this iteration (due to verbosity settings) | |
| 121 | Quiet, | |
| 122 | /// Result of this iteration (due to verbosity settings) | |
| 123 | Result(V), | |
| 124 | } | |
| 125 | ||
| 126 | /// An iterator for algorithms, produced by [`AlgIteratorFactory.prepare`]. | |
| 127 | /// For usage instructions see the help for [`AlgIteratorFactory`]. | |
| 128 | pub trait AlgIterator<V> : Sized { | |
| 129 | type State : AlgIteratorState; | |
| 130 | type Output; | |
| 131 | ||
| 132 | /// Advance the iterator. | |
| 133 | fn step(&mut self) -> Step<Self::Output>; | |
| 134 | ||
| 135 | /// Return current iteration count. | |
| 136 | fn iteration(&self) -> usize { | |
| 137 | self.state().iteration() | |
| 138 | } | |
| 139 | ||
| 140 | /// Return current iteration stats. | |
| 141 | fn state(&self) -> Self::State; | |
| 142 | ||
| 143 | /// Iterate the function `step`. It should accept one `state` ([`AlgIteratorState`] parameter, | |
| 144 | /// and return the output of `state.if_verbose`. | |
| 145 | /// Typically called through [`AlgIteratorFactory::iterate`]. | |
| 146 | #[inline] | |
| 147 | fn iterate(&mut self) { | |
| 148 | loop { | |
| 149 | if let Step::Terminated = self.step() { | |
| 150 | break | |
| 151 | } | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | /// Converts the `AlgIterator` into a plain [`Iterator`], discarding [`Step::Quiet`] results. | |
| 156 | /// (This is to avoid having to manually implement [`IntoIterator`] or [`Iterator`] | |
| 157 | /// for every single [`AlgIterator`].) | |
| 158 | fn downcast(self) -> AlgIteratorI<Self, V> { | |
| 159 | AlgIteratorI(self, PhantomData) | |
| 160 | } | |
| 161 | } | |
| 162 | ||
| 163 | /// Conversion of an `AlgIterator` into a plain [`Iterator`]. | |
| 164 | /// It discards [`Step::Quiet`] results. | |
| 165 | pub struct AlgIteratorI<A, V>(A, PhantomData<V>); | |
| 166 | ||
| 167 | impl<A, V> Iterator for AlgIteratorI<A, V> | |
| 168 | where A : AlgIterator<V> { | |
| 169 | type Item = A::Output; | |
| 170 | ||
| 171 | fn next(&mut self) -> Option<A::Output> { | |
| 172 | loop { | |
| 173 | match self.0.step() { | |
| 174 | Step::Result(v) => return Some(v), | |
| 175 | Step::Terminated => return None, | |
| 176 | Step::Quiet => continue, | |
| 177 | } | |
| 178 | } | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | /// A factory for producing an [`AlgIterator`]. To use one: | |
| 183 | /// | |
| 184 | /// ```rust | |
| 185 | /// use alg_tools::iterate::*; | |
| 186 | /// let mut iter = AlgIteratorOptions::default(); | |
| 187 | /// iter.iterate(|state|{ | |
| 188 | /// // perform iterations | |
| 189 | /// state.if_verbose(||{ | |
| 190 | /// // calculate and return function value or other displayed data v | |
| 191 | /// return 0 | |
| 192 | /// }) | |
| 193 | /// }) | |
| 194 | /// ``` | |
| 195 | pub trait AlgIteratorFactory<V> : Sized { | |
| 196 | type Iter<F> : AlgIterator<V, State = Self::State, Output = Self::Output> | |
| 197 | where F : FnMut(&Self::State) -> Step<V>; | |
| 198 | type State : AlgIteratorState; | |
| 199 | type Output; | |
| 200 | ||
| 201 | /// Prepare an [`AlgIterator`], consuming the factory. | |
| 202 | /// The function `step_fn` should accept a `state` ([`AlgIteratorState`] parameter, and return | |
| 203 | /// a [`Step`]. | |
| 204 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 205 | where F : FnMut(&Self::State) -> Step<V>; | |
| 206 | ||
| 207 | /// Iterate the function `step`. It should accept one `state` ([`AlgIteratorState`] parameter, | |
| 208 | /// and return the output of `state.if_verbose`. For usage instructions see the | |
| 209 | /// [module documentation][self]. | |
| 210 | /// | |
| 211 | /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator.iterate`]. | |
| 212 | #[inline] | |
| 213 | fn iterate<F>(self, mut step : F) -> () where F : FnMut(&Self::State) -> Option<V> { | |
| 214 | self.prepare(move |state| { | |
| 215 | match step(state) { | |
| 216 | None => Step::Quiet, | |
| 217 | Some(v) => Step::Result(v), | |
| 218 | } | |
| 219 | }).iterate() | |
| 220 | } | |
| 221 | ||
| 222 | /// Iterate the function `step`. It should accept a `state` ([`AlgIteratorState`] parameter, | |
| 223 | /// and a data parameter taking frmo `datasource`, and return the output of `state.if_verbose`. | |
| 224 | /// For usage instructions see the [module documentation][self]. | |
| 225 | #[inline] | |
| 226 | fn iterate_data<F, D, I>(self, mut datasource : I, mut step : F) | |
| 227 | where F : FnMut(&Self::State, D) -> Option<V>, | |
| 228 | I : Iterator<Item = D> { | |
| 229 | self.prepare(move |state| { | |
| 230 | match datasource.next() { | |
| 231 | None => Step::Terminated, | |
| 232 | Some(d) => match step(state, d) { | |
| 233 | None => Step::Quiet, | |
| 234 | Some(v) => Step::Result(v), | |
| 235 | } | |
| 236 | } | |
| 237 | }).iterate() | |
| 238 | } | |
| 239 | ||
| 240 | // fn make_iterate<'own>(self) | |
| 241 | // -> Box<dyn (FnMut(dyn FnMut(&Self::State) -> Option<V>) -> ()) + 'own> { | |
| 242 | // Box::new(move |step| self.iterate(step)) | |
| 243 | // } | |
| 244 | ||
| 245 | // fn make_iterate_data<'own, D, I>(self, datasource : I) | |
| 246 | // -> Box<dyn (FnMut(dyn FnMut(&Self::State, D) -> Option<V>) -> ()) + 'own> | |
| 247 | // where I : Iterator<Item = D> + 'own { | |
| 248 | // Box::new(move |step| self.iterate_data(step, datasource)) | |
| 249 | // } | |
| 250 | ||
| 251 | /// Add logging to the iterator produced by the factory. | |
| 252 | fn into_log<'log>(self, logger : &'log mut Logger<Self::Output>) | |
| 253 | -> LoggingIteratorFactory<'log, Self::Output, Self> | |
| 254 | where Self : Sized { | |
| 255 | LoggingIteratorFactory { | |
| 256 | base_options : self, | |
| 257 | logger, | |
| 258 | } | |
| 259 | } | |
| 260 | ||
| 261 | /// Map the output of the iterator produced by the factory. | |
| 262 | fn mapped<U, G>(self, map : G) | |
| 263 | -> MappingIteratorFactory<G, Self> | |
| 264 | where Self : Sized, | |
| 265 | G : Fn(usize, Self::Output) -> U { | |
| 266 | MappingIteratorFactory { | |
| 267 | base_options : self, | |
| 268 | map | |
| 269 | } | |
| 270 | } | |
| 271 | ||
| 272 | /// Adds iteration number to the output. Typically followed by [`Self::into_log`]. | |
| 273 | fn with_iteration_number(self) | |
| 274 | -> MappingIteratorFactory<fn(usize, Self::Output) -> LogItem<Self::Output>, Self> | |
| 275 | where Self : Sized { | |
| 276 | self.mapped(make_logitem) | |
| 277 | } | |
| 278 | ||
| 279 | /// Add timing to the iterator produced by the factory. | |
| 280 | fn timed(self) -> TimingIteratorFactory<Self> | |
| 281 | where Self : Sized { | |
| 282 | TimingIteratorFactory(self) | |
| 283 | } | |
| 284 | ||
| 285 | /// Add value stopping threshold to the iterator produce by the factory | |
| 286 | fn stop_target(self, target : Self::Output) -> ValueIteratorFactory<Self::Output, Self> | |
| 287 | where Self : Sized, | |
| 288 | Self::Output : Num { | |
| 289 | ValueIteratorFactory { base_options : self, target : target } | |
| 290 | } | |
| 291 | ||
| 292 | /// Add stall stopping to the iterator produce by the factory | |
| 293 | fn stop_stall(self, stall : Self::Output) -> StallIteratorFactory<Self::Output, Self> | |
| 294 | where Self : Sized, | |
| 295 | Self::Output : Num { | |
| 296 | StallIteratorFactory { base_options : self, stall : stall } | |
| 297 | } | |
| 298 | ||
| 299 | /// Is the iterator quiet, i.e., on-verbose? | |
| 300 | fn is_quiet(&self) -> bool { false } | |
| 301 | } | |
| 302 | ||
| 303 | /// Options for [`BasicAlgIteratorFactory`]. Use as: | |
| 304 | /// ``` | |
| 305 | /// # use alg_tools::iterate::*; | |
| 306 | /// let iter = AlgIteratorOptions{ max_iter : 10000, .. Default::default() }; | |
| 307 | /// ``` | |
| 308 | #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)] | |
| 309 | pub struct AlgIteratorOptions { | |
| 310 | /// Maximum number of iterations | |
| 311 | pub max_iter : usize, | |
| 312 | /// Number of iterations between verbose iterations that display state. | |
| 313 | pub verbose_iter : Verbose, | |
| 314 | /// Whether verbose iterations are displayed, or just passed onwards to a containing | |
| 315 | /// `AlgIterator`. | |
| 316 | pub quiet : bool, | |
| 317 | } | |
| 318 | ||
| 319 | #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)] | |
| 320 | pub enum Verbose { | |
| 321 | /// Be verbose every $n$ iterations. | |
| 322 | Every(usize), | |
| 323 | /// Be verbose every $n$ iterations and initial $m$ iterations. | |
| 324 | EveryAndInitial{ every : usize, initial : usize }, | |
| 325 | /// Be verbose if iteration number $n$ divides by $b^{\text{floor}(\log_b(n))}$, where | |
| 326 | /// $b$ is indicated logarithmic base. So, with $b=10$, | |
| 327 | /// * every iteration for first 10 iterations, | |
| 328 | /// * every 10 iterations from there on until 100 iterations, | |
| 329 | /// * every 100 iteartinos frmo there on until 1000 iterations, etc. | |
| 330 | Logarithmic(usize), | |
| 331 | } | |
| 332 | ||
| 333 | impl Verbose { | |
| 334 | /// Indicates whether given iteration number is verbose | |
| 335 | pub fn is_verbose(&self, iter : usize) -> bool { | |
| 336 | match self { | |
| 337 | &Verbose::Every(every) => { | |
| 338 | every != 0 && iter % every == 0 | |
| 339 | }, | |
| 340 | &Verbose::EveryAndInitial{ every, initial } => { | |
| 341 | iter <= initial || (every != 0 && iter % every == 0) | |
| 342 | }, | |
| 343 | &Verbose::Logarithmic(base) => { | |
| 344 | let every = 10usize.pow((iter as float).log(base as float).floor() as u32); | |
| 345 | iter % every == 0 | |
| 346 | } | |
| 347 | } | |
| 348 | } | |
| 349 | } | |
| 350 | ||
| 351 | impl Default for AlgIteratorOptions { | |
| 352 | fn default() -> AlgIteratorOptions { | |
| 353 | AlgIteratorOptions{ | |
| 354 | max_iter : 1000, | |
| 355 | verbose_iter : Verbose::EveryAndInitial { every : 100, initial : 10 }, | |
| 356 | quiet : false | |
| 357 | } | |
| 358 | } | |
| 359 | } | |
| 360 | ||
| 361 | /// State of a `BasicAlgIterator` | |
| 362 | #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)] | |
| 363 | pub struct BasicState { | |
| 364 | iter : usize, | |
| 365 | verbose : bool, | |
| 366 | calc : bool, | |
| 367 | } | |
| 368 | ||
| 369 | /// [`AlgIteratorFactory`] for [`BasicAlgIterator`] | |
| 370 | #[derive(Clone,Debug)] | |
| 371 | pub struct BasicAlgIteratorFactory<V> { | |
| 372 | options : AlgIteratorOptions, | |
| 373 | phantoms : PhantomData<V>, | |
| 374 | } | |
| 375 | ||
| 376 | /// The simplest [`AlgIterator`], reated by [`BasicAlgIteratorFactory`] | |
| 377 | #[derive(Clone,Debug)] | |
| 378 | pub struct BasicAlgIterator<F> { | |
| 379 | options : AlgIteratorOptions, | |
| 380 | iter : usize, | |
| 381 | step_fn : F, | |
| 382 | } | |
| 383 | ||
| 384 | impl AlgIteratorOptions { | |
| 385 | /// [`AlgIteratorOptions`] is directly a factory for [`BasicAlgIterator`], | |
| 386 | /// however, due to type inference issues, it may become convenient to instantiate | |
| 387 | /// it to a specific return type for the inner step function. This method does that. | |
| 388 | pub fn instantiate<V>(&self) -> BasicAlgIteratorFactory<V> { | |
| 389 | BasicAlgIteratorFactory { | |
| 390 | options : self.clone(), | |
| 391 | phantoms : PhantomData | |
| 392 | } | |
| 393 | } | |
| 394 | } | |
| 395 | ||
| 396 | impl<V> AlgIteratorFactory<V> for AlgIteratorOptions | |
| 397 | where V : LogRepr { | |
| 398 | type State = BasicState; | |
| 399 | type Iter<F> = BasicAlgIterator<F> where F : FnMut(&Self::State) -> Step<V>; | |
| 400 | type Output = V; | |
| 401 | ||
| 402 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 403 | where F : FnMut(&Self::State) -> Step<V> { | |
| 404 | BasicAlgIterator{ options : self, iter : 0, step_fn } | |
| 405 | } | |
| 406 | ||
| 407 | #[inline] | |
| 408 | fn is_quiet(&self) -> bool { | |
| 409 | self.quiet | |
| 410 | } | |
| 411 | } | |
| 412 | ||
| 413 | impl<V> AlgIteratorFactory<V> for BasicAlgIteratorFactory<V> | |
| 414 | where V : LogRepr { | |
| 415 | type State = BasicState; | |
| 416 | type Iter<F> = BasicAlgIterator<F> where F : FnMut(&Self::State) -> Step<V>; | |
| 417 | type Output = V; | |
| 418 | ||
| 419 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 420 | where F : FnMut(&Self::State) -> Step<V> { | |
| 421 | BasicAlgIterator{ options : self.options, iter : 0, step_fn } | |
| 422 | } | |
| 423 | ||
| 424 | #[inline] | |
| 425 | fn is_quiet(&self) -> bool { | |
| 426 | self.options.quiet | |
| 427 | } | |
| 428 | } | |
| 429 | ||
| 430 | impl<V, F> AlgIterator<V> for BasicAlgIterator<F> | |
| 431 | where V : LogRepr, | |
| 432 | F : FnMut(&BasicState) -> Step<V> { | |
| 433 | type State = BasicState; | |
| 434 | type Output = V; | |
| 435 | ||
| 436 | #[inline] | |
| 437 | fn step(&mut self) -> Step<V> { | |
| 438 | if self.iter >= self.options.max_iter { | |
| 439 | Step::Terminated | |
| 440 | } else { | |
| 441 | self.iter += 1; | |
| 442 | let state = self.state(); | |
| 443 | let res = (self.step_fn)(&state); | |
| 444 | if let Step::Result(ref val) = res { | |
| 445 | if state.verbose && !self.options.quiet { | |
| 446 | println!("{}{}/{} {}{}", "".dimmed(), | |
| 447 | state.iter, | |
| 448 | self.options.max_iter, | |
| 449 | val.logrepr(), | |
| 450 | "".clear()); | |
| 451 | } | |
| 452 | } | |
| 453 | res | |
| 454 | } | |
| 455 | } | |
| 456 | ||
| 457 | #[inline] | |
| 458 | fn iteration(&self) -> usize { | |
| 459 | self.iter | |
| 460 | } | |
| 461 | ||
| 462 | #[inline] | |
| 463 | fn state(&self) -> BasicState { | |
| 464 | let iter = self.iter; | |
| 465 | let verbose = self.options.verbose_iter.is_verbose(iter); | |
| 466 | BasicState{ | |
| 467 | iter : iter, | |
| 468 | verbose : verbose, | |
| 469 | calc : verbose, | |
| 470 | } | |
| 471 | } | |
| 472 | } | |
| 473 | ||
| 474 | impl AlgIteratorState for BasicState { | |
| 475 | #[inline] | |
| 476 | fn if_verbose<A>(&self, mut calc_objective : impl FnMut() -> A) -> Option<A> { | |
| 477 | if self.calc { | |
| 478 | Some(calc_objective()) | |
| 479 | } else { | |
| 480 | None | |
| 481 | } | |
| 482 | } | |
| 483 | ||
| 484 | #[inline] | |
| 485 | fn iteration(&self) -> usize { | |
| 486 | return self.iter; | |
| 487 | } | |
| 488 | } | |
| 489 | ||
| 490 | // | |
| 491 | // Stall detecting iteration function. | |
| 492 | // | |
| 493 | ||
| 494 | /// An [`AlgIteratorFactory`] for an [`AlgIterator`] that detects “stall” | |
| 495 | /// $(v_{k+n}-v_k)/v_k ≤ θ$, where $n$ the distance between [`Step::Result`] iterations. | |
| 496 | #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)] | |
| 497 | pub struct StallIteratorFactory<U : Num, BaseFactory> { | |
| 498 | /// Basic options | |
| 499 | pub base_options : BaseFactory, | |
| 500 | /// Stalling threshold $θ$. | |
| 501 | pub stall : U, | |
| 502 | } | |
| 503 | ||
| 504 | /// Iterator produced by [`StallIteratorFactory`]. | |
| 505 | pub struct StallIterator<U : Num, BaseIterator> { | |
| 506 | base_iterator : BaseIterator, | |
| 507 | stall : U, | |
| 508 | previous_value : Option<U>, | |
| 509 | } | |
| 510 | ||
| 511 | impl<V, U, BaseFactory> AlgIteratorFactory<V> | |
| 512 | for StallIteratorFactory<U, BaseFactory> | |
| 513 | where BaseFactory : AlgIteratorFactory<V, Output=U>, | |
| 514 | U : SignedNum + PartialOrd { | |
| 515 | type Iter<F> = StallIterator<U, BaseFactory::Iter<F>> | |
| 516 | where F : FnMut(&Self::State) -> Step<V>; | |
| 517 | type State = BaseFactory::State; | |
| 518 | type Output = BaseFactory::Output; | |
| 519 | ||
| 520 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 521 | where F : FnMut(&Self::State) -> Step<V> { | |
| 522 | StallIterator { | |
| 523 | base_iterator : self.base_options.prepare(step_fn), | |
| 524 | stall : self.stall, | |
| 525 | previous_value : None, | |
| 526 | } | |
| 527 | } | |
| 528 | ||
| 529 | fn is_quiet(&self) -> bool { | |
| 530 | self.base_options.is_quiet() | |
| 531 | } | |
| 532 | } | |
| 533 | ||
| 534 | impl<V, U, BaseIterator> AlgIterator<V> | |
| 535 | for StallIterator<U, BaseIterator> | |
| 536 | where BaseIterator : AlgIterator<V, Output=U>, | |
| 537 | U : SignedNum + PartialOrd { | |
| 538 | type State = BaseIterator::State; | |
| 539 | type Output = BaseIterator::Output; | |
| 540 | ||
| 541 | #[inline] | |
| 542 | fn step(&mut self) -> Step<U> { | |
| 543 | match self.base_iterator.step() { | |
| 544 | Step::Result(nv) => { | |
| 545 | let previous_v = self.previous_value; | |
| 546 | self.previous_value = Some(nv); | |
| 547 | match previous_v { | |
| 548 | Some(pv) if (nv - pv).abs() <= self.stall * pv.abs() => Step::Terminated, | |
| 549 | _ => Step::Result(nv), | |
| 550 | } | |
| 551 | }, | |
| 552 | val => val, | |
| 553 | } | |
| 554 | } | |
| 555 | ||
| 556 | #[inline] | |
| 557 | fn iteration(&self) -> usize { | |
| 558 | self.base_iterator.iteration() | |
| 559 | } | |
| 560 | ||
| 561 | #[inline] | |
| 562 | fn state(&self) -> Self::State { | |
| 563 | self.base_iterator.state() | |
| 564 | } | |
| 565 | } | |
| 566 | ||
| 567 | /// An [`AlgIteratorFactory`] for an [`AlgIterator`] that detect whether step function | |
| 568 | /// return value is less than `target`, and terminates if it is. | |
| 569 | #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)] | |
| 570 | pub struct ValueIteratorFactory<U : Num, BaseFactory> { | |
| 571 | /// Basic options | |
| 572 | pub base_options : BaseFactory, | |
| 573 | /// Stalling threshold $θ$. | |
| 574 | pub target : U, | |
| 575 | } | |
| 576 | ||
| 577 | /// Iterator produced by [`ValueIteratorFactory`]. | |
| 578 | pub struct ValueIterator<U : Num, BaseIterator> { | |
| 579 | base_iterator : BaseIterator, | |
| 580 | target : U, | |
| 581 | } | |
| 582 | ||
| 583 | impl<V, U, BaseFactory> AlgIteratorFactory<V> | |
| 584 | for ValueIteratorFactory<U, BaseFactory> | |
| 585 | where BaseFactory : AlgIteratorFactory<V, Output=U>, | |
| 586 | U : SignedNum + PartialOrd { | |
| 587 | type Iter<F> = ValueIterator<U, BaseFactory::Iter<F>> | |
| 588 | where F : FnMut(&Self::State) -> Step<V>; | |
| 589 | type State = BaseFactory::State; | |
| 590 | type Output = BaseFactory::Output; | |
| 591 | ||
| 592 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 593 | where F : FnMut(&Self::State) -> Step<V> { | |
| 594 | ValueIterator { | |
| 595 | base_iterator : self.base_options.prepare(step_fn), | |
| 596 | target : self.target | |
| 597 | } | |
| 598 | } | |
| 599 | ||
| 600 | fn is_quiet(&self) -> bool { | |
| 601 | self.base_options.is_quiet() | |
| 602 | } | |
| 603 | } | |
| 604 | ||
| 605 | impl<V, U, BaseIterator> AlgIterator<V> | |
| 606 | for ValueIterator<U, BaseIterator> | |
| 607 | where BaseIterator : AlgIterator<V, Output=U>, | |
| 608 | U : SignedNum + PartialOrd { | |
| 609 | type State = BaseIterator::State; | |
| 610 | type Output = BaseIterator::Output; | |
| 611 | ||
| 612 | #[inline] | |
| 613 | fn step(&mut self) -> Step<U> { | |
| 614 | match self.base_iterator.step() { | |
| 615 | Step::Result(v) => { | |
| 616 | if v <= self.target { | |
| 617 | Step::Terminated | |
| 618 | } else { | |
| 619 | Step::Result(v) | |
| 620 | } | |
| 621 | }, | |
| 622 | val => val, | |
| 623 | } | |
| 624 | } | |
| 625 | ||
| 626 | #[inline] | |
| 627 | fn iteration(&self) -> usize { | |
| 628 | self.base_iterator.iteration() | |
| 629 | } | |
| 630 | ||
| 631 | #[inline] | |
| 632 | fn state(&self) -> Self::State { | |
| 633 | self.base_iterator.state() | |
| 634 | } | |
| 635 | } | |
| 636 | ||
| 637 | // | |
| 638 | // Logging iterator | |
| 639 | // | |
| 640 | ||
| 641 | /// [`AlgIteratorFactory`] for a logging [`AlgIterator`]. | |
| 642 | /// The `Output` of the corresponding [`LoggingIterator`] is `()`: | |
| 643 | /// The data is only inserted into the log, and not passed onwards. | |
| 644 | /// Use as: | |
| 645 | /// ```rust | |
| 646 | /// use alg_tools::iterate::*; | |
| 647 | /// use alg_tools::logger::*; | |
| 648 | /// let iter = AlgIteratorOptions::default(); | |
| 649 | /// let mut log = Logger::new(); | |
| 650 | /// iter.into_log(&mut log).iterate(|state|{ | |
| 651 | /// // perform iterations | |
| 652 | /// state.if_verbose(||{ | |
| 653 | /// // calculate and return function value or other displayed data v | |
| 654 | /// return 0 | |
| 655 | /// }) | |
| 656 | /// }) | |
| 657 | /// ``` | |
| 658 | #[derive(Debug)] | |
| 659 | pub struct LoggingIteratorFactory<'log, U, BaseFactory> { | |
| 660 | pub base_options : BaseFactory, | |
| 661 | pub logger : &'log mut Logger<U>, | |
| 662 | } | |
| 663 | ||
| 664 | /// Iterator produced by `LoggingIteratorFactory`. | |
| 665 | pub struct LoggingIterator<'log, U, BaseIterator> { | |
| 666 | base_iterator : BaseIterator, | |
| 667 | logger : &'log mut Logger<U>, | |
| 668 | } | |
| 669 | ||
| 670 | ||
| 671 | impl<'log, V, BaseFactory> AlgIteratorFactory<V> | |
| 672 | for LoggingIteratorFactory<'log, BaseFactory::Output, BaseFactory> | |
| 673 | where BaseFactory : AlgIteratorFactory<V>, | |
| 674 | BaseFactory::Output : 'log { | |
| 675 | type State = BaseFactory::State; | |
| 676 | type Iter<F> = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter<F>> | |
| 677 | where F : FnMut(&Self::State) -> Step<V>; | |
| 678 | type Output = (); | |
| 679 | ||
| 680 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 681 | where F : FnMut(&Self::State) -> Step<V> { | |
| 682 | LoggingIterator { | |
| 683 | base_iterator : self.base_options.prepare(step_fn), | |
| 684 | logger : self.logger, | |
| 685 | } | |
| 686 | } | |
| 687 | ||
| 688 | #[inline] | |
| 689 | fn is_quiet(&self) -> bool { | |
| 690 | self.base_options.is_quiet() | |
| 691 | } | |
| 692 | } | |
| 693 | ||
| 694 | impl<'log, V, BaseIterator> AlgIterator<V> | |
| 695 | for LoggingIterator<'log, BaseIterator::Output, BaseIterator> | |
| 696 | where BaseIterator : AlgIterator<V>, | |
| 697 | BaseIterator::Output : 'log { | |
| 698 | type State = BaseIterator::State; | |
| 699 | type Output = (); | |
| 700 | ||
| 701 | #[inline] | |
| 702 | fn step(&mut self) -> Step<Self::Output> { | |
| 703 | match self.base_iterator.step() { | |
| 704 | Step::Result(v) => { | |
| 705 | self.logger.log(v); | |
| 706 | Step::Quiet | |
| 707 | }, | |
| 708 | Step::Quiet => Step::Quiet, | |
| 709 | Step::Terminated => Step::Terminated, | |
| 710 | } | |
| 711 | } | |
| 712 | ||
| 713 | #[inline] | |
| 714 | fn iteration(&self) -> usize { | |
| 715 | self.base_iterator.iteration() | |
| 716 | } | |
| 717 | ||
| 718 | #[inline] | |
| 719 | fn state(&self) -> Self::State { | |
| 720 | self.base_iterator.state() | |
| 721 | } | |
| 722 | } | |
| 723 | ||
| 724 | /// This [`AlgIteratorFactory`] allows output mapping. | |
| 725 | #[derive(Debug)] | |
| 726 | pub struct MappingIteratorFactory<G, BaseFactory> { | |
| 727 | pub base_options : BaseFactory, | |
| 728 | pub map : G, | |
| 729 | } | |
| 730 | ||
| 731 | /// Iterator produced by `MappingIteratorFactory`. | |
| 732 | pub struct MappingIterator<G, BaseIterator> { | |
| 733 | base_iterator : BaseIterator, | |
| 734 | map : G, | |
| 735 | } | |
| 736 | ||
| 737 | ||
| 738 | impl<V, U, G, BaseFactory> AlgIteratorFactory<V> | |
| 739 | for MappingIteratorFactory<G, BaseFactory> | |
| 740 | where BaseFactory : AlgIteratorFactory<V>, | |
| 741 | G : Fn(usize, BaseFactory::Output) -> U { | |
| 742 | type State = BaseFactory::State; | |
| 743 | type Iter<F> = MappingIterator<G, BaseFactory::Iter<F>> | |
| 744 | where F : FnMut(&Self::State) -> Step<V>; | |
| 745 | type Output = U; | |
| 746 | ||
| 747 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 748 | where F : FnMut(&Self::State) -> Step<V> { | |
| 749 | MappingIterator { | |
| 750 | base_iterator : self.base_options.prepare(step_fn), | |
| 751 | map : self.map | |
| 752 | } | |
| 753 | } | |
| 754 | ||
| 755 | #[inline] | |
| 756 | fn is_quiet(&self) -> bool { | |
| 757 | self.base_options.is_quiet() | |
| 758 | } | |
| 759 | } | |
| 760 | ||
| 761 | impl<V, U, G, BaseIterator> AlgIterator<V> | |
| 762 | for MappingIterator<G, BaseIterator> | |
| 763 | where BaseIterator : AlgIterator<V>, | |
| 764 | G : Fn(usize, BaseIterator::Output) -> U { | |
| 765 | type State = BaseIterator::State; | |
| 766 | type Output = U; | |
| 767 | ||
| 768 | #[inline] | |
| 769 | fn step(&mut self) -> Step<Self::Output> { | |
| 770 | match self.base_iterator.step() { | |
| 771 | Step::Result(v) => Step::Result((self.map)(self.iteration(), v)), | |
| 772 | Step::Quiet => Step::Quiet, | |
| 773 | Step::Terminated => Step::Terminated, | |
| 774 | } | |
| 775 | } | |
| 776 | ||
| 777 | #[inline] | |
| 778 | fn iteration(&self) -> usize { | |
| 779 | self.base_iterator.iteration() | |
| 780 | } | |
| 781 | ||
| 782 | #[inline] | |
| 783 | fn state(&self) -> Self::State { | |
| 784 | self.base_iterator.state() | |
| 785 | } | |
| 786 | } | |
| 787 | ||
| 788 | // | |
| 789 | // Timing iterator | |
| 790 | // | |
| 791 | ||
| 792 | /// An [`AlgIteratorFactory`] for an [`AlgIterator`] that adds CPU time spent to verbose events. | |
| 793 | #[derive(Debug)] | |
| 794 | pub struct TimingIteratorFactory<BaseFactory>(pub BaseFactory); | |
| 795 | ||
| 796 | /// Iterator produced by [`TimingIteratorFactory`] | |
| 797 | #[derive(Debug)] | |
| 798 | pub struct TimingIterator<BaseIterator> { | |
| 799 | base_iterator : BaseIterator, | |
| 800 | start_time : ProcessTime, | |
| 801 | } | |
| 802 | ||
| 803 | /// Data `U` with production time attached | |
| 804 | #[derive(Copy, Clone, Debug, Serialize)] | |
| 805 | pub struct Timed<U> { | |
| 806 | pub cpu_time : Duration, | |
| 807 | //#[serde(flatten)] | |
| 808 | pub data : U | |
| 809 | } | |
| 810 | ||
| 811 | impl<T> LogRepr for Timed<T> where T : LogRepr { | |
| 812 | fn logrepr(&self) -> ColoredString { | |
| 813 | format!("[{:.3}s] {}", self.cpu_time.as_secs_f64(), self.data.logrepr()).as_str().into() | |
| 814 | } | |
| 815 | } | |
| 816 | ||
| 817 | ||
| 818 | impl<V, BaseFactory> AlgIteratorFactory<V> | |
| 819 | for TimingIteratorFactory<BaseFactory> | |
| 820 | where BaseFactory : AlgIteratorFactory<V> { | |
| 821 | type State = BaseFactory::State; | |
| 822 | type Iter<F> = TimingIterator<BaseFactory::Iter<F>> | |
| 823 | where F : FnMut(&Self::State) -> Step<V>; | |
| 824 | type Output = Timed<BaseFactory::Output>; | |
| 825 | ||
| 826 | fn prepare<F>(self, step_fn : F) -> Self::Iter<F> | |
| 827 | where F : FnMut(&Self::State) -> Step<V> { | |
| 828 | TimingIterator { | |
| 829 | base_iterator : self.0.prepare(step_fn), | |
| 830 | start_time : ProcessTime::now() | |
| 831 | } | |
| 832 | } | |
| 833 | ||
| 834 | #[inline] | |
| 835 | fn is_quiet(&self) -> bool { | |
| 836 | self.0.is_quiet() | |
| 837 | } | |
| 838 | } | |
| 839 | ||
| 840 | impl<V, BaseIterator> AlgIterator<V> | |
| 841 | for TimingIterator<BaseIterator> | |
| 842 | where BaseIterator : AlgIterator<V> { | |
| 843 | type State = BaseIterator::State; | |
| 844 | type Output = Timed<BaseIterator::Output>; | |
| 845 | ||
| 846 | #[inline] | |
| 847 | fn step(&mut self) -> Step<Self::Output> { | |
| 848 | match self.base_iterator.step() { | |
| 849 | Step::Result(data) => { | |
| 850 | Step::Result(Timed{ | |
| 851 | cpu_time : self.start_time.elapsed(), | |
| 852 | data | |
| 853 | }) | |
| 854 | }, | |
| 855 | Step::Quiet => Step::Quiet, | |
| 856 | Step::Terminated => Step::Terminated, | |
| 857 | } | |
| 858 | } | |
| 859 | ||
| 860 | #[inline] | |
| 861 | fn iteration(&self) -> usize { | |
| 862 | self.base_iterator.iteration() | |
| 863 | } | |
| 864 | ||
| 865 | #[inline] | |
| 866 | fn state(&self) -> Self::State { | |
| 867 | self.base_iterator.state() | |
| 868 | } | |
| 869 | } | |
| 870 | ||
| 871 | // | |
| 872 | // Tests | |
| 873 | // | |
| 874 | ||
| 875 | #[cfg(test)] | |
| 876 | mod tests { | |
| 877 | use super::*; | |
| 878 | use crate::logger::Logger; | |
| 879 | #[test] | |
| 880 | fn iteration() { | |
| 881 | let options = AlgIteratorOptions{ | |
| 882 | max_iter : 10, | |
| 883 | verbose_iter : Verbose::Every(3), | |
| 884 | .. Default::default() | |
| 885 | }; | |
| 886 | ||
| 887 | { | |
| 888 | let mut start = 1 as int; | |
| 889 | options.iterate(|state| { | |
| 890 | start = start * 2; | |
| 891 | state.if_verbose(|| start) | |
| 892 | }); | |
| 893 | assert_eq!(start, (2 as int).pow(10)); | |
| 894 | } | |
| 895 | ||
| 896 | { | |
| 897 | let mut start = 1 as int; | |
| 898 | let mut log = Logger::new(); | |
| 899 | let factory = options.instantiate() | |
| 900 | .with_iteration_number() | |
| 901 | .into_log(&mut log); | |
| 902 | factory.iterate(|state| { | |
| 903 | start = start * 2; | |
| 904 | state.if_verbose(|| start) | |
| 905 | }); | |
| 906 | assert_eq!(start, (2 as int).pow(10)); | |
| 907 | assert_eq!(log.data() | |
| 908 | .iter() | |
| 909 | .map(|LogItem{ data : v, iter : _ }| v.clone()) | |
| 910 | .collect::<Vec<int>>(), | |
|
1
df3901ec2f5d
Fix some unit tests after fundamental changes that made them invalid
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
911 | (1..10).map(|i| (2 as int).pow(i)) |
|
df3901ec2f5d
Fix some unit tests after fundamental changes that made them invalid
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
912 | .skip(2) |
|
df3901ec2f5d
Fix some unit tests after fundamental changes that made them invalid
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
913 | .step_by(3) |
| 0 | 914 | .collect::<Vec<int>>()) |
| 915 | } | |
| 916 | } | |
| 917 | } |