Skip to main content

solverforge_scoring/constraint/grouped/
terminal.rs

1use std::hash::Hash;
2use std::marker::PhantomData;
3
4use solverforge_core::score::Score;
5use solverforge_core::{ConstraintRef, ImpactType};
6
7use crate::api::analysis::DetailedConstraintMatch;
8use crate::api::constraint_set::IncrementalConstraint;
9use crate::stream::collection_extract::CollectionExtract;
10use crate::stream::collector::{Accumulator, Collector};
11use crate::stream::filter::UniFilter;
12use crate::stream::ConstraintWeight;
13
14use super::scorer::GroupedTerminalScorer;
15use super::shared_set::SharedGroupedConstraintSet;
16use super::state::GroupedNodeState;
17
18type Inner<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc> = SharedGroupedConstraintSet<
19    S,
20    A,
21    K,
22    E,
23    Fi,
24    KF,
25    C,
26    V,
27    R,
28    Acc,
29    GroupedTerminalScorer<K, R, W, Sc>,
30    Sc,
31>;
32
33pub struct Uni<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc>
34where
35    Acc: Accumulator<V, R>,
36    Sc: Score,
37{
38    is_hard: bool,
39    inner: Inner<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc>,
40    _phantom: PhantomData<fn() -> (S, A, V, R, Acc)>,
41}
42
43impl<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc> Uni<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc>
44where
45    S: Send + Sync + 'static,
46    A: Send + Sync + 'static,
47    K: Eq + Hash + Send + Sync + 'static,
48    E: CollectionExtract<S, Item = A>,
49    Fi: UniFilter<S, A>,
50    KF: Fn(&A) -> K + Send + Sync,
51    C: for<'i> Collector<&'i A, Value = V, Result = R, Accumulator = Acc> + Send + Sync + 'static,
52    V: Send + Sync + 'static,
53    R: Send + Sync + 'static,
54    Acc: Accumulator<V, R> + Send + Sync + 'static,
55    W: Fn(&K, &R) -> Sc + Send + Sync,
56    Sc: Score + 'static,
57{
58    #[allow(clippy::too_many_arguments)]
59    pub fn new(
60        constraint_ref: ConstraintRef,
61        impact_type: ImpactType,
62        extractor: E,
63        filter: Fi,
64        key_fn: KF,
65        collector: C,
66        weight_fn: W,
67        is_hard: bool,
68    ) -> Self {
69        let state = GroupedNodeState::new(extractor, filter, key_fn, collector);
70        let scorer = GroupedTerminalScorer::new(constraint_ref, impact_type, weight_fn, is_hard);
71        Self {
72            is_hard,
73            inner: SharedGroupedConstraintSet::new(state, scorer),
74            _phantom: PhantomData,
75        }
76    }
77
78    pub fn penalize<W2>(
79        self,
80        weight: W2,
81    ) -> super::shared_set::GroupedConstraintSetBuilder<
82        S,
83        A,
84        K,
85        E,
86        Fi,
87        KF,
88        C,
89        V,
90        R,
91        Acc,
92        GroupedTerminalScorer<K, R, W, Sc>,
93        impl Fn(&K, &R) -> Sc + Send + Sync,
94        Sc,
95    >
96    where
97        W2: for<'w> ConstraintWeight<(&'w K, &'w R), Sc> + Send + Sync,
98    {
99        self.inner.penalize(weight)
100    }
101
102    pub fn reward<W2>(
103        self,
104        weight: W2,
105    ) -> super::shared_set::GroupedConstraintSetBuilder<
106        S,
107        A,
108        K,
109        E,
110        Fi,
111        KF,
112        C,
113        V,
114        R,
115        Acc,
116        GroupedTerminalScorer<K, R, W, Sc>,
117        impl Fn(&K, &R) -> Sc + Send + Sync,
118        Sc,
119    >
120    where
121        W2: for<'w> ConstraintWeight<(&'w K, &'w R), Sc> + Send + Sync,
122    {
123        self.inner.reward(weight)
124    }
125}
126
127impl<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc> IncrementalConstraint<S, Sc>
128    for Uni<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc>
129where
130    S: Send + Sync + 'static,
131    A: Send + Sync + 'static,
132    K: Eq + Hash + Send + Sync + 'static,
133    E: CollectionExtract<S, Item = A>,
134    Fi: UniFilter<S, A>,
135    KF: Fn(&A) -> K + Send + Sync,
136    C: for<'i> Collector<&'i A, Value = V, Result = R, Accumulator = Acc> + Send + Sync + 'static,
137    V: Send + Sync + 'static,
138    R: Send + Sync + 'static,
139    Acc: Accumulator<V, R> + Send + Sync + 'static,
140    W: Fn(&K, &R) -> Sc + Send + Sync,
141    Sc: Score + 'static,
142{
143    fn evaluate(&self, solution: &S) -> Sc {
144        crate::api::constraint_set::ConstraintSet::evaluate_all(&self.inner, solution)
145    }
146
147    fn match_count(&self, solution: &S) -> usize {
148        crate::api::constraint_set::ConstraintSet::evaluate_each(&self.inner, solution)
149            .first()
150            .map_or(0, |result| result.match_count)
151    }
152
153    fn initialize(&mut self, solution: &S) -> Sc {
154        crate::api::constraint_set::ConstraintSet::initialize_all(&mut self.inner, solution)
155    }
156
157    fn on_insert(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
158        crate::api::constraint_set::ConstraintSet::on_insert_all(
159            &mut self.inner,
160            solution,
161            entity_index,
162            descriptor_index,
163        )
164    }
165
166    fn on_retract(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
167        crate::api::constraint_set::ConstraintSet::on_retract_all(
168            &mut self.inner,
169            solution,
170            entity_index,
171            descriptor_index,
172        )
173    }
174
175    fn reset(&mut self) {
176        crate::api::constraint_set::ConstraintSet::reset_all(&mut self.inner);
177    }
178
179    fn constraint_ref(&self) -> &ConstraintRef {
180        self.inner.primary_constraint_ref()
181    }
182
183    fn is_hard(&self) -> bool {
184        self.is_hard
185    }
186
187    fn get_matches<'a>(&'a self, _solution: &S) -> Vec<DetailedConstraintMatch<'a, Sc>> {
188        Vec::new()
189    }
190
191    fn weight(&self) -> Sc {
192        Sc::zero()
193    }
194}
195
196impl<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc> std::fmt::Debug
197    for Uni<S, A, K, E, Fi, KF, C, V, R, Acc, W, Sc>
198where
199    Acc: Accumulator<V, R>,
200    Sc: Score,
201{
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        f.debug_struct("Uni").finish()
204    }
205}