Function solve_minimum

Source
pub fn solve_minimum<T: Clone + PartialEq + Eq + Hash>(
    facts: Vec<T>,
    infer: fn(cache: &HashSet<T>, _: &[T]) -> Option<Inference<T>>,
) -> Vec<T>
Expand description

Solves the starting condition using the infer function for inference.

Assumes that infer is deterministic and leading to a cycle for every input. Finds the minimum set of facts in the cycle.

Examples found in repository?
examples/prime.rs (line 64)
61fn main() {
62    let start = vec![Upto(100)];
63
64    let res = solve_minimum(start, infer);
65    for i in 0..res.len() {
66        println!("{:?}", res[i]);
67    }
68}
More examples
Hide additional examples
examples/walk.rs (line 51)
39fn main() {
40    let start = vec![
41        Left,
42        Left,
43        Up,
44        Left,
45        Right,
46        Down,
47        Down,
48        Right,
49    ];
50
51    let res = solve_minimum(start, infer);
52    for i in 0..res.len() {
53        println!("{:?}", res[i]);
54    }
55}
examples/le.rs (line 113)
106fn main() {
107    let start = vec![
108        Le(var("X"), var("Y")), // X <= Y
109        Le(var("Y"), var("Z")), // Y <= Z
110        Le(var("Z"), var("X")), // Z <= X
111    ];
112
113    let res = solve_minimum(start, infer);
114    for i in 0..res.len() {
115        println!("{:?}", res[i]);
116    }
117}
examples/magic_square.rs (line 385)
82pub fn infer(cache: &HashSet<Expr>, facts: &[Expr]) -> Option<Inference<Expr>> {
83    if cache.contains(&False) {return None};
84
85    // Put simplification rules first to find simplest set of facts.
86
87    // Sorting makes it easier for rules to do their job,
88    // and it makes the output easier to read.
89    // Wait for `ExpandAll` to finish to avoid premature cycle detection.
90    if cache.contains(&SortAll) && !cache.contains(&ExpandAll) {
91        for ea in facts {
92            if let Sum(ref ls, ref rs) = *ea {
93                // Sort terms on left and right side.
94                let mut sorted_ls = ls.clone();
95                sorted_ls.sort();
96                let mut sorted_rs = rs.clone();
97                sorted_rs.sort();
98                if &sorted_ls != ls || &sorted_rs != rs {
99                    let new_expr = Sum(sorted_ls, sorted_rs);
100                    return Some(SimplifyOne {from: ea.clone(), to: new_expr});
101                }
102            }
103
104            if let Sum(ref ls, ref rs) = *ea {
105                // Reorder left and right side.
106                if ls < rs {
107                    return Some(Inference::replace_one(
108                        ea.clone(),
109                        Sum(rs.clone(), ls.clone()),
110                        cache
111                    ));
112                }
113            }
114        }
115    }
116
117    // Wait for `ExpandAll` to finish so a cycle detection is not triggered prematurely.
118    if !cache.contains(&ExpandAll) {
119
120        if cache.contains(&CheckRange) {
121            for ea in facts {
122                if let Range {var, start, end} = *ea {
123                    for eb in facts {
124                        if let Some((a, x)) = eb.assignment() {
125                            if var == a && (x < start || x > end) {
126                                return Some(Propagate(False));
127                            }
128                        }
129                    }
130                }
131            }
132        }
133
134        if cache.contains(&UniqueAssignments) {
135            let mut vars = vec![];
136            let mut rss = vec![];
137            // Find all isolated variables.
138            for ea in facts {
139                if let Sum(ref ls, ref rs) = *ea {
140                    if ls.len() == 1 {
141                        if let Var(a) = ls[0] {
142                            vars.push(a);
143                            rss.push(rs.clone());
144                        }
145                    }
146                }
147            }
148
149            // Check for other variables
150            for i in 0..vars.len() {
151                let var = vars[i];
152                for j in 0..vars.len() {
153                    if vars[j] != var {
154                        if rss[j] == rss[i] {
155                            return Some(Propagate(False));
156                        }
157                    }
158                }
159            }
160        }
161
162        for ea in facts {
163
164            if cache.contains(&RemoveRefl) {
165                if let Sum(ref ls, ref rs) = *ea {
166                    if ls == rs {
167                        return Some(OneTrue {from: ea.clone()});
168                    }
169                }
170            }
171
172            if cache.contains(&RemoveRangeWhenAssigned) {
173                if let Some((a, _)) = ea.assignment() {
174                    for eb in facts {
175                        if let Range {var, ..} = *eb {
176                            if var == a {
177                                return Some(OneTrue {from: eb.clone()});
178                            }
179                        }
180                    }
181                }
182            }
183
184            if cache.contains(&CheckContradictingConstants) {
185                if let Sum(ref ls, ref rs) = *ea {
186                    if rs.len() == 0 && ls.len() == 1 {
187                        if let Const(x) = ls[0] {
188                            if x != 0 {
189                                return Some(Propagate(False));
190                            }
191                        }
192                    }
193                }
194            }
195
196            if cache.contains(&AbsoluteNumbers) {
197                if let Sum(ref ls, ref rs) = *ea {
198                    if rs.len() == 0 && ls.len() == 2 {
199                        if let Const(x) = ls[0] {
200                            if let Var(_) = ls[1] {
201                                if x != 0 {
202                                    return Some(Propagate(False));
203                                }
204                            }
205                        }
206                    }
207                }
208            }
209
210            if cache.contains(&SumConstants) {
211                if let Sum(ref ls, ref rs) = *ea {
212                    let mut sum = 0;
213                    let mut count = 0;
214                    for i in 0..ls.len() {
215                        if let Const(x) = ls[i] {
216                            sum += x;
217                            count += 1;
218                        }
219                    }
220                    if count > 1 {
221                        let mut new_ls = vec![];
222                        for i in 0..ls.len() {
223                            if let Const(_) = ls[i] {continue}
224                            new_ls.push(ls[i].clone());
225                        }
226                        new_ls.push(Const(sum));
227                        return Some(Inference::replace_one(
228                            ea.clone(),
229                            Sum(new_ls, rs.clone()),
230                            cache
231                        ));
232                    }
233                }
234            }
235
236            if cache.contains(&RemoveEqualTermsOnBothSides) {
237                if let Sum(ref ls, ref rs) = *ea {
238                    for i in 0..ls.len() {
239                        for j in 0..rs.len() {
240                            if ls[i] == rs[j] {
241                                let mut new_ls = vec![];
242                                for k in 0..ls.len() {
243                                    if k == i {continue} else {new_ls.push(ls[k].clone())}
244                                }
245                                let mut new_rs = vec![];
246                                for k in 0..rs.len() {
247                                    if k == j {continue} else {new_rs.push(rs[k].clone())}
248                                }
249                                return Some(Inference::replace_one(
250                                    ea.clone(),
251                                    Sum(new_ls, new_rs),
252                                    cache
253                                ));
254                            }
255                        }
256                    }
257                }
258            }
259
260            // Insert assignment into other equations.
261            if cache.contains(&InsertAssignments) {
262                if let Sum(ref ls, ref rs) = *ea {
263                    if ls.len() == 1 && rs.len() == 1 {
264                        if let Const(_) = rs[0] {
265                            for eb in facts {
266                                if ea == eb {continue};
267                                if let Sum(ref ls2, ref rs2) = *eb {
268                                    for i in 0..ls2.len() {
269                                        if ls2[i] == ls[0] {
270                                            let new_ls: Vec<Expr> = ls2.clone().into_iter()
271                                                .filter(|n| n != &ls[0])
272                                                .chain(rs.clone().into_iter())
273                                                .collect();
274                                            return Some(Inference::replace_one(
275                                                eb.clone(),
276                                                Sum(new_ls, rs2.clone()),
277                                                cache
278                                            ));
279                                        }
280                                    }
281                                }
282                            }
283                        }
284                    }
285                }
286            }
287
288            // Subtract constants on both sides.
289            if cache.contains(&SubtractConstants) {
290                if let Sum(ref ls, ref rs) = *ea {
291                    for i in 0..ls.len() {
292                        for j in 0..rs.len() {
293                            if let (&Const(x), &Const(y)) = (&ls[i], &rs[j]) {
294                                let mut new_ls = vec![];
295                                for k in 0..ls.len() {
296                                    if k == i {
297                                        if x == y {continue}
298                                        else if x > y {new_ls.push(Const(x-y))}
299                                    } else {
300                                        new_ls.push(ls[k].clone())
301                                    }
302                                }
303                                let mut new_rs = vec![];
304                                for k in 0..rs.len() {
305                                    if k == j {
306                                        if x == y {continue}
307                                        else if y > x {new_rs.push(Const(y-x))}
308                                    } else {
309                                        new_rs.push(rs[k].clone())
310                                    }
311                                }
312                                return Some(Inference::replace_one(
313                                    ea.clone(),
314                                    Sum(new_ls, new_rs),
315                                    cache
316                                ));
317                            }
318                        }
319                    }
320                }
321            }
322        }
323    }
324
325    if cache.contains(&ExpandAll) {
326        for ea in facts {
327            if let Sum(ref ls, ref rs) = *ea {
328                for eb in facts {
329                    if ea == eb {continue};
330                    if let Sum(ref ls2, ref rs2) = *eb {
331                        if ls == ls2 {
332                            // X = Y & X = Z => Y = Z
333                            let new_expr = Sum(rs.clone(), rs2.clone());
334                            if !cache.contains(&new_expr) {
335                                return Some(Propagate(new_expr));
336                            }
337                        }
338                        if rs == rs2 {
339                            // X = Y & Z = Y => X = Z
340                            let new_expr = Sum(ls.clone(), ls2.clone());
341                            if !cache.contains(&new_expr) {
342                                return Some(Propagate(new_expr));
343                            }
344                        }
345                    }
346                }
347            }
348        }
349
350        // Consume `ExpandAll` to allow other simplifications to take place.
351        return Some(OneTrue {from: ExpandAll});
352    }
353
354    // Narrow down alternatives with recursive theorem proving.
355    for ea in facts {
356        // A unique alternative means there is an assignment.
357        if let Alternatives(a, ref alternatives) = *ea {
358            if alternatives.len() == 1 {
359                return Some(Inference::replace_one(
360                    ea.clone(),
361                    Sum(vec![Var(a)], vec![Const(alternatives[0])]),
362                    cache
363                ));
364            }
365            if alternatives.len() == 0 {
366                return Some(Propagate(False));
367            }
368        }
369
370        if let Narrow(a) = *ea {
371            for eb in facts {
372                if let Range {var, start, end} = *eb {
373                    if var == a {
374                        // Try the whole range.
375                        // Call the solver recursively
376                        let mut alternatives = vec![];
377                        for k in start..end+1 {
378                            let mut new_facts = vec![];
379                            for i in 0..facts.len() {
380                                // Ignore `Narrow` directive to avoid infinite recursion.
381                                if &facts[i] == ea {continue};
382                                new_facts.push(facts[i].clone());
383                            }
384                            new_facts.push(Sum(vec![Var(var)], vec![Const(k)]));
385                            let res = solve_minimum(new_facts, infer);
386                            if !res.iter().any(|n| n == &False) {
387                                alternatives.push(k);
388                            }
389                        }
390                        let new_expr = Alternatives(a, alternatives);
391                        return Some(SimplifyOne {from: ea.clone(), to: new_expr});
392                    }
393                }
394            }
395        }
396    }
397
398    None
399}
400
401fn main() {
402    let start = vec![
403        // a + b + c = 15
404        Sum(vec![Var("a"), Var("b"), Var("c")], vec![Const(15)]),
405        // d + e + f = 15
406        Sum(vec![Var("d"), Var("e"), Var("f")], vec![Const(15)]),
407        // g + h + i = 15
408        Sum(vec![Var("g"), Var("h"), Var("i")], vec![Const(15)]),
409
410        // a + d + g = 15
411        Sum(vec![Var("a"), Var("d"), Var("g")], vec![Const(15)]),
412        // b + e + h = 15
413        Sum(vec![Var("b"), Var("e"), Var("h")], vec![Const(15)]),
414        // c + f + i = 15
415        Sum(vec![Var("c"), Var("f"), Var("i")], vec![Const(15)]),
416
417        // a + e + i = 15
418        Sum(vec![Var("a"), Var("e"), Var("i")], vec![Const(15)]),
419        // c + e + g = 15
420        Sum(vec![Var("c"), Var("e"), Var("g")], vec![Const(15)]),
421
422        Range {var: "a", start: 1, end: 9},
423        Range {var: "b", start: 1, end: 9},
424        Range {var: "c", start: 1, end: 9},
425        Range {var: "d", start: 1, end: 9},
426        Range {var: "e", start: 1, end: 9},
427        Range {var: "f", start: 1, end: 9},
428        Range {var: "g", start: 1, end: 9},
429        Range {var: "h", start: 1, end: 9},
430        Range {var: "i", start: 1, end: 9},
431
432        // List of tactics.
433        SortAll,
434        ExpandAll,
435        RemoveRefl,
436        RemoveEqualTermsOnBothSides,
437        SubtractConstants,
438        InsertAssignments,
439        CheckContradictingConstants,
440        AbsoluteNumbers,
441        SumConstants,
442        CheckRange,
443        UniqueAssignments,
444        RemoveRangeWhenAssigned,
445
446        // Uncomment/comment the following to investiage various solutions.
447
448        /*
449        // Get the alternative values that "a" can have.
450        // Multiple `Narrow` means nested recursive theorem proving,
451        // such that the top alternative is narrowed down.
452        Narrow("a"),
453        Narrow("b"),
454        // Narrow("c"), // skip "c" because we don't need it.
455        // Better to try "d", since it is better at narrowing down results.
456        Narrow("d"),
457        */
458
459        Sum(vec![Var("a")], vec![Const(2)]),
460        Sum(vec![Var("b")], vec![Const(7)]),
461        Narrow("d"),
462
463        /*
464        Sum(vec![Var("a")], vec![Const(4)]),
465        Sum(vec![Var("b")], vec![Const(3)]),
466        Narrow("d"),
467        */
468
469        /*
470        Sum(vec![Var("a")], vec![Const(4)]),
471        Sum(vec![Var("b")], vec![Const(9)]),
472        Narrow("d"),
473        */
474
475        /*
476        Sum(vec![Var("a")], vec![Const(6)]),
477        Sum(vec![Var("b")], vec![Const(1)]),
478        Narrow("d"),
479        */
480
481        /*
482        Sum(vec![Var("a")], vec![Const(6)]),
483        Sum(vec![Var("b")], vec![Const(7)]),
484        Narrow("d"),
485        */
486
487        /*
488        Sum(vec![Var("a")], vec![Const(8)]),
489        Sum(vec![Var("b")], vec![Const(1)]),
490        Narrow("d"),
491        */
492
493        /*
494        Sum(vec![Var("a")], vec![Const(8)]),
495        Sum(vec![Var("b")], vec![Const(3)]),
496        Narrow("d"),
497        */
498    ];
499
500    let res = solve_minimum(start, infer);
501    for i in 0..res.len() {
502        println!("{:?}", res[i]);
503    }
504}