From 893ca3b2d85976501f41a9e5625bc97dc8a5ec18 Mon Sep 17 00:00:00 2001 From: cmoins Date: Thu, 28 Jan 2016 21:45:39 +0100 Subject: [PATCH] Implementation of the Annealing search algorithm --- .../usc/citius/hipster/algorithm/Hipster.java | 500 +++++++++++------- .../localsearch/AnnealingSearch.java | 203 +++++++ ...eensProblemExampleWithAnnealingSearch.java | 112 ++++ 3 files changed, 620 insertions(+), 195 deletions(-) create mode 100644 hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java create mode 100644 hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java index 4c50380..d2fe0a5 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java @@ -1,195 +1,305 @@ -/* - * Copyright 2014 CITIUS , University of Santiago de Compostela. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package es.usc.citius.hipster.algorithm; - - -import es.usc.citius.hipster.algorithm.localsearch.HillClimbing; -import es.usc.citius.hipster.model.CostNode; -import es.usc.citius.hipster.model.HeuristicNode; -import es.usc.citius.hipster.model.Node; -import es.usc.citius.hipster.model.function.impl.ADStarNodeExpander; -import es.usc.citius.hipster.model.function.impl.ADStarNodeFactory; -import es.usc.citius.hipster.model.impl.ADStarNodeImpl; -import es.usc.citius.hipster.model.problem.SearchComponents; -import es.usc.citius.hipster.model.problem.SearchProblem; - -import java.util.Collections; - -/** - * Util class to create algorithms easily. Each method expects a {@link es.usc.citius.hipster.model.problem.SearchProblem} - * with the components of the algorithm and returns an iterable algorithm that can be used to search a goal or iterate over - * the state space. A SearchProblem can be easily defined with the {@link es.usc.citius.hipster.model.problem.ProblemBuilder} class. - * - * @see es.usc.citius.hipster.model.problem.ProblemBuilder - * - * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> - * @author Adrián González Sieira <adrian.gonzalez@usc.es> - */ -public final class Hipster { - - private Hipster(){ - - } - - /** - * Instantiates a A* algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the problem definition - */ - public static , N extends HeuristicNode> AStar createAStar(SearchProblem components){ - return new AStar(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Dijkstra algorithm (A* algorithm with no heuristic function) given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the problem definition, using no heuristic. - */ - public static , N extends HeuristicNode> AStar createDijkstra(SearchProblem components){ - //TODO: There is no difference with AStar. Actually if the NodeExpander uses heuristics, this "Dijkstra" impl works as the AStar. This should be changed! - return new AStar(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Bellman Ford algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.BellmanFord} for the problem definition - */ - public static , N extends CostNode> BellmanFord createBellmanFord(SearchProblem components){ - return new BellmanFord(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates Breadth First Search algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.BreadthFirstSearch} for the problem definition - */ - public static > BreadthFirstSearch createBreadthFirstSearch(SearchProblem components){ - return new BreadthFirstSearch(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates Depth First Search algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the problem definition - */ - public static > DepthFirstSearch createDepthFirstSearch(SearchProblem components){ - return new DepthFirstSearch(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates Depth Limited Search algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the problem definition - */ - public static > DepthLimitedSearch createDepthLimitedSearch(SearchProblem components, int depth){ - return new DepthLimitedSearch(components.getInitialNode(), components.getFinalNode(), components.getExpander(), depth); - } - - /** - * Instantiates a IDA* algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.IDAStar} for the problem definition - */ - public static , N extends HeuristicNode> IDAStar createIDAStar(SearchProblem components){ - return new IDAStar(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Hill Climbing algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param enforced flag to use Enforced Hill Climbing instead of classic Hill Climbing algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.localsearch.HillClimbing} for the problem definition - */ - public static , N extends HeuristicNode> HillClimbing createHillClimbing(SearchProblem components, boolean enforced){ - return new HillClimbing(components.getInitialNode(), components.getExpander(), enforced); - } - - /** - * Instantiates a Multi-objective Label Setting algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.MultiobjectiveLS} for the problem definition - */ - public static , N extends HeuristicNode> MultiobjectiveLS createMultiobjectiveLS(SearchProblem components){ - return new MultiobjectiveLS(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Anytime Dynamic A* algorithm given the search components. Search components can be obtained - * easily for graph-based problems using {@link es.usc.citius.hipster.util.graph.GraphSearchProblem}. - * - * @param components search components to be used by the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @return instance of {@link es.usc.citius.hipster.algorithm.ADStarForward} for the search components - */ - public static > ADStarForward> createADStar(SearchComponents components){ - //node factory instantiation - ADStarNodeFactory factory = new ADStarNodeFactory(components); - //node expander instantiation - ADStarNodeExpander> expander = - new ADStarNodeExpander>(components, factory, 1.0); - //instantiate algorithm - return new ADStarForward( - components.getBegin(), - Collections.singleton(components.getGoal()), - expander); - } -} +/* + * Copyright 2014 CITIUS , University of Santiago de Compostela. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package es.usc.citius.hipster.algorithm; + +import es.usc.citius.hipster.algorithm.localsearch.AnnealingSearch; +import es.usc.citius.hipster.algorithm.localsearch.HillClimbing; +import es.usc.citius.hipster.algorithm.localsearch.AnnealingSearch.AcceptanceProbability; +import es.usc.citius.hipster.algorithm.localsearch.AnnealingSearch.SuccessorFinder; +import es.usc.citius.hipster.model.CostNode; +import es.usc.citius.hipster.model.HeuristicNode; +import es.usc.citius.hipster.model.Node; +import es.usc.citius.hipster.model.function.NodeExpander; +import es.usc.citius.hipster.model.function.impl.ADStarNodeExpander; +import es.usc.citius.hipster.model.function.impl.ADStarNodeFactory; +import es.usc.citius.hipster.model.impl.ADStarNodeImpl; +import es.usc.citius.hipster.model.problem.SearchComponents; +import es.usc.citius.hipster.model.problem.SearchProblem; + +import java.util.Collections; + +/** + * Util class to create algorithms easily. Each method expects a + * {@link es.usc.citius.hipster.model.problem.SearchProblem} with the components + * of the algorithm and returns an iterable algorithm that can be used to search + * a goal or iterate over the state space. A SearchProblem can be easily defined + * with the {@link es.usc.citius.hipster.model.problem.ProblemBuilder} class. + * + * @see es.usc.citius.hipster.model.problem.ProblemBuilder + * + * @author Pablo Rodríguez Mier < + * pablo.rodriguez.mier@usc + * .es> + * @author Adrián González Sieira < + * adrian.gonzalez@usc.es> + */ +public final class Hipster { + + private Hipster() { + + } + + /** + * Instantiates a A* algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the + * problem definition + */ + public static , N extends HeuristicNode> AStar createAStar( + SearchProblem components) { + return new AStar(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Dijkstra algorithm (A* algorithm with no heuristic + * function) given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the + * problem definition, using no heuristic. + */ + public static , N extends HeuristicNode> AStar createDijkstra( + SearchProblem components) { + // TODO: There is no difference with AStar. Actually if the NodeExpander + // uses heuristics, this "Dijkstra" impl works as the AStar. This should + // be changed! + return new AStar(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Bellman Ford algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.BellmanFord} + * for the problem definition + */ + public static , N extends CostNode> BellmanFord createBellmanFord( + SearchProblem components) { + return new BellmanFord(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates Breadth First Search algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.BreadthFirstSearch} for + * the problem definition + */ + public static > BreadthFirstSearch createBreadthFirstSearch( + SearchProblem components) { + return new BreadthFirstSearch(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates Depth First Search algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the + * problem definition + */ + public static > DepthFirstSearch createDepthFirstSearch( + SearchProblem components) { + return new DepthFirstSearch(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates Depth Limited Search algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the + * problem definition + */ + public static > DepthLimitedSearch createDepthLimitedSearch( + SearchProblem components, int depth) { + return new DepthLimitedSearch(components.getInitialNode(), components.getFinalNode(), + components.getExpander(), depth); + } + + /** + * Instantiates a IDA* algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.IDAStar} for + * the problem definition + */ + public static , N extends HeuristicNode> IDAStar createIDAStar( + SearchProblem components) { + return new IDAStar(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Hill Climbing algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param enforced + * flag to use Enforced Hill Climbing instead of classic Hill + * Climbing algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.localsearch.HillClimbing} + * for the problem definition + */ + public static , N extends HeuristicNode> HillClimbing createHillClimbing( + SearchProblem components, boolean enforced) { + return new HillClimbing(components.getInitialNode(), components.getExpander(), enforced); + } + + /** + * Instantiates an AnnealingSearch algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param alpha + * coefficient of the geometric cooling schedule + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.localsearch.HillClimbing} + * for the problem definition + */ + public static > AnnealingSearch createAnnealingSearch( + SearchProblem components, Double alpha, Double minTemp, + AcceptanceProbability acceptanceProbability, SuccessorFinder successorFinder) { + return new AnnealingSearch(components.getInitialNode(), components.getExpander(), alpha, + minTemp, acceptanceProbability, successorFinder); + } + + /** + * Instantiates a Multi-objective Label Setting algorithm given a problem + * definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.MultiobjectiveLS} for the + * problem definition + */ + public static , N extends HeuristicNode> MultiobjectiveLS createMultiobjectiveLS( + SearchProblem components) { + return new MultiobjectiveLS(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Anytime Dynamic A* algorithm given the search components. + * Search components can be obtained easily for graph-based problems using + * {@link es.usc.citius.hipster.util.graph.GraphSearchProblem}. + * + * @param components + * search components to be used by the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @return instance of {@link es.usc.citius.hipster.algorithm.ADStarForward} + * for the search components + */ + public static > ADStarForward> createADStar( + SearchComponents components) { + // node factory instantiation + ADStarNodeFactory factory = new ADStarNodeFactory(components); + // node expander instantiation + ADStarNodeExpander> expander = new ADStarNodeExpander>( + components, factory, 1.0); + // instantiate algorithm + return new ADStarForward(components.getBegin(), Collections.singleton(components.getGoal()), expander); + } +} diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java new file mode 100644 index 0000000..cb805ea --- /dev/null +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java @@ -0,0 +1,203 @@ +package es.usc.citius.hipster.algorithm.localsearch; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Random; + +import es.usc.citius.hipster.algorithm.Algorithm; +import es.usc.citius.hipster.model.HeuristicNode; +import es.usc.citius.hipster.model.Node; +import es.usc.citius.hipster.model.function.NodeExpander; + +/** + * Implementation of the simulated annealing search that is a probabilistic + * technique for approximating the global optimum of a given function. It starts + * the exploration from a random point as a global optimum and selects one of + * its neighbors with a neighboring function. The neighbor will become the new + * optimum if its associated cost is lower or if the acceptance probability + * function returns a probability greater than a random number. The probability + * function takes as an input the cost of the current selected node, the cost of + * its randomly selected neighbour and the current temperature. The higher the + * cost of the neighbour is or the lower the temperature is, the more unlikely + * it is that the neighbour becomes the new optimum. The process continues until + * the temperature is below a given threshold. The temperature decreases at each + * iteration according to a geometric cooling schedule that has two parameters + * alpha and temperature min. The main idea of this algorithm is to avoid to be + * "trapped" in a bad local optimum by exploring more deeply the state space by + * looking at states whose cost is not optimum but that may have interesting + * neighbours. A user can adjusted the algorithm by tuning the alpha coefficient + * (default 0.9) or the min temperature (0.00001) or by providing his own + * implementation of the acceptance probability function (default: exp((old + * score - new score) / current temperature)) or the neighbouring function + * (random selection by default). Note: costs are Double in this implementation + * and have no type parameters. + * + * see in + * Wikipedia and in + * annealing search for more details. + * + * @param + * class defining the action + * @param + * class defining the state + * @param + * class defining the cost, must implement + * {@link java.lang.Comparable} + * @param + * type of the nodes + * + * @author Christophe Moins < + * christophe.moins@yahoo.fr + * > + */ +public class AnnealingSearch> extends Algorithm { + + static final private Double DEFAULT_ALPHA = 0.9; + static final private Double DEFAULT_MIN_TEMP = 0.00001; + static final private Double START_TEMP = 1.; + + private N initialNode; + private Double alpha; + private Double minTemp; + private AcceptanceProbability acceptanceProbability; + private SuccessorFinder successorFinder; + // expander to find all the successors of a given node. + private NodeExpander nodeExpander; + + public AnnealingSearch(N initialNode, NodeExpander nodeExpander, Double alpha, Double minTemp, + AcceptanceProbability acceptanceProbability, SuccessorFinder successorFinder) { + if (initialNode == null) { + throw new IllegalArgumentException("Provide a valid initial node"); + } + this.initialNode = initialNode; + if (nodeExpander == null) { + throw new IllegalArgumentException("Provide a valid node expander"); + } + this.nodeExpander = nodeExpander; + if (alpha != null) { + if ((alpha <= 0.) || (alpha >= 1.0)) { + throw new IllegalArgumentException("alpha must be between 0. and 1."); + } + this.alpha = alpha; + } else { + this.alpha = DEFAULT_ALPHA; + } + if (minTemp != null) { + if ((minTemp < 0.) || (minTemp > 1.)) { + throw new IllegalArgumentException("Minimum temperature must be between 0. and 1."); + } + this.minTemp = minTemp; + } else { + this.minTemp = DEFAULT_MIN_TEMP; + } + if (acceptanceProbability != null) { + this.acceptanceProbability = acceptanceProbability; + } else { + this.acceptanceProbability = new AcceptanceProbability() { + @Override + public Double compute(Double oldScore, Double newScore, Double temp) { + return (newScore < oldScore ? 1 : Math.exp((oldScore - newScore) / temp)); + } + }; + } + if (successorFinder != null) { + this.successorFinder = successorFinder; + } else { + // default implementation of the successor: picks up a successor + // randomly + this.successorFinder = new SuccessorFinder() { + @Override + public N estimate(N node, NodeExpander nodeExpander) { + List successors = new ArrayList<>(); + // find a random successor + for (N successor : nodeExpander.expand(node)) { + successors.add(successor); + } + Random randIndGen = new Random(); + return successors.get(Math.abs(randIndGen.nextInt()) % successors.size()); + } + }; + } + } + + @Override + public ASIterator iterator() { + // TODO Auto-generated method stub + return new ASIterator(); + } + + public class ASIterator implements Iterator { + + private Queue queue = new LinkedList(); + private Double bestScore = null; + private Double curTemp = START_TEMP; + + private ASIterator() { + bestScore = initialNode.getEstimation(); + queue.add(initialNode); + } + + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + public N next() { + N currentNode = this.queue.poll(); + if (curTemp > minTemp) { + N newNode = null; + // we add a loop to increase the effect of a change of alpha. + for (int i = 0; i < 100; i++) { + N randSuccessor = successorFinder.estimate(currentNode, nodeExpander); + Double score = randSuccessor.getScore(); + if (acceptanceProbability.compute(bestScore, score, curTemp) > Math.random()) { + newNode = randSuccessor; + bestScore = score; + } + } + if (newNode != null) { + queue.add(newNode); + } else { + queue.add(currentNode); + } + curTemp *= alpha; + } + return currentNode; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + + } + } + + /** + * Interface to compute the acceptance probability. If the new score is less + * than the old score, 1 will be returned so that the node is selected. + * Otherwise, we compute a probability that will decrease when the newScore + * or the temperature increase. + * + */ + + public interface AcceptanceProbability { + Double compute(Double oldScore, Double newScore, Double temp); + } + + /** + * Interface to find the successor of a node. + * + * @param + */ + public interface SuccessorFinder> { + /** + * @param Node + * @return the successor of a node. + */ + N estimate(N node, NodeExpander nodeExpander); + } +} diff --git a/hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java new file mode 100644 index 0000000..a8c1fcd --- /dev/null +++ b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014 CITIUS , University of Santiago de Compostela. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package es.usc.citius.hipster.examples; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import es.usc.citius.hipster.algorithm.Hipster; +import es.usc.citius.hipster.examples.problem.NQueens; +import es.usc.citius.hipster.model.Transition; +import es.usc.citius.hipster.model.function.CostFunction; +import es.usc.citius.hipster.model.function.HeuristicFunction; +import es.usc.citius.hipster.model.function.impl.StateTransitionFunction; +import es.usc.citius.hipster.model.impl.WeightedNode; +import es.usc.citius.hipster.model.problem.ProblemBuilder; +import es.usc.citius.hipster.model.problem.SearchProblem; +import es.usc.citius.hipster.util.Predicate; + +/** + * Example using the N-Queens problem (size 8x8) solved using the Annealing search. + * + * This example is a search problem with no explicit actions, only using a + * transition function which generates from a state a set of + * successor states. The cost function in this case is constant, and + * we assume it has no cost to move a queen. As heuristic for this problem + * we use the number of attacked queens. These components are defined + * as the same time the problem is being built (using a + * {@link es.usc.citius.hipster.model.problem.ProblemBuilder}. + * + * + * @see {@link es.usc.citius.hipster.examples.problem.NQueens} + * + * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> + * @author Adrián González Sieira <adrian.gonzalez@usc.es> + */ +public class EightQueensProblemExampleWithAnnealingSearch { + + public static void main(String[] args) { + // Solve the 8-Queen problem with Hill Climbing and Enforced Hill Climbing + final int size = 8; + //search problem definition, here we define also + //the transition function between states + //and the cost (always 0) + //and heuristic function (number of attacked queens) + SearchProblem> p = ProblemBuilder.create() + .initialState(new NQueens(size)) + //problem without explicit actions, only a transition function is needed + .defineProblemWithoutActions() + .useTransitionFunction(new StateTransitionFunction() { + @Override + public Iterable successorsOf(NQueens state) { + // Generate all possible movements of one queen + // There are size*(size-1) available movements + Set states = new HashSet(); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + // Change the queen at row i to column j + // If i is already in j, then do not add the state + if (state.getQueens()[i] != j) { + int[] queens = Arrays.copyOf(state.getQueens(), size); + queens[i] = j; + states.add(new NQueens(queens)); + } + } + } + return states; + } + }) + .useCostFunction(new CostFunction() { + @Override + public Double evaluate(Transition transition) { + // We assume that the cost of moving a queen is 0 + return 0d; + } + }) + .useHeuristicFunction(new HeuristicFunction() { + @Override + public Double estimate(NQueens state) { + return (double) state.attackedQueens(); + } + }).build(); + + //print some information about the search that will be executed + System.out.println("Random initial state (" + p.getInitialNode().state().attackedQueens() + " attacked queens):"); + System.out.println(p.getInitialNode().state()); + + System.out.println("Running 8-Queens problem with Annealing search and a custom goal test predicate"); + //To execute the algorithm we have two options: + // Option 1 - Run the algorithm until the predicate is satisfied (until we find a state with score 0, that is, no attacked queens) + System.out.println(Hipster.createAnnealingSearch(p, null,null,null,null).search(new Predicate>() { + @Override + public boolean apply(WeightedNode node) { + return node.getScore().equals(0d); + } + })); + } +}