package aima.core.search.csp; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; /** * Artificial Intelligence A Modern Approach (3rd Ed.): Section 6.1, Page 202.<br> * <br> * A constraint satisfaction problem or CSP consists of three components, X, D, * and C: * <ul> * <li>X is a set of variables, {X1, ... ,Xn}.</li> * <li>D is a set of domains, {D1, ... ,Dn}, one for each variable.</li> * <li>C is a set of constraints that specify allowable combinations of values.</li> * </ul> * * @author Ruediger Lunde */ public class CSP<VAR extends Variable, VAL> implements Cloneable { private List<VAR> variables; private List<Domain<VAL>> domains; private List<Constraint<VAR, VAL>> constraints; /** * Lookup, which maps a variable to its index in the list of variables. */ private Hashtable<Variable, Integer> varIndexHash; /** * Constraint network. Maps variables to those constraints in which they * participate. */ private Hashtable<Variable, List<Constraint<VAR, VAL>>> cnet; /** * Creates a new CSP. */ public CSP() { variables = new ArrayList<>(); domains = new ArrayList<>(); constraints = new ArrayList<>(); varIndexHash = new Hashtable<>(); cnet = new Hashtable<>(); } /** * Creates a new CSP. */ public CSP(List<VAR> vars) { this(); vars.forEach(this::addVariable); } /** * Adds a new variable only if its name is new. */ protected void addVariable(VAR var) { if (!varIndexHash.containsKey(var)) { Domain<VAL> emptyDomain = new Domain<>(Collections.emptyList()); variables.add(var); domains.add(emptyDomain); varIndexHash.put(var, variables.size() - 1); cnet.put(var, new ArrayList<>()); } else { throw new IllegalArgumentException("Variable with same name already exists."); } } public List<VAR> getVariables() { return Collections.unmodifiableList(variables); } public int indexOf(Variable var) { return varIndexHash.get(var); } public void setDomain(VAR var, Domain<VAL> domain) { domains.set(indexOf(var), domain); } public Domain<VAL> getDomain(Variable var) { return domains.get(varIndexHash.get(var)); } /** * Replaces the domain of the specified variable by new domain, which * contains all values of the old domain except the specified value. */ public boolean removeValueFromDomain(VAR var, VAL value) { Domain<VAL> currDomain = getDomain(var); List<VAL> values = new ArrayList<>(currDomain.size()); for (VAL v : currDomain) if (!v.equals(value)) values.add(v); if (values.size() < currDomain.size()) { setDomain(var, new Domain<>(values)); return true; } return false; } public void addConstraint(Constraint<VAR, VAL> constraint) { constraints.add(constraint); for (VAR var : constraint.getScope()) cnet.get(var).add(constraint); } public boolean removeConstraint(Constraint<VAR, VAL> constraint) { boolean result = constraints.remove(constraint); if (result) for (VAR var : constraint.getScope()) cnet.get(var).remove(constraint); return result; } public List<Constraint<VAR, VAL>> getConstraints() { return constraints; } /** * Returns all constraints in which the specified variable participates. */ public List<Constraint<VAR, VAL>> getConstraints(Variable var) { return cnet.get(var); } /** * Returns for binary constraints the other variable from the scope. * * @return a variable or null for non-binary constraints. */ public VAR getNeighbor(VAR var, Constraint<VAR, VAL> constraint) { List<VAR> scope = constraint.getScope(); if (scope.size() == 2) { if (var.equals(scope.get(0))) return scope.get(1); else if (var.equals(scope.get(1))) return scope.get(0); } return null; } /** * Returns a copy which contains a copy of the domains list and is in all * other aspects a flat copy of this. */ @SuppressWarnings("unchecked") public CSP<VAR, VAL> copyDomains() { CSP<VAR, VAL> result; try { result = (CSP<VAR, VAL>) clone(); result.domains = new ArrayList<>(domains.size()); result.domains.addAll(domains); } catch (CloneNotSupportedException e) { throw new UnsupportedOperationException("Could not copy domains."); } return result; } }