package aima.core.search.csp; import java.util.List; import java.util.stream.Collectors; import aima.core.search.csp.inference.*; /** * This backtracking search implementation can be configured with arbitrary strategies for variable selection, * value ordering, and inference. These strategies are represented by objects implementing standard interfaces. * The design supports experiments with user-defined strategies of all kinds. * * @author Ruediger Lunde */ public class FlexibleBacktrackingSolver<VAR extends Variable, VAL> extends AbstractBacktrackingSolver<VAR, VAL> { private CspHeuristics.VariableSelection<VAR, VAL> varSelectionStrategy; private CspHeuristics.ValueSelection<VAR, VAL> valSelectionStrategy; private InferenceStrategy<VAR, VAL> inferenceStrategy; /** * Selects the algorithm for SELECT-UNASSIGNED-VARIABLE. Uses the fluent interface design pattern. */ public FlexibleBacktrackingSolver<VAR, VAL> set(CspHeuristics.VariableSelection<VAR, VAL> varStrategy) { varSelectionStrategy = varStrategy; return this; } /** * Selects the algorithm for ORDER-DOMAIN-VALUES. Uses the fluent interface design pattern. */ public FlexibleBacktrackingSolver<VAR, VAL> set(CspHeuristics.ValueSelection<VAR, VAL> valStrategy) { valSelectionStrategy = valStrategy; return this; } /** * Selects the algorithm for INFERENCE. Uses the fluent interface design pattern. */ public FlexibleBacktrackingSolver<VAR, VAL> set(InferenceStrategy<VAR, VAL> iStrategy) { inferenceStrategy = iStrategy; return this; } /** * Selects MRV&DEG for variable selection, LCV for domain ordering and AC3 as inference method. */ public FlexibleBacktrackingSolver<VAR, VAL> setAll() { set(CspHeuristics.mrvDeg()).set(CspHeuristics.lcv()).set(new AC3Strategy<>()); return this; } /** * Applies an initial inference step and then calls the super class implementation. */ @Override public Assignment<VAR, VAL> solve(CSP<VAR, VAL> csp) { if (inferenceStrategy != null) { csp = csp.copyDomains(); // do not change the original CSP! InferenceLog log = inferenceStrategy.apply(csp); if (!log.isEmpty()) { fireStateChanged(csp, null); if (log.inconsistencyFound()) return null; } } return super.solve(csp); } /** * Primitive operation, selecting a not yet assigned variable. */ @Override protected VAR selectUnassignedVariable(Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) { List<VAR> vars = csp.getVariables().stream() .filter((v) -> !assignment.contains(v)).collect(Collectors.toList()); if (varSelectionStrategy != null) vars = varSelectionStrategy.apply(vars, csp); return vars.get(0); } /** * Primitive operation, ordering the domain values of the specified variable. */ @Override protected Iterable<VAL> orderDomainValues(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) { if (valSelectionStrategy != null) { return valSelectionStrategy.apply(var, assignment, csp); } else { return csp.getDomain(var); } } /** * Primitive operation, which tries to optimize the CSP representation with respect to a new assignment. * * @return An object which provides informations about (1) whether changes * have been performed, (2) possibly inferred empty domains , and * (3) how to restore the domains. */ @Override protected InferenceLog<VAR, VAL> inference(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) { if (inferenceStrategy != null) return inferenceStrategy.apply(var, assignment, csp); else return InferenceLog.emptyLog(); } }