LearnTA  0.0.1
imprecise_clock_handler.hh
1 
6 #pragma once
7 
8 #include <stack>
9 #include <boost/unordered_set.hpp>
10 
11 #include "timed_automaton.hh"
12 #include "renaming_relation.hh"
13 #include "forward_regional_elementary_language.hh"
14 #include "neighbor_conditions.hh"
15 #include "timed_automaton_runner.hh"
16 
17 namespace learnta {
22  private:
23  boost::unordered_set<std::pair<TAState *, NeighborConditions>> impreciseNeighbors;
24 
25  [[nodiscard]] static std::optional<std::pair<TAState *, NeighborConditions>>
26  handleOne(const NeighborConditions &neighbor, const Alphabet action, const TATransition &transition,
27  std::vector<TATransition> &newTransitions, bool &matchBounded, bool &noMatch) {
28  // Relax the guard if it matches
29  if (neighbor.match(transition)) {
30  noMatch = false;
31 #ifdef DEBUG
32  BOOST_LOG_TRIVIAL(debug) << "matched! " << "guard: " << transition.guard;
33 #endif
34  const bool upperBounded = std::any_of(transition.guard.begin(), transition.guard.end(),
35  std::mem_fn(&Constraint::isUpperBound));
36  matchBounded = matchBounded || upperBounded;
37  BOOST_LOG_TRIVIAL(debug) << "matchBounded: " << matchBounded;
38  auto relaxedGuard = neighbor.toRelaxedGuard();
39  if (!upperBounded) {
40  // Remove upper bound if the matched guard has no upper bound
41  relaxedGuard.erase(std::remove_if(relaxedGuard.begin(), relaxedGuard.end(),
42  [](const auto &constraint) {
43  return constraint.isUpperBound();
44  }), relaxedGuard.end());
45  }
46  BOOST_LOG_TRIVIAL(debug) << "relaxed guard: " << relaxedGuard;
47  if (isWeaker(relaxedGuard, transition.guard) && !isWeaker(transition.guard, relaxedGuard)) {
48 #ifdef DEBUG
49  BOOST_LOG_TRIVIAL(debug) << "Relaxed!!";
50 #endif
51  const auto preciseClocksAfterReset = neighbor.preciseClocksAfterReset(transition);
52  const auto neighborAfterTransition = neighbor.makeAfterTransition(action, transition);
53  const auto originalValuation = neighborAfterTransition.toOriginalValuation();
54  newTransitions.emplace_back(transition.target,
55  embedIfImprecise(transition.resetVars,
56  preciseClocksAfterReset,
57  originalValuation),
58  std::move(relaxedGuard));
59  if (preciseClocksAfterReset.empty() || neighborAfterTransition.precise()) {
60  return std::nullopt;
61  } else {
62  return std::make_pair(transition.target, neighborAfterTransition);
63  }
64  }
65  }
66 
67  return std::nullopt;
68  }
69 
70  public:
74  void push(TAState *jumpedState, const RenamingRelation &renamingRelation,
75  const ForwardRegionalElementaryLanguage &targetElementary) {
76  // There are imprecise clocks
77  if (renamingRelation.hasImpreciseClocks(targetElementary.getTimedCondition())) {
78  BOOST_LOG_TRIVIAL(debug) << "new imprecise neighbors set is added: " << jumpedState << ", "
79  << targetElementary << ", " << renamingRelation;
80  impreciseNeighbors.emplace(jumpedState, NeighborConditions{targetElementary, renamingRelation.rightVariables()});
81  }
82  }
83 
87  void run() {
88  std::unordered_set<std::size_t> visitedImpreciseNeighborsHash;
89  while (!impreciseNeighbors.empty()) {
90  BOOST_LOG_TRIVIAL(debug) << "visitedImpreciseNeighborsHash size: " << visitedImpreciseNeighborsHash.size();
91  BOOST_LOG_TRIVIAL(debug) << "impreciseNeighbors size: " << this->impreciseNeighbors.size();
92  auto [state, neighbor] = *impreciseNeighbors.begin();
93  const auto hash = boost::hash_value(*impreciseNeighbors.begin());
94  impreciseNeighbors.erase(impreciseNeighbors.begin());
95  // Skip if this pair is already visited
96  if (visitedImpreciseNeighborsHash.find(hash) != visitedImpreciseNeighborsHash.end()) {
97  continue;
98  }
99  visitedImpreciseNeighborsHash.insert(hash);
100  bool matchBounded;
101  bool noMatch = true;
102  do {
103 #ifdef DEBUG
104  BOOST_LOG_TRIVIAL(debug) << "current imprecise neighbors: " << state << ", " << neighbor;
105 #endif
106  matchBounded = false;
107  // Loop over successors
108  for (auto &[action, transitions]: state->next) {
109  const auto neighborSuccessor = neighbor.successor(action);
110  std::vector<TATransition> newTransitions;
111  for (const auto &transition: transitions) {
112  const auto result = handleOne(neighbor, action, transition, newTransitions, matchBounded, noMatch);
113  if (result) {
114  BOOST_LOG_TRIVIAL(debug) << "New imprecise neighbors by recursion: " << *result;
115  this->impreciseNeighbors.insert(*result);
116  }
117  }
118  const auto dummy = transitions; // Hack for C++17. Unnecessary after C++20.
119  // Any new transition must be a relaxation of an original transition
120  assert(std::all_of(newTransitions.begin(), newTransitions.end(), [&] (const auto &newTransition) {
121  return std::any_of(dummy.begin(), dummy.end(), [&] (const auto &transition) {
122  return transition.target == newTransition.target && isWeaker(newTransition.guard, transition.guard);
123  });
124  }));
125  transitions.reserve(transitions.size() + newTransitions.size());
126  std::move(newTransitions.begin(), newTransitions.end(), std::back_inserter(transitions));
127  }
128  neighbor.successorAssign();
129  } while (matchBounded || noMatch);
130  }
131  BOOST_LOG_TRIVIAL(debug) << "ImpreciseClockHandler: finished!";
132  }
133 
134 
135  static TATransition::Resets embedIfImprecise(TATransition::Resets resets,
136  const std::unordered_set<ClockVariables> &preciseClocks,
137  const std::vector<double> &embeddedValuation) {
138  // Remove imprecise clocks
139  resets.erase(std::remove_if(resets.begin(), resets.end(), [&](const auto &reset) {
140  return preciseClocks.find(reset.first) == preciseClocks.end();
141  }), resets.end());
142  // Add valuations if imprecise
143  for (ClockVariables clock = 0; clock < static_cast<ClockVariables>(embeddedValuation.size()); ++clock) {
144  if (preciseClocks.find(clock) == preciseClocks.end()) {
145  resets.emplace_back(clock, embeddedValuation.at(clock));
146  }
147  }
148  return resets;
149  }
150  };
151 }
A forward regional elementary language.
Definition: forward_regional_elementary_language.hh:23
Relax guards to handle imprecise clock variables.
Definition: imprecise_clock_handler.hh:21
void run()
Relax the guards if necessary.
Definition: imprecise_clock_handler.hh:87
void push(TAState *jumpedState, const RenamingRelation &renamingRelation, const ForwardRegionalElementaryLanguage &targetElementary)
Add new transition with imprecise clocks.
Definition: imprecise_clock_handler.hh:74
Elementary language with its neighbor conditions.
Definition: neighbor_conditions.hh:30
std::vector< double > toOriginalValuation() const
Construct the reset to embed to the target condition.
Definition: neighbor_conditions.hh:378
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
static auto preciseClocksAfterReset(const std::unordered_set< ClockVariables > &preciseClocks, const TATransition &transition)
Make precise clocks after applying a reset.
Definition: neighbor_conditions.hh:147
bool match(const std::vector< Constraint > &guard) const
Return if the given guard matches with this elementary language.
Definition: neighbor_conditions.hh:294
Definition: renaming_relation.hh:16
bool hasImpreciseClocks(const TimedCondition &target) const
Check if the application of this renaming causes implicit clocks.
Definition: renaming_relation.hh:171
Definition: experiment_runner.hh:23
A state of timed automata.
Definition: timed_automaton.hh:28
A state of timed automata.
Definition: timed_automaton.hh:82
TAState * target
The pointer to the target state.
Definition: timed_automaton.hh:85
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