GCC Code Coverage Report


Directory: ./
File: src/graph/edge.cc
Date: 2025-03-07 11:10:46
Exec Total Coverage
Lines: 90 405 22.2%
Branches: 73 974 7.5%

Line Branch Exec Source
1 // Copyright (c) 2014, LAAS-CNRS
2 // Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
3 //
4
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // 1. Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27 // DAMAGE.
28
29 #include "hpp/manipulation/graph/edge.hh"
30
31 #include <hpp/constraints/differentiable-function.hh>
32 #include <hpp/constraints/locked-joint.hh>
33 #include <hpp/constraints/solver/by-substitution.hh>
34 #include <hpp/core/obstacle-user.hh>
35 #include <hpp/core/path-validation.hh>
36 #include <hpp/core/path-vector.hh>
37 #include <hpp/pinocchio/configuration.hh>
38 #include <hpp/util/exception-factory.hh>
39 #include <hpp/util/pointer.hh>
40 #include <sstream>
41
42 #include "hpp/manipulation/constraint-set.hh"
43 #include "hpp/manipulation/device.hh"
44 #include "hpp/manipulation/graph/statistics.hh"
45 #include "hpp/manipulation/problem.hh"
46 #include "hpp/manipulation/steering-method/graph.hh"
47
48 namespace hpp {
49 namespace manipulation {
50 namespace graph {
51 8 Edge::Edge(const std::string& name)
52 : GraphComponent(name),
53 8 isShort_(false),
54 8 pathConstraints_(),
55 8 targetConstraints_(),
56 8 steeringMethod_(),
57
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 securityMargins_(),
58
1/2
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
32 pathValidation_() {}
59
60 Edge::~Edge() {}
61
62 18 StatePtr_t Edge::stateTo() const { return to_.lock(); }
63
64 StatePtr_t Edge::stateFrom() const { return from_.lock(); }
65
66 void Edge::relativeMotion(const RelativeMotion::matrix_type& m) {
67 if (!isInit_)
68 throw std::logic_error(
69 "The graph must be initialized before changing the relative motion "
70 "matrix.");
71 shared_ptr<core::ObstacleUserInterface> oui =
72 HPP_DYNAMIC_PTR_CAST(core::ObstacleUserInterface, pathValidation_);
73 if (oui) oui->filterCollisionPairs(m);
74 relMotion_ = m;
75 }
76
77 bool Edge::direction(const core::PathPtr_t& path) const {
78 Configuration_t q0 = path->initial(), q1 = path->end();
79 const bool src_contains_q0 = stateFrom()->contains(q0);
80 const bool dst_contains_q0 = stateTo()->contains(q0);
81 const bool src_contains_q1 = stateFrom()->contains(q1);
82 const bool dst_contains_q1 = stateTo()->contains(q1);
83 // Karnaugh table:
84 // 1 = forward, 0 = reverse, ? = I don't know, * = 0 or 1
85 // s0s1 \ d0d1 | 00 | 01 | 11 | 10
86 // 00 | ? | ? | ? | ?
87 // 01 | ? | ? | 0 | 0
88 // 11 | ? | 1 | * | 0
89 // 10 | ? | 1 | 1 | 1
90 //
91 /// true if reverse
92 if ((!src_contains_q0 && !src_contains_q1) ||
93 (!dst_contains_q0 && !dst_contains_q1) ||
94 (!src_contains_q0 && !dst_contains_q0))
95 HPP_THROW(std::runtime_error,
96 "Edge " << name() << " does not seem to have generated path from"
97 << pinocchio::displayConfig(q0) << " to "
98 << pinocchio::displayConfig(q1));
99 return !(src_contains_q0 && (!src_contains_q1 || dst_contains_q1));
100 }
101
102 void Edge::securityMarginForPair(const size_type& row, const size_type& col,
103 const value_type& margin) {
104 if ((row < 0) || (row >= securityMargins_.rows())) {
105 std::ostringstream os;
106 os << "Row index should be between 0 and " << securityMargins_.rows() + 1
107 << ", got " << row << ".";
108 throw std::runtime_error(os.str().c_str());
109 }
110 if ((col < 0) || (col >= securityMargins_.cols())) {
111 std::ostringstream os;
112 os << "Column index should be between 0 and " << securityMargins_.cols() + 1
113 << ", got " << col << ".";
114 throw std::runtime_error(os.str().c_str());
115 }
116 if (securityMargins_(row, col) != margin) {
117 securityMargins_(row, col) = margin;
118 securityMargins_(col, row) = margin;
119 invalidate();
120 }
121 }
122
123 bool Edge::intersectionConstraint(const EdgePtr_t& other,
124 ConfigProjectorPtr_t proj) const {
125 GraphPtr_t g = graph_.lock();
126
127 g->insertNumericalConstraints(proj);
128 insertNumericalConstraints(proj);
129 state()->insertNumericalConstraints(proj);
130
131 if (wkPtr_.lock() == other) // No intersection to be computed.
132 return false;
133
134 bool stateB_Eq_stateA = (state() == other->state());
135
136 other->insertNumericalConstraints(proj);
137 if (!stateB_Eq_stateA) other->state()->insertNumericalConstraints(proj);
138 return true;
139 }
140
141 8 EdgePtr_t Edge::create(const std::string& name, const GraphWkPtr_t& graph,
142 const StateWkPtr_t& from, const StateWkPtr_t& to) {
143
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 Edge* ptr = new Edge(name);
144 8 EdgePtr_t shPtr(ptr);
145
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 ptr->init(shPtr, graph, from, to);
146 8 return shPtr;
147 }
148
149 8 void Edge::init(const EdgeWkPtr_t& weak, const GraphWkPtr_t& graph,
150 const StateWkPtr_t& from, const StateWkPtr_t& to) {
151
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 GraphComponent::init(weak);
152 8 parentGraph(graph);
153 8 wkPtr_ = weak;
154 8 from_ = from;
155 8 to_ = to;
156 8 state_ = to;
157 // add 1 joint for the environment
158
2/4
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 size_type n(graph.lock()->robot()->nbJoints() + 1);
159 8 securityMargins_.resize(n, n);
160 8 securityMargins_.setZero();
161 8 }
162
163 8 void Edge::initialize() {
164
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (!isInit_) {
165 8 targetConstraints_ = buildTargetConstraint();
166 8 pathConstraints_ = buildPathConstraint();
167 }
168 8 isInit_ = true;
169 8 }
170
171 std::ostream& Edge::print(std::ostream& os) const {
172 os << "| | |-- ";
173 GraphComponent::print(os) << " --> " << to_.lock()->name();
174 return os;
175 }
176
177 std::ostream& Edge::dotPrint(std::ostream& os,
178 dot::DrawingAttributes da) const {
179 da.insertWithQuote("label", name());
180 da.insert("shape", "onormal");
181 dot::Tooltip tp;
182 tp.addLine("Edge constains:");
183 populateTooltip(tp);
184 da.insertWithQuote("tooltip", tp.toStr());
185 da.insertWithQuote("labeltooltip", tp.toStr());
186 os << stateFrom()->id() << " -> " << stateTo()->id() << " " << da << ";";
187 return os;
188 }
189
190 4 ConstraintSetPtr_t Edge::targetConstraint() const {
191 4 throwIfNotInitialized();
192 return targetConstraints_;
193 }
194
195 // Merge constraints of several graph components into a config projector
196 // Replace constraints and complement by combination of both when
197 // necessary.
198 16 static void mergeConstraintsIntoConfigProjector(
199 const ConfigProjectorPtr_t& proj,
200 const std::vector<GraphComponentPtr_t>& components,
201 const GraphPtr_t& graph) {
202 16 NumericalConstraints_t nc;
203
2/2
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 16 times.
64 for (const auto& gc : components)
204
2/4
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 48 times.
✗ Branch 9 not taken.
48 nc.insert(nc.end(), gc->numericalConstraints().begin(),
205
1/2
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 gc->numericalConstraints().end());
206
207 // Remove duplicate constraints
208 16 auto end = nc.end();
209
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
16 for (auto it = nc.begin(); it != end; ++it)
210 end = std::remove(std::next(it), end, *it);
211
1/2
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 nc.erase(end, nc.end());
212
213 16 NumericalConstraints_t::iterator itnc1, itnc2;
214
215 // Look for complement
216
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
16 for (itnc1 = nc.begin(); itnc1 != nc.end(); ++itnc1) {
217 const auto& c1 = *itnc1;
218 itnc2 = std::next(itnc1);
219 constraints::ImplicitPtr_t combination;
220 itnc2 = std::find_if(std::next(itnc1), nc.end(),
221 [&c1, &combination, &graph](const auto& c2) -> bool {
222 assert(c1 != c2);
223 return graph->isComplement(c1, c2, combination) ||
224 graph->isComplement(c2, c1, combination);
225 });
226 if (itnc2 != nc.end()) {
227 assert(*itnc1 != *itnc2);
228 *itnc1 = combination;
229 nc.erase(itnc2);
230 }
231 }
232
233
1/4
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 16 times.
16 for (const auto& _nc : nc) proj->add(_nc);
234 // Insert numerical costs
235 16 nc.clear();
236
2/2
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 16 times.
64 for (const auto& gc : components)
237
2/4
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 48 times.
✗ Branch 9 not taken.
48 nc.insert(nc.end(), gc->numericalCosts().begin(),
238
1/2
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 gc->numericalCosts().end());
239
1/4
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 16 times.
16 for (const auto& _nc : nc) proj->add(_nc, 1);
240 16 }
241
242 8 ConstraintSetPtr_t Edge::buildTargetConstraint() {
243
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 std::string n = "(" + name() + ")";
244 8 GraphPtr_t g = graph_.lock();
245
246
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 ConstraintSetPtr_t constraint = ConstraintSet::create(g->robot(), "Set " + n);
247
248 ConfigProjectorPtr_t proj = ConfigProjector::create(
249
5/10
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
16 g->robot(), "proj_" + n, g->errorThreshold(), g->maxIterations());
250 8 proj->solver().solveLevelByLevel(this->solveLevelByLevel());
251 8 std::vector<GraphComponentPtr_t> components;
252
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 components.push_back(g);
253
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 components.push_back(wkPtr_.lock());
254
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 components.push_back(stateTo());
255
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
8 if (state() != stateTo()) {
256 components.push_back(state());
257 }
258 // Copy constraints from
259 // - graph,
260 // - this edge,
261 // - the destination state,
262 // - the state in which the transition lies if different
263
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 mergeConstraintsIntoConfigProjector(proj, components, parentGraph());
264
265
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 constraint->addConstraint(proj);
266
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 constraint->edge(wkPtr_.lock());
267 16 return constraint;
268 8 }
269
270 ConstraintSetPtr_t Edge::pathConstraint() const {
271 throwIfNotInitialized();
272 return pathConstraints_;
273 }
274
275 8 ConstraintSetPtr_t Edge::buildPathConstraint() {
276
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 std::string n = "(" + name() + ")";
277 8 GraphPtr_t g = graph_.lock();
278
279
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 ConstraintSetPtr_t constraint = ConstraintSet::create(g->robot(), "Set " + n);
280
281 ConfigProjectorPtr_t proj = ConfigProjector::create(
282
5/10
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
16 g->robot(), "proj_" + n, .5 * g->errorThreshold(), g->maxIterations());
283 8 proj->solver().solveLevelByLevel(this->solveLevelByLevel());
284 8 std::vector<GraphComponentPtr_t> components;
285
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 components.push_back(g);
286
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 components.push_back(wkPtr_.lock());
287
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 components.push_back(state());
288
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 mergeConstraintsIntoConfigProjector(proj, components, parentGraph());
289
290
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 constraint->addConstraint(proj);
291
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 constraint->edge(wkPtr_.lock());
292
293 // Build steering method
294
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 const ProblemPtr_t& problem(g->problem());
295 steeringMethod_ =
296
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 problem->manipulationSteeringMethod()->innerSteeringMethod()->copy();
297 8 steeringMethod_->constraints(constraint);
298 // Build path validation and relative motion matrix
299 // TODO this path validation will not contain obstacles added after
300 // its creation.
301
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 pathValidation_ = problem->pathValidationFactory();
302 shared_ptr<core::ObstacleUserInterface> oui =
303 8 HPP_DYNAMIC_PTR_CAST(core::ObstacleUserInterface, pathValidation_);
304
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (oui) {
305
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
8 relMotion_ = RelativeMotion::matrix(g->robot());
306
2/4
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 RelativeMotion::fromConstraint(relMotion_, g->robot(), constraint);
307
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 oui->filterCollisionPairs(relMotion_);
308
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 oui->setSecurityMargins(securityMargins_);
309 }
310 16 return constraint;
311 8 }
312
313 bool Edge::canConnect(ConfigurationIn_t q1, ConfigurationIn_t q2) const {
314 ConstraintSetPtr_t constraints = pathConstraint();
315 constraints->configProjector()->rightHandSideFromConfig(q1);
316 if (!constraints->isSatisfied(q1) || !constraints->isSatisfied(q2)) {
317 return false;
318 }
319 return true;
320 }
321
322 bool Edge::build(core::PathPtr_t& path, ConfigurationIn_t q1,
323 ConfigurationIn_t q2) const {
324 using pinocchio::displayConfig;
325 if (!steeringMethod_) {
326 std::ostringstream oss;
327 oss << "No steering method set in edge " << name() << ".";
328 throw std::runtime_error(oss.str().c_str());
329 }
330 ConstraintSetPtr_t constraints = pathConstraint();
331 constraints->configProjector()->rightHandSideFromConfig(q1);
332 if (constraints->isSatisfied(q1)) {
333 if (constraints->isSatisfied(q2)) {
334 path = (*steeringMethod_)(q1, q2);
335 return (bool)path;
336 } else {
337 hppDout(info, "q2 = " << displayConfig(q2)
338 << " does not satisfy the constraints of edge "
339 << name());
340 hppDout(info, "q1 = " << displayConfig(q1));
341 return false;
342 }
343 } else {
344 value_type th(constraints->configProjector()->errorThreshold());
345 if (!constraints->configProjector()->isSatisfied(q1, 2 * th)) {
346 std::ostringstream oss;
347 oss << "The initial configuration " << displayConfig(q1)
348 << " does not satisfy the constraints of"
349 " edge "
350 << name() << "." << std::endl;
351 oss << "The graph is probably malformed";
352 throw std::runtime_error(oss.str().c_str());
353 }
354 // q1 may slightly violate the edge constraint eventhough the graph
355 // is well constructed.
356 return false;
357 }
358 }
359
360 bool Edge::generateTargetConfig(core::NodePtr_t nStart,
361 ConfigurationOut_t q) const {
362 return generateTargetConfig(nStart->configuration(), q);
363 }
364
365 bool Edge::generateTargetConfig(ConfigurationIn_t qStart,
366 ConfigurationOut_t q) const {
367 ConstraintSetPtr_t c = targetConstraint();
368 ConfigProjectorPtr_t proj = c->configProjector();
369 proj->rightHandSideFromConfig(qStart);
370 if (isShort_) q = qStart;
371 if (c->apply(q)) return true;
372 const ::hpp::statistics::SuccessStatistics& ss = proj->statistics();
373 if (ss.nbFailure() > ss.nbSuccess()) {
374 hppDout(warning, c->name() << " fails often.\n" << ss);
375 } else {
376 hppDout(warning, c->name() << " succeeds at rate "
377 << (value_type)(ss.nbSuccess()) /
378 (value_type)ss.numberOfObservations()
379 << ".");
380 }
381 return false;
382 }
383
384 WaypointEdgePtr_t WaypointEdge::create(const std::string& name,
385 const GraphWkPtr_t& graph,
386 const StateWkPtr_t& from,
387 const StateWkPtr_t& to) {
388 WaypointEdge* ptr = new WaypointEdge(name);
389 WaypointEdgePtr_t shPtr(ptr);
390 ptr->init(shPtr, graph, from, to);
391 return shPtr;
392 }
393
394 void WaypointEdge::init(const WaypointEdgeWkPtr_t& weak,
395 const GraphWkPtr_t& graph, const StateWkPtr_t& from,
396 const StateWkPtr_t& to) {
397 Edge::init(weak, graph, from, to);
398 nbWaypoints(0);
399 wkPtr_ = weak;
400 }
401
402 void WaypointEdge::initialize() {
403 Edge::initialize();
404 // Set error threshold of internal edge to error threshold of
405 // waypoint edge divided by number of edges.
406 assert(targetConstraint()->configProjector());
407 value_type eps(targetConstraint()->configProjector()->errorThreshold() /
408 (value_type)edges_.size());
409 for (Edges_t::iterator it(edges_.begin()); it != edges_.end(); ++it) {
410 (*it)->initialize();
411 assert((*it)->targetConstraint());
412 assert((*it)->targetConstraint()->configProjector());
413 (*it)->targetConstraint()->configProjector()->errorThreshold(eps);
414 }
415 }
416
417 bool WaypointEdge::canConnect(ConfigurationIn_t q1,
418 ConfigurationIn_t q2) const {
419 /// TODO: This is not correct
420 for (std::size_t i = 0; i < edges_.size(); ++i)
421 if (!edges_[i]->canConnect(q1, q2)) return false;
422 return true;
423 }
424
425 bool WaypointEdge::build(core::PathPtr_t& path, ConfigurationIn_t q1,
426 ConfigurationIn_t q2) const {
427 core::PathPtr_t p;
428 core::PathVectorPtr_t pv =
429 core::PathVector::create(graph_.lock()->robot()->configSize(),
430 graph_.lock()->robot()->numberDof());
431 // Many times, this will be called rigth after
432 // WaypointEdge::generateTargetConfig so config_ already satisfies the
433 // constraints.
434 size_type n = edges_.size();
435 assert(configs_.cols() == n + 1);
436 bool useCache = lastSucceeded_ && configs_.col(0).isApprox(q1) &&
437 configs_.col(n).isApprox(q2);
438 configs_.col(0) = q1;
439 configs_.col(n) = q2;
440
441 for (size_type i = 0; i < n; ++i) {
442 if (i < (n - 1) && !useCache) configs_.col(i + 1) = q2;
443 if (i < (n - 1) && !edges_[i]->generateTargetConfig(configs_.col(i),
444 configs_.col(i + 1))) {
445 hppDout(info, "Waypoint edge "
446 << name()
447 << ": generateTargetConfig failed at waypoint " << i
448 << "."
449 << "\nUse cache: " << useCache);
450 lastSucceeded_ = false;
451 return false;
452 }
453 assert(targetConstraint());
454 assert(targetConstraint()->configProjector());
455 value_type eps(targetConstraint()->configProjector()->errorThreshold());
456 if ((configs_.col(i) - configs_.col(i + 1)).squaredNorm() > eps * eps) {
457 if (!edges_[i]->build(p, configs_.col(i), configs_.col(i + 1))) {
458 hppDout(info, "Waypoint edge "
459 << name() << ": build failed at waypoint " << i << "."
460 << "\nUse cache: " << useCache);
461 lastSucceeded_ = false;
462 return false;
463 }
464 pv->appendPath(p);
465 }
466 }
467
468 path = pv;
469 lastSucceeded_ = true;
470 return true;
471 }
472
473 bool WaypointEdge::generateTargetConfig(ConfigurationIn_t qStart,
474 ConfigurationOut_t q) const {
475 assert(configs_.cols() == size_type(edges_.size() + 1));
476 configs_.col(0) = qStart;
477 for (std::size_t i = 0; i < edges_.size(); ++i) {
478 configs_.col(i + 1) = q;
479 if (!edges_[i]->generateTargetConfig(configs_.col(i),
480 configs_.col(i + 1))) {
481 q = configs_.col(i + 1);
482 lastSucceeded_ = false;
483 return false;
484 }
485 }
486 q = configs_.col(edges_.size());
487 lastSucceeded_ = true;
488 return true;
489 }
490
491 void WaypointEdge::nbWaypoints(const size_type number) {
492 edges_.resize(number + 1);
493 states_.resize(number + 1);
494 states_.back() = stateTo();
495 const size_type nbDof = graph_.lock()->robot()->configSize();
496 configs_ = matrix_t(nbDof, number + 2);
497 invalidate();
498 }
499
500 void WaypointEdge::setWaypoint(const std::size_t index, const EdgePtr_t wEdge,
501 const StatePtr_t wTo) {
502 assert(edges_.size() == states_.size());
503 assert(index < edges_.size());
504 if (index == states_.size() - 1) {
505 assert(!wTo || wTo == stateTo());
506 } else {
507 states_[index] = wTo;
508 }
509 edges_[index] = wEdge;
510 }
511
512 const EdgePtr_t& WaypointEdge::waypoint(const std::size_t index) const {
513 assert(index < edges_.size());
514 return edges_[index];
515 }
516
517 std::ostream& WaypointEdge::print(std::ostream& os) const {
518 os << "| | |-- ";
519 GraphComponent::print(os) << " (waypoint) --> " << stateTo()->name();
520 return os;
521 }
522
523 std::ostream& WaypointEdge::dotPrint(std::ostream& os,
524 dot::DrawingAttributes da) const {
525 // First print the waypoint node, then the first edge.
526 da["style"] = "dashed";
527 for (std::size_t i = 0; i < states_.size() - 1; ++i)
528 states_[i]->dotPrint(os, da);
529
530 da["style"] = "solid";
531 for (std::size_t i = 0; i < edges_.size(); ++i)
532 edges_[i]->dotPrint(os, da) << std::endl;
533
534 da["style"] = "dotted";
535 da["dir"] = "both";
536 da["arrowtail"] = "dot";
537
538 return Edge::dotPrint(os, da);
539 }
540
541 std::ostream& LevelSetEdge::print(std::ostream& os) const {
542 os << "| | |-- ";
543 GraphComponent::print(os) << " (level set) --> " << stateTo()->name();
544 return os;
545 }
546
547 std::ostream& LevelSetEdge::dotPrint(std::ostream& os,
548 dot::DrawingAttributes da) const {
549 da.insert("shape", "onormal");
550 da.insert("style", "dashed");
551 return Edge::dotPrint(os, da);
552 }
553
554 void LevelSetEdge::populateTooltip(dot::Tooltip& tp) const {
555 GraphComponent::populateTooltip(tp);
556 tp.addLine("");
557 tp.addLine("Foliation condition constraints:");
558 for (NumericalConstraints_t::const_iterator it =
559 condNumericalConstraints_.begin();
560 it != condNumericalConstraints_.end(); ++it) {
561 tp.addLine("- " + (*it)->function().name());
562 }
563 tp.addLine("Foliation parametrization constraints:");
564 for (NumericalConstraints_t::const_iterator it =
565 paramNumericalConstraints_.begin();
566 it != paramNumericalConstraints_.end(); ++it) {
567 tp.addLine("- " + (*it)->function().name());
568 }
569 }
570
571 bool LevelSetEdge::generateTargetConfig(ConfigurationIn_t qStart,
572 ConfigurationOut_t q) const {
573 // First, get an offset from the histogram
574 statistics::DiscreteDistribution<RoadmapNodePtr_t> distrib =
575 hist_->getDistrib();
576 if (distrib.size() == 0) {
577 hppDout(warning, "Edge " << name() << ": Distrib is empty");
578 return false;
579 }
580 const Configuration_t& qLeaf = distrib()->configuration();
581
582 return generateTargetConfigOnLeaf(qStart, qLeaf, q);
583 }
584
585 bool LevelSetEdge::generateTargetConfig(core::NodePtr_t nStart,
586 ConfigurationOut_t q) const {
587 // First, get an offset from the histogram that is not in the same connected
588 // component.
589 statistics::DiscreteDistribution<RoadmapNodePtr_t> distrib =
590 hist_->getDistribOutOfConnectedComponent(nStart->connectedComponent());
591 if (distrib.size() == 0) {
592 hppDout(warning, "Edge " << name() << ": Distrib is empty");
593 return false;
594 }
595 const Configuration_t &qLeaf = distrib()->configuration(),
596 qStart = nStart->configuration();
597
598 return generateTargetConfigOnLeaf(qStart, qLeaf, q);
599 }
600
601 bool LevelSetEdge::generateTargetConfigOnLeaf(ConfigurationIn_t qStart,
602 ConfigurationIn_t qLeaf,
603 ConfigurationOut_t q) const {
604 // First, set the offset.
605 ConstraintSetPtr_t cs = targetConstraint();
606 const ConfigProjectorPtr_t cp = cs->configProjector();
607 assert(cp);
608
609 // Set right hand side of edge constraints with qStart
610 cp->rightHandSideFromConfig(qStart);
611 // Set right hand side of constraints parameterizing the target state
612 // foliation with qLeaf.
613 for (NumericalConstraints_t::const_iterator it =
614 paramNumericalConstraints_.begin();
615 it != paramNumericalConstraints_.end(); ++it) {
616 cp->rightHandSideFromConfig(*it, qLeaf);
617 }
618
619 // Eventually, do the projection.
620 if (isShort_) q = qStart;
621 if (cs->apply(q)) return true;
622 ::hpp::statistics::SuccessStatistics& ss = cp->statistics();
623 if (ss.nbFailure() > ss.nbSuccess()) {
624 hppDout(warning, cs->name() << " fails often." << std::endl << ss);
625 } else {
626 hppDout(warning, cs->name() << " succeeds at rate "
627 << (value_type)(ss.nbSuccess()) /
628 (value_type)ss.numberOfObservations()
629 << ".");
630 }
631 return false;
632 }
633
634 void LevelSetEdge::init(const LevelSetEdgeWkPtr_t& weak,
635 const GraphWkPtr_t& graph, const StateWkPtr_t& from,
636 const StateWkPtr_t& to) {
637 Edge::init(weak, graph, from, to);
638 wkPtr_ = weak;
639 }
640
641 LevelSetEdgePtr_t LevelSetEdge::create(const std::string& name,
642 const GraphWkPtr_t& graph,
643 const StateWkPtr_t& from,
644 const StateWkPtr_t& to) {
645 LevelSetEdge* ptr = new LevelSetEdge(name);
646 LevelSetEdgePtr_t shPtr(ptr);
647 ptr->init(shPtr, graph, from, to);
648 return shPtr;
649 }
650
651 LeafHistogramPtr_t LevelSetEdge::histogram() const { return hist_; }
652
653 void LevelSetEdge::buildHistogram() {
654 Foliation f;
655
656 /// Build the constraint set.
657 std::string n = "(" + name() + ")";
658 GraphPtr_t g = graph_.lock();
659
660 // The parametrizer
661 ConstraintSetPtr_t param = ConstraintSet::create(g->robot(), "Set " + n);
662
663 ConfigProjectorPtr_t proj = ConfigProjector::create(
664 g->robot(), "projParam_" + n, g->errorThreshold(), g->maxIterations());
665
666 for (const auto& nc : paramNumericalConstraints_) proj->add(nc);
667
668 param->addConstraint(proj);
669 param->edge(wkPtr_.lock());
670
671 f.parametrizer(param);
672
673 // The codition
674 // TODO: We assumed that this part of the code can only be reached by
675 // configurations that are valid.
676 // It would be wiser to make sure configurations are valid, for instance
677 // by considering only configurations in the destination state of this
678 // edge.
679 ConstraintSetPtr_t cond = ConstraintSet::create(g->robot(), "Set " + n);
680 proj = ConfigProjector::create(g->robot(), "projCond_" + n,
681 g->errorThreshold(), g->maxIterations());
682
683 for (const auto& nc : condNumericalConstraints_) proj->add(nc);
684
685 f.condition(cond);
686 cond->addConstraint(proj);
687
688 hppDout(info, "Build histogram of LevelSetEdge " << name()
689 << "\nParametrizer:\n"
690 << *param << "\nCondition:\n"
691 << *cond);
692
693 // TODO: If hist_ is not NULL, remove the previous Histogram.
694 // It should not be of any use and it slows down node insertion in the
695 // roadmap.
696 hist_ = LeafHistogram::create(f);
697 g->insertHistogram(hist_);
698 }
699
700 void LevelSetEdge::initialize() {
701 if (!isInit_) {
702 Edge::initialize();
703 buildHistogram();
704 }
705 }
706
707 ConstraintSetPtr_t LevelSetEdge::buildTargetConstraint() {
708 std::string n = "(" + name() + ")";
709 GraphPtr_t g = graph_.lock();
710
711 ConstraintSetPtr_t constraint = ConstraintSet::create(g->robot(), "Set " + n);
712
713 ConfigProjectorPtr_t proj = ConfigProjector::create(
714 g->robot(), "proj_" + n, g->errorThreshold(), g->maxIterations());
715
716 // Copy constraints from
717 // - graph,
718 // - param numerical constraints
719 // - this edge,
720 // - the destination state,
721 // - the state in which the transition lies if different
722
723 g->insertNumericalConstraints(proj);
724 for (const auto& nc : paramNumericalConstraints_) proj->add(nc);
725
726 insertNumericalConstraints(proj);
727 stateTo()->insertNumericalConstraints(proj);
728 if (state() != stateTo()) {
729 state()->insertNumericalConstraints(proj);
730 }
731 constraint->addConstraint(proj);
732
733 constraint->edge(wkPtr_.lock());
734 return constraint;
735 }
736
737 void LevelSetEdge::insertParamConstraint(const constraints::ImplicitPtr_t& nm) {
738 invalidate();
739 paramNumericalConstraints_.push_back(nm);
740 }
741
742 const NumericalConstraints_t& LevelSetEdge::paramConstraints() const {
743 return paramNumericalConstraints_;
744 }
745
746 void LevelSetEdge::insertConditionConstraint(
747 const constraints::ImplicitPtr_t& nm) {
748 invalidate();
749 condNumericalConstraints_.push_back(nm);
750 }
751
752 const NumericalConstraints_t& LevelSetEdge::conditionConstraints() const {
753 return condNumericalConstraints_;
754 }
755
756 LevelSetEdge::LevelSetEdge(const std::string& name) : Edge(name) {}
757
758 LevelSetEdge::~LevelSetEdge() {}
759 } // namespace graph
760 } // namespace manipulation
761 } // namespace hpp
762