Skip to main content

solverforge_scoring/stream/
existence_stream.rs

1use std::hash::Hash;
2use std::marker::PhantomData;
3
4use solverforge_core::score::Score;
5use solverforge_core::{ConstraintRef, ImpactType};
6
7use crate::constraint::exists::{IncrementalExistsConstraint, SelfFlatten};
8use crate::stream::collection_extract::{FlattenExtract, TrackedCollectionExtract};
9use crate::stream::filter::UniFilter;
10use crate::stream::weighting_support::fixed_weight_is_hard;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ExistenceMode {
14    Exists,
15    NotExists,
16}
17
18pub struct ExistsConstraintStream<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc>
19where
20    Sc: Score,
21{
22    pub(super) mode: ExistenceMode,
23    pub(super) extractor_a: EA,
24    pub(super) extractor_parent: EP,
25    pub(super) key_a: KA,
26    pub(super) key_b: KB,
27    pub(super) filter_a: FA,
28    pub(super) filter_parent: FP,
29    pub(super) flatten: Flatten,
30    pub(super) _phantom: PhantomData<(
31        fn() -> S,
32        fn() -> A,
33        fn() -> P,
34        fn() -> B,
35        fn() -> K,
36        fn() -> Sc,
37    )>,
38}
39
40impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc>
41    ExistsConstraintStream<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc>
42where
43    S: Send + Sync + 'static,
44    A: Clone + Send + Sync + 'static,
45    P: Clone + Send + Sync + 'static,
46    B: Clone + Send + Sync + 'static,
47    K: Eq + Hash + Clone + Send + Sync,
48    EA: TrackedCollectionExtract<S, Item = A>,
49    EP: TrackedCollectionExtract<S, Item = P>,
50    KA: Fn(&A) -> K + Send + Sync,
51    KB: Fn(&B) -> K + Send + Sync,
52    FA: UniFilter<S, A>,
53    FP: UniFilter<S, P>,
54    Flatten: FlattenExtract<P, Item = B>,
55    Sc: Score + 'static,
56{
57    fn into_weighted_builder<W>(
58        self,
59        impact_type: ImpactType,
60        weight: W,
61        is_hard: bool,
62    ) -> ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
63    where
64        W: Fn(&A) -> Sc + Send + Sync,
65    {
66        ExistsConstraintBuilder {
67            mode: self.mode,
68            extractor_a: self.extractor_a,
69            extractor_parent: self.extractor_parent,
70            key_a: self.key_a,
71            key_b: self.key_b,
72            filter_a: self.filter_a,
73            filter_parent: self.filter_parent,
74            flatten: self.flatten,
75            impact_type,
76            weight,
77            is_hard,
78            _phantom: PhantomData,
79        }
80    }
81
82    pub fn new(
83        mode: ExistenceMode,
84        extractor_a: EA,
85        extractor_parent: EP,
86        keys: (KA, KB),
87        filter_a: FA,
88        filter_parent: FP,
89        flatten: Flatten,
90    ) -> Self {
91        let (key_a, key_b) = keys;
92        Self {
93            mode,
94            extractor_a,
95            extractor_parent,
96            key_a,
97            key_b,
98            filter_a,
99            filter_parent,
100            flatten,
101            _phantom: PhantomData,
102        }
103    }
104
105    pub fn penalize(
106        self,
107        weight: Sc,
108    ) -> ExistsConstraintBuilder<
109        S,
110        A,
111        P,
112        B,
113        K,
114        EA,
115        EP,
116        KA,
117        KB,
118        FA,
119        FP,
120        Flatten,
121        impl Fn(&A) -> Sc + Send + Sync,
122        Sc,
123    >
124    where
125        Sc: Copy,
126    {
127        self.into_weighted_builder(
128            ImpactType::Penalty,
129            move |_: &A| weight,
130            fixed_weight_is_hard(weight),
131        )
132    }
133
134    pub fn penalize_with<W>(
135        self,
136        weight_fn: W,
137    ) -> ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
138    where
139        W: Fn(&A) -> Sc + Send + Sync,
140    {
141        self.into_weighted_builder(ImpactType::Penalty, weight_fn, false)
142    }
143
144    pub fn penalize_hard_with<W>(
145        self,
146        weight_fn: W,
147    ) -> ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
148    where
149        W: Fn(&A) -> Sc + Send + Sync,
150    {
151        self.into_weighted_builder(ImpactType::Penalty, weight_fn, true)
152    }
153
154    pub fn penalize_hard(
155        self,
156    ) -> ExistsConstraintBuilder<
157        S,
158        A,
159        P,
160        B,
161        K,
162        EA,
163        EP,
164        KA,
165        KB,
166        FA,
167        FP,
168        Flatten,
169        impl Fn(&A) -> Sc + Send + Sync,
170        Sc,
171    >
172    where
173        Sc: Copy,
174    {
175        self.penalize(Sc::one_hard())
176    }
177
178    pub fn penalize_soft(
179        self,
180    ) -> ExistsConstraintBuilder<
181        S,
182        A,
183        P,
184        B,
185        K,
186        EA,
187        EP,
188        KA,
189        KB,
190        FA,
191        FP,
192        Flatten,
193        impl Fn(&A) -> Sc + Send + Sync,
194        Sc,
195    >
196    where
197        Sc: Copy,
198    {
199        self.penalize(Sc::one_soft())
200    }
201
202    pub fn reward(
203        self,
204        weight: Sc,
205    ) -> ExistsConstraintBuilder<
206        S,
207        A,
208        P,
209        B,
210        K,
211        EA,
212        EP,
213        KA,
214        KB,
215        FA,
216        FP,
217        Flatten,
218        impl Fn(&A) -> Sc + Send + Sync,
219        Sc,
220    >
221    where
222        Sc: Copy,
223    {
224        self.into_weighted_builder(
225            ImpactType::Reward,
226            move |_: &A| weight,
227            fixed_weight_is_hard(weight),
228        )
229    }
230
231    pub fn reward_with<W>(
232        self,
233        weight_fn: W,
234    ) -> ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
235    where
236        W: Fn(&A) -> Sc + Send + Sync,
237    {
238        self.into_weighted_builder(ImpactType::Reward, weight_fn, false)
239    }
240
241    pub fn reward_hard_with<W>(
242        self,
243        weight_fn: W,
244    ) -> ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
245    where
246        W: Fn(&A) -> Sc + Send + Sync,
247    {
248        self.into_weighted_builder(ImpactType::Reward, weight_fn, true)
249    }
250
251    pub fn reward_hard(
252        self,
253    ) -> ExistsConstraintBuilder<
254        S,
255        A,
256        P,
257        B,
258        K,
259        EA,
260        EP,
261        KA,
262        KB,
263        FA,
264        FP,
265        Flatten,
266        impl Fn(&A) -> Sc + Send + Sync,
267        Sc,
268    >
269    where
270        Sc: Copy,
271    {
272        self.reward(Sc::one_hard())
273    }
274
275    pub fn reward_soft(
276        self,
277    ) -> ExistsConstraintBuilder<
278        S,
279        A,
280        P,
281        B,
282        K,
283        EA,
284        EP,
285        KA,
286        KB,
287        FA,
288        FP,
289        Flatten,
290        impl Fn(&A) -> Sc + Send + Sync,
291        Sc,
292    >
293    where
294        Sc: Copy,
295    {
296        self.reward(Sc::one_soft())
297    }
298}
299
300impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc: Score> std::fmt::Debug
301    for ExistsConstraintStream<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc>
302{
303    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304        f.debug_struct("ExistsConstraintStream")
305            .field("mode", &self.mode)
306            .finish()
307    }
308}
309
310pub struct ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
311where
312    Sc: Score,
313{
314    mode: ExistenceMode,
315    extractor_a: EA,
316    extractor_parent: EP,
317    key_a: KA,
318    key_b: KB,
319    filter_a: FA,
320    filter_parent: FP,
321    flatten: Flatten,
322    impact_type: ImpactType,
323    weight: W,
324    is_hard: bool,
325    _phantom: PhantomData<(
326        fn() -> S,
327        fn() -> A,
328        fn() -> P,
329        fn() -> B,
330        fn() -> K,
331        fn() -> Sc,
332    )>,
333}
334
335impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
336    ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
337where
338    S: Send + Sync + 'static,
339    A: Clone + Send + Sync + 'static,
340    P: Clone + Send + Sync + 'static,
341    B: Clone + Send + Sync + 'static,
342    K: Eq + Hash + Clone + Send + Sync,
343    EA: TrackedCollectionExtract<S, Item = A>,
344    EP: TrackedCollectionExtract<S, Item = P>,
345    KA: Fn(&A) -> K + Send + Sync,
346    KB: Fn(&B) -> K + Send + Sync,
347    FA: UniFilter<S, A> + Send + Sync,
348    FP: UniFilter<S, P> + Send + Sync,
349    Flatten: FlattenExtract<P, Item = B> + Send + Sync,
350    W: Fn(&A) -> Sc + Send + Sync,
351    Sc: Score + 'static,
352{
353    pub fn named(
354        self,
355        name: &str,
356    ) -> IncrementalExistsConstraint<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc> {
357        IncrementalExistsConstraint::new(
358            ConstraintRef::new("", name),
359            self.impact_type,
360            self.mode,
361            self.extractor_a,
362            self.extractor_parent,
363            self.key_a,
364            self.key_b,
365            self.filter_a,
366            self.filter_parent,
367            self.flatten,
368            self.weight,
369            self.is_hard,
370        )
371    }
372}
373
374impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc: Score> std::fmt::Debug
375    for ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
376{
377    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378        f.debug_struct("ExistsConstraintBuilder")
379            .field("mode", &self.mode)
380            .field("impact_type", &self.impact_type)
381            .finish()
382    }
383}
384
385pub(crate) type DirectExistenceStream<S, A, B, K, EA, EP, KA, KB, FA, FP, Sc> =
386    ExistsConstraintStream<S, A, B, B, K, EA, EP, KA, KB, FA, FP, SelfFlatten, Sc>;