Skip to content

Commit d386459

Browse files
rhaschkev4hn
authored andcommitted
Replace Priority::enabled() with status()
The key to pruning in the Connecting stage was the following: - Don't remove states during pruning, but only disable them. They might become re-enabled due to further input. - Distinguish START and END sides of a disabled solution tree to break their symmetry. The START side from where we started disabling, can be re-enabled by a new partner state in Connecting, the END side must not. This was important as, otherwise, the states would simply get re-enabled immediately. The END side only gets re-enabled if the START side actually connects the whole solution branch.
1 parent f229743 commit d386459

File tree

6 files changed

+85
-53
lines changed

6 files changed

+85
-53
lines changed

core/include/moveit/task_constructor/stage_p.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ class StagePrivate
157157
/** compute cost for solution through configured CostTerm */
158158
void computeCost(const InterfaceState& from, const InterfaceState& to, SolutionBase& solution);
159159

160+
/// Set ENABLED / DISABLED status of the solution tree starting from s into given direction
161+
template <Interface::Direction dir>
162+
static void setStatus(const InterfaceState* s, InterfaceState::Status status);
163+
160164
protected:
161165
// associated/owning Stage instance
162166
Stage* me_;

core/include/moveit/task_constructor/storage.h

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,26 +77,33 @@ class InterfaceState
7777
friend class SolutionBase; // addIncoming() / addOutgoing() should be called only by SolutionBase
7878
friend class Interface; // allow Interface to set owner_ and priority_
7979
public:
80+
enum Status
81+
{
82+
ENABLED,
83+
DISABLED_START, // state that was the root cause for disabling due to failure
84+
DISABLED_END // state on the other end of a disabled solution sequence
85+
};
8086
/** InterfaceStates are ordered according to two values:
8187
* Depth of interlinked trajectory parts and accumulated trajectory costs along that path.
8288
* Preference ordering considers high-depth first and within same depth, minimal cost paths.
8389
*/
84-
struct Priority : std::tuple<bool, unsigned int, double>
90+
struct Priority : std::tuple<Status, unsigned int, double>
8591
{
86-
Priority(unsigned int depth, double cost, bool enabled = true)
87-
: std::tuple<bool, unsigned int, double>(enabled, depth, cost) {
92+
Priority(unsigned int depth, double cost, Status status = ENABLED)
93+
: std::tuple<Status, unsigned int, double>(status, depth, cost) {
8894
assert(std::isfinite(cost));
8995
}
90-
// Constructor copying depth and cost, but modifying enabled status
91-
Priority(const Priority& other, bool enabled) : Priority(other.depth(), other.cost(), enabled) {}
96+
// Constructor copying depth and cost, but modifying its status
97+
Priority(const Priority& other, Status status) : Priority(other.depth(), other.cost(), status) {}
9298

93-
inline bool enabled() const { return std::get<0>(*this); }
99+
inline Status status() const { return std::get<0>(*this); }
100+
inline bool enabled() const { return std::get<0>(*this) == ENABLED; }
94101
inline unsigned int depth() const { return std::get<1>(*this); }
95102
inline double cost() const { return std::get<2>(*this); }
96103

97104
// add priorities
98105
Priority operator+(const Priority& other) const {
99-
return Priority(depth() + other.depth(), cost() + other.cost(), enabled() || other.enabled());
106+
return Priority(depth() + other.depth(), cost() + other.cost(), std::min(status(), other.status()));
100107
}
101108
// comparison operators
102109
bool operator<(const Priority& rhs) const;

core/src/container.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,8 @@ struct SolutionCollector
341341
inline void updateStatePrio(const InterfaceState* state, const InterfaceState::Priority& prio) {
342342
if (state->owner()) // owner becomes NULL if state is removed from (pending) Interface list
343343
state->owner()->updatePriority(const_cast<InterfaceState*>(state),
344-
// update depth + cost, but keep current enabled status
345-
InterfaceState::Priority(prio, state->priority().enabled()));
344+
// update depth + cost, but keep current status
345+
InterfaceState::Priority(prio, state->priority().status()));
346346
}
347347

348348
template <Interface::Direction dir>
@@ -406,40 +406,26 @@ void SerialContainer::onNewSolution(const SolutionBase& current) {
406406
impl->liftSolution(solution, solution->internalStart(), solution->internalEnd());
407407
}
408408

409-
template <Interface::Direction dir>
410-
void markAsEnabled(const InterfaceState* s, bool enabled) {
411-
if (s->priority().enabled() == enabled)
412-
return; // nothing changing
413-
414-
// actually enable/disable the state
415-
if (s->owner())
416-
s->owner()->updatePriority(const_cast<InterfaceState*>(s), InterfaceState::Priority(s->priority(), enabled));
417-
418-
// traverse solution tree
419-
for (const SolutionBase* successor : trajectories<dir>(s))
420-
markAsEnabled<dir>(state<dir>(*successor), enabled);
421-
}
422-
423409
void SerialContainer::onNewFailure(const Stage& child, const InterfaceState* from, const InterfaceState* to) {
424410
switch (child.pimpl()->interfaceFlags()) {
425411
case GENERATE:
426412
break; // just ignore: the pair of (new) states isn't known to us anyway
427413
// TODO: If child is a container, from and to might have associated solutions already!
428414

429415
case PROPAGATE_FORWARDS: // mark from as dead (backwards)
430-
markAsEnabled<Interface::BACKWARD>(from, false);
416+
StagePrivate::setStatus<Interface::BACKWARD>(from, InterfaceState::Status::DISABLED_START);
431417
break;
432418
case PROPAGATE_BACKWARDS: // mark to as dead (forwards)
433-
markAsEnabled<Interface::FORWARD>(to, false);
419+
StagePrivate::setStatus<Interface::FORWARD>(to, InterfaceState::Status::DISABLED_START);
434420
break;
435421

436422
case CONNECT:
437423
if (const Connecting* conn = dynamic_cast<const Connecting*>(&child)) {
438424
auto cimpl = conn->pimpl();
439425
if (!cimpl->hasPendingOpposites<Interface::FORWARD>(from))
440-
markAsEnabled<Interface::BACKWARD>(from, false);
426+
StagePrivate::setStatus<Interface::BACKWARD>(from, InterfaceState::Status::DISABLED_START);
441427
if (!cimpl->hasPendingOpposites<Interface::BACKWARD>(to))
442-
markAsEnabled<Interface::FORWARD>(to, false);
428+
StagePrivate::setStatus<Interface::FORWARD>(to, InterfaceState::Status::DISABLED_START);
443429
}
444430
break;
445431
}

core/src/stage.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,30 @@ bool StagePrivate::storeSolution(const SolutionBasePtr& solution, const Interfac
148148
return true;
149149
}
150150

151+
template <Interface::Direction dir>
152+
void StagePrivate::setStatus(const InterfaceState* s, InterfaceState::Status status) {
153+
if (s->priority().status() == status)
154+
return; // nothing changing
155+
156+
// actually enable/disable the state
157+
if (s->owner())
158+
s->owner()->updatePriority(const_cast<InterfaceState*>(s), InterfaceState::Priority(s->priority(), status));
159+
160+
// To break symmetry between both ends of a partial solution sequence that gets disabled,
161+
// we mark the start state with START and all other states down the tree with END.
162+
// This allows us to re-enable the START side, while not (yet) consider the END side,
163+
// when a new states arrives in a Connecting stage.
164+
// The END side is only re-enabled if the START side actually was connected with a solution.
165+
if (status == InterfaceState::Status::DISABLED_START)
166+
status = InterfaceState::Status::DISABLED_END; // ensure that only the first state is marked as START
167+
168+
// traverse solution tree
169+
for (const SolutionBase* successor : trajectories<dir>(s))
170+
setStatus<dir>(state<dir>(*successor), status);
171+
}
172+
template void StagePrivate::setStatus<Interface::FORWARD>(const InterfaceState* s, InterfaceState::Status status);
173+
template void StagePrivate::setStatus<Interface::BACKWARD>(const InterfaceState* s, InterfaceState::Status status);
174+
151175
void StagePrivate::sendForward(const InterfaceState& from, InterfaceState&& to, const SolutionBasePtr& solution) {
152176
assert(nextStarts());
153177

@@ -705,17 +729,20 @@ ConnectingPrivate::StatePair ConnectingPrivate::make_pair<Interface::FORWARD>(In
705729
template <Interface::Direction other>
706730
void ConnectingPrivate::newState(Interface::iterator it, bool updated) {
707731
if (updated) { // many pairs might be affected: resort
708-
if (!it->priority().enabled()) // remove all pending pairs involving this state
732+
if (it->priority().status() == InterfaceState::Status::DISABLED_END)
733+
// remove all pending pairs involving this state
709734
pending.remove_if([it](const StatePair& p) { return std::get<opposite<other>()>(p) == it; });
710735
else
711736
pending.sort();
712737
} else { // new state: insert all pairs with other interface
713738
assert(it->priority().enabled()); // new solutions are feasible, aren't they?
714739
InterfacePtr other_interface = pullInterface(other);
715740
for (Interface::iterator oit = other_interface->begin(), oend = other_interface->end(); oit != oend; ++oit) {
741+
// Don't re-enable states that are marked DISABLED_END
716742
if (static_cast<Connecting*>(me_)->compatible(*it, *oit)) {
717-
// if needed, re-enable the opposing state oit
718-
oit->owner()->updatePriority(&*oit, InterfaceState::Priority(oit->priority(), true));
743+
// re-enable the opposing state oit if its status is DISABLED_START
744+
if (oit->priority().status() == InterfaceState::DISABLED_START)
745+
oit->owner()->updatePriority(&*oit, InterfaceState::Priority(oit->priority(), InterfaceState::ENABLED));
719746
pending.insert(make_pair<other>(it, oit));
720747
}
721748
}

core/src/storage.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,16 @@ InterfaceState::InterfaceState(const InterfaceState& other)
7070
: scene_(other.scene_), properties_(other.properties_), priority_(other.priority_) {}
7171

7272
bool InterfaceState::Priority::operator<(const InterfaceState::Priority& other) const {
73-
// disabled states always after enabled ones
74-
if (enabled() != other.enabled())
75-
return enabled();
76-
77-
// both prios have same enabled_ state
78-
if (depth() == other.depth())
79-
return cost() < other.cost();
80-
else
73+
// first order by status if that differs
74+
if (status() != other.status())
75+
return status() < other.status();
76+
77+
// then by depth if that differs
78+
if (depth() != other.depth())
8179
return depth() > other.depth(); // larger depth = smaller prio!
80+
81+
// finally by cost
82+
return cost() < other.cost();
8283
}
8384

8485
Interface::Interface(const Interface::NotifyFunction& notify) : notify_(notify) {}
@@ -142,10 +143,14 @@ std::ostream& operator<<(std::ostream& os, const Interface& interface) {
142143
return os;
143144
}
144145
std::ostream& operator<<(std::ostream& os, const InterfaceState::Priority& prio) {
145-
static const char* red = "\033[31m";
146-
static const char* green = "\033[32m";
146+
// maps InterfaceState::Status values to output (color-changing) prefix
147+
static const char* prefix[] = {
148+
"\033[32me:", // ENABLED - green
149+
"\033[33md:", // DISABLED - yellow
150+
"\033[31mf:", // DISABLED_FAILED - red
151+
};
147152
static const char* color_reset = "\033[m";
148-
os << (prio.enabled() ? green : red) << prio.depth() << ":" << prio.cost() << color_reset;
153+
os << prefix[prio.status()] << prio.depth() << ":" << prio.cost() << color_reset;
149154
return os;
150155
}
151156

core/test/test_interface_state.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ TEST(InterfaceStatePriority, compare) {
1919
EXPECT_TRUE(Prio(1, 42) < Prio(0, 0));
2020
EXPECT_TRUE(Prio(0, 0) < Prio(0, 42)); // at same depth, higher cost is larger
2121

22-
EXPECT_TRUE(Prio(0, 0, false) == Prio(0, 0, false));
23-
EXPECT_TRUE(Prio(1, 0, false) < Prio(0, 0, false));
24-
EXPECT_TRUE(Prio(1, 42, false) < Prio(0, 0, false));
25-
EXPECT_TRUE(Prio(0, 0, false) < Prio(0, 42, false));
22+
auto dstart = InterfaceState::DISABLED_START;
23+
EXPECT_TRUE(Prio(0, 0, dstart) == Prio(0, 0, dstart));
24+
EXPECT_TRUE(Prio(1, 0, dstart) < Prio(0, 0, dstart));
25+
EXPECT_TRUE(Prio(1, 42, dstart) < Prio(0, 0, dstart));
26+
EXPECT_TRUE(Prio(0, 0, dstart) < Prio(0, 42, dstart));
2627

2728
// disabled prios are always larger than enabled ones
28-
EXPECT_TRUE(Prio(0, 42) < Prio(1, 0, false));
29-
EXPECT_TRUE(Prio(1, 0) < Prio(0, 42, false));
29+
EXPECT_TRUE(Prio(0, 42) < Prio(1, 0, dstart));
30+
EXPECT_TRUE(Prio(1, 0) < Prio(0, 42, dstart));
3031

3132
// other comparison operators
3233
EXPECT_TRUE(Prio(0, 0) <= Prio(0, 0));
@@ -67,7 +68,7 @@ TEST(Interface, update) {
6768
i.updatePriority(*i.rbegin(), Prio(5, 0.0));
6869
EXPECT_THAT(i.depths(), ::testing::ElementsAreArray({ 5, 3 }));
6970

70-
i.updatePriority(*i.begin(), Prio(6, 0, false));
71+
i.updatePriority(*i.begin(), Prio(6, 0, InterfaceState::DISABLED_START));
7172
EXPECT_THAT(i.depths(), ::testing::ElementsAreArray({ 3, 6 }));
7273
}
7374

@@ -78,14 +79,16 @@ inline bool operator<(const PrioPair& lhs, const PrioPair& rhs) {
7879
PrioPair pair(Prio&& p1, Prio&& p2) {
7980
return std::make_pair(std::move(p1), std::move(p2));
8081
}
81-
PrioPair pair(bool e1, bool e2) {
82-
return pair(Prio(0, 0, e1), Prio(0, 0, e2));
82+
PrioPair pair(InterfaceState::Status s1, InterfaceState::Status s2) {
83+
return pair(Prio(0, 0, s1), Prio(0, 0, s2));
8384
}
8485
TEST(StatePairs, compare) {
8586
EXPECT_TRUE(pair(Prio(1, 0), Prio(0, 1)) < pair(Prio(1, 1), Prio(0, 1)));
8687
EXPECT_TRUE(pair(Prio(1, 1), Prio(1, 1)) < pair(Prio(1, 0), Prio(0, 0)));
8788

88-
EXPECT_TRUE(pair(true, true) < pair(true, false));
89-
EXPECT_TRUE(pair(true, true) < pair(false, true));
90-
EXPECT_TRUE(pair(false, true) < pair(true, false));
89+
auto good = InterfaceState::ENABLED;
90+
auto bad = InterfaceState::DISABLED_START;
91+
EXPECT_TRUE(pair(good, good) < pair(good, bad));
92+
EXPECT_TRUE(pair(good, good) < pair(bad, good));
93+
EXPECT_TRUE(pair(bad, good) < pair(good, bad));
9194
}

0 commit comments

Comments
 (0)