Skip to main content

solverforge_scoring/constraint/
macros.rs

1/* Macros for reducing boilerplate in N-ary constraint implementations.
2
3The `impl_get_matches_nary!` macro generates `get_matches()` implementations
4for self-join constraints with identical structure but varying arity.
5*/
6
7/* Generates `get_matches()` implementation for N-ary self-join constraints.
8
9All N-ary constraints share the same pattern:
101. Extract entities and build key index
112. Iterate over N-tuples within each key group
123. Filter and collect DetailedConstraintMatch with EntityRefs
13
14# Usage
15
16This macro is used internally in constraint implementations:
17
18```text
19fn get_matches<'a>(&'a self, solution: &S) -> Vec<DetailedConstraintMatch<'a, Sc>> {
20impl_get_matches_nary!(bi: self, solution)
21}
22```
23
24Available arities: `bi`, `tri`, `quad`, `penta`
25*/
26#[macro_export]
27macro_rules! impl_get_matches_nary {
28    // Bi-constraint: 2 entities
29    (bi: $self:expr, $solution:expr) => {{
30        use std::collections::HashMap;
31        use $crate::api::analysis::{ConstraintJustification, DetailedConstraintMatch, EntityRef};
32
33        let entities = $crate::stream::collection_extract::CollectionExtract::extract(
34            &$self.extractor,
35            $solution,
36        );
37        let cref = $self.constraint_ref();
38
39        let mut temp_index: HashMap<_, Vec<usize>> = HashMap::new();
40        for (i, entity) in entities.iter().enumerate() {
41            let key = $crate::stream::key_extract::KeyExtract::extract(
42                &$self.key_extractor,
43                $solution,
44                entity,
45                i,
46            );
47            temp_index.entry(key).or_default().push(i);
48        }
49
50        let mut matches = Vec::new();
51        for indices in temp_index.values() {
52            for i in 0..indices.len() {
53                for j in (i + 1)..indices.len() {
54                    let idx_a = indices[i];
55                    let idx_b = indices[j];
56                    let a = &entities[idx_a];
57                    let b = &entities[idx_b];
58                    if ($self.filter)($solution, a, b, idx_a, idx_b) {
59                        let justification = ConstraintJustification::new(vec![
60                            EntityRef::new(a),
61                            EntityRef::new(b),
62                        ]);
63                        let score = $self.compute_score($solution, idx_a, idx_b);
64                        matches.push(DetailedConstraintMatch::new(cref, score, justification));
65                    }
66                }
67            }
68        }
69        matches
70    }};
71
72    // Tri-constraint: 3 entities
73    (tri: $self:expr, $solution:expr) => {{
74        use std::collections::HashMap;
75        use $crate::api::analysis::{ConstraintJustification, DetailedConstraintMatch, EntityRef};
76
77        let entities = $crate::stream::collection_extract::CollectionExtract::extract(
78            &$self.extractor,
79            $solution,
80        );
81        let cref = $self.constraint_ref();
82
83        let mut temp_index: HashMap<_, Vec<usize>> = HashMap::new();
84        for (i, entity) in entities.iter().enumerate() {
85            let key = $crate::stream::key_extract::KeyExtract::extract(
86                &$self.key_extractor,
87                $solution,
88                entity,
89                i,
90            );
91            temp_index.entry(key).or_default().push(i);
92        }
93
94        let mut matches = Vec::new();
95        for indices in temp_index.values() {
96            for pos_i in 0..indices.len() {
97                for pos_j in (pos_i + 1)..indices.len() {
98                    for pos_k in (pos_j + 1)..indices.len() {
99                        let i = indices[pos_i];
100                        let j = indices[pos_j];
101                        let k = indices[pos_k];
102                        let a = &entities[i];
103                        let b = &entities[j];
104                        let c = &entities[k];
105                        if ($self.filter)($solution, a, b, c) {
106                            let justification = ConstraintJustification::new(vec![
107                                EntityRef::new(a),
108                                EntityRef::new(b),
109                                EntityRef::new(c),
110                            ]);
111                            let score = $self.compute_score($solution, i, j, k);
112                            matches.push(DetailedConstraintMatch::new(cref, score, justification));
113                        }
114                    }
115                }
116            }
117        }
118        matches
119    }};
120
121    // Quad-constraint: 4 entities
122    (quad: $self:expr, $solution:expr) => {{
123        use std::collections::HashMap;
124        use $crate::api::analysis::{ConstraintJustification, DetailedConstraintMatch, EntityRef};
125
126        let entities = $crate::stream::collection_extract::CollectionExtract::extract(
127            &$self.extractor,
128            $solution,
129        );
130        let cref = $self.constraint_ref();
131
132        let mut temp_index: HashMap<_, Vec<usize>> = HashMap::new();
133        for (i, entity) in entities.iter().enumerate() {
134            let key = $crate::stream::key_extract::KeyExtract::extract(
135                &$self.key_extractor,
136                $solution,
137                entity,
138                i,
139            );
140            temp_index.entry(key).or_default().push(i);
141        }
142
143        let mut matches = Vec::new();
144        for indices in temp_index.values() {
145            for pos_i in 0..indices.len() {
146                for pos_j in (pos_i + 1)..indices.len() {
147                    for pos_k in (pos_j + 1)..indices.len() {
148                        for pos_l in (pos_k + 1)..indices.len() {
149                            let i = indices[pos_i];
150                            let j = indices[pos_j];
151                            let k = indices[pos_k];
152                            let l = indices[pos_l];
153                            let a = &entities[i];
154                            let b = &entities[j];
155                            let c = &entities[k];
156                            let d = &entities[l];
157                            if ($self.filter)($solution, a, b, c, d) {
158                                let justification = ConstraintJustification::new(vec![
159                                    EntityRef::new(a),
160                                    EntityRef::new(b),
161                                    EntityRef::new(c),
162                                    EntityRef::new(d),
163                                ]);
164                                let score = $self.compute_score($solution, i, j, k, l);
165                                matches.push(DetailedConstraintMatch::new(
166                                    cref,
167                                    score,
168                                    justification,
169                                ));
170                            }
171                        }
172                    }
173                }
174            }
175        }
176        matches
177    }};
178
179    // Penta-constraint: 5 entities
180    (penta: $self:expr, $solution:expr) => {{
181        use std::collections::HashMap;
182        use $crate::api::analysis::{ConstraintJustification, DetailedConstraintMatch, EntityRef};
183
184        let entities = $crate::stream::collection_extract::CollectionExtract::extract(
185            &$self.extractor,
186            $solution,
187        );
188        let cref = $self.constraint_ref();
189
190        let mut temp_index: HashMap<_, Vec<usize>> = HashMap::new();
191        for (i, entity) in entities.iter().enumerate() {
192            let key = $crate::stream::key_extract::KeyExtract::extract(
193                &$self.key_extractor,
194                $solution,
195                entity,
196                i,
197            );
198            temp_index.entry(key).or_default().push(i);
199        }
200
201        let mut matches = Vec::new();
202        for indices in temp_index.values() {
203            for pos_i in 0..indices.len() {
204                for pos_j in (pos_i + 1)..indices.len() {
205                    for pos_k in (pos_j + 1)..indices.len() {
206                        for pos_l in (pos_k + 1)..indices.len() {
207                            for pos_m in (pos_l + 1)..indices.len() {
208                                let i = indices[pos_i];
209                                let j = indices[pos_j];
210                                let k = indices[pos_k];
211                                let l = indices[pos_l];
212                                let m = indices[pos_m];
213                                let a = &entities[i];
214                                let b = &entities[j];
215                                let c = &entities[k];
216                                let d = &entities[l];
217                                let e = &entities[m];
218                                if ($self.filter)($solution, a, b, c, d, e) {
219                                    let justification = ConstraintJustification::new(vec![
220                                        EntityRef::new(a),
221                                        EntityRef::new(b),
222                                        EntityRef::new(c),
223                                        EntityRef::new(d),
224                                        EntityRef::new(e),
225                                    ]);
226                                    let score = $self.compute_score($solution, i, j, k, l, m);
227                                    matches.push(DetailedConstraintMatch::new(
228                                        cref,
229                                        score,
230                                        justification,
231                                    ));
232                                }
233                            }
234                        }
235                    }
236                }
237            }
238        }
239        matches
240    }};
241}
242
243pub use impl_get_matches_nary;