13 #include <unordered_map>
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>
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"
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"
33 #ifdef PRINT_REFINEMENT_INFO
34 #define LOG_REFINEMENT_INFO BOOST_LOG_TRIVIAL(info)
36 #define LOG_REFINEMENT_INFO BOOST_LOG_TRIVIAL(debug)
45 std::unique_ptr<SymbolicMembershipOracle> memOracle;
46 std::vector<Alphabet> alphabet;
47 std::vector<ForwardRegionalElementaryLanguage> prefixes;
48 std::vector<BackwardRegionalElementaryLanguage> suffixes;
50 std::vector<std::vector<TimedCondition>> concatenations;
52 std::unordered_set<std::size_t> pIndices;
55 std::unordered_map<std::size_t, std::unordered_map<std::size_t, RenamingRelation>> closedRelation;
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;
61 boost::unordered_set<std::pair<std::size_t, std::size_t>> distinguishedPrefix;
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();
94 void moveToP(
const std::size_t index) {
96 assert(!this->inP(index));
98 assert(index < prefixes.size());
99 pIndices.insert(index);
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));
106 continuousSuccessors[index] = prefixes.size();
107 prefixes.emplace_back(prefixes.at(index).successor());
111 assert(this->inP(index));
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();
117 assert(std::all_of(alphabet.begin(), alphabet.end(), [&](Alphabet c) {
118 return !this->inP(discreteSuccessors.at(std::make_pair(index, c)));
121 assert(continuousSuccessors.at(index) < prefixes.size());
122 assert(!this->inP(continuousSuccessors.at(index)));
124 if (!equivalentWithMemo(continuousSuccessors.at(index), index)) {
125 this->moveToP(continuousSuccessors.at(index));
129 std::optional<RenamingRelation> equivalent(std::size_t i, std::size_t j) {
131 this->prefixes.at(j), this->table.at(j), this->concatenations.at(j),
133 if (renamingRelation) {
134 this->closedRelation[i][j] = renamingRelation.value();
135 return renamingRelation;
137 this->distinguishedPrefix.insert(std::make_pair(i, j));
142 std::optional<RenamingRelation> equivalentWithMemo(std::size_t i, std::size_t j) {
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()) {
147 assert(!this->equivalent(i, j));
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)) {
162 return this->equivalent(i, j);
165 boost::unordered_map<std::tuple<std::size_t, std::size_t, BackwardRegionalElementaryLanguage>, std::pair<std::size_t, bool>> equivalentWithColumnCache;
172 auto key = std::make_tuple(i, j, newSuffix);
174 auto it = equivalentWithColumnCache.find(key);
175 if (it != equivalentWithColumnCache.end() && this->suffixes.size() == it->second.first) {
176 return it->second.second;
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);
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);
206 this->prefixes.at(j), rightRow, rightConcatenations,
207 newSuffixes).has_value();
208 equivalentWithColumnCache[key] = std::make_pair(this->suffixes.size(), result);
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);
233 return equivalence(leftPrefix, leftRow, leftConcatenation,
234 rightPrefix, rightRow, rightConcatenation,
235 newSuffixes, renaming);
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);
254 this->prefixes.at(j), rightRow,
255 tmpSuffixes).has_value();
262 [[nodiscard]]
bool isMatch(std::size_t i)
const {
263 return !this->table.at(i).at(0).empty();
269 [[nodiscard]]
bool inP(std::size_t i)
const {
270 return this->pIndices.find(i) != this->pIndices.end();
276 [[nodiscard]]
bool hasDiscreteSuccessor(std::size_t i, Alphabet c)
const {
277 return this->discreteSuccessors.find(std::make_pair(i, c)) != this->discreteSuccessors.end();
283 [[nodiscard]]
bool hasContinuousSuccessor(std::size_t i)
const {
284 return this->continuousSuccessors.find(i) != this->continuousSuccessors.end();
293 static void splitStates(std::vector<std::shared_ptr<TAState>> &originalStates,
294 std::shared_ptr<TAState> &initialState,
295 const std::vector<TAState *> &needSplit);
301 ObservationTable(std::vector<Alphabet> alphabet, std::unique_ptr<SymbolicMembershipOracle> memOracle) :
302 memOracle(
std::move(memOracle)),
303 alphabet(
std::move(alphabet)),
307 this->refreshTable();
318 for (std::size_t i = 0; i < this->prefixes.size(); i++) {
323 auto prefix = this->prefixes.at(i);
324 auto prefixRow = this->table.at(i);
329 auto it = this->closedRelation.find(i);
330 if (it != closedRelation.end()) {
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)) {
345 targetIt = it->second.erase(targetIt);
354 found = std::any_of(this->pIndices.begin(), this->pIndices.end(), [&](
const auto j) {
355 return this->continuousSuccessors.at(j) == i && equivalentWithMemo(i, j);
358 found = std::any_of(this->pIndices.begin(), this->pIndices.end(), [&](
const auto j) {
359 return equivalentWithMemo(i, j);
366 const auto prePSize = this->pIndices.size();
369 assert(prePSize < this->pIndices.size());
370 LOG_REFINEMENT_INFO <<
"Observation table is not closed because of " << this->prefixes.at(i);
371 this->refreshTable();
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();
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();
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);
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);
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);
424 void resolveDiscreteInconsistency(std::size_t i, std::size_t j, Alphabet action) {
426 auto it = std::find_if_not(suffixes.begin(), suffixes.end(), [&](
const auto &suffix) {
427 return equivalent(i, j, suffix.predecessor(action));
430 if (it == suffixes.end()) {
433 const auto newSuffix = it->predecessor(action);
434 LOG_REFINEMENT_INFO <<
"New suffix " << newSuffix <<
" is added";
435 suffixes.emplace_back(newSuffix);
437 this->refreshTable();
439 assert(!this->equivalentWithMemo(i, j));
451 [[nodiscard]]
bool resolveContinuousInconsistency(std::size_t i, std::size_t j) {
453 auto it = std::find_if_not(suffixes.begin(), suffixes.end(), [&](
const auto &suffix) {
454 return equivalent(i, j, suffix.predecessor());
457 if (it == suffixes.end()) {
460 const auto newSuffix = it->predecessor();
461 LOG_REFINEMENT_INFO <<
"New suffix " << newSuffix <<
" is added";
462 suffixes.emplace_back(newSuffix);
464 this->refreshTable();
466 assert(!this->equivalentWithMemo(i, j));
478 static void handleInactiveClocks(std::vector<std::shared_ptr<TAState>> &states);
486 std::vector<ElementaryLanguage> P;
487 std::vector<ElementaryLanguage>
final;
488 for (std::size_t i = 0; i < this->prefixes.size(); ++i) {
490 P.emplace_back(this->prefixes.at(i));
491 if (this->isMatch(i)) {
492 final.emplace_back(this->prefixes.at(i));
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),
508 it = mapping.erase(it);
523 for (
const auto i: pIndices) {
524 for (
const auto j: pIndices) {
528 if (this->equivalentWithMemo(i, j)) {
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);
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);
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);
563 if (this->inP(successorIndex)) {
567 if (prefixes.at(pIndex).hasEqualityN()) {
570 LOG_REFINEMENT_INFO <<
"Observation table is exterior-inconsistent because of "
571 << this->prefixes.at(successorIndex);
572 newP.emplace_back(successorIndex);
579 for (
auto newIndex: newP) {
580 this->moveToP(newIndex);
582 this->refreshTable();
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);
600 if (this->inP(successorIndex)) {
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()) {
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),
620 LOG_REFINEMENT_INFO <<
"Observation table is not time-saturated because of "
621 << this->prefixes.at(successorIndex);
622 newP.emplace_back(successorIndex);
629 for (
auto newIndex: newP) {
630 this->moveToP(newIndex);
632 this->refreshTable();
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)) {
643 const auto &[rootIndex, rootRenaming] = *it;
644 const auto rootLanguage = this->prefixes.at(rootIndex);
645 const auto impreciseAfterMorphism = rootRenaming.impreciseClocks(rootLanguage.getTimedCondition());
647 if (impreciseAfterMorphism.empty()) {
651 std::stack<std::size_t> toSearch;
652 toSearch.push(rootIndex);
653 while(!toSearch.empty()) {
654 const auto currentIndex = toSearch.top();
656 if (this->inP(currentIndex)) {
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)));
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)) {
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))) {
672 LOG_REFINEMENT_INFO <<
"Observation table is renaming inconsistent: x"
673 <<
static_cast<int>(clock);
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();
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
691 this->suffixes.emplace_back(newSuffix2);
692 this->refreshTable();
697 LOG_REFINEMENT_INFO <<
"Failed to resolve the renaming inconsistency!!";
705 it = mapping.erase(it);
718 auto newSuffixOpt = analyzeCEX(cex, *this->memOracle, this->toRecognizable(), this->suffixes);
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();
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))) {
735 this->moveToP(std::distance(this->prefixes.begin(), it));
736 if (!this->close()) {
743 BOOST_LOG_TRIVIAL(error) <<
"Learning has got stuck!!";
753 for (std::size_t source = 0; source < this->prefixes.size(); ++source) {
754 if (this->inP(source)) {
757 const auto mapping = this->closedRelation.at(source);
758 bool withPreciseMapping =
false;
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;
772 if (withPreciseMapping) {
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;
785 BOOST_LOG_TRIVIAL(debug) <<
"Failed to optimize the target";
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;
797 [[nodiscard]] std::shared_ptr<TAState> toState(
const std::size_t index)
const {
798 return indexToState.at(index);
801 [[nodiscard]] std::vector<std::size_t> toIndices(
const std::shared_ptr<TAState> &state)
const {
802 return stateToIndices.at(state);
805 [[nodiscard]]
bool isNew(
const std::shared_ptr<TAState> &state)
const {
806 auto it = stateToIndices.find(state);
807 return it == stateToIndices.end();
810 [[nodiscard]]
bool isNew(std::size_t index)
const {
811 auto it = indexToState.find(index);
812 return it == indexToState.end();
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};
821 it->second.emplace_back(index);
835 std::vector<std::shared_ptr<TAState>> states;
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);
858 const auto mergeContinuousSuccessors = [&](
const std::size_t initialSourceIndex) {
859 auto sourceIndex = initialSourceIndex;
860 const auto state = stateManager.toState(initialSourceIndex);
862 auto nextIndex = this->continuousSuccessors.at(sourceIndex);
864 while (this->inP(nextIndex)) {
865 if (isMatch(sourceIndex) == isMatch(nextIndex)) {
866 stateManager.add(state, nextIndex);
868 BOOST_LOG_TRIVIAL(error)
869 <<
"We have not implemented such a case that an unobservable transition is necessary";
872 sourceIndex = nextIndex;
873 nextIndex = this->continuousSuccessors.at(nextIndex);
877 if (isMatch(sourceIndex) == isMatch(nextIndex)) {
878 stateManager.add(state, nextIndex);
880 BOOST_LOG_TRIVIAL(error)
881 <<
"We have not implemented such a case that an unobservable transition is necessary";
886 auto initialState = addState(0);
888 mergeContinuousSuccessors(0);
890 std::vector<std::pair<std::size_t, Alphabet>> discreteBoundaries;
893 std::queue<std::shared_ptr<TAState>> newStates;
894 newStates.push(initialState);
895 while (!newStates.empty()) {
896 auto newState = newStates.front();
898 const auto newStateIndices = stateManager.toIndices(newState);
899 for (
const auto action: alphabet) {
902 for (
const auto &newStateIndex: newStateIndices) {
904 BOOST_LOG_TRIVIAL(trace) <<
"Start exploration of the discrete successor from the prefix "
905 << this->prefixes.at(newStateIndex) <<
" with action " << action;
909 if (!this->hasDiscreteSuccessor(newStateIndex, action)) {
911 BOOST_LOG_TRIVIAL(trace) <<
"No discrete successor";
916 const auto discrete = this->discreteSuccessors.at(std::make_pair(newStateIndex, action));
918 if (!this->inP(discrete)) {
920 BOOST_LOG_TRIVIAL(trace) <<
"The discrete successor is not in P";
922 discreteBoundaries.emplace_back(newStateIndex, action);
925 BOOST_LOG_TRIVIAL(trace) <<
"The discrete successor is in P";
927 if (stateManager.isNew(discrete)) {
929 BOOST_LOG_TRIVIAL(trace) <<
"The discrete successor is new";
931 const auto successor = addState(discrete);
932 newStates.push(successor);
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;
940 sourceMap.
add(successor, this->prefixes.at(newStateIndex).getTimedCondition());
942 BOOST_LOG_TRIVIAL(trace) <<
"The new state: " << successor.get();
945 if (this->hasContinuousSuccessor(discrete)) {
947 BOOST_LOG_TRIVIAL(trace) <<
"The discrete successor has continuous successors";
949 mergeContinuousSuccessors(discrete);
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]));
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) {
970 BOOST_LOG_TRIVIAL(debug) <<
"Constructing a transition from: " << this->prefixes.at(sourceIndex)
971 <<
" with action " << action;
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);
986 const auto targetIndex = this->discreteSuccessors.at(std::make_pair(sourceIndex, action));
987 if (!stateManager.isNew(targetIndex)) {
989 BOOST_LOG_TRIVIAL(trace) <<
"The boundary is already handled: " << this->prefixes.at(sourceIndex) <<
" "
994 assert(!this->inP(targetIndex));
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);
1000 assert(it != this->closedRelation.at(targetIndex).end());
1002 const auto jumpedTargetIndex = it->first;
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;
1013 renamingRelation.
eraseLeft(this->prefixes.at(sourceIndex).getTimedCondition().size());
1014 addNewTransition(sourceIndex, jumpedTargetIndex, targetIndex, renamingRelation);
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]));
1026 for (
const auto source: this->pIndices) {
1027 const auto continuousSuccessor = this->continuousSuccessors.at(source);
1028 if (this->inP(continuousSuccessor)) {
1031 const auto sourceState = stateManager.toState(continuousSuccessor);
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);
1037 assert(it != this->closedRelation.at(continuousSuccessor).end());
1039 const auto jumpedSourceIndex = it->first;
1040 const auto jumpedSourceState = stateManager.toState(jumpedSourceIndex);
1041 const auto jumpedSourceCondition = this->prefixes.at(jumpedSourceIndex).getTimedCondition();
1043 impreciseNeighbors.
push(jumpedSourceState.get(), it->second, this->prefixes.at(jumpedSourceIndex));
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));
1050 if (sourceState == jumpedSourceState) {
1052 for (
const auto action: alphabet) {
1053 const auto jumpedSourceInvariant = this->prefixes.at(jumpedSourceIndex).getTimedCondition().toGuard();
1055 auto transitionIt = std::find_if(jumpedSourceState->next.at(action).begin(),
1056 jumpedSourceState->next.at(action).end(),
1058 return isWeaker(transition.guard, jumpedSourceInvariant);
1060 assert(transitionIt != jumpedSourceState->next.at(action).end());
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);
1070 const auto newReset = composition(transitionIt->resetVars, resetByContinuousExterior);
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));
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,
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));
1093 sourceState->next.at(action).emplace_back(transitionIt->target, newReset,
1094 this->prefixes.at(continuousSuccessor)
1095 .removeUpperBound().getTimedCondition().toGuard());
1099 maker.
add(jumpedSourceState, it->second,
1100 this->prefixes.at(continuousSuccessor).removeUpperBound().getTimedCondition(),
1101 jumpedSourceCondition);
1102 assert(maker.
make().size() == 1);
1104 sourceState->next[UNOBSERVABLE].emplace_back(maker.
make().front());
1108 BOOST_LOG_TRIVIAL(debug) <<
"as recognizable: " << this->toRecognizable();
1109 BOOST_LOG_TRIVIAL(info) <<
"Hypothesis before handling imprecise clocks\n" <<
1111 TimedAutomaton::makeMaxConstants(states)}.
simplify();
1113 impreciseNeighbors.
run();
1115 BOOST_LOG_TRIVIAL(debug) <<
"Hypothesis after handling imprecise clocks\n" <<
1117 TimedAutomaton::makeMaxConstants(states)}.
simplify();
1118 BOOST_LOG_TRIVIAL(debug) <<
"as recognizable: " << this->toRecognizable();
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());
1129 BOOST_LOG_TRIVIAL(debug) <<
"Hypothesis before state splitting\n"
1131 TimedAutomaton::makeMaxConstants(states)}.
simplify();
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();
1140 BOOST_LOG_TRIVIAL(debug) <<
"Hypothesis after state splitting\n"
1142 TimedAutomaton::makeMaxConstants(states)}.
simplify();
1145 for (
const auto& state: states) {
1146 for (
auto& [action, transitions]: state->next) {
1149 std::sort(transition.resetVars.begin(), transition.resetVars.end(), [&] (
const auto& left,
const auto& right) {
1150 return left.first < right.first;
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);
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");
1177 it->guard = unionHull(it->guard, it2->guard);
1178 it2 = transitions.erase(it2);
1186 state->removeTransitionsWithWeakerGuards();
1187 state->mergeNondeterministicBranching();
1190 BOOST_LOG_TRIVIAL(debug) <<
"Hypothesis after making transitions deterministic\n" <<
1192 TimedAutomaton::makeMaxConstants(states)}.
simplify();
1196 assert(std::all_of(this->pIndices.begin(), this->pIndices.end(), [&](std::size_t pIndex) {
1197 return !stateManager.isNew(pIndex);
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();
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";
1220 return TimedAutomaton{{states, {initialState}}, TimedAutomaton::makeMaxConstants(states)}.simplify();
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";
1229 stream <<
"S is as follows\n";
1230 for (
const auto &suffix: this->suffixes) {
1231 stream << suffix <<
"\n";
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";
1242 return this->memOracle->printStatistics(stream);
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