7 #include <unordered_map>
9 #include <boost/unordered_map.hpp>
10 #include <boost/unordered_set.hpp>
11 #include <boost/log/trivial.hpp>
12 #include <boost/log/core.hpp>
13 #include <boost/log/expressions.hpp>
15 #include "common_types.hh"
18 #define NO_EIGEN_CONSTRAINT
27 inline bool toBool(
Order odr) {
return odr == Order::EQ; }
29 using IntBounds = std::pair<int, bool>;
41 [[nodiscard]]
bool satisfy(
double d)
const {
55 [[nodiscard]]
bool satisfy(std::vector<double> valuation)
const {
56 return satisfy(valuation.at(x));
59 using Interpretation = std::vector<double>;
62 if (satisfy(val.at(x))) {
63 return learnta::Order::EQ;
64 }
else if (odr == Order::lt || odr == Order::le) {
65 return learnta::Order::GT;
67 return learnta::Order::LT;
72 return this->odr == another.odr && this->c == another.c && this->x == another.x;
75 [[nodiscard]]
inline bool isUpperBound()
const {
76 return this->odr == Order::le || this->odr == Order::lt;
86 if (this->x != another.x) {
89 const bool thisLess = this->isUpperBound();
90 const bool anotherLess = another.isUpperBound();
91 const Bounds thisBounds = this->toDBMBound();
92 const Bounds anotherBounds = another.toDBMBound();
94 return thisLess == anotherLess && anotherBounds <= thisBounds;
100 return Constraint{x, learnta::Constraint::Order::ge, c};
102 return Constraint{x, learnta::Constraint::Order::gt, c};
104 return Constraint{x, learnta::Constraint::Order::le, c};
106 return Constraint{x, learnta::Constraint::Order::lt, c};
108 throw std::range_error(
"Invalid order is used");
112 [[nodiscard]]
inline Bounds lowerBoundDurationToSatisfy(std::vector<double> valuation)
const {
117 return valuation.at(this->x) <= this->c ? Bounds{0.0,
true} :
118 Bounds{-std::numeric_limits<double>::infinity(),
false};
121 return std::min(Bounds{0.0,
true}, Bounds{-this->c + valuation.at(this->x),
true});
124 return std::min(Bounds{0.0,
true}, Bounds{-this->c + valuation.at(this->x),
false});
126 return Bounds{-std::numeric_limits<double>::infinity(),
false};
129 [[nodiscard]] IntBounds toBound()
const {
131 case learnta::Constraint::Order::le:
132 case learnta::Constraint::Order::ge:
133 return IntBounds{c,
true};
134 case learnta::Constraint::Order::lt:
135 case learnta::Constraint::Order::gt:
136 return IntBounds{c,
false};
138 throw std::range_error(
"Invalid order is used");
143 [[nodiscard]] Bounds toDBMBound()
const {
145 case learnta::Constraint::Order::le:
146 return Bounds{c,
true};
147 case learnta::Constraint::Order::ge:
148 return Bounds{-c,
true};
149 case learnta::Constraint::Order::lt:
150 return Bounds{c,
false};
151 case learnta::Constraint::Order::gt:
152 return Bounds{-c,
false};
154 throw std::range_error(
"Invalid order is used");
159 inline int orderToInt(learnta::Constraint::Order order) {
161 case learnta::Constraint::Order::lt:
163 case learnta::Constraint::Order::le:
165 case learnta::Constraint::Order::ge:
167 case learnta::Constraint::Order::gt:
174 inline std::size_t hash_value(
const Constraint guard) {
175 return boost::hash_value(std::make_tuple(guard.c, orderToInt(guard.odr), guard.x));
178 static inline bool isWeaker(
const std::vector<Constraint> &left,
const std::vector<Constraint> &right) {
179 return std::all_of(left.begin(), left.end(), [&](
const auto &leftGuard) {
180 return std::any_of(right.begin(), right.end(), [&](const auto &rightGuard) {
181 return leftGuard.isWeaker(rightGuard);
186 static inline std::ostream &operator<<(std::ostream &os,
const Constraint::Order &odr) {
188 case Constraint::Order::lt:
191 case Constraint::Order::le:
194 case Constraint::Order::ge:
197 case Constraint::Order::gt:
204 static inline std::ostream &operator<<(std::ostream &os,
const Constraint &p) {
205 os <<
"x" << int(p.x) <<
" " << p.odr <<
" " << p.c;
217 return Constraint{x, Constraint::Order::lt, c};
221 return Constraint{x, Constraint::Order::le, c};
225 return Constraint{x, Constraint::Order::gt, c};
229 return Constraint{x, Constraint::Order::ge, c};
232 explicit operator ClockVariables()
const {
240 static inline void widen(std::vector<Constraint> &guard) {
241 guard.erase(std::remove_if(guard.begin(), guard.end(),
243 return g.odr == Constraint::Order::ge ||
244 g.odr == Constraint::Order::gt;
249 inline std::vector<Constraint> negateAll(
const std::vector<Constraint> &constraints) {
250 std::vector<Constraint> result;
251 result.reserve(constraints.size());
252 std::transform(constraints.begin(), constraints.end(), std::back_inserter(result), [&](
const auto &constraint) {
253 return constraint.negate();
259 inline std::vector<Constraint> conjunction(
const std::vector<Constraint> &left,
260 const std::vector<Constraint> &right) {
261 std::vector<Constraint> result = left;
262 result.reserve(left.size() + right.size());
263 result.insert(result.end(), right.begin(), right.end());
268 inline auto toBounds(
const std::vector<Constraint> &constraints) {
269 std::vector<IntBounds> upperBounds;
270 std::vector<IntBounds> lowerBounds;
271 const auto resizeBounds = [&](
const ClockVariables x) {
272 if (x >= upperBounds.size()) {
273 const auto oldSize = upperBounds.size();
274 upperBounds.resize(x + 1);
275 lowerBounds.resize(x + 1);
276 for (
auto i = oldSize; i <= x; ++i) {
277 upperBounds.at(i) = IntBounds(std::numeric_limits<int>::max(),
false);
278 lowerBounds.at(i) = IntBounds(0,
true);
283 for (
const auto &constraint: constraints) {
284 resizeBounds(constraint.x);
285 const auto bound = constraint.toBound();
287 if (constraint.isUpperBound()) {
288 if (upperBounds.at(constraint.x) > bound) {
289 upperBounds.at(constraint.x) = bound;
292 if (-lowerBounds.at(constraint.x) > -bound) {
293 lowerBounds.at(constraint.x) = bound;
298 return std::make_pair(upperBounds, lowerBounds);
306 inline std::vector<ClockVariables>
simpleVariables(
const std::vector<Constraint> &constraints) {
307 std::vector<ClockVariables> result;
308 const auto &[upperBounds, lowerBounds] = toBounds(constraints);
309 assert(upperBounds.size() == lowerBounds.size());
310 result.reserve(upperBounds.size());
311 for (std::size_t i = 0; i < upperBounds.size(); ++i) {
312 if (isSimple(upperBounds.at(i), -lowerBounds.at(i))) {
321 inline bool satisfiable(
const std::vector<Constraint> &constraints) {
322 const auto &[upperBounds, lowerBounds] = toBounds(constraints);
324 for (std::size_t i = 0; i < lowerBounds.size(); ++i) {
325 if (lowerBounds.at(i).first > upperBounds.at(i).first ||
326 (lowerBounds.at(i).first == upperBounds.at(i).first &&
327 !(lowerBounds.at(i).second && upperBounds.at(i).second))) {
335 inline std::vector<Constraint> simplify(
const std::vector<Constraint> &constraints) {
336 const auto &[upperBounds, lowerBounds] = toBounds(constraints);
337 std::vector<Constraint> result;
338 result.reserve(upperBounds.size() + lowerBounds.size());
340 for (std::size_t i = 0; i < lowerBounds.size(); ++i) {
341 if (lowerBounds.at(i) != IntBounds(0,
true)) {
342 if (lowerBounds.at(i).second) {
343 result.push_back(ConstraintMaker(i) >= lowerBounds.at(i).first);
345 result.push_back(ConstraintMaker(i) > lowerBounds.at(i).first);
350 for (std::size_t i = 0; i < upperBounds.size(); ++i) {
351 if (upperBounds.at(i) != IntBounds(std::numeric_limits<int>::max(),
false)) {
352 if (upperBounds.at(i).second) {
353 result.push_back(ConstraintMaker(i) <= upperBounds.at(i).first);
355 result.push_back(ConstraintMaker(i) < upperBounds.at(i).first);
363 [[nodiscard]]
inline Bounds lowerBoundDurationToSatisfy(
const std::vector<Constraint> &guard,
364 const std::vector<double> &valuation) {
365 std::vector<Bounds> lowerBoundDurations;
366 lowerBoundDurations.reserve(guard.size());
367 std::transform(guard.begin(), guard.end(), std::back_inserter(lowerBoundDurations), [&](
const auto &constraint) {
368 return constraint.lowerBoundDurationToSatisfy(valuation);
371 return *std::min_element(lowerBoundDurations.begin(), lowerBoundDurations.end());
374 static inline std::ostream &operator<<(std::ostream &os,
const std::vector<Constraint> &guards) {
376 for (
const Constraint &guard: guards) {
387 #ifdef NO_EIGEN_CONSTRAINT
389 inline std::vector<std::vector<Constraint>> negate(
const std::vector<std::vector<Constraint>> &dnfConstraints) {
390 std::vector<std::vector<Constraint>> cnfNegated;
391 cnfNegated.reserve(dnfConstraints.size());
393 std::transform(dnfConstraints.begin(), dnfConstraints.end(), std::back_inserter(cnfNegated), negateAll);
394 std::vector<std::vector<Constraint>> dnfNegated;
397 for (
const auto &disjunct: cnfNegated) {
398 if (disjunct.empty()) {
402 dnfNegated.reserve(disjunct.size());
403 std::transform(disjunct.begin(), disjunct.end(), std::back_inserter(dnfNegated), [](
const auto &constraint) {
404 return std::vector<Constraint>{constraint};
408 std::vector<std::vector<Constraint>> newDnfNegated;
409 for (
const auto &constraint: disjunct) {
410 for (
auto conjunct: dnfNegated) {
411 conjunct.push_back(constraint);
412 if (satisfiable(conjunct)) {
413 newDnfNegated.push_back(simplify(conjunct));
417 for (
auto it = newDnfNegated.begin(); it != newDnfNegated.end();) {
418 if (std::any_of(it + 1, newDnfNegated.end(), [&](
const auto &constraints) {
419 return isWeaker(constraints, *it);
420 }) || std::any_of(newDnfNegated.begin(), it, [&](
const auto &constraints) {
421 return isWeaker(constraints, *it);
423 it = newDnfNegated.erase(it);
428 dnfNegated = std::move(newDnfNegated);
440 static inline auto unionHull(
const std::vector<std::vector<Constraint>> &guards) {
441 boost::unordered_map<std::pair<ClockVariables, bool>, Bounds> guardsAsBounds;
443 for (
const auto &guard: guards) {
444 boost::unordered_set<std::pair<ClockVariables, bool>> boundedKeys;
445 for (
const auto &constraint: guard) {
446 const auto clock = constraint.x;
447 const auto upperBound = constraint.isUpperBound();
448 boundedKeys.emplace(clock, upperBound);
449 auto it = guardsAsBounds.find(std::make_pair(clock, upperBound));
450 if (it == guardsAsBounds.end()) {
452 guardsAsBounds[std::make_pair(clock, upperBound)] = constraint.toDBMBound();
455 it->second = std::max(it->second, constraint.toDBMBound());
459 for (
auto it = guardsAsBounds.begin(); it != guardsAsBounds.end();) {
460 if (boundedKeys.find(it->first) == boundedKeys.end()) {
461 it = guardsAsBounds.erase(it);
469 std::vector<Constraint> result;
470 result.reserve(guardsAsBounds.size());
471 std::transform(guardsAsBounds.begin(), guardsAsBounds.end(), std::back_inserter(result), [](
const auto p) {
472 const auto clock = p.first.first;
473 const auto upperBound = p.first.second;
474 const auto bound = p.second;
477 return ConstraintMaker(clock) <= bound.first;
479 return ConstraintMaker(clock) < bound.first;
483 return ConstraintMaker(clock) >= -bound.first;
485 return ConstraintMaker(clock) > -bound.first;
491 assert(std::all_of(guards.begin(), guards.end(), [&](
const auto guard) {
492 return isWeaker(result, guard);
501 static inline auto unionHull(
const std::vector<Constraint> &left,
const std::vector<Constraint> &right) {
502 return unionHull(std::vector<std::vector<Constraint>>{left, right});
505 inline void addUpperBound(std::vector<Constraint> &guard) {
506 std::unordered_map<ClockVariables, std::vector<Constraint>> mapFromClock;
507 for (
const auto &constraint: guard) {
508 auto it = mapFromClock.find(constraint.x);
509 if (it == mapFromClock.end()) {
510 mapFromClock[constraint.x] = {constraint};
512 mapFromClock.at(constraint.x).push_back(constraint);
515 for (
const auto &[clock, constraints]: mapFromClock) {
517 if (constraints.size() == 1 && !constraints.front().isUpperBound()) {
518 if (constraints.front().odr == Constraint::Order::le) {
519 guard.push_back(ConstraintMaker(clock) <= constraints.front().c);
521 guard.push_back(ConstraintMaker(clock) < constraints.front().c + 1);
530 static inline bool adjacent(
const std::vector<Constraint> &left,
const std::vector<Constraint> &right) {
531 auto [leftUpperBound, leftLowerBound] = toBounds(left);
532 auto [rightUpperBound, rightLowerBound] = toBounds(right);
533 if (leftUpperBound.size() != rightUpperBound.size()) {
536 std::vector<std::size_t> differentIndices;
537 const auto maxBound = IntBounds{std::numeric_limits<int>::max(),
false};
538 for (std::size_t i = 0; i < leftUpperBound.size(); ++i) {
539 if (leftUpperBound.at(i) == maxBound && rightUpperBound.at(i) != maxBound) {
540 leftUpperBound.at(i) = leftLowerBound.at(i).second ? leftLowerBound.at(i) :
541 IntBounds{leftLowerBound.at(i).first + 1, leftLowerBound.at(i).second};
542 }
else if (leftUpperBound.at(i) != maxBound && rightUpperBound.at(i) == maxBound) {
543 rightUpperBound.at(i) = rightLowerBound.at(i).second ? rightLowerBound.at(i) :
544 IntBounds{rightLowerBound.at(i).first + 1, rightLowerBound.at(i).second};
546 if (leftUpperBound.at(i) == rightUpperBound.at(i) && leftLowerBound.at(i) == rightLowerBound.at(i)) {
549 differentIndices.push_back(i);
551 if (leftUpperBound.at(i).first == rightLowerBound.at(i).first) {
552 if (leftUpperBound.at(i).second == rightLowerBound.at(i).second) {
555 }
else if (rightUpperBound.at(i).first == leftLowerBound.at(i).first) {
556 if (rightUpperBound.at(i).second == leftLowerBound.at(i).second) {
563 if (differentIndices.size() <= 1) {
576 Kinds kind = Kinds::DEFAULT;
578 for (
const auto index: differentIndices) {
579 if (leftLowerBound.at(index).second) {
580 if (kind == Kinds::DEFAULT) {
581 kind = Kinds::LEFT_LOWER_EQ;
582 }
else if (kind != Kinds::LEFT_LOWER_EQ) {
585 }
else if (rightLowerBound.at(index).second) {
586 if (kind == Kinds::DEFAULT) {
587 kind = Kinds::RIGHT_LOWER_EQ;
588 }
else if (kind != Kinds::RIGHT_LOWER_EQ) {
591 }
else if (leftUpperBound.at(index).second) {
592 if (kind == Kinds::DEFAULT) {
593 kind = Kinds::LEFT_UPPER_EQ;
594 }
else if (kind != Kinds::LEFT_UPPER_EQ) {
597 }
else if (rightUpperBound.at(index).second) {
598 if (kind == Kinds::DEFAULT) {
599 kind = Kinds::RIGHT_UPPER_EQ;
600 }
else if (kind != Kinds::RIGHT_UPPER_EQ) {
Definition: constraint.hh:210
Definition: experiment_runner.hh:23
std::vector< ClockVariables > simpleVariables(const std::vector< Constraint > &constraints)
Return the variables that are bounded by a simple constraint, i.e., c < x < c + 1 or x = c.
Definition: constraint.hh:306
static bool adjacent(const std::vector< Constraint > &left, const std::vector< Constraint > &right)
Check if two guards are adjacent.
Definition: constraint.hh:530
bool is_ascending(const std::vector< T > &container)
Check if the elements are sorted in the ascending order.
Definition: common_types.hh:39
Order
The return values of comparison of two values. Similar to strcmp.
Definition: constraint.hh:23
static void widen(std::vector< Constraint > &guard)
remove any inequality x > c or x >= c
Definition: constraint.hh:240
static auto unionHull(const std::vector< std::vector< Constraint >> &guards)
Return the strongest guard that is weaker than all of the given guards.
Definition: constraint.hh:440
A constraint in a guard of transitions.
Definition: constraint.hh:32
bool isWeaker(const Constraint &another) const
Check if this constraint is weaker than the given one.
Definition: constraint.hh:85