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