Skip to content

Update Graph search in AIMA4e #450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: AIMA4e
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 49 additions & 57 deletions core/src/main/java/aima/core/search/basic/uninformed/GraphSearch.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
package aima.core.search.basic.uninformed;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import aima.core.search.api.Node;
import aima.core.search.api.NodeFactory;
import aima.core.search.api.Problem;
import aima.core.search.api.SearchController;
import aima.core.search.api.SearchForActionsFunction;
import aima.core.search.api.*;
import aima.core.search.basic.support.BasicNodeFactory;
import aima.core.search.basic.support.BasicSearchController;

import java.util.*;

/**
* Artificial Intelligence A Modern Approach (4th Edition): Figure ??, page ??.
* <br>
Expand All @@ -31,84 +23,84 @@
* expand the chosen node, adding the resulting nodes to the frontier
* only if not in the frontier or explored set
* </pre>
*
* <p>
* Figure ?? An informal description of the general graph-search algorithm.
*
*
* @author Ciaran O'Reilly
*/
public class GraphSearch<A, S> implements SearchForActionsFunction<A, S> {


protected NodeFactory<A, S> nodeFactory = new BasicNodeFactory<>();
protected SearchController<A, S> searchController = new BasicSearchController<A, S>();

public GraphSearch() {
//empty constructor
}

// function GRAPH-SEARCH(problem) returns a solution, or failure
@Override
public List<A> apply(Problem<A, S> problem) {
// initialize the frontier using the initial state of problem
Queue<Node<A, S>> frontier = newFrontier(problem.initialState());
// initialize the explored set to be empty
Set<S> explored = newExploredSet();
// initialize the reached table to be empty
Map<S, Node<A, S>> reached = new HashMap<>();
// initialize the solution
List<A> solution = failure();

// if the frontier is empty then return failure
if (frontier.isEmpty()) {
return failure();
}
// loop do
while (true) {
// if the frontier is empty then return failure
if (frontier.isEmpty()) {
return failure();
}
while (!frontier.isEmpty()) {
// choose a leaf node and remove it from the frontier
Node<A, S> node = frontier.remove();
// if the node contains a goal state then return the corresponding
// solution
if (isGoalState(node, problem)) {
return solution(node);
}
// add the node to the explored set
explored.add(node.state());
// expand the chosen node, adding the resulting nodes to the
// frontier
for (A action : problem.actions(node.state())) {
Node<A, S> child = newChildNode(problem, node, action);
// only if not in the frontier or explored set
if (!(containsState(frontier, child) || explored.contains(child.state()))) {
Node<A, S> parent = frontier.remove();
// expand the chosen node
for (Node<A, S> child : expand(problem, parent)) {
// only if the child is not in reached or child is a cheaper path than reached[child.state()]
if (!reached.containsKey(child.state()) || child.pathCost() < reached.get(child.state()).pathCost()) {
// add child in reached and frontier
reached.put(child.state(), child);
frontier.add(child);
// if child is a goal and is cheaper than the best solution found so far then update the solution
if (isGoalState(child, problem)) {
solution = getSolution(child);
}
}
}
}
return solution;
}

//
// Supporting Code
protected NodeFactory<A, S> nodeFactory = new BasicNodeFactory<>();
protected SearchController<A, S> searchController = new BasicSearchController<A, S>();

public GraphSearch() {

public List<Node<A, S>> expand(Problem<A, S> problem, Node<A, S> parent) {
List<Node<A, S>> nodes = new ArrayList<>();
for (A action : problem.actions(parent.state())) {
Node<A, S> node = newChildNode(problem, parent, action);
nodes.add(node);
}
return nodes;
}

public Node<A, S> newChildNode(Problem<A, S> problem, Node<A, S> node, A action) {
return nodeFactory.newChildNode(problem, node, action);
}

public Queue<Node<A, S>> newFrontier(S initialState) {
Queue<Node<A, S>> frontier = new LinkedList<>();
frontier.add(nodeFactory.newRootNode(initialState));
return frontier;
}

public Set<S> newExploredSet() {
return new HashSet<>();
}


public List<A> failure() {
return searchController.failure();
}

public List<A> solution(Node<A, S> node) {
public List<A> getSolution(Node<A, S> node) {
return searchController.solution(node);
}

public boolean isGoalState(Node<A, S> node, Problem<A, S> problem) {
return searchController.isGoalState(node, problem);
}

public boolean containsState(Queue<Node<A, S>> frontier, Node<A, S> child) {
// NOTE: Not very efficient (i.e. linear in the size of the frontier)
return frontier.stream().anyMatch(frontierNode -> frontierNode.state().equals(child.state()));
}

}