diff options
Diffstat (limited to 'casa/search')
-rw-r--r-- | casa/search/Filter.H | 60 | ||||
-rw-r--r-- | casa/search/Goal.H | 32 | ||||
-rw-r--r-- | casa/search/GreedyFilter.H | 64 | ||||
-rw-r--r-- | casa/search/Guide.H | 34 | ||||
-rw-r--r-- | casa/search/Heuristic.H | 32 | ||||
-rw-r--r-- | casa/search/Node.H | 137 | ||||
-rw-r--r-- | casa/search/Search.H | 460 | ||||
-rw-r--r-- | casa/search/SearchConfiguration.H | 99 | ||||
-rw-r--r-- | casa/search/SearchFinish.H | 55 | ||||
-rw-r--r-- | casa/search/SearchIteration.H | 28 | ||||
-rw-r--r-- | casa/search/StateGuide.H | 34 | ||||
-rw-r--r-- | casa/search/StateSpace.H | 50 |
12 files changed, 1085 insertions, 0 deletions
diff --git a/casa/search/Filter.H b/casa/search/Filter.H new file mode 100644 index 0000000..071eb71 --- /dev/null +++ b/casa/search/Filter.H @@ -0,0 +1,60 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef FILTER_H +#define FILTER_H + +#include <set> + +#include "search/Heuristic.H" +#include "search/Goal.H" + +// Controls which children (immediately reachable neighbors) are explored by a +// search. The filtering happens after children are enumerated; limitations that +// apply to the enumeration process itself (such as only computing a fixed +// number of random children) are better managed by the parameters in a +// SearchConfiguration, which are passed to the getChildren() method of the +// StateSpace. + +template<class STATE, class COST>class Filter { +public: + typedef Heuristic<STATE,COST> HeuristicT; + typedef Goal<STATE> GoalT; + + virtual ~Filter() {} + + // Mutates the children set; the heuristic and goal may guide the mutation. + virtual void operator() + (std::set<STATE>&children, const HeuristicT&heuristic, const GoalT&goal) + const {} + + // Just as the other operator(), but, at the filter's option, the parent may + // be considered to be its own child. This is useful, for example, when the + // SearchConfiguration is sampling a random subset of the children and the + // filter would prefer a different pool to choose from. + virtual void operator() + (std::set<STATE>&children, + const STATE&parent, + const HeuristicT&heuristic, + const GoalT&goal) const { + children.insert(parent); + operator()(children, heuristic, goal); + } +}; + +#endif diff --git a/casa/search/Goal.H b/casa/search/Goal.H new file mode 100644 index 0000000..d7209c8 --- /dev/null +++ b/casa/search/Goal.H @@ -0,0 +1,32 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef GOAL_H +#define GOAL_H + +// Decides when a search should terminate because it has found a solution. In +// some applications the goal's RTTI is also used to inform other search objects +// (such as the heuristic). + +template<class STATE>class Goal { +public: + virtual ~Goal() {} + virtual bool isGoal(const STATE&state) = 0; +}; + +#endif diff --git a/casa/search/GreedyFilter.H b/casa/search/GreedyFilter.H new file mode 100644 index 0000000..47ddef4 --- /dev/null +++ b/casa/search/GreedyFilter.H @@ -0,0 +1,64 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef GREEDYFILTER_H +#define GREEDYFILTER_H + +#include "search/Filter.H" + +// A specialization of Filter where only the heuristically best child is +// explored. Furthermore, if given the option to treat the parent as a child, +// the filter will only have the search explore children if at least one of them +// is heuristically better than the parent. + +template<class STATE, class COST>class GreedyFilter : + public Filter<STATE, COST> { + typedef Heuristic<STATE, COST> HeuristicT; + typedef Goal<STATE> GoalT; + +public: + void operator() + (std::set<STATE>&children, const HeuristicT&heuristic, const GoalT&goal) + const { + typename std::set<STATE>::iterator best = children.end(); + COST score = 0; + for(typename std::set<STATE>::iterator + iterator = children.begin(), + end = children.end(); + iterator != end; + ++iterator) { + COST estimate = heuristic.estimate(*iterator, goal); + if (best == children.end() || estimate < score) { + best = iterator; + score = estimate; + } + } + for(typename std::set<STATE>::iterator + iterator = children.begin(), + end = children.end(); + iterator != end;){ + if (iterator == best) { + ++iterator; + } else { + children.erase(iterator++); + } + } + } +}; + +#endif diff --git a/casa/search/Guide.H b/casa/search/Guide.H new file mode 100644 index 0000000..797d39f --- /dev/null +++ b/casa/search/Guide.H @@ -0,0 +1,34 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef GUIDE_H +#define GUIDE_H + +// Decides the order in which states are explored. Usual policies include +// visiting the heuristically best states first, visiting states whose distance +// from the start state added to the heuristic is minimal, visiting states +// randomly, etc. There is provision to treat start states specially. + +template<class STATE, class COST>class Guide { +public: + virtual ~Guide() {} + virtual COST rankStart(const Node<STATE, COST>&start) const = 0; + virtual COST rank(const Node<STATE, COST>&node) const = 0; +}; + +#endif diff --git a/casa/search/Heuristic.H b/casa/search/Heuristic.H new file mode 100644 index 0000000..40affe5 --- /dev/null +++ b/casa/search/Heuristic.H @@ -0,0 +1,32 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef HEURISTIC_H +#define HEURISTIC_H + +#include "search/Goal.H" + +// Rates the quality of a state (never a path). + +template<class STATE, class COST>class Heuristic { +public: + virtual ~Heuristic() {} + virtual COST estimate(const STATE&state, const Goal<STATE>&goal) const = 0; +}; + +#endif diff --git a/casa/search/Node.H b/casa/search/Node.H new file mode 100644 index 0000000..5e5b478 --- /dev/null +++ b/casa/search/Node.H @@ -0,0 +1,137 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef NODE_H +#define NODE_H + +#include <cassert> +#include <iostream> +#include <set> + +// Represents an explored state, its heuristic estimate, the best known path +// leading to that state, and other nodes whose best paths this state is on. + +template<class STATE, class COST>class Node { +protected: + typedef Node<STATE,COST>* Edge; + + // The node immediately preceding this one on a best path. + Edge parent; + // The state. + const STATE state; + // The distance from the start state along the best path. + COST traveled; + // The heuristic estimate of the distance to a goal state. + COST estimate; + // The set of nodes who have this one as their parent. + std::set<Edge> children; + +public: + Node(Edge parent, const STATE&state, COST traveled, COST estimate) : + parent(parent), + state(state), + traveled(traveled), + estimate(estimate) { + if (parent) { + parent->children.insert(this); + } + } + + virtual ~Node() { + if (parent) { + parent->removeChild(this); + } + for (typename std::set<Edge>::iterator + iterator = children.begin(), + end = children.end(); + iterator != end;) { + Edge child = *iterator; + ++iterator; + removeChild(child); + } + } + + const STATE&getState() const { + return state; + } + + COST getTraveled() const { + return traveled; + } + void setTraveled(COST traveled) { + this->traveled = traveled; + } + + COST getEstimate() const { + return estimate; + } + void setEstimate(COST estimate) { + this->estimate = estimate; + } + + Edge getParent() const { + return parent; + } + + const std::set<Edge>&getChildren() const { + return children; + } + + void addChild(Edge child) { + assert(child); + if (child->parent) { + if (child->parent == this) { + return; + } + child->parent->removeChild(child); + } + child->parent = this; + children.insert(child); + } + + void removeChild(Edge child) { + assert(child); + if (child->parent != this) { + return; + } + child->parent = NULL; + children.erase(child); + } + + // Comparisons are made solely by state. +#define COMP(op) bool operator op(const Node&other) const { \ + return state op other.state; \ + } + COMP(<); + COMP(>); + COMP(==); + COMP(!=); +#undef COMP +}; + +template<class STATE, class COST>std::ostream&operator << + (std::ostream&out, const Node<STATE, COST>node) { + out << node.getState() << '(' << node.getTraveled() << '+' << + node.getEstimate() << "*)"; + if (node.getParent()) { + return out << "<-" << *node.getParent(); + } + return out; +} + +#endif diff --git a/casa/search/Search.H b/casa/search/Search.H new file mode 100644 index 0000000..0524ea5 --- /dev/null +++ b/casa/search/Search.H @@ -0,0 +1,460 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef SEARCH_H +#define SEARCH_H + +#include <cassert> +#include <vector> +#include <set> + +#include "utility/pless.H" +#include "utility/relation.H" + +#include "events/EventSource.H" + +#include "search/SearchConfiguration.H" +#include "search/Node.H" +#include "search/StateSpace.H" +#include "search/Heuristic.H" +#include "search/Guide.H" +#include "search/Goal.H" +#include "search/Filter.H" +#include "search/SearchIteration.H" +#include "search/SearchFinish.H" + +// A highly parameterized searching object. + +// Clients of this code mostly need to understand the objects passed at +// construction time and, if pathfinding, fulfill the assumption that shortening +// a path prefix will shorten an entire path. The complexity here is merely for +// careful bookkeeping and memory management. + +// The general calling pattern is: +// <constructor> +// foreach search { +// addStartState(...); +// // Possibly more calls to addStartState(...) +// search(...); +// ... = getBest() // If we care about the best results +// // Possibly more calls to search(...) if the search is restartable +// clear(); +// } +// <destructor> + +template<class STATE, class COST>class Search : + public EventSource<SearchIteration>, + public EventSource<SearchFinish<STATE, COST> > { +public: + typedef Node<STATE, COST> NodeT; + +protected: + typedef relation<NodeT*, COST, true, false, pless<NodeT> > + VisitSetT; + typedef StateSpace<STATE, COST> + StateSpaceT; + typedef Heuristic<STATE, COST> + HeuristicT; + typedef Guide<STATE, COST> + GuideT; + typedef Goal<STATE> GoalT; + typedef Filter<STATE,COST> FilterT; + typedef SearchFinish<STATE, COST> + SearchFinishT; + + SearchConfiguration configuration; + StateSpaceT* space; + HeuristicT* heuristic; + GuideT* guide; + GoalT* goal; + FilterT* filter; + bool oneBest; + VisitSetT open; + VisitSetT closed; + std::set<const NodeT*> best; + COST bestRank; + +public: + // See the classes SearchConfiguration, StateSpace, Heuristic, Guide, Goal, + // and Filter for documentation on their role in search. The parameter + // oneBest does not affect the method of search, but merely how many solutions + // are remembered in the case of a tie in the guide's ranking. + Search + (SearchConfiguration configuration, + StateSpaceT*space, + HeuristicT*heuristic, + GuideT*guide, + GoalT*goal, + FilterT*filter, + bool oneBest) : + configuration(configuration), + space(space), + heuristic(heuristic), + guide(guide), + goal(goal), + filter(filter), + oneBest(oneBest) { + assert(space); + assert(heuristic); + assert(guide); + assert(goal); + assert(filter); + } + + virtual ~Search() { + clear(); + } + +protected: + // A leak-free way to clear the set of best nodes. + void clearBest() { + if (!configuration.useClosed) { + for (typename std::set<const NodeT*>::const_iterator + iterator = best.begin(), + end = best.end(); + iterator != end; + ++iterator) { + NodeT*node = const_cast<NodeT*>(*iterator); + if (open.key_find(node) == open.key_end()) { + delete node; + } + } + } + best.clear(); + } + + // See if the given node with the given guide ranking should be entered into + // the set of best nodes. + void updateBest(const NodeT&node, COST rank) { + if (rank < bestRank) { + clearBest(); + } + if (best.empty()) { + best.insert(&node); + bestRank = rank; + } else if (rank == bestRank) { + if (oneBest) clearBest(); + best.insert(&node); + } + } + + // Pop the best ranked node from the set of nodes that have been seen but not + // explored. + NodeT&popBestOpen() { + assert(!open.empty()); + typename VisitSetT::data_iterator pop = open.data_begin(); + assert(pop->second); + if (configuration.useClosed) { + closed.key_insert(pop->second, pop->first); + } + NodeT&result = *(pop->second); + open.data_erase(pop); + return result; + } + + // Get the children (immediately reachable neighbors) according to the + // SearchConfiguration. + std::set<STATE>getChildren(const NodeT&parent) { + if (configuration.proportionChildren) { + return space->getChildren + (parent.getState(), configuration.childrenAsk.proportion); + } + return space->getChildren + (parent.getState(), configuration.childrenAsk.count); + } + + // Return true if a node should be discarded because we have seen a better one + // representing the same state, but not explored it yet. Also, forget about + // any nodes that we have seen but not explored if they represent the same + // state but are worse. + bool replaceInOpen(NodeT&parent, NodeT&node, COST traveled) { + typename VisitSetT::key_iterator similar = open.key_find(&node); + if (similar == open.key_end()) { + // The node does not have an already seen state. + return false; + } + NodeT*visited = similar->first; + assert(visited); + if (visited->getTraveled() <= traveled) { + // The node has an already seen state and cannot improve a path; discard + // it. + return true; + } + // The node has an already seen state, but may improve some paths; use it + // instead. + if (configuration.useClosed) { + parent.addChild(visited); + } + visited->setTraveled(traveled); + open.key_erase(similar); + COST rank = guide->rank(*visited); + open.key_insert(visited, rank); + updateBest(*visited, rank); + return true; + } + + // Correct any out-of-date distance calculations when we change a path prefix. + // The arguments are the newly connected parent and child nodes. + void updateTraveled(NodeT&parent, NodeT&visited) { + // Setup to DFS from the visited node. + std::set<NodeT*>parentSet; + std::set<NodeT*>visitedSet; + parentSet.insert(&parent); + visitedSet.insert(&visited); + std::vector<const std::set<NodeT*>*>extrusion; + std::vector<typename std::set<NodeT*>::const_iterator>path; + extrusion.push_back(&parentSet); + path.push_back(extrusion.back()->begin()); + extrusion.push_back(&visitedSet); + path.push_back(extrusion.back()->begin()); + // Run the DFS, updating traveled distances and resorting. + for (;;) { + if (path.back() == extrusion.back()->end()) { + path.pop_back(); + extrusion.pop_back(); + if (path.empty()) { + break; + } + ++path.back(); + } else { + typename + std::vector<typename std::set<NodeT*>::const_iterator>:: + const_reverse_iterator back = path.rbegin(); + NodeT&update = ***back; + assert(&update); + ++back; + NodeT&source = ***back; + assert(&source); + update.setTraveled(space->getTraveled(source, update.getState())); + COST rank = guide->rank(update); + typename VisitSetT::key_iterator moribund = open.key_find(&update); + if (moribund != open.key_end()) { + open.key_erase(moribund); + open.key_insert(&update, rank); + updateBest(update, rank); + ++path.back(); + } else { + moribund = closed.key_find(&update); + assert(moribund != closed.key_end()); + closed.key_erase(moribund); + closed.key_insert(&update, rank); + updateBest(update, rank); + // Push children. + extrusion.push_back(&update.getChildren()); + path.push_back(extrusion.back()->begin()); + } + } + } + } + + // Return true if a node should be discarded because we have explored a better + // node representing the same state. Also, forget about any nodes that we + // have explored if they represent the same state but are worse. + bool replaceInClosed(NodeT&parent, NodeT&node, COST traveled) { + typename VisitSetT::key_iterator similar = closed.key_find(&node); + if (similar == closed.key_end()) { + // The node does not have an already explored state. + return false; + } + NodeT*visited = similar->first; + assert(visited); + if (visited->getTraveled() <= traveled) { + // The node has an already explored state and cannot improve a path; + // discard it. + return true; + } + // The node has an already explored state, but will improve some paths; use + // it instead. + parent.addChild(visited); + updateTraveled(parent, *visited); + return true; + } + + //Add a newly seen node to the set of seen but not yet explored nodes. + void addNew(NodeT*node) { + COST rank = guide->rank(*node); + open.key_insert(node,rank); + updateBest(*node,rank); + } + +public: + // Add a start state before searching. + void addStartState(const STATE&start) { + NodeT*node = + new NodeT + (NULL, + start, + space->getTraveled(start), + heuristic->estimate(start, *goal)); + addNew(node); + } + + // Try to find a goal in the given budget. If restartable is true the search + // can be resumed by a later call to this method. + std::set<NodeT*>search(unsigned iterations, bool restartable) { + std::set<NodeT*>result; + if (open.empty()) { + return result; + } + for (unsigned i = iterations, j = configuration.prunePeriod; + i-- && result.empty();) { +#ifdef SEARCH_PROGRESS + if (!(i & 0x3FF)) { + std::cout << i << " iterations left after this one" << std::endl; + } +#endif + NodeT&parent = popBestOpen(); + // If it is time to prune exploration to the most promising frontier: + if (!--j) { + j = configuration.prunePeriod; + std::set<const NodeT*>lineage; + typename std::set<const NodeT*>::const_iterator + lineageEnd = lineage.end(); + for (const NodeT*k = &parent; k; k = k->getParent()) { + lineage.insert(k); + } + for (typename VisitSetT::key_iterator + k = closed.key_begin(), + kend = closed.key_end(); + k != kend;) { + if (lineage.find(k->first) == lineageEnd) { + delete k->first; + closed.key_erase(k++); + } else { + ++k; + } + } + for (typename VisitSetT::key_iterator + k = open.key_begin(), + kend = open.key_end(); + k != kend;++k) { + delete k->first; + } + open.clear(); + } + // Explore. + std::set<STATE>children = getChildren(parent); + if (configuration.retryChildren) { + (*filter)(children, parent.getState(), *heuristic, *goal); + } else { + (*filter)(children, *heuristic, *goal); + } + // The flag to decide if the parent information can be deleted. It is + // true when we aren't looking for paths and the parent isn't part of the + // best set. + bool parentMoribund = false; + if (!configuration.useClosed) { + typename std::set<const NodeT*>::const_iterator + asBest = best.find(&parent), + bestEnd = best.end(); + if (asBest == bestEnd) { + parentMoribund = true; + } + } + // See children. + for (typename std::set<STATE>::const_iterator + iterator = children.begin(), + end = children.end(); + iterator != end; + ++iterator) { + COST traveled = space->getTraveled(parent, *iterator); + NodeT*node = + new NodeT + (configuration.useClosed ? &parent : NULL, + *iterator, + traveled, + heuristic->estimate(*iterator, *goal)); + if (replaceInOpen(parent, *node, traveled)) { + // The new node was beaten by something in the open set. + delete node; + } else if (configuration.useClosed && + replaceInClosed(parent, *node, traveled)) { + // The new node was beaten by something in the closed set. + delete node; + } else { + // The new node is worth exploring. + addNew(node); + // Track goals. + if (goal->isGoal(*iterator)) { + result.insert(node); + // If we are just interested in finding a goal, we can return now. + if (!restartable) { + if (parentMoribund) { + delete &parent; + } + // Complete the search. + SearchFinishT finish(*this, result, iterations - i, iterations); + EventSource<SearchFinishT>::dispatch(finish); + return result; + } + } + } + } + if (parentMoribund) { + delete &parent; + } + // Complete the iteration. + SearchIteration iteration; + EventSource<SearchIteration>::dispatch(iteration); + } + // Complete the search. + SearchFinishT finish(*this, result, iterations, iterations); + EventSource<SearchFinishT>::dispatch(finish); + return result; + } + +#define GET_SET(type, member, capMember) \ + const type get ## capMember() const { \ + return member; \ + } \ + void set ## capMember(const type&member) { \ + this->member = member; \ + } + GET_SET(SearchConfiguration, configuration, SearchConfiguration); + GET_SET(StateSpaceT*, space, Space); + GET_SET(HeuristicT*, heuristic, Heuristic); + GET_SET(GuideT*, guide, Guide); + GET_SET(GoalT*, goal, Goal); +#undef GET_SET + + const std::set<const NodeT*>getBest() const { + return best; + } + + void clear() { + clearBest(); + for (typename VisitSetT::key_iterator + k = open.key_begin(), + kend = open.key_end(); + k != kend; + ++k) { + delete k->first; + } + open.clear(); + for (typename VisitSetT::key_iterator + k = closed.key_begin(), + kend = closed.key_end(); + k != kend; + ++k) { + delete k->first; + } + closed.clear(); + } +}; + +#endif diff --git a/casa/search/SearchConfiguration.H b/casa/search/SearchConfiguration.H new file mode 100644 index 0000000..469a6ae --- /dev/null +++ b/casa/search/SearchConfiguration.H @@ -0,0 +1,99 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef SEARCH_CONFIGURATION_H +#define SEARCH_CONFIGURATION_H + +// Controls behaviors of the search that are parameterized by value, not code. + +struct SearchConfiguration { + // Should we keep a history of where we've been? + // Defaults to true. + // If false: + // Nodes are always without parent, so the search cannot find paths. + // States that have been visited and expanded may be visited and expanded + // again. + bool useClosed; + + // Should we allow a parent to consider itself its own child? + // Defaults to false. + // If true: + // Node's states are forcibly added to their own sets of children. + // Filters can therefore choose the trivial (no-move) transition, like in + // hill-climbing. + // The search may spin at local maxima. + bool retryChildren; + + // Should we limit the number of children by a proportion rather than a count? + // Defaults to true. + // If true: + // The state space is asked to enumerate a fixed proportion of children. + // States with more children will have longer enumerations. + // If false: + // The state space is asked to enumerate a fixed number of children. + // States with too few children will have all of them listed. + // States with too many children will not. + bool proportionChildren; + + // How many children should we ask for? + // (See the documentation of proportionChildren above.) + union { + float proportion; + unsigned count; + } childrenAsk; + + // How many iterations before each pruning of the space? + // Defaults to zero, which is treated as infinity. + // If nonzero: + // After the given number of iterations, the search will forget all visits + // except for the best nodes in the closed and open sets. + // This is sometimes an appropriate way to trade performance for memory. + unsigned prunePeriod; + + SearchConfiguration() : + useClosed(true), + retryChildren(false), + proportionChildren(true), + prunePeriod(0) { + childrenAsk.proportion = 1; + } + SearchConfiguration + (bool useClosed, + bool retryChildren, + float proportion, + unsigned prunePeriod) : + useClosed(useClosed), + retryChildren(retryChildren), + proportionChildren(true), + prunePeriod(prunePeriod) { + childrenAsk.proportion = proportion; + } + SearchConfiguration + (bool useClosed, + bool retryChildren, + unsigned count, + unsigned prunePeriod) : + useClosed(useClosed), + retryChildren(retryChildren), + proportionChildren(false), + prunePeriod(prunePeriod) { + childrenAsk.count = count; + } +}; + +#endif diff --git a/casa/search/SearchFinish.H b/casa/search/SearchFinish.H new file mode 100644 index 0000000..67d4835 --- /dev/null +++ b/casa/search/SearchFinish.H @@ -0,0 +1,55 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef SEARCH_FINISH_H +#define SEARCH_FINISH_H + +#include <set> + +#include "search/Node.H" + +// A forward declaration for when this header is included by Search.H: +template<class, class>class Search; + +// A message broadcast when a search finishes (even if it was unsuccessful). + +template<class STATE, class COST>class SearchFinish { +public: + typedef Search<STATE, COST> SearchT; + typedef Node<STATE, COST> NodeT; + + const SearchT& source; + std::set<NodeT*> results; + unsigned iterations; + unsigned maxIterations; + + SearchFinish + (const SearchT&source, + const std::set<NodeT*>&results, + unsigned iterations, + unsigned maxIterations) : + source(source), + results(results), + iterations(iterations), + maxIterations(maxIterations) {} +}; + +// For inclusion from anywhere but Search.H: +#include "search/Search.H" + +#endif diff --git a/casa/search/SearchIteration.H b/casa/search/SearchIteration.H new file mode 100644 index 0000000..41a267a --- /dev/null +++ b/casa/search/SearchIteration.H @@ -0,0 +1,28 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef SEARCH_ITERATION_H +#define SEARCH_ITERATION_H + +// A message broadcast when a search completes an iteration but does not finish. + +class SearchIteration { + // Information about the progress of search could go here. +}; + +#endif diff --git a/casa/search/StateGuide.H b/casa/search/StateGuide.H new file mode 100644 index 0000000..0551904 --- /dev/null +++ b/casa/search/StateGuide.H @@ -0,0 +1,34 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef STATE_GUIDE_H +#define STATE_GUIDE_H + +// A specialized guide that ranks strictly by the states' heuristic estimates. + +template<class STATE, class COST>class StateGuide : public Guide<STATE, COST> { +public: + COST rankStart(const Node<STATE, COST>&start) const { + return start.getEstimate(); + } + COST rank(const Node<STATE, COST>&node) const { + return node.getEstimate(); + } +}; + +#endif diff --git a/casa/search/StateSpace.H b/casa/search/StateSpace.H new file mode 100644 index 0000000..5003f9b --- /dev/null +++ b/casa/search/StateSpace.H @@ -0,0 +1,50 @@ +// Copyright 2008, 2009 Brady J. Garvin + +// This file is part of Covering Arrays by Simulated Annealing (CASA). + +// CASA is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// CASA is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with CASA. If not, see <http://www.gnu.org/licenses/>. + + +#ifndef STATE_SPACE_H +#define STATE_SPACE_H + +#include <set> + +#include "search/Node.H" + +// A description of a state space, namely the state interconnections and +// distances (for pathfinding rather than statefinding). + +template<class STATE, class COST>class StateSpace { +public: + virtual ~StateSpace() {} + + // Determine the distance incurred by starting at the given state. + virtual COST getTraveled(const STATE&start) const = 0; + + // Determine the total distance traveled to reach state after taking the best + // known path reaching parent. + virtual COST getTraveled(const Node<STATE, COST>&parent, const STATE&state) + const = 0; + + // Enumerate a fixed fraction of the children of state, rounding up. + virtual std::set<STATE>getChildren(const STATE&state, float proportion) + const = 0; + + // Enumerate the children of state up to some limit. + virtual std::set<STATE>getChildren(const STATE&state, unsigned count) + const = 0; +}; + +#endif |