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}