From ed5217c21e4fafe32cb935269f57326815fc9db1 Mon Sep 17 00:00:00 2001
From: Le Quang Anh <43576719+Toefinder@users.noreply.github.com>
Date: Mon, 21 Mar 2022 17:46:56 +0100
Subject: [PATCH] Refactor graph search data

Make graph search data part of the path planner object, so that we don't
have to search the graph from scratch everytime we want to compute a new
list of configurations.
---
 .../path-planner/states-path-finder.hh        |  3 +-
 src/path-planner/states-path-finder.cc        | 94 +++++++++++--------
 2 files changed, 55 insertions(+), 42 deletions(-)

diff --git a/include/hpp/manipulation/path-planner/states-path-finder.hh b/include/hpp/manipulation/path-planner/states-path-finder.hh
index 46438681..a045e82f 100644
--- a/include/hpp/manipulation/path-planner/states-path-finder.hh
+++ b/include/hpp/manipulation/path-planner/states-path-finder.hh
@@ -235,6 +235,7 @@ namespace hpp {
           std::map <ImplicitPtr_t, ImplicitPtr_t> stricterConstraints_;
 
           mutable OptimizationData* optData_;
+          mutable std::shared_ptr <GraphSearchData> graphData_;
           /// Index of the sequence of states
           std::size_t idxSol_ = 0;
           graph::Edges_t lastBuiltTransitions_;
@@ -244,8 +245,6 @@ namespace hpp {
           // Constraints defining the goal
           NumericalConstraints_t goalConstraints_;
           bool goalDefinedByConstraints_;
-          /// list of potential goal states, used if goal is defined as a set of constraints
-          graph::States_t goalStates_;
           // Variables used across several calls to oneStep
           ConfigurationPtr_t q1_, q2_;
           core::Configurations_t configList_;
diff --git a/src/path-planner/states-path-finder.cc b/src/path-planner/states-path-finder.cc
index 11cec2ca..cf2cd58d 100644
--- a/src/path-planner/states-path-finder.cc
+++ b/src/path-planner/states-path-finder.cc
@@ -102,9 +102,9 @@ namespace hpp {
         PathPlanner(problem, roadmap),
         problem_ (HPP_STATIC_PTR_CAST(const manipulation::Problem, problem)),
         constraints_(), index_(), sameRightHandSide_(),
-        stricterConstraints_(), optData_(0x0),
+        stricterConstraints_(), optData_(0x0), graphData_(0x0),
         idxSol_(0), lastBuiltTransitions_(), skipColAnalysis_(false),
-        goalConstraints_(), goalDefinedByConstraints_(false), goalStates_(),
+        goalConstraints_(), goalDefinedByConstraints_(false),
         q1_(0x0), q2_(0x0), configList_(), idxConfigList_(0),
         nTryConfigList_(0), solved_(false), interrupt_(false),
         weak_()
@@ -171,7 +171,13 @@ namespace hpp {
 
       struct StatesPathFinder::GraphSearchData
       {
-        StatePtr_t s1, s2;
+        StatePtr_t s1;
+        // list of potential goal states
+        // can be multiple if goal is defined as a set of constraints
+        graph::States_t s2;
+
+        // index of the path
+        size_t idxSol;
 
         // Datas for findNextTransitions
         struct state_with_depth {
@@ -328,6 +334,7 @@ namespace hpp {
 
       bool StatesPathFinder::findTransitions (GraphSearchData& d) const
       {
+        assert (!goalDefinedByConstraints_);
         while (! d.queue1.empty())
         {
           GraphSearchData::state_with_depth_ptr_t _state = d.queue1.front();
@@ -358,7 +365,7 @@ namespace hpp {
             );
 
             // Consider done if either the target state of a transition is the goal state
-            done = done || (transition->stateTo() == d.s2);
+            done = done || (transition->stateTo() == d.s2[0]);
           }
           if (done) break;
         }
@@ -373,15 +380,15 @@ namespace hpp {
         // the queue is empty if search is exhausted and goal state not found
         if (d.queue1.empty()) return true;
 
-        // all the state sequences should be attempted before finding more
-        assert (d.queueIt == d.queue1.size());
+        // all sequences in the queue should be attempted before finding more
+        if (d.queueIt < d.queue1.size()) return false;
 
         GraphSearchData::state_with_depth_ptr_t _state = d.queue1.front();
 
         const GraphSearchData::state_with_depth& parent = d.getParent(_state);
         if (parent.l >= d.maxDepth) return true;
         d.queue1.pop_front();
-        d.queueIt = d.queue1.size();
+        --d.queueIt;
 
         const Neighbors_t& neighbors = _state.state->first->neighbors();
         for (Neighbors_t::const_iterator _n = neighbors.begin();
@@ -410,8 +417,8 @@ namespace hpp {
           const GraphSearchData& d, const std::size_t& i) const
       {
         assert (!goalDefinedByConstraints_);
-        assert (d.parent1.find (d.s2) != d.parent1.end());
-        const GraphSearchData::state_with_depths_t& roots = d.parent1.at(d.s2);
+        assert (d.parent1.find (d.s2[0]) != d.parent1.end());
+        const GraphSearchData::state_with_depths_t& roots = d.parent1.at(d.s2[0]);
         Edges_t transitions;
         if (i >= roots.size()) return transitions;
 
@@ -442,8 +449,8 @@ namespace hpp {
           GraphSearchData::state_with_depth_ptr_t _state = d.queue1.at(d.queueIt);
           ++d.queueIt;
           // check that the state is one of the goal states
-          if (std::find(goalStates_.begin(), goalStates_.end(),
-              _state.state->first) == goalStates_.end()) {
+          if (std::find(d.s2.begin(), d.s2.end(),
+              _state.state->first) == d.s2.end()) {
             continue;
           }
           const GraphSearchData::state_with_depth* current = &d.getParent(_state);
@@ -1053,15 +1060,13 @@ namespace hpp {
         return true;
       }
 
-      static std::pair<size_type, size_type> my_make_pair(size_type a, size_type b)
+      // use this function to make a pair of ascending indexes
+      static std::pair<size_type, size_type> orderPair(size_type a, size_type b)
       {
         if ( a < b ) return std::pair<size_type,size_type>(a,b);
         else return std::pair<size_type,size_type>(b,a);
       }
 
-
-
-      // TODO: analyse optimization problem when goal is a set of constraints
       bool StatesPathFinder::analyseOptimizationProblem2
         (const graph::Edges_t& transitions, core::ProblemConstPtr_t _problem)
       {        
@@ -1115,7 +1120,7 @@ namespace hpp {
             // insert if necessary
             JointConstraintMap::iterator next = jcmap.insert(
               JointConstraintMap::value_type(
-                my_make_pair(index1, index2), NumericalConstraints_t ()
+                orderPair(index1, index2), NumericalConstraints_t ()
               )
             ).first;
             // if constraint is not in map, insert it
@@ -1387,20 +1392,15 @@ namespace hpp {
       {
         assert (!goalDefinedByConstraints_);
         const graph::GraphPtr_t& graph(problem_->constraintGraph ());
-        GraphSearchData d;
-        d.s1 = graph->getState (q1);
-        d.s2 = graph->getState (q2);
-        d.maxDepth = problem_->getParameter
-	        ("StatesPathFinder/maxDepth").intValue();
+        GraphSearchData& d = *graphData_;
 
-        // Find
-        d.queue1.push_back (d.addInitState());
-        std::size_t idxSol = (d.s1 == d.s2 ? 1 : 0);
+        size_t& idxSol = d.idxSol;
         if (idxSol_ < idxSol) idxSol_ = idxSol;
 
         bool maxDepthReached;
         while (!(maxDepthReached = findTransitions (d))) { // mut
-          Edges_t transitions = getTransitionList (d, idxSol); // const, const
+          // if there is a working sequence, try it first
+          Edges_t transitions = (nTryConfigList_ > 0)? lastBuiltTransitions_ : getTransitionList (d, idxSol); // const, const
           while (! transitions.empty()) {
             if (idxSol >= idxSol_) {
 #ifdef HPP_DEBUG
@@ -1435,6 +1435,7 @@ namespace hpp {
             } // if (idxSol >= idxSol_)
             transitions = getTransitionList(d, ++idxSol);
             if (idxSol_ < idxSol) idxSol_ = idxSol;
+            nTryConfigList_ = 0;
           }
         }
         core::Configurations_t empty_path;
@@ -1451,21 +1452,15 @@ namespace hpp {
       {
         assert (goalDefinedByConstraints_);
         const graph::GraphPtr_t& graph(problem_->constraintGraph ());
-        GraphSearchData d;
-        d.s1 = graph->getState (q1);
-        d.s2 = nullptr;
-        d.maxDepth = problem_->getParameter
-	        ("StatesPathFinder/maxDepth").intValue();
+        GraphSearchData& d = *graphData_;
 
-        // Find
-        d.queue1.push_back (d.addInitState());
-        d.queueIt = d.queue1.size();
-        std::size_t idxSol = (d.s1 == d.s2 ? 1 : 0);
+        size_t& idxSol = d.idxSol;
         if (idxSol_ < idxSol) idxSol_ = idxSol;
 
         bool maxDepthReached;
         while (!(maxDepthReached = findTransitions2 (d))) { // mut
-          Edges_t transitions = getTransitionList2 (d); // const, const
+          // if there is a working sequence, try it first before getting another transition list
+          Edges_t transitions = (nTryConfigList_ > 0)? lastBuiltTransitions_ : getTransitionList2 (d);
           while (! transitions.empty()) {
             if (idxSol >= idxSol_) {
 #ifdef HPP_DEBUG
@@ -1501,6 +1496,8 @@ namespace hpp {
             ++idxSol;
             transitions = getTransitionList2(d);
             if (idxSol_ < idxSol) idxSol_ = idxSol;
+            // reset of the number of tries for a sequence
+            nTryConfigList_ = 0;
           }
         }
         core::Configurations_t empty_path;
@@ -1526,6 +1523,17 @@ namespace hpp {
         assert(problem_);
         q1_ = problem_->initConfig();
         assert(q1_);
+
+        const graph::GraphPtr_t& graph(problem_->constraintGraph ());
+        graphData_.reset(new GraphSearchData());
+        GraphSearchData& d = *graphData_;
+        d.s1 = graph->getState (*q1_);
+        d.maxDepth = problem_->getParameter
+          ("StatesPathFinder/maxDepth").intValue();
+
+        d.queue1.push_back (d.addInitState());
+        d.queueIt = d.queue1.size();
+
         // Detect whether the goal is defined by a configuration or by a
         // set of constraints
         ProblemTargetPtr_t target(problem()->target());
@@ -1542,6 +1550,9 @@ namespace hpp {
             throw std::logic_error(os.str().c_str());
           }
           q2_ =  q2s[0];
+          d.s2.push_back(graph->getState(*q2_));
+          // skip the first entry (the root state_with_depth)
+          d.idxSol = (d.s1 == d.s2[0] ? 1 : 0);
         } else {
           TaskTargetPtr_t taskTarget(HPP_DYNAMIC_PTR_CAST(TaskTarget,target));
           if(!taskTarget){
@@ -1550,6 +1561,7 @@ namespace hpp {
               "either a configuration or a set of constraints.";
             throw std::logic_error(os.str().c_str());
           }
+          assert (!q2_);
           goalDefinedByConstraints_ = true;
           goalConstraints_ = taskTarget->constraints();
           hppDout(info, "goal defined as a set of constraints");
@@ -1567,14 +1579,15 @@ namespace hpp {
                         << goalConstraint->function().name() << "\"");
               }
             }
-            if (numConstr == maxNumConstr) {
-              goalStates_.push_back(state);
-            } else if (numConstr > maxNumConstr) {
-              goalStates_.clear();
-              goalStates_.push_back(state);
+            if (numConstr > maxNumConstr) {
+              d.s2.clear();
+              d.s2.push_back(state);
               maxNumConstr = numConstr;
+            } else if (numConstr == maxNumConstr) {
+              d.s2.push_back(state);
             }
           }
+          d.idxSol = 0;
         }
         reset();
       }
@@ -1655,6 +1668,7 @@ namespace hpp {
           if (++nTryConfigList_ >= nTryMax) {
             nTryConfigList_ = 0;
             idxSol_++;
+            graphData_->idxSol++;
           }
         }
       }
-- 
GitLab