package aima.core.search.csp;
import java.util.*;
import aima.core.util.Util;
/**
* Artificial Intelligence A Modern Approach (3rd Ed.): Figure 6.11, Page
* 224.<br>
* <br>
* <p>
* <pre>
* <code>
* function TREE-CSP-SOLVER(csp) returns a solution, or failure
* inputs: csp, a CSP with components X, D, C
* n ← number of variables in X
* assignment ← an empty assignment
* root ← any variable in X
* X ← TOPOLOGICALSORT(X, root )
* for j = n down to 2 do
* MAKE-ARC-CONSISTENT(PARENT(Xj),Xj )
* if it cannot be made consistent then return failure
* for i = 1 to n do
* assignment[Xi] ← any consistent value from Di
* if there is no consistent value then return failure (*)
* return assignment
* </code>
*
* <pre>
*
* Figure 6.11 The TREE-CSP-SOLVER algorithm for solving tree-structured CSPs.
* If the CSP has a solution, we will find it in linear time; if not, we will
* detect a contradiction. Comment to (*) (RL): If no empty domain was found in the
* previous loop, this will never happen.
*
* @author Ruediger Lunde
* @author Anurag Rai
*/
public class TreeCspSolver<VAR extends Variable, VAL> extends CspSolver<VAR, VAL> {
@Override
public Assignment<VAR, VAL> solve(CSP<VAR, VAL> csp) {
Assignment<VAR, VAL> assignment = new Assignment<>();
// Select a random root from the List of Variables
VAR root = Util.selectRandomlyFromList(csp.getVariables());
// Sort the variables in topological order
List<VAR> orderedVars = new ArrayList<>();
Map<VAR, Constraint<VAR, VAL>> parentConstraints = new HashMap<>();
topologicalSort(csp, root, orderedVars, parentConstraints);
if (orderedVars.size() < csp.getVariables().size())
return null; // CSP is not tree-structured or not connected or has no solution!
// Establish arc consistency from top to bottom (starting at the bottom).
csp = csp.copyDomains(); // do not change the original CSP!
for (int i = orderedVars.size() - 1; i >= 1; i--) {
VAR var = orderedVars.get(i);
Constraint<VAR, VAL> constraint = parentConstraints.get(var);
VAR parent = csp.getNeighbor(var, constraint);
if (makeArcConsistent(parent, var, constraint, csp)) {
if (csp.getDomain(parent).isEmpty())
return null; // CSP has no solution!
}
}
// Assign values to variables from top to bottom.
for (int i = 0; i < orderedVars.size(); i++) {
VAR var = orderedVars.get(i);
for (VAL value : csp.getDomain(var)) {
assignment.add(var, value);
if (assignment.isConsistent(csp.getConstraints(var)))
break;
}
}
return assignment;
}
/**
* Computes an explicit representation of the tree structure and a total order which is consistent with the
* parent-child relations.
*
* @param csp A CSP
* @param root A root variable
* @param orderedVars The computed total order (initially empty)
* @param parentConstraints The tree structure, maps a variable to the constraint representing the arc to the parent
* variable (initially empty)
*/
private void topologicalSort(CSP<VAR, VAL> csp, VAR root, List<VAR> orderedVars,
Map<VAR, Constraint<VAR, VAL>> parentConstraints) {
if (csp.getDomain(root).isEmpty())
return; // no solution!
parentConstraints.put(root, null);
orderedVars.add(root);
int currParentIdx = -1;
while (currParentIdx < orderedVars.size() - 1) {
currParentIdx++;
VAR currParent = orderedVars.get(currParentIdx);
int arcsPointingUpwards = 0;
for (Constraint<VAR, VAL> constraint : csp.getConstraints(currParent)) {
VAR neighbor = csp.getNeighbor(currParent, constraint);
if (neighbor == null)
return; // this constraint is not binary!
if (parentConstraints.containsKey(neighbor)) {
arcsPointingUpwards++;
if (arcsPointingUpwards > 1)
return; // CSP is not a tree!
} else {
parentConstraints.put(neighbor, constraint);
orderedVars.add(neighbor);
}
}
}
}
private boolean makeArcConsistent(VAR xi, VAR xj, Constraint<VAR, VAL> constraint, CSP<VAR, VAL> csp) {
boolean revised = false;
Assignment<VAR, VAL> assignment = new Assignment<>();
for (VAL vi : csp.getDomain(xi)) {
assignment.add(xi, vi);
boolean consistentExtensionFound = false;
for (VAL vj : csp.getDomain(xj)) {
assignment.add(xj, vj);
if (constraint.isSatisfiedWith(assignment)) {
consistentExtensionFound = true;
break;
}
}
if (!consistentExtensionFound) {
csp.removeValueFromDomain(xi, vi);
revised = true;
}
}
return revised;
}
}