diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8b2a2ecc07a070c8662e4d1ad65389585c4db4c3..722cd6dc1732c94f2d6ae15988245360a1c25c96 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -73,6 +73,7 @@ SET (${PROJECT_NAME}_HEADERS
   include/hpp/manipulation/roadmap-node.hh
   include/hpp/manipulation/connected-component.hh
   include/hpp/manipulation/symbolic-component.hh
+  include/hpp/manipulation/symbolic-planner.hh
   include/hpp/manipulation/manipulation-planner.hh
   include/hpp/manipulation/graph-path-validation.hh
   include/hpp/manipulation/graph-steering-method.hh
diff --git a/include/hpp/manipulation/fwd.hh b/include/hpp/manipulation/fwd.hh
index bb35069bbc99cb413a7636298f81dc95f1d91c82..075a4011e34313c2a5c42034f413d86f1897a3b6 100644
--- a/include/hpp/manipulation/fwd.hh
+++ b/include/hpp/manipulation/fwd.hh
@@ -80,6 +80,8 @@ namespace hpp {
     typedef model::vectorOut_t vectorOut_t;
     HPP_PREDEF_CLASS (ManipulationPlanner);
     typedef boost::shared_ptr < ManipulationPlanner > ManipulationPlannerPtr_t;
+    HPP_PREDEF_CLASS (SymbolicPlanner);
+    typedef boost::shared_ptr < SymbolicPlanner > SymbolicPlannerPtr_t;
     HPP_PREDEF_CLASS (GraphPathValidation);
     typedef boost::shared_ptr < GraphPathValidation > GraphPathValidationPtr_t;
     HPP_PREDEF_CLASS (GraphSteeringMethod);
diff --git a/include/hpp/manipulation/symbolic-planner.hh b/include/hpp/manipulation/symbolic-planner.hh
new file mode 100644
index 0000000000000000000000000000000000000000..556dcbe652d268287a8ff479072db171a6f3a47d
--- /dev/null
+++ b/include/hpp/manipulation/symbolic-planner.hh
@@ -0,0 +1,155 @@
+// Copyright (c) 2014, LAAS-CNRS
+// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
+//
+// This file is part of hpp-manipulation.
+// hpp-manipulation is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation, either version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation 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 Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef HPP_MANIPULATION_SYMBOLIC_PLANNER_HH
+# define HPP_MANIPULATION_SYMBOLIC_PLANNER_HH
+
+#include <hpp/core/path-planner.hh>
+
+#include <hpp/statistics/success-bin.hh>
+
+#include "hpp/manipulation/graph/statistics.hh"
+
+#include "hpp/manipulation/fwd.hh"
+#include "hpp/manipulation/config.hh"
+#include "hpp/manipulation/graph/fwd.hh"
+
+namespace hpp {
+  namespace manipulation {
+    /// \addtogroup path_planning
+    /// \{
+
+    class HPP_MANIPULATION_DLLAPI SymbolicPlanner :
+      public hpp::core::PathPlanner
+    {
+      public:
+        typedef std::list<std::size_t> ErrorFreqs_t;
+        struct ExtendStatus {
+          int status;
+          int info;
+          graph::EdgePtr_t edge;
+          PathValidationReportPtr_t validationReport;
+        };
+
+        /// Create an instance and return a shared pointer to the instance
+        static SymbolicPlannerPtr_t create (const core::Problem& problem,
+            const core::RoadmapPtr_t& roadmap);
+
+        /// One step of extension.
+        ///
+        /// A set of constraints is chosen using the graph of constraints.
+        /// A constraint extension is done using a chosen set.
+        ///
+        virtual void oneStep ();
+
+        /// Extend configuration q_near toward q_rand.
+        /// \param q_near the configuration to be extended.
+        /// \param q_rand the configuration toward extension is performed.
+        /// \retval validPath the longest valid path (possibly of length 0),
+        ///         resulting from the extension.
+        /// \retval status an integer
+        /// \return True if the returned path is valid.
+        bool extend (RoadmapNodePtr_t q_near, const ConfigurationPtr_t &q_rand,
+            core::PathPtr_t& validPath, ExtendStatus& status);
+
+        /// Get the number of occurrence of each errors.
+        ///
+        /// \sa ManipulationPlanner::errorList
+        ErrorFreqs_t getEdgeStat (const graph::EdgePtr_t& edge) const;
+
+        /// Get the list of possible outputs of the extension step.
+        ///
+        /// \sa ManipulationPlanner::getEdgeStat
+        static StringList_t errorList ();
+
+      protected:
+        /// Protected constructor
+        SymbolicPlanner (const Problem& problem,
+            const RoadmapPtr_t& roadmap);
+
+        /// Store weak pointer to itself
+        void init (const SymbolicPlannerWkPtr_t& weak);
+
+      private:
+        /// Try to connect nodes of the roadmap to other connected components.
+        /// \return the number of connection made.
+        std::size_t tryConnectToRoadmap (const core::Nodes_t nodes);
+        /// Try to connect nodes in a list between themselves.
+        /// \return the number of connection made.
+        std::size_t tryConnectNewNodes (const core::Nodes_t nodes);
+
+        void updateWeightsAndProbabilities (
+            const RoadmapNodePtr_t& near, const RoadmapNodePtr_t& newN,
+            const ExtendStatus& es);
+
+        /// The transitions probabilities are updated as follow:
+        ///
+        /// \li let \f$p_{edge}\f$ be the edge probability before update,
+        /// \li the edge probability is multiplied by \f$\alpha\f$,
+        /// \li all other probabilities are multiplied by \f$ \frac{1 - \alpha * p_{edge}}{1 - p_{edge}} \f$
+        ///
+        /// \param alpha should be between 0 and 1.
+        ///
+        /// \note Theoretically, the described operation preserves the
+        /// normalization of the sum. WeighedSymbolicComponent::normalizeProba
+        /// is called to avoid numerical errors.
+        static void updateEdgeProba (
+            const graph::EdgePtr_t edge,
+            WeighedSymbolicComponentPtr_t wsc,
+            const value_type alpha);
+
+        /// Configuration shooter
+        ConfigurationShooterPtr_t shooter_;
+        /// Pointer to the problem
+        const Problem& problem_;
+        /// Pointer to the roadmap
+        RoadmapPtr_t roadmap_;
+        /// weak pointer to itself
+        SymbolicPlannerWkPtr_t weakPtr_;
+
+        /// Keep track of success and failure for method
+        /// extend.
+        typedef ::hpp::statistics::SuccessStatistics SuccessStatistics;
+        typedef ::hpp::statistics::SuccessBin SuccessBin;
+        typedef ::hpp::statistics::SuccessBin::Reason Reason;
+        SuccessStatistics& edgeStat (const graph::EdgePtr_t& edge);
+        std::vector<int> indexPerEdgeStatistics_;
+        std::vector<SuccessStatistics> perEdgeStatistics_;
+
+        /// A Reason is associated to each EdgePtr_t that generated a failure.
+        enum TypeOfFailure {
+          SUCCESS = -1,
+          PROJECTION = 0,
+          STEERING_METHOD = 1,
+          PATH_VALIDATION_ZERO = 2,
+          PATH_PROJECTION_ZERO = 3,
+          UNKNOWN = 4,
+          PATH_PROJECTION_SHORTER = 5,
+          PATH_VALIDATION_SHORTER = 6,
+          PARTLY_EXTENDED = 7,
+          PATH_PROJECTION_AND_VALIDATION_SHORTER = PATH_PROJECTION_SHORTER + PATH_VALIDATION_SHORTER
+        };
+        static const std::vector<Reason> reasons_;
+
+        const value_type extendStep_;
+
+        mutable Configuration_t qProj_;
+    };
+    /// \}
+  } // namespace manipulation
+} // namespace hpp
+
+#endif // HPP_MANIPULATION_SYMBOLIC_PLANNER_HH
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5f841b610fd02e65120e70f5074c8e868065291b..81ba479814aa83ce10c1578a34796491af753f30 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -22,6 +22,7 @@ SET(LIBRARY_NAME ${PROJECT_NAME})
 SET(SOURCES
   axial-handle.cc
   handle.cc
+  symbolic-planner.cc
   manipulation-planner.cc
   problem-solver.cc
   roadmap.cc
diff --git a/src/problem-solver.cc b/src/problem-solver.cc
index e3dca5478d1e232b9270eb3dc59fac04bf08d270..cc3a711b52b9fa5160390b37ffd54349993deaf4 100644
--- a/src/problem-solver.cc
+++ b/src/problem-solver.cc
@@ -38,6 +38,7 @@
 #include "hpp/manipulation/device.hh"
 #include "hpp/manipulation/handle.hh"
 #include "hpp/manipulation/graph/graph.hh"
+#include "hpp/manipulation/symbolic-planner.hh"
 #include "hpp/manipulation/manipulation-planner.hh"
 #include "hpp/manipulation/roadmap.hh"
 #include "hpp/manipulation/constraint-set.hh"
@@ -80,6 +81,8 @@ namespace hpp {
     {
       parent_t::add<core::PathPlannerBuilder_t>
         ("M-RRT", ManipulationPlanner::create);
+      parent_t::add<core::PathPlannerBuilder_t>
+        ("SymbolicPlanner", SymbolicPlanner::create);
       using core::PathOptimizerBuilder_t;
       parent_t::add <PathOptimizerBuilder_t> ("Graph-RandomShortcut",
           GraphOptimizer::create <core::RandomShortcut>);
diff --git a/src/symbolic-planner.cc b/src/symbolic-planner.cc
new file mode 100644
index 0000000000000000000000000000000000000000..362ecf4ead1f111da5a78504c58a8056a38c5f50
--- /dev/null
+++ b/src/symbolic-planner.cc
@@ -0,0 +1,668 @@
+// Copyright (c) 2014, LAAS-CNRS
+// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
+//
+// This file is part of hpp-manipulation.
+// hpp-manipulation is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation, either version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation 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 Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>.
+
+#include "hpp/manipulation/symbolic-planner.hh"
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/assign/list_of.hpp>
+
+#include <hpp/util/pointer.hh>
+#include <hpp/util/timer.hh>
+#include <hpp/util/assertion.hh>
+
+#include <hpp/model/configuration.hh>
+#include <hpp/model/collision-object.hh>
+
+#include <hpp/core/roadmap.hh>
+#include <hpp/core/distance.hh>
+#include <hpp/core/path-validation.hh>
+#include <hpp/core/connected-component.hh>
+#include <hpp/core/path-projector.hh>
+#include <hpp/core/projection-error.hh>
+#include <hpp/core/nearest-neighbor.hh>
+#include <hpp/core/basic-configuration-shooter.hh>
+#include <hpp/core/path-validation-report.hh>
+#include <hpp/core/collision-validation-report.hh>
+
+#include "hpp/manipulation/graph/statistics.hh"
+#include "hpp/manipulation/device.hh"
+#include "hpp/manipulation/problem.hh"
+#include "hpp/manipulation/roadmap.hh"
+#include "hpp/manipulation/roadmap-node.hh"
+#include "hpp/manipulation/symbolic-component.hh"
+#include "hpp/manipulation/graph/edge.hh"
+#include "hpp/manipulation/graph/graph.hh"
+#include "hpp/manipulation/graph/node-selector.hh"
+
+#define CastToWSC_ptr(var, scPtr) \
+  WeighedSymbolicComponentPtr_t var = \
+  HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent,scPtr)
+
+namespace hpp {
+  namespace manipulation {
+    using core::CollisionValidationReport;
+    using core::CollisionValidationReportPtr_t;
+    using core::CollisionObjectPtr_t;
+    typedef graph::Edge::RelativeMotion RelativeMotion;
+
+    namespace {
+      HPP_DEFINE_TIMECOUNTER(oneStep);
+      HPP_DEFINE_TIMECOUNTER(extend);
+      HPP_DEFINE_TIMECOUNTER(tryConnect);
+      HPP_DEFINE_TIMECOUNTER(nearestNeighbor);
+      HPP_DEFINE_TIMECOUNTER(delayedEdges);
+      HPP_DEFINE_TIMECOUNTER(tryConnectNewNodes);
+      HPP_DEFINE_TIMECOUNTER(tryConnectToRoadmap);
+      /// extend steps
+      HPP_DEFINE_TIMECOUNTER(chooseEdge);
+      HPP_DEFINE_TIMECOUNTER(applyConstraints);
+      HPP_DEFINE_TIMECOUNTER(buildPath);
+      HPP_DEFINE_TIMECOUNTER(projectPath);
+      HPP_DEFINE_TIMECOUNTER(validatePath);
+
+      struct WeighedSymbolicComponentComp {
+        bool operator() (const SymbolicComponentPtr_t& lhs, const SymbolicComponentPtr_t& rhs)
+        {
+          return HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, lhs)->weight_
+            > HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, rhs)->weight_;
+        }
+      };
+      typedef std::list<WeighedSymbolicComponentPtr_t> SymbolicComponentList_t;
+      SymbolicComponentList_t sorted_list (const SymbolicComponents_t& sc)
+      {
+        SymbolicComponentList_t l;
+        WeighedSymbolicComponentComp comp;
+        for (SymbolicComponents_t::const_iterator _sc = sc.begin();
+            _sc != sc.end(); ++_sc)
+          l.insert (std::upper_bound(l.begin(), l.end(), *_sc, comp),
+              HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, *_sc));
+        return l;
+      }
+
+      struct DistanceToConfiguration {
+        const Configuration_t& q_;
+        core::Distance& d_;
+        value_type mind_;
+        RoadmapNodePtr_t node_;
+        DistanceToConfiguration(const Configuration_t& q, core::Distance& d)
+          : q_(q), d_(d), mind_(std::numeric_limits<value_type>::max()) {}
+        void operator() (const RoadmapNodePtr_t& n)
+        {
+          value_type dist = d_ (*(n->configuration()), q_);
+          if (dist < mind_) {
+            node_ = n;
+            mind_ = dist;
+          }
+        }
+      };
+
+      bool belongs (const ConfigurationPtr_t& q, const core::Nodes_t& nodes)
+      {
+        for (core::Nodes_t::const_iterator itNode = nodes.begin ();
+            itNode != nodes.end (); ++itNode) {
+          if (*((*itNode)->configuration ()) == *q) return true;
+        }
+        return false;
+      }
+    }
+
+    const std::vector<SymbolicPlanner::Reason>
+      SymbolicPlanner::reasons_ = boost::assign::list_of
+      (SuccessBin::createReason ("[Fail] Projection"))
+      (SuccessBin::createReason ("[Fail] SteeringMethod"))
+      (SuccessBin::createReason ("[Fail] Path validation returned length 0"))
+      (SuccessBin::createReason ("[Fail] Path could not be projected"))
+      (SuccessBin::createReason ("[Fail] Unknow"))
+      (SuccessBin::createReason ("[Info] Path could not be fully projected"))
+      (SuccessBin::createReason ("[Info] Path could not be fully validated"))
+      (SuccessBin::createReason ("[Info] Extended partly"));
+
+    SymbolicPlannerPtr_t SymbolicPlanner::create (const core::Problem& problem,
+        const core::RoadmapPtr_t& roadmap)
+    {
+      SymbolicPlanner* ptr;
+      core::RoadmapPtr_t r2 = roadmap;
+      RoadmapPtr_t r;
+      try {
+        const Problem& p = dynamic_cast < const Problem& > (problem);
+        RoadmapPtr_t r = HPP_DYNAMIC_PTR_CAST (Roadmap, r2);
+        ptr = new SymbolicPlanner (p, r);
+      } catch (std::exception&) {
+        if (!r)
+          throw std::invalid_argument ("The roadmap must be of type hpp::manipulation::Roadmap.");
+        else
+        throw std::invalid_argument ("The problem must be of type hpp::manipulation::Problem.");
+      }
+      SymbolicPlannerPtr_t shPtr (ptr);
+      ptr->init (shPtr);
+      return shPtr;
+    }
+
+    SymbolicPlanner::ErrorFreqs_t SymbolicPlanner::getEdgeStat
+      (const graph::EdgePtr_t& edge) const
+    {
+      const std::size_t& id = edge->id ();
+      ErrorFreqs_t ret;
+      if (indexPerEdgeStatistics_.size() <= id ||
+         indexPerEdgeStatistics_[id] < 0) {
+        for (std::size_t i = 0; i < reasons_.size(); ++i) ret.push_back (0);
+      } else {
+        const SuccessStatistics& ss =
+          perEdgeStatistics_[indexPerEdgeStatistics_[id]];
+        ret.push_back (ss.nbSuccess ());
+        for (std::size_t i = 0; i < reasons_.size(); ++i)
+          ret.push_back (ss.nbFailure (reasons_[i]));
+      }
+      return ret;
+    }
+
+    StringList_t SymbolicPlanner::errorList ()
+    {
+      StringList_t ret;
+      ret.push_back ("Success");
+      for (std::size_t i = 0; i < reasons_.size(); ++i) ret.push_back (reasons_[i].what);
+      return ret;
+    }
+
+    void SymbolicPlanner::oneStep ()
+    {
+      HPP_START_TIMECOUNTER(oneStep);
+
+      // Get the robot
+      DevicePtr_t robot = HPP_DYNAMIC_PTR_CAST(Device, problem ().robot ());
+      HPP_ASSERT(robot);
+      // Get the roadmap and the symbolic components
+      RoadmapPtr_t rdm = HPP_DYNAMIC_PTR_CAST(Roadmap, roadmap());
+      HPP_ASSERT(rdm);
+      SymbolicComponentList_t scs = sorted_list (rdm->symbolicComponents());
+
+      core::Nodes_t newNodes;
+      core::PathPtr_t path;
+
+      typedef boost::tuple <RoadmapNodePtr_t, ConfigurationPtr_t, core::PathPtr_t,
+              ExtendStatus>
+	DelayedEdge_t;
+      typedef std::vector <DelayedEdge_t> DelayedEdges_t;
+      DelayedEdges_t delayedEdges;
+
+      // Pick a random node
+      ConfigurationPtr_t q_rand = shooter_->shoot();
+
+      // Extend each connected component
+      for (core::ConnectedComponents_t::const_iterator itcc =
+          rdm->connectedComponents ().begin ();
+          itcc != rdm->connectedComponents ().end (); ++itcc) {
+        // Find the symbolic component in this connected component which has
+        // the highest value.
+        SymbolicComponentPtr_t sc;
+        for (SymbolicComponentList_t::iterator _sc = scs.begin();
+            _sc != scs.end(); ++_sc) {
+          sc = *_sc;
+          if (sc->connectedComponent() == *itcc) break;
+        }
+        if (!sc) {
+          hppDout (error, "There should always be a connected component corresponding to symbolic component");
+          continue;
+        }
+
+        // Find the nearest neighbor.
+        HPP_START_TIMECOUNTER(nearestNeighbor);
+        DistanceToConfiguration dtc (*q_rand, *rdm->distance());
+        RoadmapNodePtr_t near =
+          std::for_each(sc->nodes().begin(), sc->nodes().end(), dtc).node_;
+        HPP_STOP_TIMECOUNTER(nearestNeighbor);
+        HPP_DISPLAY_LAST_TIMECOUNTER(nearestNeighbor);
+        if (!near) continue;
+
+        // Extension
+        HPP_START_TIMECOUNTER(extend);
+        ExtendStatus status;
+        bool pathIsValid = extend (near, q_rand, path, status);
+        HPP_STOP_TIMECOUNTER(extend);
+        HPP_DISPLAY_LAST_TIMECOUNTER(extend);
+
+        // Insert new path to q_near in roadmap
+        if (pathIsValid) {
+          value_type t_final = path->timeRange ().second;
+          if (t_final != path->timeRange ().first) {
+            bool success;
+            ConfigurationPtr_t q_new (new Configuration_t
+                ((*path) (t_final, success)));
+            delayedEdges.push_back (DelayedEdge_t (near, q_new, path, status));
+          }
+        } else {
+          updateWeightsAndProbabilities (near, RoadmapNodePtr_t(), status);
+        }
+      }
+
+      // Insert delayed edges
+      HPP_START_TIMECOUNTER(delayedEdges);
+      for (DelayedEdges_t::const_iterator itEdge = delayedEdges.begin ();
+	   itEdge != delayedEdges.end (); ++itEdge) {
+	const core::NodePtr_t& near = itEdge-> get <0> ();
+	const ConfigurationPtr_t& q_new = itEdge-> get <1> ();
+	const core::PathPtr_t& validPath = itEdge-> get <2> ();
+        core::NodePtr_t newNode = rdm->addNode (q_new);
+	roadmap()->addEdge (near, newNode, validPath);
+        core::interval_t timeRange = validPath->timeRange ();
+	roadmap()->addEdge (newNode, near, validPath->extract
+			     (core::interval_t (timeRange.second ,
+                                                timeRange.first)));
+        updateWeightsAndProbabilities (
+            static_cast<RoadmapNode*>(near),
+            static_cast<RoadmapNode*>(newNode),
+            itEdge->get<3>());
+      }
+      HPP_STOP_TIMECOUNTER(delayedEdges);
+
+      // Try to connect the new nodes together
+      HPP_START_TIMECOUNTER(tryConnectNewNodes);
+      const std::size_t nbConn = tryConnectNewNodes (newNodes);
+      HPP_STOP_TIMECOUNTER(tryConnectNewNodes);
+      HPP_DISPLAY_LAST_TIMECOUNTER(tryConnectNewNodes);
+      if (nbConn == 0) {
+        HPP_START_TIMECOUNTER(tryConnectToRoadmap);
+        tryConnectToRoadmap (newNodes);
+        HPP_STOP_TIMECOUNTER(tryConnectToRoadmap);
+        HPP_DISPLAY_LAST_TIMECOUNTER(tryConnectToRoadmap);
+      }
+      HPP_STOP_TIMECOUNTER(oneStep);
+      HPP_DISPLAY_LAST_TIMECOUNTER(oneStep);
+      HPP_DISPLAY_TIMECOUNTER(oneStep);
+      HPP_DISPLAY_TIMECOUNTER(extend);
+      HPP_DISPLAY_TIMECOUNTER(tryConnect);
+      HPP_DISPLAY_TIMECOUNTER(tryConnectNewNodes);
+      HPP_DISPLAY_TIMECOUNTER(tryConnectToRoadmap);
+      HPP_DISPLAY_TIMECOUNTER(nearestNeighbor);
+      HPP_DISPLAY_TIMECOUNTER(delayedEdges);
+      HPP_DISPLAY_TIMECOUNTER(chooseEdge);
+      HPP_DISPLAY_TIMECOUNTER(applyConstraints);
+      HPP_DISPLAY_TIMECOUNTER(buildPath);
+      HPP_DISPLAY_TIMECOUNTER(projectPath);
+      HPP_DISPLAY_TIMECOUNTER(validatePath);
+    }
+
+    bool SymbolicPlanner::extend(
+        RoadmapNodePtr_t n_near,
+        const ConfigurationPtr_t& q_rand,
+        core::PathPtr_t& validPath,
+        ExtendStatus& status)
+    {
+      status.info = SUCCESS;
+      graph::GraphPtr_t graph = problem_.constraintGraph ();
+      // Select next node in the constraint graph.
+      const ConfigurationPtr_t q_near = n_near->configuration ();
+
+      HPP_START_TIMECOUNTER (chooseEdge);
+      // This code should go into a NodeSelector derived class.
+      WeighedSymbolicComponentPtr_t wscPtr = HPP_DYNAMIC_PTR_CAST
+        (WeighedSymbolicComponent, n_near->symbolicComponent());
+      if (wscPtr) {
+        WeighedSymbolicComponent wsc = *wscPtr;
+        value_type R = rand() / RAND_MAX;
+        std::size_t i = 0;
+        for (value_type sum = wsc.p_[0]; sum < R; sum += wsc.p_[i]) { ++i; }
+        assert(i < wsc.edges_.size());
+        status.edge = wsc.edges_[i];
+      } else status.edge = graph->chooseEdge (n_near);
+      HPP_STOP_TIMECOUNTER (chooseEdge);
+      if (!status.edge) {
+        status.status = UNKNOWN;
+        return false;
+      }
+
+      qProj_ = *q_rand;
+      HPP_START_TIMECOUNTER (applyConstraints);
+      SuccessStatistics& es = edgeStat (status.edge);
+      if (!status.edge->applyConstraints (n_near, qProj_)) {
+        HPP_STOP_TIMECOUNTER (applyConstraints);
+        es.addFailure (reasons_[PROJECTION]);
+        status.status = PROJECTION;
+        return false;
+      }
+      HPP_STOP_TIMECOUNTER (applyConstraints);
+
+      core::PathPtr_t path;
+      HPP_START_TIMECOUNTER (buildPath);
+      if (!status.edge->build (path, *q_near, qProj_)) {
+        HPP_STOP_TIMECOUNTER (buildPath);
+        es.addFailure (reasons_[STEERING_METHOD]);
+        status.status = STEERING_METHOD;
+        return false;
+      }
+      HPP_STOP_TIMECOUNTER (buildPath);
+
+      core::PathPtr_t projPath;
+      bool projShorter = false;
+      PathProjectorPtr_t pathProjector = problem_.pathProjector ();
+      if (pathProjector) {
+        HPP_START_TIMECOUNTER (projectPath);
+        projShorter = !pathProjector->apply (path, projPath);
+        if (projShorter) {
+          if (!projPath || projPath->length () == 0) {
+            HPP_STOP_TIMECOUNTER (projectPath);
+            es.addFailure (reasons_[PATH_PROJECTION_ZERO]);
+            status.status = PATH_PROJECTION_ZERO;
+            return false;
+          }
+          status.info = PATH_PROJECTION_SHORTER;
+          es.addFailure (reasons_[PATH_PROJECTION_SHORTER]);
+        }
+        HPP_STOP_TIMECOUNTER (projectPath);
+      } else projPath = path;
+      GraphPathValidationPtr_t pathValidation (problem_.pathValidation ());
+      core::PathPtr_t fullValidPath;
+
+      HPP_START_TIMECOUNTER (validatePath);
+      bool fullyValid = false;
+      try {
+        fullyValid = pathValidation->validate
+          (projPath, false, fullValidPath, status.validationReport);
+      } catch (const core::projection_error& e) {
+        hppDout (error, e.what ());
+        es.addFailure (reasons_[PATH_VALIDATION_ZERO]);
+        status.status = PATH_VALIDATION_ZERO;
+        return false;
+      }
+      HPP_STOP_TIMECOUNTER (validatePath);
+
+      if (fullValidPath->length () == 0) {
+        es.addFailure (reasons_[PATH_VALIDATION_ZERO]);
+        validPath = fullValidPath;
+        status.status = PATH_PROJECTION_ZERO;
+        return false;
+      } else {
+        if (!fullyValid) {
+          es.addFailure (reasons_[PATH_VALIDATION_SHORTER]);
+          status.status = (projShorter?PATH_PROJECTION_AND_VALIDATION_SHORTER:PATH_VALIDATION_SHORTER);
+        }
+        if (extendStep_ == 1 || fullyValid) {
+          validPath = fullValidPath;
+        } else {
+          const value_type& length = fullValidPath->length();
+          const value_type& t_init = fullValidPath->timeRange ().first;
+          try {
+            validPath = fullValidPath->extract
+              (core::interval_t(t_init, t_init + length * extendStep_));
+          } catch (const core::projection_error& e) {
+            hppDout (error, e.what());
+            es.addFailure (reasons_[PATH_PROJECTION_SHORTER]);
+            status.status = UNKNOWN;
+            return false;
+          }
+        }
+        hppDout (info, "Extension:" << std::endl
+            << es);
+      }
+      if (!projShorter && fullValidPath) es.addSuccess ();
+      else es.addFailure (reasons_[PARTLY_EXTENDED]);
+      status.status = SUCCESS;
+      return true;
+    }
+
+    SymbolicPlanner::SuccessStatistics& SymbolicPlanner::edgeStat
+      (const graph::EdgePtr_t& edge)
+    {
+      const std::size_t& id = edge->id ();
+      if (indexPerEdgeStatistics_.size () <= id) {
+        indexPerEdgeStatistics_.resize (id + 1, -1);
+      }
+      if (indexPerEdgeStatistics_[id] < 0) {
+        indexPerEdgeStatistics_[id] = perEdgeStatistics_.size();
+        perEdgeStatistics_.push_back (SuccessStatistics (edge->name (), 2));
+      }
+      return perEdgeStatistics_[indexPerEdgeStatistics_[id]];
+    }
+
+    inline std::size_t SymbolicPlanner::tryConnectToRoadmap (const core::Nodes_t nodes)
+    {
+      const core::SteeringMethodPtr_t& sm (problem ().steeringMethod ());
+      core::PathValidationPtr_t pathValidation (problem ().pathValidation ());
+      PathProjectorPtr_t pathProjector (problem().pathProjector ());
+      core::PathPtr_t path, projPath, validPath;
+      graph::GraphPtr_t graph = problem_.constraintGraph ();
+      bool connectSucceed = false;
+      std::size_t nbConnection = 0;
+      const std::size_t K = 7;
+      value_type distance;
+      for (core::Nodes_t::const_iterator itn1 = nodes.begin ();
+          itn1 != nodes.end (); ++itn1) {
+        ConfigurationPtr_t q1 ((*itn1)->configuration ());
+        connectSucceed = false;
+        for (core::ConnectedComponents_t::const_iterator itcc =
+            roadmap ()->connectedComponents ().begin ();
+            itcc != roadmap ()->connectedComponents ().end (); ++itcc) {
+          if (*itcc == (*itn1)->connectedComponent ())
+            continue;
+          core::Nodes_t knearest = roadmap()->nearestNeighbor ()
+            ->KnearestSearch (q1, *itcc, K, distance);
+          for (core::Nodes_t::const_iterator itn2 = knearest.begin ();
+              itn2 != knearest.end (); ++itn2) {
+            bool _1to2 = (*itn1)->isOutNeighbor (*itn2);
+            bool _2to1 = (*itn1)->isInNeighbor (*itn2);
+            if (_1to2 && _2to1) {
+              hppDout (info, "the two nodes are already connected");
+              continue;
+            }
+            ConfigurationPtr_t q2 ((*itn2)->configuration ());
+            assert (*q1 != *q2);
+            path = (*sm) (*q1, *q2);
+            if (!path) continue;
+            if (pathProjector) {
+              if (!pathProjector->apply (path, projPath)) continue;
+            } else projPath = path;
+	    PathValidationReportPtr_t report;
+            if (pathValidation->validate (projPath, false, validPath, report)) {
+              nbConnection++;
+              if (!_1to2) roadmap ()->addEdge (*itn1, *itn2, projPath);
+              if (!_2to1) {
+                core::interval_t timeRange = projPath->timeRange ();
+                roadmap ()->addEdge (*itn2, *itn1, projPath->extract
+                    (core::interval_t (timeRange.second,
+                                       timeRange.first)));
+              }
+              connectSucceed = true;
+              break;
+            }
+          }
+          if (connectSucceed) break;
+        }
+      }
+      return nbConnection;
+    }
+
+    inline std::size_t SymbolicPlanner::tryConnectNewNodes (const core::Nodes_t nodes)
+    {
+      const core::SteeringMethodPtr_t& sm (problem ().steeringMethod ());
+      core::PathValidationPtr_t pathValidation (problem ().pathValidation ());
+      PathProjectorPtr_t pathProjector (problem().pathProjector ());
+      core::PathPtr_t path, projPath, validPath;
+      graph::GraphPtr_t graph = problem_.constraintGraph ();
+      std::size_t nbConnection = 0;
+      for (core::Nodes_t::const_iterator itn1 = nodes.begin ();
+          itn1 != nodes.end (); ++itn1) {
+        ConfigurationPtr_t q1 ((*itn1)->configuration ());
+        for (core::Nodes_t::const_iterator itn2 = boost::next (itn1);
+            itn2 != nodes.end (); ++itn2) {
+          if ((*itn1)->connectedComponent () == (*itn2)->connectedComponent ())
+            continue;
+          bool _1to2 = (*itn1)->isOutNeighbor (*itn2);
+          bool _2to1 = (*itn1)->isInNeighbor (*itn2);
+          if (_1to2 && _2to1) {
+            hppDout (info, "the two nodes are already connected");
+            continue;
+          }
+          ConfigurationPtr_t q2 ((*itn2)->configuration ());
+          assert (*q1 != *q2);
+          path = (*sm) (*q1, *q2);
+          if (!path) continue;
+          if (pathProjector) {
+            if (!pathProjector->apply (path, projPath)) continue;
+          } else projPath = path;
+          PathValidationReportPtr_t report;
+          if (pathValidation->validate (projPath, false, validPath, report)) {
+            nbConnection++;
+            if (!_1to2) roadmap ()->addEdge (*itn1, *itn2, projPath);
+            if (!_2to1) {
+              core::interval_t timeRange = projPath->timeRange ();
+              roadmap ()->addEdge (*itn2, *itn1, projPath->extract
+                  (core::interval_t (timeRange.second,
+                                     timeRange.first)));
+            }
+          }
+        }
+      }
+      return nbConnection;
+    }
+
+    inline void SymbolicPlanner::updateWeightsAndProbabilities (
+        const RoadmapNodePtr_t& near, const RoadmapNodePtr_t& newN,
+        const ExtendStatus& extend)
+    {
+      // 0.99 ^ 26 * 1.3 ~= 1  => A new extension followed by 26 failures
+      // results in similar weights for the symbolic components.
+      const value_type weightInc = 1.3;
+      const value_type weightDec = 0.99;
+      CastToWSC_ptr (oldWSC, near->symbolicComponent());
+      CollisionValidationReportPtr_t colRep;
+      switch (extend.status) {
+        case SUCCESS:
+          {
+            CastToWSC_ptr (newWSC, newN->symbolicComponent());
+            switch (extend.info) {
+              case SUCCESS:
+                // If the corresponding edge is a loop, no adjustment.
+                // Otherwise, a new symbolic component has been created. This new SC
+                // should have higher priority than the old one.
+                if (oldWSC != newWSC) {
+                  newWSC->weight_ = oldWSC->weight_ * weightInc;
+                  // Maybe we should decrease the edge probability as well.
+                  // A new connection with this edge should be tried whenever
+                  // we have tried to extend the new SC and we could not.
+                }
+                oldWSC->weight_ *= weightDec;
+                updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big.
+                break;
+              case PATH_PROJECTION_SHORTER:
+              case PATH_PROJECTION_AND_VALIDATION_SHORTER:
+                // Hard to say what to do on this case...
+                break;
+              case PATH_VALIDATION_SHORTER:
+                // The path was made shorter, check the validation report to know
+                // why.
+                colRep = HPP_DYNAMIC_PTR_CAST(CollisionValidationReport,
+                    extend.validationReport->configurationReport);
+                if (colRep) {
+                  CollisionObjectPtr_t o1 = colRep->object1, o2 = colRep->object2;
+                  // If it is a collision between the robot and an unactuated object,
+                  // which cannot move in this state.
+                  if (o1->joint() != NULL && o2->joint() != NULL) {
+                    // TODO: Currently, the RelativeMotion matrix does not cover cases
+                    // like ConvexShapeContact (2 function making a 6D constraints).
+                    RelativeMotion::matrix_type m = extend.edge->relativeMotion();
+                    size_type i0 = RelativeMotion::idx(NULL);
+                    size_type i1 = RelativeMotion::idx(o1->joint());
+                    size_type i2 = RelativeMotion::idx(o2->joint());
+                    if (m(i0, i1) == RelativeMotion::Parameterized
+                        || m(i0, i1) == RelativeMotion::Constrained) {
+                      // Object1 should be moved out of the way first...
+                      updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big.
+                    }
+                    if (m(i0, i2) == RelativeMotion::Parameterized
+                        || m(i0, i2) == RelativeMotion::Constrained) {
+                      // Object2 should be moved out of the way first...
+                      updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big.
+                    }
+                  }
+                }
+                break;
+            }
+          }
+          break;
+          // For cases below, newN is a NULL pointer.
+        case PROJECTION:
+        case PATH_PROJECTION_ZERO: // unclear for PATH_PROJECTION_ZERO
+          // It was not possible to project. It may be that projection is not
+          // possible, or only that it is not yet possible.
+          // We decrease the probability of sampling this edge.
+          updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big.
+          oldWSC->weight_ *= weightDec;
+          break;
+        case PATH_VALIDATION_ZERO:
+          // The path validation report will say what's wrong.
+          // The only interesting thing are collisions with objects.
+          colRep = HPP_DYNAMIC_PTR_CAST(CollisionValidationReport,
+                extend.validationReport->configurationReport);
+          if (colRep) {
+            CollisionObjectPtr_t o1 = colRep->object1, o2 = colRep->object2;
+            // If it is a collision between the robot and an unactuated object,
+            // which cannot move in this state.
+            if (o1->joint() != NULL && o2->joint() != NULL) {
+              // TODO: Currently, the RelativeMotion matrix does not cover cases
+              // like ConvexShapeContact (2 function making a 6D constraints).
+              RelativeMotion::matrix_type m = extend.edge->relativeMotion();
+              size_type i0 = RelativeMotion::idx(NULL);
+              size_type i1 = RelativeMotion::idx(o1->joint());
+              size_type i2 = RelativeMotion::idx(o2->joint());
+              if (m(i0, i1) == RelativeMotion::Parameterized
+                  || m(i0, i1) == RelativeMotion::Constrained) {
+                // Object1 should be moved out of the way first...
+                updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big.
+              }
+              if (m(i0, i2) == RelativeMotion::Parameterized
+                  || m(i0, i2) == RelativeMotion::Constrained) {
+                // Object2 should be moved out of the way first...
+                updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big.
+              }
+            }
+          }
+          break;
+        case UNKNOWN:
+        case STEERING_METHOD:
+          // Hard to say what to do.
+          oldWSC->weight_ *= weightDec;
+          break;
+      }
+    }
+
+    void SymbolicPlanner::updateEdgeProba (
+        const graph::EdgePtr_t edge,
+        WeighedSymbolicComponentPtr_t wsc,
+        const value_type alpha) {
+      std::size_t i = wsc->indexOf (edge);
+      assert(i < wsc->p_.size());
+      const value_type pi = wsc->p_[i];
+      wsc->p_ *= ( 1 - alpha * pi ) / ( 1 - pi );
+      wsc->p_[i] = alpha * pi;
+      wsc->normalizeProba();
+    }
+
+    SymbolicPlanner::SymbolicPlanner (const Problem& problem,
+        const RoadmapPtr_t& roadmap) :
+      core::PathPlanner (problem, roadmap),
+      shooter_ (problem.configurationShooter()),
+      problem_ (problem), roadmap_ (roadmap),
+      extendStep_ (1),
+      qProj_ (problem.robot ()->configSize ())
+    {}
+
+    void SymbolicPlanner::init (const SymbolicPlannerWkPtr_t& weak)
+    {
+      core::PathPlanner::init (weak);
+      weakPtr_ = weak;
+    }
+  } // namespace manipulation
+} // namespace hpp