66import aima .core .search .csp .Variable ;
77import aima .core .util .Tasks ;
88
9- import java .util .HashSet ;
10- import java .util .List ;
11- import java .util .Optional ;
12- import java .util .Set ;
9+ import java .util .*;
1310import java .util .stream .Collectors ;
1411
1512// vm-options (Java > 8): --module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.fxml
1613
1714/**
18- * Simple version of Backtracking with backjumping. It analyzes reasons for dead-ends by means of so called nogoods
19- * and by that tries to avoid useless search in subspaces without solution.
15+ * Artificial Intelligence A Modern Approach (3rd Ed.): Section 6.3, Page 223.<br>
16+ * A more intelligent approach to backtracking is to backtrack to a variable that might fix the problem — a variable
17+ * that was responsible for making one of the possible values of SA impossible. To do this, we will keep track of
18+ * a set of assignments that are in conflict with some value for SA. The set (in this case {Q=red, NSW=green, V=blue,}),
19+ * is called the conflict set for SA. The backjumping method backtracks to the most recent assignment in
20+ * the conflict set; in this case, backjumping would jump over Tasmania and try a new value for V. This method
21+ * is easily implemented by a modification to BACK TRACK such that it accumulates the conflict set while checking
22+ * for a legal value to assign. If no legal value is found, the algorithm should return the most recent element of
23+ * the conflict set along with the failure indicator.<br>
24+ *
25+ * In this implementation, backtrack returns the whole set of variables causing the dead-end instead of just the most
26+ * recent element. The set of variables involved in a conflict set is called nogood here.
2027 *
2128 * Limitations:
2229 * Only one nogood is tracked at a time thought there could be more of them
@@ -82,11 +89,7 @@ private SolutionOrNogood<VAR, VAL> backtrack(CSP<VAR, VAL> csp, Assignment<VAR,
8289 else
8390 result .nogood .addAll (res .nogood );
8491 } else {
85- for (Constraint <VAR , VAL > cons : csp .getConstraints (var ))
86- if (!cons .isSatisfiedWith (assignment )) {
87- result .nogood .addAll (cons .getScope ());
88- break ; // only one nogood is tracked (kiss)
89- }
92+ result .nogood .addAll (findCause (csp , assignment , var ));
9093 }
9194 assignment .remove (var );
9295 }
@@ -95,6 +98,18 @@ private SolutionOrNogood<VAR, VAL> backtrack(CSP<VAR, VAL> csp, Assignment<VAR,
9598 return result ;
9699 }
97100
101+ /**
102+ * Gets an assignment which is inconsistent at <code>var</code> and returns a collection of variables causing the
103+ * inconsistency. This implementation just returns the scope of one unsatisfied constraint. A smarter
104+ * selection could increase jump length.
105+ */
106+ protected Collection <VAR > findCause (CSP <VAR , VAL > csp , Assignment <VAR , VAL > assignment , VAR var ) {
107+ for (Constraint <VAR , VAL > cons : csp .getConstraints (var ))
108+ if (!cons .isSatisfiedWith (assignment ))
109+ return cons .getScope ();
110+ return null ; // will never happen!
111+ }
112+
98113 /**
99114 * Primitive operation, selecting a not yet assigned variable.
100115 */
@@ -113,6 +128,9 @@ private Iterable<VAL> orderDomainValues(CSP<VAR, VAL> csp, Assignment<VAR, VAL>
113128 return (valOrderingStrategy != null ) ? valOrderingStrategy .apply (csp , assignment , var ) : csp .getDomain (var );
114129 }
115130
131+ /**
132+ * Data structure which can store an assignment as well as a nogood.
133+ */
116134 private static class SolutionOrNogood <VAR extends Variable , VAL > {
117135 Assignment <VAR , VAL > solution ;
118136 Set <VAR > nogood = new HashSet <>(); // set of variables whose current value bindings caused a dead-end
0 commit comments