LearnTA  0.0.1
observation_table.hh
1 
6 #pragma once
7 
8 #include <utility>
9 #include <vector>
10 #include <stack>
11 #include <list>
12 #include <queue>
13 #include <unordered_map>
14 
15 #include <boost/unordered_map.hpp>
16 #include <boost/unordered_set.hpp>
17 #include <boost/log/trivial.hpp>
18 #include <boost/log/core.hpp>
19 #include <boost/log/expressions.hpp>
20 
21 #include "forward_regional_elementary_language.hh"
22 #include "backward_regional_elementary_language.hh"
23 #include "symbolic_membership_oracle.hh"
24 #include "timed_automaton.hh"
25 #include "equivalence.hh"
26 #include "renaming_relation.hh"
27 #include "internal_transition_maker.hh"
28 #include "external_transition_maker.hh"
29 #include "counterexample_analyzer.hh"
30 #include "neighbor_conditions.hh"
31 #include "imprecise_clock_handler.hh"
32 
33 #ifdef PRINT_REFINEMENT_INFO
34 #define LOG_REFINEMENT_INFO BOOST_LOG_TRIVIAL(info)
35 #else
36 #define LOG_REFINEMENT_INFO BOOST_LOG_TRIVIAL(debug)
37 #endif
38 
39 namespace learnta {
44  private:
45  std::unique_ptr<SymbolicMembershipOracle> memOracle;
46  std::vector<Alphabet> alphabet;
47  std::vector<ForwardRegionalElementaryLanguage> prefixes;
48  std::vector<BackwardRegionalElementaryLanguage> suffixes;
49  // concatenations.at(i).at(j) = prefixes.at(i).getTimedCondition() + suffixes.at(j).getTimedCondition()
50  std::vector<std::vector<TimedCondition>> concatenations;
51  // The indexes of prefixes in P
52  std::unordered_set<std::size_t> pIndices;
53  // prefixes[first] and prefixes[second.first] are in the same equivalence class witnessed by second.second
54  // Maybe we should use vector instead
55  std::unordered_map<std::size_t, std::unordered_map<std::size_t, RenamingRelation>> closedRelation;
56  // The table containing the symbolic membership
57  std::vector<std::vector<TimedConditionSet>> table;
58  std::unordered_map<std::size_t, std::size_t> continuousSuccessors;
59  boost::unordered_map<std::pair<std::size_t, Alphabet>, std::size_t> discreteSuccessors;
60  // The pair of prefixes such that we know that they are distinguished
61  boost::unordered_set<std::pair<std::size_t, std::size_t>> distinguishedPrefix;
62 
68  void refreshTable() {
69  table.resize(prefixes.size());
70  concatenations.resize(prefixes.size());
71  for (std::size_t prefixIndex = 0; prefixIndex < prefixes.size(); ++prefixIndex) {
72  const auto originalSize = table.at(prefixIndex).size();
73  table.at(prefixIndex).resize(suffixes.size());
74  concatenations.at(prefixIndex).resize(suffixes.size());
75  for (auto suffixIndex = originalSize; suffixIndex < suffixes.size(); ++suffixIndex) {
76  const auto concatenation = prefixes.at(prefixIndex) + suffixes.at(suffixIndex);
77  table.at(prefixIndex).at(suffixIndex) = this->memOracle->query(concatenation);
78  concatenations.at(prefixIndex).at(suffixIndex) = concatenation.getTimedCondition();
79  }
80  }
81  }
82 
94  void moveToP(const std::size_t index) {
95  // index should not be in P yet
96  assert(!this->inP(index));
97  // The index should be in a valid range
98  assert(index < prefixes.size());
99  pIndices.insert(index);
100  // Add successors to the prefixes
101  prefixes.reserve(prefixes.size() + alphabet.size() + 1);
102  for (Alphabet c: alphabet) {
103  discreteSuccessors[std::make_pair(index, c)] = prefixes.size();
104  prefixes.emplace_back(prefixes.at(index).successor(c));
105  }
106  continuousSuccessors[index] = prefixes.size();
107  prefixes.emplace_back(prefixes.at(index).successor());
108  // fill the observation table
109  refreshTable();
110  // index should point P
111  assert(this->inP(index));
112  // the discrete successors of prefixes.at(index) should be in ext(P)
113  assert(std::all_of(alphabet.begin(), alphabet.end(), [&](Alphabet c) {
114  return 0 <= discreteSuccessors.at(std::make_pair(index, c)) &&
115  discreteSuccessors.at(std::make_pair(index, c)) < prefixes.size();
116  }));
117  assert(std::all_of(alphabet.begin(), alphabet.end(), [&](Alphabet c) {
118  return !this->inP(discreteSuccessors.at(std::make_pair(index, c)));
119  }));
120  // the continuous successor of prefixes.at(index) should be in ext(P)
121  assert(continuousSuccessors.at(index) < prefixes.size());
122  assert(!this->inP(continuousSuccessors.at(index)));
123  // We add continuous successors to P if it is clearly not saturated
124  if (!equivalentWithMemo(continuousSuccessors.at(index), index)) {
125  this->moveToP(continuousSuccessors.at(index));
126  }
127  }
128 
129  std::optional<RenamingRelation> equivalent(std::size_t i, std::size_t j) {
130  auto renamingRelation = findDeterministicEquivalentRenaming(this->prefixes.at(i), this->table.at(i), this->concatenations.at(i),
131  this->prefixes.at(j), this->table.at(j), this->concatenations.at(j),
132  this->suffixes);
133  if (renamingRelation) {
134  this->closedRelation[i][j] = renamingRelation.value();
135  return renamingRelation;
136  } else {
137  this->distinguishedPrefix.insert(std::make_pair(i, j));
138  return std::nullopt;
139  }
140  }
141 
142  std::optional<RenamingRelation> equivalentWithMemo(std::size_t i, std::size_t j) {
143 #if 1
144  if (this->distinguishedPrefix.find(std::make_pair(i, j)) != this->distinguishedPrefix.end() ||
145  this->distinguishedPrefix.find(std::make_pair(j, i)) != this->distinguishedPrefix.end()) {
146  // we already know that they are not equivalent
147  assert(!this->equivalent(i, j));
148  return std::nullopt;
149  }
150 #endif
151  auto equivalentPrefixesIT = this->closedRelation.find(i);
152  if (equivalentPrefixesIT != this->closedRelation.end()) {
153  auto it = equivalentPrefixesIT->second.find(j);
154  if (it != this->closedRelation.at(i).end()) {
155  if (equivalence(this->prefixes.at(i), this->table.at(i), this->concatenations.at(i),
156  this->prefixes.at(j), this->table.at(j), this->concatenations.at(j),
157  this->suffixes, it->second)) {
158  return it->second;
159  }
160  }
161  }
162  return this->equivalent(i, j);
163  }
164 
165  boost::unordered_map<std::tuple<std::size_t, std::size_t, BackwardRegionalElementaryLanguage>, std::pair<std::size_t, bool>> equivalentWithColumnCache;
166 
170  [[nodiscard]] bool equivalent(std::size_t i, std::size_t j, const BackwardRegionalElementaryLanguage &newSuffix) {
171  // First, we use the cache
172  auto key = std::make_tuple(i, j, newSuffix);
173  {
174  auto it = equivalentWithColumnCache.find(key);
175  if (it != equivalentWithColumnCache.end() && this->suffixes.size() == it->second.first) {
176  return it->second.second;
177  }
178  }
179  // Then, we try the known renaming relations
180  {
181  auto equivalentPrefixesIT = this->closedRelation.find(i);
182  if (equivalentPrefixesIT != this->closedRelation.end()) {
183  auto it = equivalentPrefixesIT->second.find(j);
184  if (it != this->closedRelation.at(i).end()) {
185  if (equivalent(i, j, newSuffix, it->second)) {
186  equivalentWithColumnCache[key] = std::make_pair(this->suffixes.size(), true);
187  return true;
188  }
189  }
190  }
191  }
192  // Finally, we try to find an equivalent renaming
193  auto leftRow = this->table.at(i);
194  auto leftConcatenations = this->concatenations.at(i);
195  const auto newLeftConcatenation = prefixes.at(i) + newSuffix;
196  leftRow.emplace_back(this->memOracle->query(newLeftConcatenation));
197  leftConcatenations.emplace_back(newLeftConcatenation.getTimedCondition());
198  auto rightRow = this->table.at(j);
199  auto rightConcatenations = this->concatenations.at(j);
200  const auto newRightConcatenation = prefixes.at(j) + newSuffix;
201  rightRow.emplace_back(this->memOracle->query(newRightConcatenation));
202  rightConcatenations.emplace_back(newRightConcatenation.getTimedCondition());
203  auto newSuffixes = this->suffixes;
204  newSuffixes.emplace_back(newSuffix);
205  const auto result = findDeterministicEquivalentRenaming(this->prefixes.at(i), leftRow, leftConcatenations,
206  this->prefixes.at(j), rightRow, rightConcatenations,
207  newSuffixes).has_value();
208  equivalentWithColumnCache[key] = std::make_pair(this->suffixes.size(), result);
209 
210  return result;
211  }
212 
216  [[nodiscard]] bool equivalent(std::size_t i, std::size_t j, const BackwardRegionalElementaryLanguage &newSuffix,
217  const RenamingRelation &renaming) const {
218  const auto leftPrefix = this->prefixes.at(i);
219  auto leftRow = this->table.at(i);
220  auto leftConcatenation = this->concatenations.at(i);
221  auto newLeftConcatenation = prefixes.at(i) + newSuffix;
222  leftRow.emplace_back(this->memOracle->query(newLeftConcatenation));
223  leftConcatenation.emplace_back(newLeftConcatenation.getTimedCondition());
224  const auto rightPrefix = this->prefixes.at(j);
225  auto rightRow = this->table.at(j);
226  auto rightConcatenation = this->concatenations.at(j);
227  auto newRightConcatenation = prefixes.at(j) + newSuffix;
228  rightRow.emplace_back(this->memOracle->query(newRightConcatenation));
229  rightConcatenation.emplace_back(newRightConcatenation.getTimedCondition());
230  auto newSuffixes = this->suffixes;
231  newSuffixes.emplace_back(newSuffix);
232 
233  return equivalence(leftPrefix, leftRow, leftConcatenation,
234  rightPrefix, rightRow, rightConcatenation,
235  newSuffixes, renaming);
236  }
237 
238 #if 0
239  [[nodiscard]] bool
240  equivalent(std::size_t i, std::size_t j, const std::list<BackwardRegionalElementaryLanguage> &newSuffixes) const {
241  auto leftRow = this->table.at(i);
242  leftRow.reserve(leftRow.size() + newSuffixes.size());
243  auto rightRow = this->table.at(j);
244  rightRow.reserve(rightRow.size() + newSuffixes.size());
245  auto tmpSuffixes = this->suffixes;
246  tmpSuffixes.reserve(tmpSuffixes.size() + newSuffixes.size());
247  for (const auto &newSuffix: newSuffixes) {
248  leftRow.emplace_back(this->memOracle->query(prefixes.at(i) + newSuffix));
249  rightRow.emplace_back(this->memOracle->query(prefixes.at(j) + newSuffix));
250  tmpSuffixes.emplace_back(newSuffix);
251  }
252 
253  return findDeterministicEquivalentRenaming(this->prefixes.at(i), leftRow,
254  this->prefixes.at(j), rightRow,
255  tmpSuffixes).has_value();
256  }
257 #endif
258 
262  [[nodiscard]] bool isMatch(std::size_t i) const {
263  return !this->table.at(i).at(0).empty();
264  }
265 
269  [[nodiscard]] bool inP(std::size_t i) const {
270  return this->pIndices.find(i) != this->pIndices.end();
271  }
272 
276  [[nodiscard]] bool hasDiscreteSuccessor(std::size_t i, Alphabet c) const {
277  return this->discreteSuccessors.find(std::make_pair(i, c)) != this->discreteSuccessors.end();
278  }
279 
283  [[nodiscard]] bool hasContinuousSuccessor(std::size_t i) const {
284  return this->continuousSuccessors.find(i) != this->continuousSuccessors.end();
285  }
286 
293  static void splitStates(std::vector<std::shared_ptr<TAState>> &originalStates,
294  std::shared_ptr<TAState> &initialState,
295  const std::vector<TAState *> &needSplit);
296 
297  public:
301  ObservationTable(std::vector<Alphabet> alphabet, std::unique_ptr<SymbolicMembershipOracle> memOracle) :
302  memOracle(std::move(memOracle)),
303  alphabet(std::move(alphabet)),
306  this->moveToP(0);
307  this->refreshTable();
308  }
309 
317  bool close() {
318  for (std::size_t i = 0; i < this->prefixes.size(); i++) {
319  // Skip if this prefix is in P
320  if (this->inP(i)) {
321  continue;
322  }
323  auto prefix = this->prefixes.at(i);
324  auto prefixRow = this->table.at(i);
325 
326  // Use the memoized information for efficiency
327  bool found = false;
328  {
329  auto it = this->closedRelation.find(i);
330  if (it != closedRelation.end()) {
331  // When we already know that this prefix is equivalent to one of p \in P, we just confirm it.
332  for (auto targetIt = it->second.begin(); targetIt != it->second.end();) {
333  auto renamingRelation = targetIt->second;
334  if (equivalence(prefix, prefixRow, this->concatenations.at(i),
335  this->prefixes.at(targetIt->first), this->table.at(targetIt->first),
336  this->concatenations.at(targetIt->first),
337  this->suffixes, renamingRelation)) {
338  if (this->inP(targetIt->first)) {
339  found = true;
340  break;
341  } else {
342  ++targetIt;
343  }
344  } else {
345  targetIt = it->second.erase(targetIt);
346  }
347  }
348  }
349  }
350 
351  // We have no idea of the target prefix, and we find an equivalent row
352  if (!found) {
353  // First, we try to "jump" to the same state
354  found = std::any_of(this->pIndices.begin(), this->pIndices.end(), [&](const auto j) {
355  return this->continuousSuccessors.at(j) == i && equivalentWithMemo(i, j);
356  });
357  if (!found) {
358  found = std::any_of(this->pIndices.begin(), this->pIndices.end(), [&](const auto j) {
359  return equivalentWithMemo(i, j);
360  });
361  }
362  }
363  if (!found) {
364  // The observation table is not closed
365 #ifndef NDEBUG
366  const auto prePSize = this->pIndices.size();
367 #endif
368  this->moveToP(i);
369  assert(prePSize < this->pIndices.size());
370  LOG_REFINEMENT_INFO << "Observation table is not closed because of " << this->prefixes.at(i);
371  this->refreshTable();
372  return false;
373  }
374  }
375 
376  // Assert that we have prefixes for any successors
377  assert(std::all_of(this->pIndices.begin(), this->pIndices.end(), [&](const auto &pIndex) {
378  const auto successor = this->continuousSuccessors.at(pIndex);
379  return successor < this->prefixes.size();
380  }));
381  assert(std::all_of(this->pIndices.begin(), this->pIndices.end(), [&](const auto &pIndex) {
382  return std::all_of(this->alphabet.begin(), this->alphabet.end(), [&](const auto &action) {
383  const auto successor = this->discreteSuccessors.at(std::make_pair(pIndex, action));
384  return successor < this->prefixes.size();
385  });
386  }));
387  // Assert that we have the information of the closedness in closed relation
388  for (std::size_t pIndex = 0; pIndex < this->prefixes.size(); ++pIndex) {
389  assert(this->inP(pIndex) || std::any_of(this->closedRelation.at(pIndex).begin(),
390  this->closedRelation.at(pIndex).end(),
391  [&](const auto &rPair) {
392  return this->inP(rPair.first);
393  }));
394  }
395  assert(std::all_of(this->pIndices.begin(), this->pIndices.end(), [&](const auto &pIndex) {
396  return this->pIndices.find(this->continuousSuccessors.at(pIndex)) != this->pIndices.end() ||
397  std::any_of(this->closedRelation.at(this->continuousSuccessors.at(pIndex)).begin(),
398  this->closedRelation.at(this->continuousSuccessors.at(pIndex)).end(),
399  [&](const auto &rPair) {
400  return this->inP(rPair.first);
401  });
402  }));
403  assert(std::all_of(this->pIndices.begin(), this->pIndices.end(), [&](const auto &pIndex) {
404  return std::all_of(this->alphabet.begin(), this->alphabet.end(), [&](const auto &action) {
405  const auto successor = this->discreteSuccessors.at(std::make_pair(pIndex, action));
406  return this->inP(successor) || std::any_of(this->closedRelation.at(successor).begin(),
407  this->closedRelation.at(successor).end(),
408  [&](const std::pair<std::size_t, RenamingRelation> &rPair) {
409  return this->inP(rPair.first);
410  });
411  });
412  }));
413  return true;
414  }
415 
416  private:
424  void resolveDiscreteInconsistency(std::size_t i, std::size_t j, Alphabet action) {
425  // Find a single witness of the inconsistency
426  auto it = std::find_if_not(suffixes.begin(), suffixes.end(), [&](const auto &suffix) {
427  return equivalent(i, j, suffix.predecessor(action));
428  });
429  // we assume that we have such a suffix
430  if (it == suffixes.end()) {
431  abort();
432  }
433  const auto newSuffix = it->predecessor(action);
434  LOG_REFINEMENT_INFO << "New suffix " << newSuffix << " is added";
435  suffixes.emplace_back(newSuffix);
436 
437  this->refreshTable();
438  // i and j should not be equivalent after adding the new suffix
439  assert(!this->equivalentWithMemo(i, j));
440  }
441 
451  [[nodiscard]] bool resolveContinuousInconsistency(std::size_t i, std::size_t j) {
452  // Find a single witness of the inconsistency
453  auto it = std::find_if_not(suffixes.begin(), suffixes.end(), [&](const auto &suffix) {
454  return equivalent(i, j, suffix.predecessor());
455  });
456  // We may fail to resolve continuous inconsistency
457  if (it == suffixes.end()) {
458  return false;
459  }
460  const auto newSuffix = it->predecessor();
461  LOG_REFINEMENT_INFO << "New suffix " << newSuffix << " is added";
462  suffixes.emplace_back(newSuffix);
463 
464  this->refreshTable();
465  // i and j should not be equivalent after adding the new suffix
466  assert(!this->equivalentWithMemo(i, j));
467 
468  return true;
469  }
470 
478  static void handleInactiveClocks(std::vector<std::shared_ptr<TAState>> &states);
479 
480  public:
486  std::vector<ElementaryLanguage> P;
487  std::vector<ElementaryLanguage> final;
488  for (std::size_t i = 0; i < this->prefixes.size(); ++i) {
489  if (this->inP(i)) {
490  P.emplace_back(this->prefixes.at(i));
491  if (this->isMatch(i)) {
492  final.emplace_back(this->prefixes.at(i));
493  }
494  }
495  }
496 
497  std::vector<SingleMorphism> morphisms;
498  morphisms.reserve(this->closedRelation.size());
499  for (auto &[i, mapping]: this->closedRelation) {
500  if (!this->inP(i) && !mapping.empty()) {
501  for (auto it = mapping.begin(); it != mapping.end();) {
502  if (this->inP(it->first) && this->equivalentWithMemo(i, it->first)) {
503  morphisms.emplace_back(this->prefixes.at(i),
504  this->prefixes.at(it->first),
505  it->second);
506  break;
507  } else {
508  it = mapping.erase(it);
509  }
510  }
511  }
512  }
513 
514  return RecognizableLanguage{P, final, morphisms};
515  }
516 
522  bool consistent() {
523  for (const auto i: pIndices) {
524  for (const auto j: pIndices) {
525  if (i <= j) {
526  continue;
527  }
528  if (this->equivalentWithMemo(i, j)) {
529  // Check the consistency
530  for (const auto action: this->alphabet) {
531  if (!this->equivalentWithMemo(this->discreteSuccessors.at({i, action}),
532  this->discreteSuccessors.at({j, action}))) {
533  LOG_REFINEMENT_INFO << "Observation table is inconsistent because of the discrete successors of "
534  << this->prefixes.at(i) << " and " << this->prefixes.at(j)
535  << " with action " << action;
536  resolveDiscreteInconsistency(i, j, action);
537  return false;
538  }
539  }
540  if (!this->equivalentWithMemo(this->continuousSuccessors.at(i), this->continuousSuccessors.at(j))
541  && resolveContinuousInconsistency(i, j)) {
542  LOG_REFINEMENT_INFO << "Observation table is inconsistent because of the continuous successors of "
543  << this->prefixes.at(i) << " and " << this->prefixes.at(j);
544  return false;
545  }
546  }
547  }
548  }
549  return true;
550  }
551 
558  std::vector<std::size_t> newP;
559  newP.reserve(pIndices.size());
560  for (const std::size_t pIndex: pIndices) {
561  const auto successorIndex = continuousSuccessors.at(pIndex);
562  // Skip if p is not a boundary
563  if (this->inP(successorIndex)) {
564  continue;
565  }
566  // Skip if p has equality constraints in \f$\mathbb{T}_{i,N}\f$.
567  if (prefixes.at(pIndex).hasEqualityN()) {
568  continue;
569  }
570  LOG_REFINEMENT_INFO << "Observation table is exterior-inconsistent because of "
571  << this->prefixes.at(successorIndex);
572  newP.emplace_back(successorIndex);
573  }
574  if (newP.empty()) {
575  return true;
576  }
577 
578  // update the observation table
579  for (auto newIndex: newP) {
580  this->moveToP(newIndex);
581  }
582  this->refreshTable();
583  return false;
584  }
585 
594  bool timeSaturate() {
595  std::vector<std::size_t> newP;
596  newP.reserve(pIndices.size());
597  for (const std::size_t pIndex: pIndices) {
598  const auto successorIndex = continuousSuccessors.at(pIndex);
599  // Skip if p is not a boundary
600  if (this->inP(successorIndex)) {
601  continue;
602  }
603  if (equivalentWithMemo(successorIndex, pIndex)) {
604  auto it = this->closedRelation.find(successorIndex);
605  assert(it != this->closedRelation.end());
606  auto it2 = it->second.find(pIndex);
607  assert(it2 != it->second.end());
608  if (it2->second.empty()) {
609  // Modify the cache to jump to pIndex to construct a DTA without unobservable transitions
610  it->second = {*it2};
611  continue;
612  } else if (equivalence(this->prefixes.at(successorIndex), this->table.at(successorIndex), this->concatenations.at(successorIndex),
613  this->prefixes.at(pIndex), this->table.at(pIndex), this->concatenations.at(pIndex),
614  suffixes, RenamingRelation{})) {
615  it->second.clear();
616  it->second[pIndex] = RenamingRelation{};
617  continue;
618  }
619  }
620  LOG_REFINEMENT_INFO << "Observation table is not time-saturated because of "
621  << this->prefixes.at(successorIndex);
622  newP.emplace_back(successorIndex);
623  }
624  if (newP.empty()) {
625  return true;
626  }
627 
628  // update the observation table
629  for (auto newIndex: newP) {
630  this->moveToP(newIndex);
631  }
632  this->refreshTable();
633 
634  return false;
635  }
636 
637  bool renameConsistent() {
638  for (auto &[i, mapping]: this->closedRelation) {
639  if (!this->inP(i) && !mapping.empty()) {
640  for (auto it = mapping.begin(); it != mapping.end();) {
641  if (this->inP(it->first) && this->equivalentWithMemo(i, it->first)) {
642  // find a variable that gets imprecise after morphism
643  const auto &[rootIndex, rootRenaming] = *it;
644  const auto rootLanguage = this->prefixes.at(rootIndex);
645  const auto impreciseAfterMorphism = rootRenaming.impreciseClocks(rootLanguage.getTimedCondition());
646  ++it;
647  if (impreciseAfterMorphism.empty()) {
648  continue;
649  }
650  // Confirm that such variables are not used in the renaming relation later
651  std::stack<std::size_t> toSearch;
652  toSearch.push(rootIndex);
653  while(!toSearch.empty()) {
654  const auto currentIndex = toSearch.top();
655  toSearch.pop();
656  if (this->inP(currentIndex)) {
657  // Add the successors
658  toSearch.push(this->continuousSuccessors.at(currentIndex));
659  for (const auto action: this->alphabet) {
660  toSearch.push(this->discreteSuccessors.at(std::make_pair(currentIndex, action)));
661  }
662  } else {
663  const auto currentLanguage = this->prefixes.at(currentIndex);
664  const auto& currentCondition = currentLanguage.getTimedCondition();
665  for (const auto &[indexAfterMap, renaming]: this->closedRelation.at(currentIndex)) {
666  if (this->inP(indexAfterMap) && this->equivalentWithMemo(currentIndex, indexAfterMap)) {
667  // confirm that imprecise variables are not used
668  for (const ClockVariables clock: impreciseAfterMorphism) {
669  if (!currentCondition.isPoint(clock) &&
670  std::any_of(renaming.begin(), renaming.end(), is_second<std::size_t, std::size_t>(clock))) {
671  // clock is imprecise but used!!
672  LOG_REFINEMENT_INFO << "Observation table is renaming inconsistent: x"
673  << static_cast<int>(clock);
674  // Try to extend the suffixes
675  const TimedWord extension = currentLanguage.suffix(rootLanguage).sample();
676  for (const auto &suffix: this->suffixes) {
677  const auto newSuffix = BackwardRegionalElementaryLanguage::fromTimedWord(extension + suffix.sample());
678  if (!equivalent(i, rootIndex, newSuffix, rootRenaming) ||
679  !equivalent(currentIndex, indexAfterMap, newSuffix, renaming)) {
680  LOG_REFINEMENT_INFO << "renameConsistent: New suffix " << newSuffix << " is added";
681  this->suffixes.emplace_back(newSuffix);
682  this->refreshTable();
683  return false;
684  }
685  for (const auto &simple: (currentLanguage + suffix).enumerate()) {
686  const auto newSuffix2 = ForwardRegionalElementaryLanguage::fromTimedWord(simple.sample()).suffix(rootLanguage);
687  if (!equivalent(i, rootIndex, newSuffix2, rootRenaming) ||
688  !equivalent(currentIndex, indexAfterMap, newSuffix2, renaming)) {
689  LOG_REFINEMENT_INFO << "renameConsistent: New suffix " << newSuffix2
690  << " is added";
691  this->suffixes.emplace_back(newSuffix2);
692  this->refreshTable();
693  return false;
694  }
695  }
696  }
697  LOG_REFINEMENT_INFO << "Failed to resolve the renaming inconsistency!!";
698  }
699  }
700  }
701  }
702  }
703  }
704  } else {
705  it = mapping.erase(it);
706  }
707  }
708  }
709  }
710  return true;
711  }
712 
717  void handleCEX(const TimedWord &cex) {
718  auto newSuffixOpt = analyzeCEX(cex, *this->memOracle, this->toRecognizable(), this->suffixes);
719  if (newSuffixOpt) {
720  LOG_REFINEMENT_INFO << "New suffix " << *newSuffixOpt << " is added";
721  auto newSuffix = BackwardRegionalElementaryLanguage::fromTimedWord(*newSuffixOpt);
722  suffixes.emplace_back(std::move(newSuffix));
723  this->refreshTable();
724  } else {
725  LOG_REFINEMENT_INFO << "Failed to find a new suffix. We add prefixes to P";
726  const auto newPrefixes = ForwardRegionalElementaryLanguage::fromTimedWord(cex).prefixes();
727  bool updated = false;
728  for (const auto &newPrefix: newPrefixes) {
729  auto it = std::find(this->prefixes.begin(), this->prefixes.end(), newPrefix);
730  assert(it != this->prefixes.end());
731  if (this->inP(std::distance(this->prefixes.begin(), it))) {
732  continue;
733  } else {
734  updated = true;
735  this->moveToP(std::distance(this->prefixes.begin(), it));
736  if (!this->close()) {
737  // The observation table is refined
738  return;
739  }
740  }
741  }
742  if (!updated) {
743  BOOST_LOG_TRIVIAL(error) << "Learning has got stuck!!";
744  abort();
745  }
746  }
747  }
748 
752  void optimizeTarget() {
753  for (std::size_t source = 0; source < this->prefixes.size(); ++source) {
754  if (this->inP(source)) {
755  continue;
756  }
757  const auto mapping = this->closedRelation.at(source);
758  bool withPreciseMapping = false;
759  // Find a mapping with no imprecise clocks
760  for (const auto &[target, renaming]: mapping) {
761  if (this->inP(target) && !renaming.hasImpreciseClocks(this->prefixes.at(target).getTimedCondition())) {
762  withPreciseMapping = true;
763  if (std::make_pair(target, renaming) != std::make_pair(mapping.begin()->first, mapping.begin()->second)) {
764  BOOST_LOG_TRIVIAL(debug) << "Optimized the target: " << renaming;
765  BOOST_LOG_TRIVIAL(debug) << "Before: " << this->closedRelation.at(source).begin()->second;
766  this->closedRelation.at(source) = {std::make_pair(target, renaming)};
767  BOOST_LOG_TRIVIAL(debug) << "After: " << this->closedRelation.at(source).begin()->second;
768  }
769  break;
770  }
771  }
772  if (withPreciseMapping) {
773  continue;
774  }
775  for (const auto target: pIndices) {
776  const auto renaming = this->equivalentWithMemo(source, target);
777  if (renaming && !renaming->hasImpreciseClocks(this->prefixes.at(target).getTimedCondition())) {
778  BOOST_LOG_TRIVIAL(debug) << "Optimized the target: " << *renaming;
779  BOOST_LOG_TRIVIAL(debug) << "Before: " << this->closedRelation.at(source).begin()->second;
780  this->closedRelation.at(source) = {std::make_pair(target, *renaming)};
781  BOOST_LOG_TRIVIAL(debug) << "After: " << this->closedRelation.at(source).begin()->second;
782  break;
783  }
784  }
785  BOOST_LOG_TRIVIAL(debug) << "Failed to optimize the target";
786  }
787  }
788 
792  class StateManager {
793  // map from the index in this->prefix to the corresponding state
794  std::unordered_map<std::size_t, std::shared_ptr<TAState>> indexToState;
795  std::unordered_map<std::shared_ptr<TAState>, std::vector<std::size_t>> stateToIndices;
796  public:
797  [[nodiscard]] std::shared_ptr<TAState> toState(const std::size_t index) const {
798  return indexToState.at(index);
799  }
800 
801  [[nodiscard]] std::vector<std::size_t> toIndices(const std::shared_ptr<TAState> &state) const {
802  return stateToIndices.at(state);
803  }
804 
805  [[nodiscard]] bool isNew(const std::shared_ptr<TAState> &state) const {
806  auto it = stateToIndices.find(state);
807  return it == stateToIndices.end();
808  }
809 
810  [[nodiscard]] bool isNew(std::size_t index) const {
811  auto it = indexToState.find(index);
812  return it == indexToState.end();
813  }
814 
815  void add(const std::shared_ptr<TAState> &state, const std::size_t index) {
816  indexToState[index] = state;
817  auto it = stateToIndices.find(state);
818  if (it == stateToIndices.end()) {
819  stateToIndices[state] = {index};
820  } else {
821  it->second.emplace_back(index);
822  }
823  }
824  };
825 
833  // this->optimizeTarget();
834  StateManager stateManager;
835  std::vector<std::shared_ptr<TAState>> states;
836 
843  auto addState = [&](std::size_t index) {
844  auto state = std::make_shared<TAState>(this->isMatch(index));
845  assert(stateManager.isNew(state));
846  stateManager.add(state, index);
847  states.emplace_back(state);
848 
849  return state;
850  };
851 
858  const auto mergeContinuousSuccessors = [&](const std::size_t initialSourceIndex) {
859  auto sourceIndex = initialSourceIndex;
860  const auto state = stateManager.toState(initialSourceIndex);
861 
862  auto nextIndex = this->continuousSuccessors.at(sourceIndex);
863  // Include all the continuous successors to the state
864  while (this->inP(nextIndex)) {
865  if (isMatch(sourceIndex) == isMatch(nextIndex)) {
866  stateManager.add(state, nextIndex);
867  } else {
868  BOOST_LOG_TRIVIAL(error)
869  << "We have not implemented such a case that an unobservable transition is necessary";
870  abort();
871  }
872  sourceIndex = nextIndex;
873  nextIndex = this->continuousSuccessors.at(nextIndex);
874  }
875 
876  // Our optimization to merge the continuous exterior
877  if (isMatch(sourceIndex) == isMatch(nextIndex)) {
878  stateManager.add(state, nextIndex);
879  } else {
880  BOOST_LOG_TRIVIAL(error)
881  << "We have not implemented such a case that an unobservable transition is necessary";
882  abort();
883  }
884  };
885 
886  auto initialState = addState(0);
887  // construct the initial state
888  mergeContinuousSuccessors(0);
889  // vector of states and actions such that the discrete successor is not in P
890  std::vector<std::pair<std::size_t, Alphabet>> discreteBoundaries;
891 
892  // explore new states
893  std::queue<std::shared_ptr<TAState>> newStates;
894  newStates.push(initialState);
895  while (!newStates.empty()) {
896  auto newState = newStates.front();
897  newStates.pop();
898  const auto newStateIndices = stateManager.toIndices(newState);
899  for (const auto action: alphabet) {
900  // TargetState -> the timed condition to reach that state
901  InternalTransitionMaker sourceMap;
902  for (const auto &newStateIndex: newStateIndices) {
903 #ifdef DEBUG
904  BOOST_LOG_TRIVIAL(trace) << "Start exploration of the discrete successor from the prefix "
905  << this->prefixes.at(newStateIndex) << " with action " << action;
906 #endif
907 
908  // Skip if there is no discrete successor in the observation table
909  if (!this->hasDiscreteSuccessor(newStateIndex, action)) {
910 #ifdef DEBUG
911  BOOST_LOG_TRIVIAL(trace) << "No discrete successor";
912 #endif
913  continue;
914  }
915  // q' in the following diagram
916  const auto discrete = this->discreteSuccessors.at(std::make_pair(newStateIndex, action));
917  // Add states only if the successor is also in P
918  if (!this->inP(discrete)) {
919 #ifdef DEBUG
920  BOOST_LOG_TRIVIAL(trace) << "The discrete successor is not in P";
921 #endif
922  discreteBoundaries.emplace_back(newStateIndex, action);
923  } else {
924 #ifdef DEBUG
925  BOOST_LOG_TRIVIAL(trace) << "The discrete successor is in P";
926 #endif
927  if (stateManager.isNew(discrete)) {
928 #ifdef DEBUG
929  BOOST_LOG_TRIVIAL(trace) << "The discrete successor is new";
930 #endif
931  const auto successor = addState(discrete);
932  newStates.push(successor);
933 #ifdef DEBUG
934  BOOST_LOG_TRIVIAL(trace) << "Generate discrete transitions from " << this->prefixes.at(newStateIndex)
935  << " with action " << action;
936  BOOST_LOG_TRIVIAL(trace) << "Source: " << stateManager.toState(newStateIndex);
937  BOOST_LOG_TRIVIAL(trace) << "Guard: " << this->prefixes.at(newStateIndex).getTimedCondition().toGuard();
938  BOOST_LOG_TRIVIAL(trace) << "Target: " << successor;
939 #endif
940  sourceMap.add(successor, this->prefixes.at(newStateIndex).getTimedCondition());
941 #ifdef DEBUG
942  BOOST_LOG_TRIVIAL(trace) << "The new state: " << successor.get();
943 #endif
944  }
945  if (this->hasContinuousSuccessor(discrete)) {
946 #ifdef DEBUG
947  BOOST_LOG_TRIVIAL(trace) << "The discrete successor has continuous successors";
948 #endif
949  mergeContinuousSuccessors(discrete);
950  }
951  }
952  }
953 
954  // Make transitions
955  if (!sourceMap.empty()) {
956  auto newTransitions = sourceMap.make();
957  newState->next[action].reserve(newState->next[action].size() + newTransitions.size());
958  std::move(newTransitions.begin(), newTransitions.end(), std::back_inserter(newState->next[action]));
959  }
960  }
961  }
962 
963  ImpreciseClockHandler impreciseNeighbors;
965  std::sort(discreteBoundaries.begin(), discreteBoundaries.end());
966  discreteBoundaries.erase(std::unique(discreteBoundaries.begin(), discreteBoundaries.end()),
967  discreteBoundaries.end());
968  for (const auto &[sourceIndex, action]: discreteBoundaries) {
969 #ifdef DEBUG
970  BOOST_LOG_TRIVIAL(debug) << "Constructing a transition from: " << this->prefixes.at(sourceIndex)
971  << " with action " << action;
972 #endif
973  ExternalTransitionMaker transitionMaker;
974  const auto addNewTransition = [&](std::size_t source, std::size_t jumpedTarget, std::size_t target,
975  const auto &renamingRelation) {
976  auto jumpedState = stateManager.toState(jumpedTarget);
977  transitionMaker.add(jumpedState, renamingRelation,
978  this->prefixes.at(source).getTimedCondition(),
979  this->prefixes.at(jumpedTarget).getTimedCondition());
980  impreciseNeighbors.push(jumpedState.get(), renamingRelation, this->prefixes.at(jumpedTarget));
981  if (stateManager.isNew(target)) {
982  stateManager.add(jumpedState, target);
983  }
984  };
985  // The target state of the transitions, which should be in ext(P)
986  const auto targetIndex = this->discreteSuccessors.at(std::make_pair(sourceIndex, action));
987  if (!stateManager.isNew(targetIndex)) {
988 #ifdef DEBUG
989  BOOST_LOG_TRIVIAL(trace) << "The boundary is already handled: " << this->prefixes.at(sourceIndex) << " "
990  << action;
991 #endif
992  continue;
993  }
994  assert(!this->inP(targetIndex));
995  // Find a successor in P
996  auto it = std::find_if(this->closedRelation.at(targetIndex).begin(),
997  this->closedRelation.at(targetIndex).end(), [&](const auto &rel) {
998  return this->inP(rel.first);
999  });
1000  assert(it != this->closedRelation.at(targetIndex).end());
1001  // The target state of the transitions after mapping to P.
1002  const auto jumpedTargetIndex = it->first;
1003  // The renaming relation connecting targetIndex and jumpedTargetIndex
1004  RenamingRelation renamingRelation = it->second;
1005 #ifdef DEBUG
1006  BOOST_LOG_TRIVIAL(debug) << "source: " << this->prefixes.at(sourceIndex);
1007  BOOST_LOG_TRIVIAL(debug) << "action: " << action;
1008  BOOST_LOG_TRIVIAL(debug) << "target: " << this->prefixes.at(jumpedTargetIndex);
1009  BOOST_LOG_TRIVIAL(debug) << "renaming: " << renamingRelation;
1010 #endif
1011 
1012  // renamingRelation should not have the last variable on the left hand side.
1013  renamingRelation.eraseLeft(this->prefixes.at(sourceIndex).getTimedCondition().size());
1014  addNewTransition(sourceIndex, jumpedTargetIndex, targetIndex, renamingRelation);
1015 
1016  auto newTransitions = transitionMaker.make();
1017  if (!newTransitions.empty()) {
1018  stateManager.toState(sourceIndex)->next[action].reserve(
1019  stateManager.toState(sourceIndex)->next[action].size() + newTransitions.size());
1020  std::move(newTransitions.begin(), newTransitions.end(),
1021  std::back_inserter(stateManager.toState(sourceIndex)->next[action]));
1022  }
1023  }
1024 
1026  for (const auto source: this->pIndices) {
1027  const auto continuousSuccessor = this->continuousSuccessors.at(source);
1028  if (this->inP(continuousSuccessor)) {
1029  continue;
1030  }
1031  const auto sourceState = stateManager.toState(continuousSuccessor);
1032  // Find a successor in P
1033  const auto it = std::find_if(this->closedRelation.at(continuousSuccessor).begin(),
1034  this->closedRelation.at(continuousSuccessor).end(), [&](const auto &rel) {
1035  return this->inP(rel.first);
1036  });
1037  assert(it != this->closedRelation.at(continuousSuccessor).end());
1038  // The continuous successor after mapping to P.
1039  const auto jumpedSourceIndex = it->first;
1040  const auto jumpedSourceState = stateManager.toState(jumpedSourceIndex);
1041  const auto jumpedSourceCondition = this->prefixes.at(jumpedSourceIndex).getTimedCondition();
1042  // addInactiveClocks(jumpedSourceState.get(), it->second, jumpedSourceCondition);
1043  impreciseNeighbors.push(jumpedSourceState.get(), it->second, this->prefixes.at(jumpedSourceIndex));
1044  // We project to the non-exterior area
1045  const auto nonExteriorValuation = ExternalTransitionMaker::toValuation(jumpedSourceCondition);
1046  TATransition::Resets resetByContinuousExterior;
1047  for (std::size_t var = 0; var < nonExteriorValuation.size(); ++var) {
1048  resetByContinuousExterior.emplace_back(var, nonExteriorValuation.at(var));
1049  }
1050  if (sourceState == jumpedSourceState) {
1051  // Merge to the next discrete successor if it is a self loop
1052  for (const auto action: alphabet) {
1053  const auto jumpedSourceInvariant = this->prefixes.at(jumpedSourceIndex).getTimedCondition().toGuard();
1054  // Find a transition consistent with jumpedSourceInvariant
1055  auto transitionIt = std::find_if(jumpedSourceState->next.at(action).begin(),
1056  jumpedSourceState->next.at(action).end(),
1057  [&](const TATransition &transition) {
1058  return isWeaker(transition.guard, jumpedSourceInvariant);
1059  });
1060  assert(transitionIt != jumpedSourceState->next.at(action).end());
1061 #ifdef DEBUG
1062  BOOST_LOG_TRIVIAL(debug) << "source: " << sourceState;
1063  BOOST_LOG_TRIVIAL(debug) << "jumpedSource: " << jumpedSourceState;
1064  BOOST_LOG_TRIVIAL(debug) << "target: " << transitionIt->target;
1065  BOOST_LOG_TRIVIAL(debug) << "resetByContinuousExterior: " << resetByContinuousExterior;
1066  BOOST_LOG_TRIVIAL(debug) << "resetByTransition: " << transitionIt->resetVars;
1067  BOOST_LOG_TRIVIAL(debug) << "composition: "
1068  << composition(transitionIt->resetVars, resetByContinuousExterior);
1069 #endif
1070  const auto newReset = composition(transitionIt->resetVars, resetByContinuousExterior);
1071  // Add neighbors if we have imprecise clocks
1072  RenamingRelation renaming;
1073  renaming.reserve(newReset.size());
1074  for (const auto &[target, value]: newReset) {
1075  if (value.index() == 1) {
1076  renaming.emplace_back(std::make_pair(std::get<ClockVariables>(value), target));
1077  }
1078  }
1079  if (this->inP(this->discreteSuccessors.at(std::make_pair(jumpedSourceIndex, action)))) {
1080  impreciseNeighbors.push(transitionIt->target, renaming,
1081  this->prefixes.at(this->discreteSuccessors.at(std::make_pair(jumpedSourceIndex,
1082  action))));
1083  } else {
1084  const auto &map = this->closedRelation.at(
1085  this->discreteSuccessors.at(std::make_pair(jumpedSourceIndex, action)));
1086  for (const auto &[mappedIndex, relation]: map) {
1087  if (this->inP(mappedIndex)) {
1088  impreciseNeighbors.push(transitionIt->target, renaming, this->prefixes.at(mappedIndex));
1089  break;
1090  }
1091  }
1092  }
1093  sourceState->next.at(action).emplace_back(transitionIt->target, newReset,
1094  this->prefixes.at(continuousSuccessor)
1095  .removeUpperBound().getTimedCondition().toGuard());
1096  }
1097  } else {
1099  maker.add(jumpedSourceState, it->second,
1100  this->prefixes.at(continuousSuccessor).removeUpperBound().getTimedCondition(),
1101  jumpedSourceCondition);
1102  assert(maker.make().size() == 1);
1103  // TODO: Consider adding neighbors
1104  sourceState->next[UNOBSERVABLE].emplace_back(maker.make().front());
1105  }
1106  }
1107 #ifdef DEBUG
1108  BOOST_LOG_TRIVIAL(debug) << "as recognizable: " << this->toRecognizable();
1109  BOOST_LOG_TRIVIAL(info) << "Hypothesis before handling imprecise clocks\n" <<
1110  TimedAutomaton{{states, {initialState}},
1111  TimedAutomaton::makeMaxConstants(states)}.simplify();
1112 #endif
1113  impreciseNeighbors.run();
1114 #ifdef DEBUG
1115  BOOST_LOG_TRIVIAL(debug) << "Hypothesis after handling imprecise clocks\n" <<
1116  TimedAutomaton{{states, {initialState}},
1117  TimedAutomaton::makeMaxConstants(states)}.simplify();
1118  BOOST_LOG_TRIVIAL(debug) << "as recognizable: " << this->toRecognizable();
1119 #endif
1120  // Make the transitions deterministic
1121  std::vector<TAState *> needSplit;
1122  needSplit.reserve(states.size());
1123  for (const auto &state: states) {
1124  if (state->needSplitting()) {
1125  needSplit.emplace_back(state.get());
1126  }
1127  }
1128 #ifdef DEBUG
1129  BOOST_LOG_TRIVIAL(debug) << "Hypothesis before state splitting\n"
1130  << TimedAutomaton{{states, {initialState}},
1131  TimedAutomaton::makeMaxConstants(states)}.simplify();
1132 #endif
1133  // Conduct state splitting if necessary
1134  if (!needSplit.empty()) {
1135  BOOST_LOG_TRIVIAL(info) << "We conduct state splitting to keep it deterministic";
1136  BOOST_LOG_TRIVIAL(info) << "# of states before splitting: " << states.size();
1137  learnta::ObservationTable::splitStates(states, initialState, needSplit);
1138  BOOST_LOG_TRIVIAL(info) << "# of states after splitting: " << states.size();
1139 #ifdef DEBUG
1140  BOOST_LOG_TRIVIAL(debug) << "Hypothesis after state splitting\n"
1141  << TimedAutomaton{{states, {initialState}},
1142  TimedAutomaton::makeMaxConstants(states)}.simplify();
1143 #endif
1144  }
1145  for (const auto& state: states) {
1146  for (auto& [action, transitions]: state->next) {
1147  // sort the order of the resets
1148  for (TATransition& transition: transitions) {
1149  std::sort(transition.resetVars.begin(), transition.resetVars.end(), [&] (const auto& left, const auto& right) {
1150  return left.first < right.first;
1151  });
1152  }
1153  // Merge adjacent transitions
1154  for (auto it = transitions.begin(); it != transitions.end(); ++it) {
1155  for (auto it2 = std::next(it); it2 != transitions.end();) {
1156  if (it->mergeable(*it2)) {
1157  *it = it->merge(*it2);
1158  it2 = transitions.erase(it2);
1159  } else {
1160  ++it2;
1161  }
1162  }
1163  }
1164 #if 0
1165  // Take union hull
1166  // We need to debug the handling of reset variables
1167  for (auto it = transitions.begin(); it != transitions.end(); ++it) {
1168  for (auto it2 = std::next(it); it2 != transitions.end();) {
1169  if (satisfiable(conjunction(it->guard, it2->guard))) {
1170  if (it->target != it2->target || it->resetVars != it2->resetVars) {
1171  BOOST_LOG_TRIVIAL(error) << "it->target: " << it->target;
1172  BOOST_LOG_TRIVIAL(error) << "it2->target: " << it2->target;
1173  BOOST_LOG_TRIVIAL(error) << "it->resetVars: " << it->resetVars;
1174  BOOST_LOG_TRIVIAL(error) << "it2->resetVars: " << it2->resetVars;
1175  throw std::logic_error("error in transition merging");
1176  }
1177  it->guard = unionHull(it->guard, it2->guard);
1178  it2 = transitions.erase(it2);
1179  } else {
1180  ++it2;
1181  }
1182  }
1183  }
1184 #endif
1185  }
1186  state->removeTransitionsWithWeakerGuards();
1187  state->mergeNondeterministicBranching();
1188  }
1189 #ifdef DEBUG
1190  BOOST_LOG_TRIVIAL(debug) << "Hypothesis after making transitions deterministic\n" <<
1191  TimedAutomaton{{states, {initialState}},
1192  TimedAutomaton::makeMaxConstants(states)}.simplify();
1193 #endif
1194 
1195  // Assert the totality of the constructed DTA
1196  assert(std::all_of(this->pIndices.begin(), this->pIndices.end(), [&](std::size_t pIndex) {
1197  return !stateManager.isNew(pIndex);
1198  }));
1199  #if 0
1200  assert(std::all_of(this->discreteSuccessors.begin(), this->discreteSuccessors.end(), [&](const auto &pair) {
1201  auto sourceStateIndex = pair.first.first;
1202  auto action = pair.first.second;
1203  auto targetStateIndex = pair.second;
1204  auto targetState = stateManager.toState(targetStateIndex);
1205  return stateManager.toState(sourceStateIndex)->next[action].end() !=
1206  std::find_if(stateManager.toState(sourceStateIndex)->next[action].begin(),
1207  stateManager.toState(sourceStateIndex)->next[action].end(),
1208  [&](const TATransition &transition) {
1209  return transition.target == targetState.get();
1210  });
1211  }));
1212  #endif
1213  for (std::size_t index = 0; index < this->prefixes.size(); index++) {
1214  if (stateManager.isNew(index)) {
1215  BOOST_LOG_TRIVIAL(warning) << "Partial transitions is detected";
1216  abort();
1217  }
1218  }
1219 
1220  return TimedAutomaton{{states, {initialState}}, TimedAutomaton::makeMaxConstants(states)}.simplify();
1221  }
1222 
1223  std::ostream &printDetail(std::ostream &stream) const {
1224  printStatistics(stream);
1225  stream << "P is as follows\n";
1226  for (const auto &pIndex: this->pIndices) {
1227  stream << this->prefixes.at(pIndex) << "\n";
1228  }
1229  stream << "S is as follows\n";
1230  for (const auto &suffix: this->suffixes) {
1231  stream << suffix << "\n";
1232  }
1233 
1234  return stream;
1235  }
1236 
1237  std::ostream &printStatistics(std::ostream &stream) const {
1238  stream << "|P| = " << this->pIndices.size() << "\n";
1239  stream << "|ext(P)| = " << this->prefixes.size() - this->pIndices.size() << "\n";
1240  stream << "|S| = " << this->suffixes.size() << "\n";
1241 
1242  return this->memOracle->printStatistics(stream);
1243  }
1244  };
1245 }
A back regional elementary language.
Definition: backward_regional_elementary_language.hh:22
A class to make a transition from P to ext(P)
Definition: external_transition_maker.hh:27
std::vector< TATransition > make() const
Generate transitions.
Definition: external_transition_maker.hh:84
void add(const std::shared_ptr< TAState > &targetState, const RenamingRelation &renamingRelation, const TimedCondition &sourceCondition, const TimedCondition &targetCondition)
Add a transition to targetState.
Definition: external_transition_maker.hh:68
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
A class to make a transition from P to P.
Definition: internal_transition_maker.hh:24
void add(const std::shared_ptr< TAState > &targetState, const TimedCondition &sourceCondition)
Add a transition to targetState.
Definition: internal_transition_maker.hh:36
std::vector< TATransition > make() const
Generate transitions.
Definition: internal_transition_maker.hh:64
The class to manage the correspondence between states and indices.
Definition: observation_table.hh:792
Timed observation table.
Definition: observation_table.hh:43
bool exteriorConsistent()
Make the observation table exterior-consistent.
Definition: observation_table.hh:557
bool timeSaturate()
Make the observation table time-saturated.
Definition: observation_table.hh:594
TimedAutomaton generateHypothesis()
Construct a hypothesis DTA from the current timed observation table.
Definition: observation_table.hh:832
void handleCEX(const TimedWord &cex)
Refine the suffixes by the given counterexample.
Definition: observation_table.hh:717
bool close()
Close the observation table.
Definition: observation_table.hh:317
ObservationTable(std::vector< Alphabet > alphabet, std::unique_ptr< SymbolicMembershipOracle > memOracle)
Initialize the observation table.
Definition: observation_table.hh:301
void optimizeTarget()
Optimize the morphisms to reduce the imprecise clocks.
Definition: observation_table.hh:752
bool consistent()
Make the observation table consistent.
Definition: observation_table.hh:522
RecognizableLanguage toRecognizable()
Construct a recognizable timed language corresponding to the observation table.
Definition: observation_table.hh:485
Recognizable timed language in [MP04].
Definition: recognizable_languages.hh:18
Definition: renaming_relation.hh:16
void eraseLeft(std::size_t left)
Erase pairs such that (left, right) for some right.
Definition: renaming_relation.hh:105
A timed word.
Definition: timed_word.hh:25
This file implements functions on the equivalence relation defined by the distinguishing suffixes.
Definition: experiment_runner.hh:23
static bool equivalence(const ElementaryLanguage &left, const std::vector< TimedConditionSet > &leftRow, const ElementaryLanguage &right, const std::vector< TimedConditionSet > &rightRow, const std::vector< BackwardRegionalElementaryLanguage > &suffixes, const RenamingRelation &renaming)
Return if two elementary languages are equivalent.
Definition: equivalence.hh:22
static std::optional< RenamingRelation > findDeterministicEquivalentRenaming(const ElementaryLanguage &left, const std::vector< TimedConditionSet > &leftRow, const ElementaryLanguage &right, const std::vector< TimedConditionSet > &rightRow, const std::vector< BackwardRegionalElementaryLanguage > &suffixes)
Return a renaming constraint if two elementary languages are equivalent.
Definition: equivalence.hh:365
Definition: external_transition_maker.hh:149
A state of timed automata.
Definition: timed_automaton.hh:82
A timed automaton.
Definition: timed_automaton.hh:213
TimedAutomaton simplify()
Simplify the timed automaton.
Definition: timed_automaton.hh:600