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::{CollectionExtract, FlattenExtract};
9use crate::stream::filter::UniFilter;
10use crate::stream::weighting_support::ConstraintWeight;
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 + 'static,
48    EA: CollectionExtract<S, Item = A>,
49    EP: CollectionExtract<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<W>(
106        self,
107        weight: W,
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        W: for<'w> ConstraintWeight<(&'w A,), Sc> + Send + Sync,
126    {
127        let is_hard = weight.is_hard();
128        self.into_weighted_builder(
129            ImpactType::Penalty,
130            move |a: &A| weight.score((a,)),
131            is_hard,
132        )
133    }
134
135    pub fn reward<W>(
136        self,
137        weight: W,
138    ) -> ExistsConstraintBuilder<
139        S,
140        A,
141        P,
142        B,
143        K,
144        EA,
145        EP,
146        KA,
147        KB,
148        FA,
149        FP,
150        Flatten,
151        impl Fn(&A) -> Sc + Send + Sync,
152        Sc,
153    >
154    where
155        W: for<'w> ConstraintWeight<(&'w A,), Sc> + Send + Sync,
156    {
157        let is_hard = weight.is_hard();
158        self.into_weighted_builder(ImpactType::Reward, move |a: &A| weight.score((a,)), is_hard)
159    }
160}
161
162impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc: Score> std::fmt::Debug
163    for ExistsConstraintStream<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, Sc>
164{
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        f.debug_struct("ExistsConstraintStream")
167            .field("mode", &self.mode)
168            .finish()
169    }
170}
171
172pub struct ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
173where
174    Sc: Score,
175{
176    mode: ExistenceMode,
177    extractor_a: EA,
178    extractor_parent: EP,
179    key_a: KA,
180    key_b: KB,
181    filter_a: FA,
182    filter_parent: FP,
183    flatten: Flatten,
184    impact_type: ImpactType,
185    weight: W,
186    is_hard: bool,
187    _phantom: PhantomData<(
188        fn() -> S,
189        fn() -> A,
190        fn() -> P,
191        fn() -> B,
192        fn() -> K,
193        fn() -> Sc,
194    )>,
195}
196
197impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
198    ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
199where
200    S: Send + Sync + 'static,
201    A: Clone + Send + Sync + 'static,
202    P: Clone + Send + Sync + 'static,
203    B: Clone + Send + Sync + 'static,
204    K: Eq + Hash + Clone + Send + Sync + 'static,
205    EA: CollectionExtract<S, Item = A>,
206    EP: CollectionExtract<S, Item = P>,
207    KA: Fn(&A) -> K + Send + Sync,
208    KB: Fn(&B) -> K + Send + Sync,
209    FA: UniFilter<S, A> + Send + Sync,
210    FP: UniFilter<S, P> + Send + Sync,
211    Flatten: FlattenExtract<P, Item = B> + Send + Sync,
212    W: Fn(&A) -> Sc + Send + Sync,
213    Sc: Score + 'static,
214{
215    pub fn named(
216        self,
217        name: &str,
218    ) -> IncrementalExistsConstraint<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc> {
219        IncrementalExistsConstraint::new(
220            ConstraintRef::new("", name),
221            self.impact_type,
222            self.mode,
223            self.extractor_a,
224            self.extractor_parent,
225            self.key_a,
226            self.key_b,
227            self.filter_a,
228            self.filter_parent,
229            self.flatten,
230            self.weight,
231            self.is_hard,
232        )
233    }
234}
235
236impl<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc: Score> std::fmt::Debug
237    for ExistsConstraintBuilder<S, A, P, B, K, EA, EP, KA, KB, FA, FP, Flatten, W, Sc>
238{
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        f.debug_struct("ExistsConstraintBuilder")
241            .field("mode", &self.mode)
242            .field("impact_type", &self.impact_type)
243            .finish()
244    }
245}
246
247pub(crate) type DirectExistenceStream<S, A, B, K, EA, EP, KA, KB, FA, FP, Sc> =
248    ExistsConstraintStream<S, A, B, B, K, EA, EP, KA, KB, FA, FP, SelfFlatten, Sc>;