All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aima.core.search.csp.ImprovedBacktrackingStrategy Maven / Gradle / Ivy

Go to download

AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.

There is a newer version: 3.0.0
Show newest version
package aima.core.search.csp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import aima.core.util.datastructure.Pair;

public class ImprovedBacktrackingStrategy extends BacktrackingStrategy {
	protected Selection selectionStrategy = Selection.DEFAULT_ORDER;
	protected Inference inferenceStrategy = Inference.NONE;
	protected boolean isLCVHeuristicEnabled;

	/** Creates a strategy which is by default equivalent to plain backtracking. */
	public ImprovedBacktrackingStrategy() {
	}

	/** Creates a backtracking strategy with the specified features. */
	public ImprovedBacktrackingStrategy(boolean enableMRV, boolean enableDeg,
			boolean enableAC3, boolean enableLCV) {
		if (enableMRV)
			setVariableSelection(enableDeg ? Selection.MRV_DEG : Selection.MRV);
		if (enableAC3)
			setInference(Inference.AC3);
		enableLCV(enableLCV);
	}

	/** Selects the algorithm for SELECT-UNASSIGNED-VARIABLE */
	public void setVariableSelection(Selection sStrategy) {
		selectionStrategy = sStrategy;
	}

	/** Selects the algorithm for INFERENCE. */
	public void setInference(Inference iStrategy) {
		inferenceStrategy = iStrategy;
	}

	/**
	 * Selects the least constraining value heuristic as implementation for
	 * ORDER-DOMAIN-VALUES.
	 */
	public void enableLCV(boolean state) {
		isLCVHeuristicEnabled = state;
	}

	/**
	 * Starts with a constraint propagation if AC-3 is enabled and then calls
	 * the super class implementation.
	 */
	public Assignment solve(CSP csp) {
		if (inferenceStrategy == Inference.AC3) {
			DomainRestoreInfo info = new AC3Strategy().reduceDomains(csp);
			if (!info.isEmpty()) {
				fireStateChanged(csp);
				if (info.isEmptyDomainFound())
					return null;
			}
		}
		return super.solve(csp);
	}

	/**
	 * Primitive operation, selecting a not yet assigned variable.
	 */
	@Override
	protected Variable selectUnassignedVariable(Assignment assignment, CSP csp) {
		switch (selectionStrategy) {
		case MRV:
			return applyMRVHeuristic(csp, assignment).get(0);
		case MRV_DEG:
			List vars = applyMRVHeuristic(csp, assignment);
			return applyDegreeHeuristic(vars, assignment, csp).get(0);
		default:
			for (Variable var : csp.getVariables()) {
				if (!(assignment.hasAssignmentFor(var)))
					return var;
			}
		}
		return null;
	}

	/**
	 * Primitive operation, ordering the domain values of the specified
	 * variable.
	 */
	@Override
	protected Iterable orderDomainValues(Variable var,
			Assignment assignment, CSP csp) {
		if (!isLCVHeuristicEnabled) {
			return csp.getDomain(var);
		} else {
			return applyLeastConstrainingValueHeuristic(var, csp);
		}
	}

	/**
	 * Primitive operation, which tries to prune out values from the CSP which
	 * are not possible anymore when extending the given assignment to a
	 * solution.
	 * 
	 * @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 DomainRestoreInfo inference(Variable var, Assignment assignment,
			CSP csp) {
		switch (inferenceStrategy) {
		case FORWARD_CHECKING:
			return doForwardChecking(var, assignment, csp);
		case AC3:
			return new AC3Strategy().reduceDomains(var,
					assignment.getAssignment(var), csp);
		default:
			return new DomainRestoreInfo().compactify();
		}
	}

	// //////////////////////////////////////////////////////////////
	// heuristics for selecting the next unassigned variable and domain ordering

	/** Implements the minimum-remaining-values heuristic. */
	private List applyMRVHeuristic(CSP csp, Assignment assignment) {
		List result = new ArrayList();
		int mrv = Integer.MAX_VALUE;
		for (Variable var : csp.getVariables()) {
			if (!assignment.hasAssignmentFor(var)) {
				int num = csp.getDomain(var).size();
				if (num <= mrv) {
					if (num < mrv) {
						result.clear();
						mrv = num;
					}
					result.add(var);
				}
			}
		}
		return result;
	}

	/** Implements the degree heuristic. */
	private List applyDegreeHeuristic(List vars,
			Assignment assignment, CSP csp) {
		List result = new ArrayList();
		int maxDegree = Integer.MIN_VALUE;
		for (Variable var : vars) {
			int degree = 0;
			for (Constraint constraint : csp.getConstraints(var)) {
				Variable neighbor = csp.getNeighbor(var, constraint);
				if (!assignment.hasAssignmentFor(neighbor)
						&& csp.getDomain(neighbor).size() > 1)
					++degree;
			}
			if (degree >= maxDegree) {
				if (degree > maxDegree) {
					result.clear();
					maxDegree = degree;
				}
				result.add(var);
			}
		}
		return result;
	}

	/** Implements the least constraining value heuristic. */
	private List applyLeastConstrainingValueHeuristic(Variable var,
			CSP csp) {
		List> pairs = new ArrayList>();
		for (Object value : csp.getDomain(var)) {
			int num = countLostValues(var, value, csp);
			pairs.add(new Pair(value, num));
		}
		Collections.sort(pairs, new Comparator>() {
			@Override
			public int compare(Pair o1,
					Pair o2) {
				return o1.getSecond() < o2.getSecond() ? -1
						: o1.getSecond() > o2.getSecond() ? 1 : 0;
			}
		});
		List result = new ArrayList();
		for (Pair pair : pairs)
			result.add(pair.getFirst());
		return result;
	}

	private int countLostValues(Variable var, Object value, CSP csp) {
		int result = 0;
		Assignment assignment = new Assignment();
		assignment.setAssignment(var, value);
		for (Constraint constraint : csp.getConstraints(var)) {
			Variable neighbor = csp.getNeighbor(var, constraint);
			for (Object nValue : csp.getDomain(neighbor)) {
				assignment.setAssignment(neighbor, nValue);
				if (!constraint.isSatisfiedWith(assignment)) {
					++result;
				}
			}
		}
		return result;
	}

	// //////////////////////////////////////////////////////////////
	// inference algorithms

	/** Implements forward checking. */
	private DomainRestoreInfo doForwardChecking(Variable var,
			Assignment assignment, CSP csp) {
		DomainRestoreInfo result = new DomainRestoreInfo();
		for (Constraint constraint : csp.getConstraints(var)) {
			List scope = constraint.getScope();
			if (scope.size() == 2) {
				for (Variable neighbor : constraint.getScope()) {
					if (!assignment.hasAssignmentFor(neighbor)) {
						if (revise(neighbor, constraint, assignment, csp,
								result)) {
							if (csp.getDomain(neighbor).isEmpty()) {
								result.setEmptyDomainFound(true);
								return result;
							}
						}
					}
				}
			}
		}
		return result;
	}

	private boolean revise(Variable var, Constraint constraint,
			Assignment assignment, CSP csp, DomainRestoreInfo info) {

		boolean revised = false;
		for (Object value : csp.getDomain(var)) {
			assignment.setAssignment(var, value);
			if (!constraint.isSatisfiedWith(assignment)) {
				info.storeDomainFor(var, csp.getDomain(var));
				csp.removeValueFromDomain(var, value);
				revised = true;
			}
			assignment.removeAssignment(var);
		}
		return revised;
	}

	// //////////////////////////////////////////////////////////////
	// two enumerations

	public enum Selection {
		DEFAULT_ORDER, MRV, MRV_DEG
	}

	public enum Inference {
		NONE, FORWARD_CHECKING, AC3
	}
}