package aima.core.search.csp;
import aima.core.util.datastructure.Pair;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* Defines variable and value selection heuristics for CSP backtracking strategies.
* @author Ruediger Lunde
*/
public class CspHeuristics {
public interface VariableSelection<VAR extends Variable, VAL> {
List<VAR> apply(List<VAR> vars, CSP<VAR, VAL> csp);
}
public interface ValueSelection<VAR extends Variable, VAL> {
List<VAL> apply(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp);
}
public static <VAR extends Variable, VAL> VariableSelection<VAR, VAL> mrv() { return new MrvHeuristic<>(); }
public static <VAR extends Variable, VAL> VariableSelection<VAR, VAL> deg() { return new DegHeuristic<>(); }
public static <VAR extends Variable, VAL> VariableSelection<VAR, VAL> mrvDeg() {
return (vars, csp) -> new DegHeuristic<VAR, VAL>().apply(new MrvHeuristic<VAR, VAL>().apply(vars, csp), csp);
}
public static <VAR extends Variable, VAL> ValueSelection<VAR, VAL> lcv() { return new LcvHeuristic<>();}
/**
* Implements the minimum-remaining-values heuristic.
*/
public static class MrvHeuristic<VAR extends Variable, VAL> implements VariableSelection<VAR, VAL> {
public List<VAR> apply(List<VAR> vars, CSP<VAR, VAL> csp) {
List<VAR> result = new ArrayList<>();
int mrv = Integer.MAX_VALUE;
for (VAR var : vars) {
int rv = csp.getDomain(var).size();
if (rv <= mrv) {
if (rv < mrv) {
result.clear();
mrv = rv;
}
result.add(var);
}
}
return result;
}
}
/**
* Implements the degree heuristic. Constraints with arbitrary scope size are supported.
*/
public static class DegHeuristic<VAR extends Variable, VAL> implements VariableSelection<VAR, VAL> {
public List<VAR> apply(List<VAR> vars, CSP<VAR, VAL> csp) {
List<VAR> result = new ArrayList<>();
int maxDegree = -1;
for (VAR var : vars) {
int degree = csp.getConstraints(var).size();
if (degree >= maxDegree) {
if (degree > maxDegree) {
result.clear();
maxDegree = degree;
}
result.add(var);
}
}
return result;
}
}
/**
* Implements the least constraining value heuristic.
*/
public static class LcvHeuristic<VAR extends Variable, VAL> implements ValueSelection<VAR, VAL> {
public List<VAL> apply(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) {
List<Pair<VAL, Integer>> pairs = new ArrayList<>();
for (VAL value : csp.getDomain(var)) {
int num = countLostValues(var, value, assignment, csp);
pairs.add(new Pair<>(value, num));
}
return pairs.stream().sorted(Comparator.comparing(Pair::getSecond)).map(Pair::getFirst)
.collect(Collectors.toList());
}
/**
* Ignores constraints which are not binary.
*/
private int countLostValues(VAR var, VAL value, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) {
int result = 0;
Assignment<VAR, VAL> assign = new Assignment<>();
assign.add(var, value);
for (Constraint<VAR, VAL> constraint : csp.getConstraints(var)) {
if (constraint.getScope().size() == 2) {
VAR neighbor = csp.getNeighbor(var, constraint);
if (!assignment.contains(neighbor))
for (VAL nValue : csp.getDomain(neighbor)) {
assign.add(neighbor, nValue);
if (!constraint.isSatisfiedWith(assign)) {
++result;
}
}
}
}
return result;
}
}
}