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?
More 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}