package aima.core.search.csp;
import aima.core.search.csp.inference.InferenceLog;
import aima.core.util.CancelableThread;
/**
* Artificial Intelligence A Modern Approach (3rd Ed.): Figure 6.5, Page 215.<br>
* <br>
* <p>
* <pre>
* <code>
* function BACKTRACKING-SEARCH(csp) returns a solution, or failure
* return BACKTRACK({ }, csp)
*
* function BACKTRACK(assignment, csp) returns a solution, or failure
* if assignment is complete then return assignment
* var = SELECT-UNASSIGNED-VARIABLE(csp)
* for each value in ORDER-DOMAIN-VALUES(var, assignment, csp) do
* if value is consistent with assignment then
* add {var = value} to assignment
* inferences = INFERENCE(csp, var, value)
* if inferences != failure then
* add inferences to assignment
* result = BACKTRACK(assignment, csp)
* if result != failure then
* return result
* remove {var = value} and inferences from assignment
* return failure
* </code>
* </pre>
* <p>
* Figure 6.5 A simple backtracking algorithm for constraint satisfaction
* problems. The algorithm is modeled on the recursive depth-first search of
* Chapter 3. By varying the functions SELECT-UNASSIGNED-VARIABLE and
* ORDER-DOMAIN-VALUES, we can implement the general-purpose heuristic discussed
* in the text. The function INFERENCE can optionally be used to impose arc-,
* path-, or k-consistency, as desired. If a value choice leads to failure
* (noticed wither by INFERENCE or by BACKTRACK), then value assignments
* (including those made by INFERENCE) are removed from the current assignment
* and a new value is tried.
*
* @author Ruediger Lunde
*/
public abstract class AbstractBacktrackingSolver<VAR extends Variable, VAL> extends CspSolver<VAR, VAL> {
/** Applies a recursive backtracking search to solve the CSP. */
public Assignment<VAR, VAL> solve(CSP<VAR, VAL> csp) {
return backtrack(new Assignment<>(), csp);
}
/**
* Template method, which can be configured by overriding the three
* primitive operations below.
*/
private Assignment<VAR, VAL> backtrack(Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) {
Assignment<VAR, VAL> result = null;
if (assignment.isComplete(csp.getVariables()) || CancelableThread.currIsCanceled()) {
result = assignment;
} else {
VAR var = selectUnassignedVariable(assignment, csp);
for (VAL value : orderDomainValues(var, assignment, csp)) {
assignment.add(var, value);
fireStateChanged(csp, assignment);
if (assignment.isConsistent(csp.getConstraints(var))) {
InferenceLog<VAR, VAL> log = inference(var, assignment, csp);
if (!log.isEmpty())
fireStateChanged(csp, null);
if (!log.inconsistencyFound()) {
result = backtrack(assignment, csp);
if (result != null)
break;
}
log.undo(csp);
}
assignment.remove(var);
}
}
return result;
}
/**
* Primitive operation, selecting a not yet assigned variable.
*/
protected abstract VAR selectUnassignedVariable(Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp);
/**
* Primitive operation, ordering the domain values of the specified variable.
*/
protected abstract Iterable<VAL> orderDomainValues(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp);
/**
* Primitive operation, which tries to optimize the CSP representation with respect to a new assignment.
*/
protected abstract InferenceLog<VAR, VAL> inference(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp);
}