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 | } |