LearnTA  0.0.1
neighbor_conditions.hh
1 
6 #pragma once
7 
8 #include <utility>
9 #include <vector>
10 #include <unordered_set>
11 
12 #include "common_types.hh"
13 #include "timed_automaton.hh"
14 #include "forward_regional_elementary_language.hh"
15 #include "external_transition_maker.hh"
16 
17 namespace learnta {
18 
31  private:
32  // The original elementary language
34  // The precise clock variables
35  std::unordered_set<ClockVariables> preciseClocks;
36  // The neighbor elementary languages due to imprecise clocks
37  std::vector<ForwardRegionalElementaryLanguage> neighbors;
38  std::size_t clockSize;
39 
40  void assertInvariants() const {
41  assert(std::all_of(neighbors.begin(), neighbors.end(), [&](const auto &neighbor) {
42  return neighbor.getWord() == original.getWord();
43  }));
44  assert(clockSize == original.getTimedCondition().size());
45  assert(std::all_of(neighbors.begin(), neighbors.end(), [&](const auto &neighbor) {
46  return neighbor.getTimedCondition().size() == clockSize;
47  }));
48  assert(std::all_of(neighbors.begin(), neighbors.end(), [&](const auto &neighbor) {
49  return std::all_of(preciseClocks.begin(), preciseClocks.end(), [&](const auto &preciseClock) {
50  return neighbor.getTimedCondition().getLowerBound(preciseClock, clockSize - 1) ==
51  original.getTimedCondition().getLowerBound(preciseClock, clockSize - 1);
52  });
53  }));
54  assert(std::all_of(neighbors.begin(), neighbors.end(), [&](const auto &neighbor) {
55  return std::all_of(preciseClocks.begin(), preciseClocks.end(), [&](const auto &preciseClock) {
56  return neighbor.getTimedCondition().getUpperBound(preciseClock, clockSize - 1) ==
57  original.getTimedCondition().getUpperBound(preciseClock, clockSize - 1);
58  });
59  }));
60  }
61 
62  NeighborConditions(ForwardRegionalElementaryLanguage &&original,
63  std::vector<ForwardRegionalElementaryLanguage> &&neighbors,
64  const std::unordered_set<ClockVariables> &preciseClocks,
65  size_t clockSize) : original(original), preciseClocks(preciseClocks),
66  neighbors(neighbors), clockSize(clockSize) {
67  assertInvariants();
68  }
69 
70  NeighborConditions(ForwardRegionalElementaryLanguage &&original,
71  std::vector<ForwardRegionalElementaryLanguage> &&neighbors,
72  std::unordered_set<ClockVariables> &&preciseClocks,
73  size_t clockSize) : original(original), preciseClocks(preciseClocks),
74  neighbors(neighbors), clockSize(clockSize) {
75  assertInvariants();
76  }
77 
83  void addImplicitPreciseClocks() {
84  BOOST_LOG_TRIVIAL(debug) << "explicit precise clocks: ";
85  for (const auto &preciseClock: preciseClocks) {
86  BOOST_LOG_TRIVIAL(debug) << "x" << static_cast<int>(preciseClock);
87  }
88  for (std::size_t i = 0; i < clockSize; ++i) {
89  // We skip explicitly precise clocks
90  if (this->preciseClocks.find(i) != this->preciseClocks.end()) {
91  continue;
92  }
93  const auto lowerBound = this->original.getTimedCondition().getLowerBound(i, clockSize - 1);
94  const auto upperBound = this->original.getTimedCondition().getUpperBound(i, clockSize - 1);
95  if (isPoint(upperBound, lowerBound)) {
96  assert(std::all_of(this->neighbors.begin(), this->neighbors.end(), [&] (const auto& neighbor) {
97  return neighbor.getTimedCondition().getLowerBound(i, clockSize - 1) == lowerBound && neighbor.getTimedCondition().getUpperBound(i, clockSize - 1) == upperBound;
98  }));
99  this->preciseClocks.insert(i);
100  }
101  }
102  BOOST_LOG_TRIVIAL(debug) << "appended precise clocks: ";
103  for (const auto &preciseClock: preciseClocks) {
104  BOOST_LOG_TRIVIAL(debug) << "x" << static_cast<int>(preciseClock);
105  }
106  }
107 
108  [[nodiscard]] auto updateNeighborsWithContinuousSuccessors(const ForwardRegionalElementaryLanguage &originalSuccessor) const {
109  BOOST_LOG_TRIVIAL(debug) << "originalSuccessor: " << originalSuccessor;
110  // The neighbor elementary languages due to imprecise clocks
111  std::vector<ForwardRegionalElementaryLanguage> newNeighbors;
112  newNeighbors.reserve(neighbors.size());
113  for (auto neighbor: neighbors) {
114  while (std::all_of(preciseClocks.begin(), preciseClocks.end(), [&](const auto &preciseClock) {
115  return neighbor.getTimedCondition().getUpperBound(preciseClock, neighbor.getTimedCondition().size() - 1) <=
116  originalSuccessor.getTimedCondition().getUpperBound(preciseClock,
117  originalSuccessor.getTimedCondition().size() - 1);
118  })) {
119  if (std::all_of(preciseClocks.begin(), preciseClocks.end(), [&](const auto &preciseClock) {
120  return neighbor.getTimedCondition().getLowerBound(preciseClock, neighbor.getTimedCondition().size() - 1) ==
121  originalSuccessor.getTimedCondition().getLowerBound(preciseClock,
122  originalSuccessor.getTimedCondition().size() -
123  1) &&
124  neighbor.getTimedCondition().getUpperBound(preciseClock, neighbor.getTimedCondition().size() - 1) ==
125  originalSuccessor.getTimedCondition().getUpperBound(preciseClock,
126  originalSuccessor.getTimedCondition().size() -
127  1);
128  })) {
129  newNeighbors.push_back(neighbor);
130  }
131  neighbor = neighbor.successor();
132  }
133  }
134 
135  std::sort(newNeighbors.begin(), newNeighbors.end(), [] (const auto& left, const auto& right) {
136  return left.hash_value() < right.hash_value();
137  });
138  newNeighbors.erase(std::unique(newNeighbors.begin(), newNeighbors.end()), newNeighbors.end());
139 
140  return newNeighbors;
141  }
142  public:
143 
147  static auto preciseClocksAfterReset(const std::unordered_set<ClockVariables>& preciseClocks,
148  const TATransition &transition) {
149  std::unordered_set<ClockVariables> newPreciseClocks;
150  const auto targetClockSize = computeTargetClockSize(transition);
151  for (const auto &[targetVariable, assignedValue]: transition.resetVars) {
152  if (targetVariable >= targetClockSize) {
153  continue;
154  }
155  if (assignedValue.index() == 1 &&
156  preciseClocks.find(std::get<ClockVariables>(assignedValue)) != preciseClocks.end()) {
157  // targetVariable is precise if its value is updated to a precise variable
158  newPreciseClocks.insert(targetVariable);
159  } else if (assignedValue.index() == 0 &&
160  std::get<double>(assignedValue) == std::floor(std::get<double>(assignedValue))) {
161  // targetVariable is precise if its value is updated to an integer
162  newPreciseClocks.insert(targetVariable);
163  }
164  }
165  for (const auto &preciseClock: preciseClocks) {
166  // Check if the precise clock is in the range and not updated
167  if (preciseClock >= targetClockSize || newPreciseClocks.find(preciseClock) != newPreciseClocks.end()) {
168  continue;
169  }
170  auto it = std::find_if(transition.resetVars.begin(), transition.resetVars.end(), [&] (const auto &reset) {
171  return reset.first == preciseClock;
172  });
173  if (it == transition.resetVars.end()) {
174  newPreciseClocks.insert(preciseClock);
175  }
176  }
177 
178  assert(newPreciseClocks.size() <= targetClockSize);
179  return newPreciseClocks;
180  }
181 
185  [[nodiscard]] auto preciseClocksAfterReset(const TATransition &transition) const {
186  return NeighborConditions::preciseClocksAfterReset(this->preciseClocks, transition);
187  }
188 
192  [[nodiscard]] NeighborConditions reconstruct(std::unordered_set<ClockVariables> currentPreciseClocks) const {
193  // Restrict the range of precise clocks
194  for (auto it = currentPreciseClocks.begin(); it != currentPreciseClocks.end();) {
195  if (*it > this->original.wordSize()) {
196  it = currentPreciseClocks.erase(it);
197  } else {
198  ++it;
199  }
200  }
201  return NeighborConditions{this->original, std::move(currentPreciseClocks)};
202  }
203 
204  static auto makeNeighbors(const ForwardRegionalElementaryLanguage &original,
205  const std::unordered_set<ClockVariables> &preciseClocks) {
206  const auto clockSize = original.getTimedCondition().size();
207  std::vector<ForwardRegionalElementaryLanguage> neighbors;
208 
209  // Construct the neighbors
210  auto neighborsCondition = TimedCondition(Zone::top(clockSize + 1));
211  for (std::size_t i = 0; i < clockSize; ++i) {
212  // The constraints of the form x \in I is used
213  neighborsCondition.restrictLowerBound(i, clockSize - 1,
214  original.getTimedCondition().getLowerBound(i, clockSize - 1));
215  neighborsCondition.restrictUpperBound(i, clockSize - 1,
216  original.getTimedCondition().getUpperBound(i, clockSize - 1));
217  for (std::size_t j = i + 1; j < clockSize; ++j) {
218  if ((preciseClocks.find(i) == preciseClocks.end()) ==
219  (preciseClocks.find(j) == preciseClocks.end())) {
220  // The constraints of the form x - y \in I is used if both are precise or imprecise
221  neighborsCondition.restrictLowerBound(i, j - 1, original.getTimedCondition().getLowerBound(i, j - 1));
222  neighborsCondition.restrictUpperBound(i, j - 1, original.getTimedCondition().getUpperBound(i, j - 1));
223  }
224  }
225  }
226  const auto simpleNeighborsCondition = neighborsCondition.enumerate();
227  neighbors.reserve(simpleNeighborsCondition.size());
228  std::transform(simpleNeighborsCondition.begin(), simpleNeighborsCondition.end(), std::back_inserter(neighbors),
229  [&](const auto &neighborCondition) {
230  return ForwardRegionalElementaryLanguage::fromTimedWord(
231  ElementaryLanguage{original.getWord(), neighborCondition}.sample());
232  });
233 
234  return neighbors;
235  }
236 
237  NeighborConditions(const NeighborConditions& conditions) = default;
238  NeighborConditions(NeighborConditions&& conditions) = default;
239  NeighborConditions& operator=(const NeighborConditions& conditions) = default;
240  NeighborConditions& operator=(NeighborConditions&& conditions) = default;
241  NeighborConditions(ForwardRegionalElementaryLanguage original,
242  std::unordered_set<ClockVariables> preciseClocks) : original(std::move(original)),
243  preciseClocks(std::move(preciseClocks)),
244  neighbors(makeNeighbors(this->original,
245  this->preciseClocks)),
246  clockSize(
247  this->original.getTimedCondition().size()) {
248  addImplicitPreciseClocks();
249  if (!this->preciseClocks.empty()) {
250  this->neighbors = updateNeighborsWithContinuousSuccessors(this->original);
251  }
252  assertInvariants();
253  }
254 
255  NeighborConditions(ForwardRegionalElementaryLanguage original,
256  const std::vector<ClockVariables> &preciseClocks) : original(std::move(original)),
257  preciseClocks(
258  std::unordered_set<ClockVariables>(
259  preciseClocks.begin(),
260  preciseClocks.end())),
261  neighbors(makeNeighbors(this->original,
262  this->preciseClocks)),
263  clockSize(
264  this->original.getTimedCondition().size()) {
265  addImplicitPreciseClocks();
266  if (!this->preciseClocks.empty()) {
267  this->neighbors = updateNeighborsWithContinuousSuccessors(this->original);
268  }
269  assertInvariants();
270  }
271 
272 
276  [[nodiscard]] NeighborConditions makeAfterTransition(const Alphabet action, const TATransition &transition) const {
277  return NeighborConditions{this->constructOriginalAfterTransition(action, transition),
278  this->preciseClocksAfterReset(transition)};
279  }
280 
286  [[nodiscard]] auto toOriginalGuard() const {
287  assertInvariants();
288  return this->original.getTimedCondition().toGuard();
289  }
290 
294  [[nodiscard]] bool match(const std::vector<Constraint> &guard) const {
295  assertInvariants();
296  return isWeaker(guard, this->toOriginalGuard());
297  }
298 
299  [[nodiscard]] size_t getClockSize() const {
300  return clockSize;
301  }
302 
306  [[nodiscard]] bool precise() const {
307  return this->neighbors.size() == 1;
308  }
309 
313  [[nodiscard]] bool match(const TATransition &transition) const {
314  assertInvariants();
315  return this->match(transition.guard);
316  }
317 
321  [[nodiscard]] auto toRelaxedGuard() const {
322  assertInvariants();
323  std::vector<std::vector<Constraint>> guards;
324  guards.reserve(this->neighbors.size());
325  std::transform(this->neighbors.begin(), this->neighbors.end(), std::back_inserter(guards),
326  [&](const auto &neighbor) {
327  return neighbor.getTimedCondition().toGuard();
328  });
329 
330  return unionHull(guards);
331  }
332 
333  [[nodiscard]] NeighborConditions successor(const Alphabet action) const {
334  // The neighbor elementary languages due to imprecise clocks
335  std::vector<ForwardRegionalElementaryLanguage> newNeighbors;
336  newNeighbors.reserve(neighbors.size());
337  std::transform(neighbors.begin(), neighbors.end(), std::back_inserter(newNeighbors), [&](const auto &neighbor) {
338  return neighbor.successor(action);
339  });
340  auto newPreciseClocks = preciseClocks;
341  newPreciseClocks.insert(clockSize);
342  return NeighborConditions{original.successor(action), std::move(newNeighbors),
343  newPreciseClocks, clockSize + 1};
344  }
345 
346  [[nodiscard]] NeighborConditions successor() const {
347  auto originalSuccessor = original.successor();
348  auto newNeighbors = updateNeighborsWithContinuousSuccessors(originalSuccessor);
349 
350  return NeighborConditions{std::move(originalSuccessor), std::move(newNeighbors), preciseClocks, clockSize};
351  }
352 
353  void successorAssign() {
354  original.successorAssign();
355  neighbors = updateNeighborsWithContinuousSuccessors(original);
356  }
357 
363  [[nodiscard]] std::vector<ClockVariables> impreciseClocks() const {
364  std::vector<ClockVariables> impreciseClockVec;
365  impreciseClockVec.reserve(this->clockSize);
366  for (std::size_t clock = 0; clock < this->clockSize; ++clock) {
367  if (this->preciseClocks.find(clock) == this->preciseClocks.end()) {
368  impreciseClockVec.push_back(clock);
369  }
370  }
371 
372  return impreciseClockVec;
373  }
374 
378  [[nodiscard]] std::vector<double> toOriginalValuation() const {
379  return ExternalTransitionMaker::toValuation(this->original.getTimedCondition());
380  }
381 
385  [[nodiscard]] std::vector<double> toOriginalValuation(const std::size_t minSize) const {
386  auto valuation = ExternalTransitionMaker::toValuation(this->original.getTimedCondition());
387  if (valuation.size() < minSize) {
388  valuation.resize(minSize);
389  }
390 
391  return valuation;
392  }
393 
397  [[nodiscard]] NeighborConditions applyResets(const TATransition::Resets &resets) const {
398  auto newNeighborConditions = *this;
399  newNeighborConditions.original = newNeighborConditions.original.applyResets(resets);
400  for (auto &neighbor: newNeighborConditions.neighbors) {
401  neighbor = neighbor.applyResets(resets);
402  }
403  for (const auto &[updatedVariable, assignedValue]: resets) {
404  if (assignedValue.index() == 0) {
405  newNeighborConditions.preciseClocks.insert(updatedVariable);
406  } else {
407  // This case is not supported
408  BOOST_LOG_TRIVIAL(error) << "Unimplemented case";
409  abort();
410  }
411  }
412 
413  return newNeighborConditions;
414  }
415 
419  [[nodiscard]] bool isInternal(const TATransition &transition) const {
420  return transition.resetVars.size() == 1 &&
421  transition.resetVars.front().first == this->getClockSize() &&
422  transition.resetVars.front().second.index() == 0 &&
423  std::get<double>(transition.resetVars.front().second) == 0.0;
424  }
425 
426  [[nodiscard]] ForwardRegionalElementaryLanguage constructOriginalAfterTransition(const Alphabet action,
427  const TATransition &transition) const {
428  BOOST_LOG_TRIVIAL(debug) << "original before transition: " << this->original;
429  if (isInternal(transition)) {
430  BOOST_LOG_TRIVIAL(debug) << "original after transition: " << this->original.successor(action);
431  return this->original.successor(action);
432  } else {
433  // make words
434  std::string newWord = this->original.getWord();
435  const auto targetClockSize = computeTargetClockSize(transition);
436  assert(targetClockSize > 0);
437  newWord.resize(targetClockSize - 1, this->original.getWord().back());
438 
439  BOOST_LOG_TRIVIAL(debug) << "newWord: " << newWord;
440  BOOST_LOG_TRIVIAL(debug) << "resetVars: " << transition.resetVars;
441  BOOST_LOG_TRIVIAL(debug) << "targetClockSize: " << targetClockSize;
442  BOOST_LOG_TRIVIAL(debug) << "original after transition: "
443  << this->original.applyResets(newWord, transition.resetVars, targetClockSize);
444  return this->original.applyResets(newWord, transition.resetVars, targetClockSize);
445  }
446  }
447 
448  [[nodiscard]] static std::size_t computeClockSize(const TAState* state) {
449  std::size_t targetClockSize = 0;
450  for (const auto &[action, transitions]: state->next) {
451  for (const auto &cTransition: transitions) {
452  std::for_each(cTransition.guard.begin(), cTransition.guard.end(), [&](const Constraint &constraint) {
453  targetClockSize = std::max(targetClockSize, static_cast<std::size_t>(constraint.x + 1));
454  });
455  }
456  }
457 
458  return targetClockSize;
459  }
460 
461  [[nodiscard]] static std::size_t computeTargetClockSize(const TATransition &transition) {
462  return computeClockSize(transition.target);
463  }
464 
465  std::ostream &print(std::ostream &os) const {
466  os << this->original << " {";
467  bool initial = true;
468  for (const auto &preciseClock: preciseClocks) {
469  if (!initial) {
470  os << ", ";
471  }
472  os << "x" << static_cast<int>(preciseClock);
473  initial = false;
474  }
475  os << "} {";
476  for (const auto &neighbor: neighbors) {
477  os << "\n" << neighbor;
478  }
479  os << "\n}";
480 
481  return os;
482  }
483 
484  bool operator==(const NeighborConditions &conditions) const {
485  return original == conditions.original &&
486  preciseClocks == conditions.preciseClocks &&
487  neighbors == conditions.neighbors &&
488  clockSize == conditions.clockSize;
489  }
490 
491  bool operator!=(const NeighborConditions &conditions) const {
492  return !(conditions == *this);
493  }
494 
495  [[nodiscard]] std::size_t hash_value() const {
496  return boost::hash_value(std::make_tuple(original,
497  std::vector<ClockVariables>{preciseClocks.begin(), preciseClocks.end()},
498  neighbors, clockSize));
499  }
500  };
501 
502  static inline std::ostream &operator<<(std::ostream &os, const learnta::NeighborConditions &conditions) {
503  return conditions.print(os);
504  }
505 
506  static inline std::size_t hash_value(const NeighborConditions &conditions) {
507  return conditions.hash_value();
508  }
509 }
A forward regional elementary language.
Definition: forward_regional_elementary_language.hh:23
ForwardRegionalElementaryLanguage successor(char action) const
Construct the discrete successor.
Definition: forward_regional_elementary_language.hh:65
Elementary language with its neighbor conditions.
Definition: neighbor_conditions.hh:30
std::vector< ClockVariables > impreciseClocks() const
Returns the list of imprecise clocks.
Definition: neighbor_conditions.hh:363
std::vector< double > toOriginalValuation() const
Construct the reset to embed to the target condition.
Definition: neighbor_conditions.hh:378
auto toOriginalGuard() const
Return the guard of the original elementary language.
Definition: neighbor_conditions.hh:286
NeighborConditions makeAfterTransition(const Alphabet action, const TATransition &transition) const
Construct the neighbor conditions after a transition.
Definition: neighbor_conditions.hh:276
auto toRelaxedGuard() const
Return the guard of the original elementary language and its neighbors.
Definition: neighbor_conditions.hh:321
NeighborConditions reconstruct(std::unordered_set< ClockVariables > currentPreciseClocks) const
Reconstruct the neighbor conditions with new precise clocks.
Definition: neighbor_conditions.hh:192
bool match(const TATransition &transition) const
Return if the given transition matches with this elementary language.
Definition: neighbor_conditions.hh:313
std::vector< double > toOriginalValuation(const std::size_t minSize) const
Construct the reset to embed to the target condition.
Definition: neighbor_conditions.hh:385
bool precise() const
Returns if the relaxed guard is precise.
Definition: neighbor_conditions.hh:306
static auto preciseClocksAfterReset(const std::unordered_set< ClockVariables > &preciseClocks, const TATransition &transition)
Make precise clocks after applying a reset.
Definition: neighbor_conditions.hh:147
NeighborConditions applyResets(const TATransition::Resets &resets) const
Return the neighbor conditions after applying the given resets.
Definition: neighbor_conditions.hh:397
auto preciseClocksAfterReset(const TATransition &transition) const
Make precise clocks after applying a reset.
Definition: neighbor_conditions.hh:185
bool isInternal(const TATransition &transition) const
Check if the transition from the current neighbor is internal.
Definition: neighbor_conditions.hh:419
bool match(const std::vector< Constraint > &guard) const
Return if the given guard matches with this elementary language.
Definition: neighbor_conditions.hh:294
A timed condition, a finite conjunction of inequalities of the form , where and .
Definition: timed_condition.hh:19
Bounds getUpperBound(std::size_t i, std::size_t j) const
Returns the upper bound of .
Definition: timed_condition.hh:232
Bounds getLowerBound(std::size_t i, std::size_t j) const
Returns the lower bound of .
Definition: timed_condition.hh:219
size_t size() const
Return the number of the variables in this timed condition.
Definition: timed_condition.hh:89
Definition: experiment_runner.hh:23
Definition: external_transition_maker.hh:149
A state of timed automata.
Definition: timed_automaton.hh:82
std::vector< Constraint > guard
The guard for this transition.
Definition: timed_automaton.hh:93
std::vector< std::pair< ClockVariables, std::variant< double, ClockVariables > > > resetVars
The clock variables reset after this transition.
Definition: timed_automaton.hh:91