Skip to main content

solverforge_solver/termination/
composite.rs

1/* Composite termination conditions (AND/OR).
2
3Uses macro-generated tuple implementations for zero-erasure architecture.
4*/
5
6use std::fmt::Debug;
7use std::marker::PhantomData;
8
9use solverforge_core::domain::PlanningSolution;
10use solverforge_scoring::Director;
11
12use super::Termination;
13use crate::scope::BestSolutionCallback;
14use crate::scope::SolverScope;
15
16/* Combines multiple terminations with OR logic.
17
18Terminates when ANY of the child terminations triggers.
19
20# Example
21
22```
23use solverforge_solver::termination::{OrTermination, TimeTermination, StepCountTermination};
24use solverforge_scoring::ScoreDirector;
25use solverforge_core::domain::PlanningSolution;
26use solverforge_core::score::SoftScore;
27use std::time::Duration;
28
29#[derive(Clone)]
30struct MySolution;
31impl PlanningSolution for MySolution {
32type Score = SoftScore;
33fn score(&self) -> Option<Self::Score> { None }
34fn set_score(&mut self, _: Option<Self::Score>) {}
35}
36
37type MyDirector = ScoreDirector<MySolution, ()>;
38
39// Terminate after 30 seconds OR 1000 steps
40let term: OrTermination<_, MySolution, MyDirector> = OrTermination::new((
41TimeTermination::seconds(30),
42StepCountTermination::new(1000),
43));
44```
45*/
46#[derive(Clone)]
47pub struct OrTermination<T, S, D>(pub T, PhantomData<fn(S, D)>);
48
49impl<T: Debug, S, D> Debug for OrTermination<T, S, D> {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        f.debug_tuple("OrTermination").field(&self.0).finish()
52    }
53}
54
55impl<T, S, D> OrTermination<T, S, D> {
56    pub fn new(terminations: T) -> Self {
57        Self(terminations, PhantomData)
58    }
59}
60
61/* Combines multiple terminations with AND logic.
62
63All terminations must agree before solving terminates.
64
65# Example
66
67```
68use solverforge_solver::termination::{AndTermination, TimeTermination, StepCountTermination};
69use solverforge_scoring::ScoreDirector;
70use solverforge_core::score::SoftScore;
71use solverforge_core::domain::PlanningSolution;
72use std::time::Duration;
73
74#[derive(Clone)]
75struct MySolution;
76impl PlanningSolution for MySolution {
77type Score = SoftScore;
78fn score(&self) -> Option<Self::Score> { None }
79fn set_score(&mut self, _: Option<Self::Score>) {}
80}
81
82type MyDirector = ScoreDirector<MySolution, ()>;
83
84// Terminate when both conditions are met
85let term: AndTermination<_, MySolution, MyDirector> = AndTermination::new((
86TimeTermination::seconds(10),
87StepCountTermination::new(100),
88));
89```
90*/
91#[derive(Clone)]
92pub struct AndTermination<T, S, D>(pub T, PhantomData<fn(S, D)>);
93
94impl<T: Debug, S, D> Debug for AndTermination<T, S, D> {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        f.debug_tuple("AndTermination").field(&self.0).finish()
97    }
98}
99
100impl<T, S, D> AndTermination<T, S, D> {
101    pub fn new(terminations: T) -> Self {
102        Self(terminations, PhantomData)
103    }
104}
105
106macro_rules! impl_composite_termination {
107    ($($idx:tt: $T:ident),+) => {
108        impl<S, D, BestCb, $($T),+> Termination<S, D, BestCb> for OrTermination<($($T,)+), S, D>
109        where
110            S: PlanningSolution,
111            D: Director<S>,
112            BestCb: BestSolutionCallback<S>,
113            $($T: Termination<S, D, BestCb>,)+
114        {
115            fn is_terminated(&self, solver_scope: &SolverScope<S, D, BestCb>) -> bool {
116                $(
117                    if self.0.$idx.is_terminated(solver_scope) {
118                        return true;
119                    }
120                )+
121                false
122            }
123
124            fn install_inphase_limits(&self, solver_scope: &mut SolverScope<S, D, BestCb>) {
125                // Propagate in-phase limits from all child terminations.
126                // For OR, each child independently may set a limit.
127                $(
128                    self.0.$idx.install_inphase_limits(solver_scope);
129                )+
130            }
131        }
132
133        impl<S, D, BestCb, $($T),+> Termination<S, D, BestCb> for AndTermination<($($T,)+), S, D>
134        where
135            S: PlanningSolution,
136            D: Director<S>,
137            BestCb: BestSolutionCallback<S>,
138            $($T: Termination<S, D, BestCb>,)+
139        {
140            fn is_terminated(&self, solver_scope: &SolverScope<S, D, BestCb>) -> bool {
141                $(
142                    if !self.0.$idx.is_terminated(solver_scope) {
143                        return false;
144                    }
145                )+
146                true
147            }
148
149            fn install_inphase_limits(&self, solver_scope: &mut SolverScope<S, D, BestCb>) {
150                $(
151                    self.0.$idx.install_inphase_limits(solver_scope);
152                )+
153            }
154        }
155    };
156}
157
158impl_composite_termination!(0: T0);
159impl_composite_termination!(0: T0, 1: T1);
160impl_composite_termination!(0: T0, 1: T1, 2: T2);
161impl_composite_termination!(0: T0, 1: T1, 2: T2, 3: T3);
162impl_composite_termination!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4);
163impl_composite_termination!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5);
164impl_composite_termination!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6);
165impl_composite_termination!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7);