Skip to main content

iter_solver/
lib.rs

1#![doc = include_str!("../README.md")]
2
3extern crate self as iter_solver;
4
5pub use iter_state_derive::IterState;
6
7use std::{marker::PhantomData, mem, time::{Duration, Instant}};
8
9use crate::error::{ReachMaxIteration, TimeOut};
10
11pub mod error;
12mod utils;
13mod build_test;
14
15/// Intermediate state of an iterative algorithm.
16///
17/// For many iterative algorithms, the intermediate state often requires a more complex structure to describe compared to the initial iteration value and the solution.
18///
19/// In practical iterative algorithms, it is often necessary to perform some simple computations or obtain algorithm metadata after obtaining the solution.
20///
21/// Therefore, IterState allows you to customize the intermediate state and separate the abstractions of value and solution.
22///
23/// If you expect the simplest behavior, this crate has already implemented `IterState` for basic data types `i*`, `u*`, and `f*`, where their associated types `Value` and `Solution` are themselves.
24/// 
25/// Additionally, you can use the macro `#[derive(IterState)]` to derive `IterState`. In this case, the associated types `Value` and `Solution` will be set to `Self`. Moreover, similar to the implementation for basic data types, the `init_from_value` method will simply return the parameter directly, and the `to_sol` method will directly clone `self` and return it.
26pub trait IterState: Sized {
27    /// Type representing the value during iteration (e.g., intermediate computation results).
28    type Value;
29
30    /// Type representing the final solution.  
31    type Solution;  
32
33    /// Initializes the state from an initial value.  
34    fn init_from_value(initial_point: Self::Value) -> Self;  
35
36    /// Converts the current state into the solution.  
37    fn to_sol(&self) -> Self::Solution;  
38}
39
40macro_rules! iterstate_impl {
41    ($($ty:ty),*) => {
42        $(
43            impl IterState for $ty {
44                type Value = $ty;
45                type Solution = $ty;
46                fn init_from_value(initial_point: Self::Value) -> Self {
47                    initial_point
48                }
49                fn to_sol(&self) -> Self::Solution {
50                    *self
51                }
52            }
53        )*
54    };
55}
56
57iterstate_impl!(
58    u8,u16,u32,u64,u128,usize,
59    i8,i16,i32,i64,i128,isize,
60    f32, f64
61);
62
63
64/// Solver type.
65/// 
66/// Note: This type does not provide specific algorithms but allows you to customize iteration methods and stopping conditions.
67/// 
68/// The `Problem` generic parameter has no constraints, allowing you to define any kind of interface in the Problem generic for use by iteration methods and stopping conditions.
69#[derive(Debug)]
70pub struct Solver<State, Problem, IterFn, TermFn>
71where
72State: IterState,
73IterFn: Fn(&State, &Problem) -> State,
74TermFn: Fn(&State, &Problem) -> bool,
75{
76    /// Intermediate state of the solver (uninitialized at the start)
77    state: PhantomData<State>,
78    /// Placeholder for the problem type (no runtime storage)
79    problem: PhantomData<Problem>,
80    /// Function defining the iteration logic
81    iter_fn: IterFn,
82    /// Function defining the termination condition
83    term_cond: TermFn,
84}
85impl<State, Problem, IterFn, TermFn> Solver<State, Problem, IterFn, TermFn>
86where 
87    State: IterState,
88    IterFn: Fn(&State, &Problem) -> State,
89    TermFn: Fn(&State, &Problem) -> bool
90{
91    /// Creates a Solver instance with the specified methods.
92    ///
93    /// Parameter `iter_fn` defines the state iteration rule for the Solver.
94    ///
95    /// Parameter `term_cond` specifies that the Solver stops iterating if and only if
96    /// the condition is met: `term_cond` returns `true` in the current state.
97    pub fn new(
98        iter_fn: IterFn,
99        term_cond: TermFn
100    ) -> Self {
101        Self { 
102            state: PhantomData::<State>, 
103            problem: PhantomData::<Problem>, 
104            iter_fn: iter_fn, 
105            term_cond: term_cond 
106        }
107    }
108
109    /// Solves the problem by executing iterative logic with the given initial value and specific problem.
110    ///
111    /// # Note
112    /// If the algorithm defined by the solver contains logical errors, 
113    /// the solve function may enter an infinite loop. 
114    /// To avoid this, try [`Solver::solve_with_max_iteration`] or [`Solver::solve_with_timeout`].
115    pub fn solve(&self, initial_point: State::Value, problem: &Problem) -> State::Solution {
116        // init state
117        let initial_state = State::init_from_value(initial_point);
118        let mut state = initial_state;
119        
120        // do iter
121        loop {
122            //let state = unsafe { self.state.assume_init_mut() };
123
124            state = (self.iter_fn)(&state, problem);
125
126            // check termination cond
127            if (self.term_cond)(&state, problem) {
128                break;
129            }
130        }
131        
132        let final_state = state;
133
134        let sol = final_state.to_sol();
135
136        //unsafe { self.state.assume_init_drop(); }
137
138        sol
139    }
140
141
142    /// A solution method with a maximum iteration limit.
143    /// If the termination condition is met before reaching the maximum number of iterations, it returns an [`Ok`] value with the type [`IterState::Solution`].
144    /// Otherwise, it returns an [`Err`] with [`error::ReachMaxIteration`].
145    /// 
146    /// # Example
147    /// ```
148    /// use iter_solver::Solver;
149    /// 
150    /// // define a never stop solver
151    /// let loop_solver = Solver::new(|_state: &f64, _: &()| {*_state}, |_: &f64, _: &()| {false});
152    /// let try_solve = loop_solver.solve_with_max_iteration(0.0, &(), 10);
153    /// 
154    /// assert!(try_solve.is_err());
155    /// ```
156    pub fn solve_with_max_iteration(
157        &self,
158        initial_point: State::Value,
159        problem: &Problem,
160        max_iteration: u64
161    ) -> Result<State::Solution, error::ReachMaxIteration<State>> {
162        let mut reach_max = true;
163
164        // init state
165        let initial_state = State::init_from_value(initial_point);
166        let mut state = initial_state;        
167        
168        for _iteration in 0..max_iteration {
169            //state = unsafe { self.state.assume_init_mut() };
170
171            state = (self.iter_fn)(&state, problem);
172
173            // check termination cond
174            if (self.term_cond)(&state, problem) {
175                reach_max = false;
176                break;
177            }
178        }
179
180        let ret_res = if !reach_max {
181            let final_state = state;
182            let sol = final_state.to_sol();
183            //unsafe { self.state.assume_init_drop(); }
184            Ok(sol)
185        } else {
186            let final_state = state;
187            Err(ReachMaxIteration{
188                max_iteration: max_iteration, 
189                final_state: final_state
190            })
191        };
192
193        ret_res
194    }
195
196    /// A solution method with a time limit.
197    /// If the termination condition is met before reaching the timeout duration elapses, it returns an [`Ok`] value with the type [`IterState::Solution`].
198    /// Otherwise, it returns an [`Err`] with [`error::TimeOut`].
199    /// 
200    /// # Example
201    /// ```
202    /// use iter_solver::Solver;
203    /// use std::time::Duration;
204    /// 
205    /// // define a never stop solver
206    /// let loop_solver = Solver::new(|_state: &f64, _: &()| {*_state}, |_: &f64, _: &()| {false});
207    /// let try_solve = loop_solver.solve_with_timeout(0.0, &(), Duration::from_secs(1));
208    /// 
209    /// assert!(try_solve.is_err());
210    /// ```
211    pub fn solve_with_timeout(
212        &self,
213        initial_point: State::Value,
214        problem: &Problem,
215        timeout: Duration        
216    ) -> Result<State::Solution, TimeOut<State>> {
217        let start_time = Instant::now();
218        let mut is_timeout = true;
219        
220        // init state
221        let initial_state = State::init_from_value(initial_point);
222        let mut state = initial_state;
223        
224        // do iter
225        loop {
226            //state = unsafe { self.state.assume_init_mut() };
227
228            state = (self.iter_fn)(&state, problem);
229
230            if start_time.elapsed() > timeout {
231                break;
232            }
233
234            // check termination cond
235            if (self.term_cond)(&state, problem) {
236                is_timeout = false;
237                break;
238            }
239        }
240
241        if !is_timeout {
242            let final_state = state;
243
244            let sol = final_state.to_sol();
245
246            //unsafe { self.state.assume_init_drop(); }
247
248            Ok(sol)                
249        } else {
250            let final_state = state; //unsafe { self.state.assume_init_read() };
251            Err(TimeOut { timeout: timeout, final_state: final_state })            
252        }
253
254    }
255
256
257
258    /// Consumes the `self` and generates an iterator that outputs the current State at each step based on the given initial value and problem.
259    /// 
260    /// Use this method when you want to manipulate the state at each step in detail.
261    /// 
262    /// # Example
263    /// ```ignore
264    /// use iter_solver::Solver;
265    /// 
266    /// let solver: Solver<f64, _, _, _> = Solver::new(iter_fn, term_cond);
267    /// let mut iteration = 1usize;
268    /// 
269    /// for state_float in solver.into_iter(2.0, &problem) {
270    ///     println!("the solution after {} iteration(s): {}", iteration, state_float);
271    ///     iteration += 1;
272    /// } 
273    /// ```
274    pub fn into_iter<'prob>(self, 
275        initial_point: State::Value, 
276        problem: &'prob Problem
277    ) -> SolverIterator<'prob, State, Problem, IterFn, TermFn> {
278        // init Slover
279        let initial_state = State::init_from_value(initial_point);
280
281        SolverIterator { 
282            problem: problem,
283            state: Some(initial_state),
284            iter_fn: self.iter_fn,
285            term_cond: self.term_cond, 
286            //need_term: false
287        }
288    }
289
290    /// Consumes `self` and returns a new [`Solver`] with the given new termination condition.
291    pub fn change_term_cond<NewTermCond>(self, new_cond: NewTermCond) -> Solver<State, Problem, IterFn, NewTermCond> 
292    where 
293        NewTermCond: Fn(&State, &Problem) -> bool
294    {
295        Solver { 
296            state: self.state, 
297            problem: self.problem, 
298            iter_fn: self.iter_fn,
299            term_cond: new_cond 
300        }
301    }
302}
303
304impl<State, Problem, IterFn, TermFn> Clone for Solver<State, Problem, IterFn, TermFn>
305where 
306   State: IterState,
307   IterFn: Fn(&State, &Problem) -> State + Clone,
308   TermFn: Fn(&State, &Problem) -> bool + Clone
309{
310    fn clone(&self) -> Self {
311        Self { state: PhantomData::<State>, problem: PhantomData::<Problem>, iter_fn: self.iter_fn.clone(), term_cond: self.term_cond.clone() }
312    }
313}
314
315/// Solver iterator, used to step through the solving process and output the state of each iteration.
316///
317/// This struct is created by consuming (taking ownership) a configured `Solver`,
318/// and implements the `Iterator` trait, allowing users to obtain intermediate solving states
319/// step by step through loops. Each call to the `next` method applies one iteration function
320/// and checks whether the termination condition is met.
321/// 
322/// # Examples
323/// See the example for the [`Solver::into_iter`] method.
324///
325/// [`into_iter`]: crate::Solver::into_iter
326#[derive(Debug)]
327pub struct SolverIterator<'prob, State, Problem, IterFn, TermFn> 
328where 
329    State: IterState,
330    IterFn: Fn(&State, &Problem) -> State,
331    TermFn: Fn(&State, &Problem) -> bool
332{
333    state: Option<State>,
334    iter_fn: IterFn,
335    term_cond: TermFn,
336    problem: &'prob Problem,
337    //need_term: bool
338}
339
340impl<'prob, State, Problem, IterFn, TermFn> Iterator for SolverIterator<'prob, State, Problem, IterFn, TermFn> 
341where 
342    State: IterState,
343    IterFn: Fn(&State, &Problem) -> State,
344    TermFn: Fn(&State, &Problem) -> bool
345{
346    type Item = State;
347
348    fn next(&mut self) -> Option<Self::Item> {
349        let state = if (&self.state).is_none() {
350            return None;
351        } else {
352            &self.state.as_ref().unwrap()
353        };
354
355        //let next_state = (self.iter_fn)(state, &self.problem);
356
357        if (self.term_cond)(&state, &self.problem) {
358            let old_state = mem::replace(&mut self.state, None);
359
360            return old_state;
361        } else {
362            let next_state = (self.iter_fn)(state, &self.problem);
363            let old_state = mem::replace(&mut self.state, Some(next_state));
364
365            return old_state;
366        }
367    }
368}
369
370
371
372
373
374
375#[cfg(test)]
376mod test {
377    mod newton {
378        use crate::{IterState, Solver};
379
380        fn f_and_df(x: f64) -> (f64, f64) {
381            let fx = x.exp() - 1.5;
382            let dfx = x.exp();
383            (fx, dfx)            
384        }
385        #[test]
386    fn show() {
387        let iter_fn = |state: &f64, problem: &fn(f64) -> (f64, f64)| {
388            let x_n = *state;
389            let (fx, dfx) = problem(x_n);
390            x_n - (fx / dfx)
391        };
392
393        let term_cond = |state: &f64, problem: &fn(f64) -> (f64, f64)| {
394            let (fx, _) = problem(*state);
395            fx.abs() < 1e-6
396        };
397
398        let  solver = Solver::new(iter_fn, term_cond);
399
400        let solution = solver.solve(1.5, &(f_and_df as fn(f64) -> (f64, f64)));
401
402        println!("solver's solution: {}", solution);
403        println!("use std function ln: {}", 1.5_f64.ln());
404    }
405
406        #[derive(Clone)]
407        enum Equation {
408            Exp {
409                // $ae^x - k = 0$
410                a: f64,
411                k: f64
412            },
413
414            Square {
415                // $ax^2 + bx + c = 0$ 
416                a: f64,
417                b: f64,
418                c: f64
419            }
420        }
421
422        impl Equation {
423            fn calc(&self, val: f64) -> f64 {
424                match self {
425                    Self::Exp { a, k } => {
426                        a * val.exp() - k
427                    }
428
429                    Self::Square { a, b, c } => {
430                        let x2 = a * val * val;
431                        let x1 = b * val;
432                        let x0 = c;
433                        x2 + x1 + x0
434                    }
435                }
436            }
437
438            fn diff(&self, val: f64) -> f64 {
439                match self {
440                    Self::Exp { a, k: _ } => {
441                        a * val.exp() 
442                    }
443
444                    Self::Square { a, b, c: _ } => {
445                        ((2. * a) * val) + b
446                    }
447                }                
448            }
449        }
450
451        #[derive(Debug, Clone)]
452        struct NewtonState(f64);
453
454        impl IterState for NewtonState {
455            type Value = f64;
456        
457            type Solution = f64;
458        
459            fn init_from_value(initial_point: Self::Value) -> Self {
460                Self(initial_point)
461            }
462        
463            fn to_sol(&self) -> Self::Solution {
464                self.0
465            }
466        }
467
468        #[test]
469        fn test() {
470            let iter_fn = |state: &NewtonState, problem: &Equation| {
471                let x = state.0;
472                let dx = problem.diff(x);
473                let fx = problem.calc(x);
474
475                let next_x = x - (fx / dx);
476
477                NewtonState(next_x)
478            };
479
480            let term_cond = |state: &NewtonState, problem: &Equation| {
481                let epsilon = 1e-6;
482                problem.calc(state.0) < epsilon
483            };
484
485            let  solver = Solver::new(iter_fn, term_cond);
486
487            let  solver1 = solver.clone().change_term_cond(|state, equation| {
488                equation.calc(state.0) < 1e-9
489            });
490
491            let prob1 = (Equation::Exp { a: 2., k: 3. }, 2.);
492
493            let cloned_and_change_cond_sol = solver1.solve(prob1.1, &prob1.0.clone());
494
495            let prob2 = (Equation::Square { a: 2., b: -5., c: 3. }, 6.);
496
497            let prob1_sol = solver.solve(prob1.1, &prob1.0);
498            let prob2_sol = solver.solve(prob2.1, &prob2.0);
499
500            println!("the numerical solution of $2e^x - 3 = 0$ is: {}", prob1_sol);
501            println!("with direct calc: {}", (1.5_f64).ln());
502            println!("the numerical solution of $2x^2 - 5x + 3 = 0$ is: {}", prob2_sol);
503            println!("with direct calc: {} or {}", ((5. + 1.)/4.) , (3./4.));
504
505            println!("cloned sol: {}", cloned_and_change_cond_sol);
506
507            assert!(prob1.0.calc(prob1_sol) < 1e-6);
508            assert!(prob2.0.calc(prob2_sol) < 1e-6)
509        }
510
511        #[test]
512        fn solver_iter() {
513                let iter_fn = |state: &NewtonState, problem: &Equation| {
514                let x = state.0;
515                let dx = problem.diff(x);
516                let fx = problem.calc(x);
517
518                let next_x = x - (fx / dx);
519
520                NewtonState(next_x)
521            };
522
523            let term_cond = |state: &NewtonState, problem: &Equation| {
524                let epsilon = 1e-6;
525                problem.calc(state.0).abs() < epsilon
526            };
527
528            let solver = Solver::new(iter_fn, term_cond);
529
530            let prob1 = (Equation::Exp { a: 2., k: 3. }, 2.);
531
532            let prob2 = (Equation::Square { a: 2., b: -5., c: 3. }, 6.);
533
534            {
535                let iter = solver.clone().into_iter(prob1.1, &prob1.0);
536                let mut counter = 0usize;
537                
538                for state in iter {
539                    counter += 1;
540                    if counter == 5 {
541                        println!("after 5 times iter, the f(x) = {}", (prob1.0).calc(state.0));
542                        break;
543                    }
544                }
545            }
546
547            //solver.init();
548
549            {
550                let iter = solver.into_iter(prob2.1, &prob2.0);
551                let mut counter = 0usize;
552                
553                for state in iter {
554                    counter += 1;
555                    if counter == 5 {
556                        println!("after 5 times iter, the f(x) = {}", (prob2.0).calc(state.0));
557                        break;
558                    }
559                }
560            }
561        }
562    }
563
564
565    mod guard {
566        use std::time::Duration;
567
568        use crate::Solver;
569
570        #[test]
571        fn test() {
572            
573            // define a never stop solver
574            let loop_solver = Solver::new(|_state: &f64, _: &()| {*_state}, |_: &f64, _: &()| {false});
575            let try_solve = loop_solver.solve_with_timeout(0.0, &(), Duration::from_secs(1));
576            
577            assert!(try_solve.is_err());
578
579            let try_solve = loop_solver.solve_with_max_iteration(0.0, &(), 10);
580            assert!(try_solve.is_err());
581
582        }
583    }
584
585
586    mod derive_test {
587        use crate::IterState;
588
589        #[derive(PartialEq, Eq , Clone, IterState, Debug)]
590        struct State(Vec<u8>, Box<String>);
591
592        #[test]
593        fn test_derive() {
594            let vec1 = vec![0,12, 39];
595            let boxed_str = Box::new("some str".to_string());
596            let s = State(vec1, boxed_str);
597            let s_cloned = s.clone();
598            assert_eq!(State::init_from_value(s_cloned), s);
599            let final_s = State::init_from_value(s.clone()).to_sol();
600            assert_eq!(final_s, s);
601        }
602    }
603}