aima.core.search.uninformed.DepthLimitedSearch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aima-core Show documentation
Show all versions of aima-core Show documentation
AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.
The newest version!
package aima.core.search.uninformed;
import java.util.List;
import aima.core.agent.Action;
import aima.core.search.framework.Metrics;
import aima.core.search.framework.Node;
import aima.core.search.framework.NodeExpander;
import aima.core.search.framework.SearchForActions;
import aima.core.search.framework.SearchForStates;
import aima.core.search.framework.SearchUtils;
import aima.core.search.framework.problem.Problem;
import aima.core.util.CancelableThread;
/**
* Artificial Intelligence A Modern Approach (3rd Edition): Figure 3.17, page
* 88.
*
*
*
* function DEPTH-LIMITED-SEARCH(problem, limit) returns a solution, or failure/cutoff
* return RECURSIVE-DLS(MAKE-NODE(problem.INITIAL-STATE), problem, limit)
*
* function RECURSIVE-DLS(node, problem, limit) returns a solution, or failure/cutoff
* if problem.GOAL-TEST(node.STATE) then return SOLUTION(node)
* else if limit = 0 then return cutoff
* else
* cutoff_occurred? <- false
* for each action in problem.ACTIONS(node.STATE) do
* child <- CHILD-NODE(problem, node, action)
* result <- RECURSIVE-DLS(child, problem, limit - 1)
* if result = cutoff then cutoff_occurred? <- true
* else if result != failure then return result
* if cutoff_occurred? then return cutoff else return failure
*
*
* Figure 3.17 A recursive implementation of depth-limited search.
*
* @author Ravi Mohan
* @author Ciaran O'Reilly
* @author Mike Stampone
* @author Ruediger Lunde
*/
public class DepthLimitedSearch implements SearchForActions, SearchForStates {
public static final String METRIC_NODES_EXPANDED = "nodesExpanded";
public static final String METRIC_PATH_COST = "pathCost";
public static final Node CUTOFF_NODE = new Node(null);
private final int limit;
private final NodeExpander nodeExpander;
private Metrics metrics = new Metrics();
public DepthLimitedSearch(int limit) {
this(limit, new NodeExpander());
}
public DepthLimitedSearch(int limit, NodeExpander nodeExpander) {
this.limit = limit;
this.nodeExpander = nodeExpander;
}
// function DEPTH-LIMITED-SEARCH(problem, limit) returns a solution, or
// failure/cutoff
/**
* Returns a list of actions to the goal if the goal was found, a list
* containing a single NoOp Action if already at the goal, an empty list if
* the goal could not be found.
*
* @return if goal found, the list of actions to the Goal. If already at the
* goal you will receive a List with a single NoOp Action in it. If
* fail to find the Goal, an empty list will be returned to indicate
* that the search failed.
*/
@Override
public List findActions(Problem p) {
nodeExpander.useParentLinks(true);
Node node = findNode(p);
return (node == null || node == CUTOFF_NODE) ? SearchUtils.failure() : SearchUtils.getSequenceOfActions(node);
}
@Override
public Object findState(Problem p) {
nodeExpander.useParentLinks(false);
Node node = findNode(p);
return (node == null || node == CUTOFF_NODE) ? null : node.getState();
}
public Node findNode(Problem p) {
clearInstrumentation();
// return RECURSIVE-DLS(MAKE-NODE(INITIAL-STATE[problem]), problem,
// limit)
return recursiveDLS(nodeExpander.createRootNode(p.getInitialState()), p, limit);
}
// function RECURSIVE-DLS(node, problem, limit) returns a solution, or
// failure/cutoff
// In Java 8 the result type should be Optional!
/**
* Returns a solution node, the {@link #CUTOFF_NODE}, or null (failure).
*/
private Node recursiveDLS(Node node, Problem problem, int limit) {
// if problem.GOAL-TEST(node.STATE) then return SOLUTION(node)
if (SearchUtils.isGoalState(problem, node)) {
metrics.set(METRIC_PATH_COST, node.getPathCost());
return node;
} else if (0 == limit || CancelableThread.currIsCanceled()) {
// else if limit = 0 then return cutoff
return CUTOFF_NODE;
} else {
// else
// cutoff_occurred? <- false
boolean cutoff_occurred = false;
// for each action in problem.ACTIONS(node.STATE) do
metrics.incrementInt(METRIC_NODES_EXPANDED);
for (Node child : nodeExpander.expand(node, problem)) {
// child <- CHILD-NODE(problem, node, action)
// result <- RECURSIVE-DLS(child, problem, limit - 1)
Node result = recursiveDLS(child, problem, limit - 1);
// if result = cutoff then cutoff_occurred? <- true
if (result == CUTOFF_NODE) {
cutoff_occurred = true;
} else if (result != null) {
// else if result != failure then return result
return result;
}
}
// if cutoff_occurred? then return cutoff else return failure
if (cutoff_occurred) {
return CUTOFF_NODE;
} else {
return null;
}
}
}
@Override
public NodeExpander getNodeExpander() {
return nodeExpander;
}
/**
* Returns all the search metrics.
*/
@Override
public Metrics getMetrics() {
return metrics;
}
/**
* Sets the nodes expanded and path cost metrics to zero.
*/
private void clearInstrumentation() {
metrics.set(METRIC_NODES_EXPANDED, 0);
metrics.set(METRIC_PATH_COST, 0);
}
}