diff --git a/alib2algo/src/automaton/simplify/Trim.cpp b/alib2algo/src/automaton/simplify/Trim.cpp index 13e43c6834dae22aef532a50aa7f91561a54bf1f..5e639c8453c79e0a10e9a9ddf936f2b1f6b39ae5 100644 --- a/alib2algo/src/automaton/simplify/Trim.cpp +++ b/alib2algo/src/automaton/simplify/Trim.cpp @@ -12,6 +12,7 @@ #include <automaton/FSM/EpsilonNFA.h> #include <automaton/FSM/NFA.h> #include <automaton/FSM/DFA.h> +#include <automaton/TA/DFTA.h> namespace automaton { @@ -23,6 +24,7 @@ auto TrimMultiInitialStateNFA = Trim::RegistratorWrapper<automaton::MultiInitial auto TrimEpsilonNFA = Trim::RegistratorWrapper<automaton::EpsilonNFA < >, automaton::EpsilonNFA < > >(Trim::trim); auto TrimCompactNFA = Trim::RegistratorWrapper<automaton::CompactNFA < >, automaton::CompactNFA < > >(Trim::trim); auto TrimExtendedNFA = Trim::RegistratorWrapper<automaton::ExtendedNFA < >, automaton::ExtendedNFA < > >(Trim::trim); +auto TrimDFTA = Trim::RegistratorWrapper<automaton::DFTA<>, automaton::DFTA<>>(Trim::trim); automaton::Automaton Trim::trim(const automaton::Automaton& automaton) { return dispatch(automaton.getData()); diff --git a/alib2algo/src/automaton/simplify/UnreachableStatesRemover.cpp b/alib2algo/src/automaton/simplify/UnreachableStatesRemover.cpp index 0732bf1a4f01d8fcb0ec8b2821fddaa9f70910f5..39701c6f10faa863c0eeaabd9566e128fbe24445 100644 --- a/alib2algo/src/automaton/simplify/UnreachableStatesRemover.cpp +++ b/alib2algo/src/automaton/simplify/UnreachableStatesRemover.cpp @@ -17,6 +17,7 @@ auto UnreachableStatesRemoverCompactNFA = UnreachableStatesRemover::RegistratorW auto UnreachableStatesRemoverExtendedNFA = UnreachableStatesRemover::RegistratorWrapper<automaton::ExtendedNFA < >, automaton::ExtendedNFA < > >(UnreachableStatesRemover::remove); auto UnreachableStatesRemoverDFA = UnreachableStatesRemover::RegistratorWrapper<automaton::DFA<>, automaton::DFA<>>(UnreachableStatesRemover::remove); auto UnreachableStatesRemoverMultiInitialStateNFA = UnreachableStatesRemover::RegistratorWrapper<automaton::MultiInitialStateNFA < > , automaton::MultiInitialStateNFA < > >(UnreachableStatesRemover::remove); +auto UnreachableStatesRemoverDFTA = UnreachableStatesRemover::RegistratorWrapper<automaton::DFTA<>, automaton::DFTA < > >(UnreachableStatesRemover::remove); automaton::Automaton UnreachableStatesRemover::remove(const automaton::Automaton& automaton) { return dispatch(automaton.getData()); diff --git a/alib2algo/src/automaton/simplify/UnreachableStatesRemover.h b/alib2algo/src/automaton/simplify/UnreachableStatesRemover.h index 64eb16c32c711535de149cf27d2f7594a8792c06..d4e7e5a3c754aed4fae60f4706da328278c6f248 100644 --- a/alib2algo/src/automaton/simplify/UnreachableStatesRemover.h +++ b/alib2algo/src/automaton/simplify/UnreachableStatesRemover.h @@ -17,6 +17,7 @@ #include <automaton/FSM/MultiInitialStateNFA.h> #include <automaton/FSM/NFA.h> #include <automaton/FSM/DFA.h> +#include <automaton/TA/DFTA.h> #include "../properties/ReachableStates.h" @@ -37,6 +38,8 @@ public: static automaton::DFA < SymbolType, StateType > remove( const automaton::DFA < SymbolType, StateType > & fsm ); template < class SymbolType, class StateType > static automaton::MultiInitialStateNFA < SymbolType, StateType > remove( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm ); + template < class SymbolType, class RankType, class StateType > + static automaton::DFTA < SymbolType, RankType, StateType > remove( const automaton::DFTA < SymbolType, RankType, StateType > & dfta ); }; template<class T, class SymbolType, class StateType > @@ -121,6 +124,61 @@ automaton::MultiInitialStateNFA < SymbolType, StateType > UnreachableStatesRemov return M; } +template < class SymbolType, class RankType, class StateType > +automaton::DFTA < SymbolType, RankType, StateType > UnreachableStatesRemover::remove( const automaton::DFTA < SymbolType, RankType, StateType > & dfta ) { + automaton::DFTA < SymbolType, RankType, StateType > res; + res.setInputAlphabet(dfta.getInputAlphabet()); + + typedef std::pair < const std::pair < std::ranked_symbol < SymbolType, RankType >, std::vector < StateType > >, StateType > Transition; + std::vector<std::pair<const Transition *, int>> transitionsUnreachableCount; + transitionsUnreachableCount.reserve(dfta.getTransitions().size()); + + //for a state, transitions with unreachable count (initially all unreachable) and number of occurences of this state (at least 1) + std::map<StateType, std::map<std::pair<const Transition *, int> *, int>> stateOccurences; + std::deque<StateType> queue; + for(const auto & transition : dfta.getTransitions()) { + if (transition.first.second.empty()) { + queue.push_back(transition.second); + res.addState(transition.second); + res.addTransition(transition.first.first, transition.first.second, transition.second); + } else { + transitionsUnreachableCount.push_back({&transition, transition.first.second.size()}); + for (const auto & state : transition.first.second) { + auto & occurences = stateOccurences[state]; + auto it = occurences.find(&transitionsUnreachableCount.back()); + if (it == occurences.end()) occurences[&transitionsUnreachableCount.back()] = 1; + else it->second++; + } + } + } + + while(!queue.empty()) { + const auto & occurences = stateOccurences[queue.front()]; + queue.pop_front(); + for (const auto & occurence : occurences) { + int & unreachableCount = occurence.first -> second; + const StateType & to = occurence.first -> first -> second; + unreachableCount -= occurence.second; + if (unreachableCount == 0) { + if (res.addState(to)) { + queue.push_back(to); + } + } + } + } + + for (const auto & state : res.getStates()) { + if (dfta.getFinalStates().count(state) != 0) res.addFinalState(state); + } + for (const auto & transitionUnreachableCount : transitionsUnreachableCount) { + if (transitionUnreachableCount.second == 0) { + const Transition transition = *(transitionUnreachableCount.first); + res.addTransition(transition.first.first, transition.first.second, transition.second); + } + } + return res; +} + } /* namespace simplify */ } /* namespace automaton */ diff --git a/alib2algo/src/automaton/simplify/UselessStatesRemover.cpp b/alib2algo/src/automaton/simplify/UselessStatesRemover.cpp index b416822197913011739f32b36c9b9f8f531537f3..8b008413f2a8e8b8a90438817c7c87d135fe631c 100644 --- a/alib2algo/src/automaton/simplify/UselessStatesRemover.cpp +++ b/alib2algo/src/automaton/simplify/UselessStatesRemover.cpp @@ -17,6 +17,7 @@ auto UselessStatesRemoverCompactNFA = UselessStatesRemover::RegistratorWrapper<a auto UselessStatesRemoverExtendedNFA = UselessStatesRemover::RegistratorWrapper<automaton::ExtendedNFA < >, automaton::ExtendedNFA < > >(UselessStatesRemover::remove); auto UselessStatesRemoverDFA = UselessStatesRemover::RegistratorWrapper<automaton::DFA<>, automaton::DFA<>>(UselessStatesRemover::remove); auto UselessStatesRemoverMultiInitialStateNFA = UselessStatesRemover::RegistratorWrapper<automaton::MultiInitialStateNFA < > , automaton::MultiInitialStateNFA < > >(UselessStatesRemover::remove); +auto UselessStatesRemoverDFTA = UselessStatesRemover::RegistratorWrapper<automaton::DFTA<>, automaton::DFTA<>>(UselessStatesRemover::remove); automaton::Automaton UselessStatesRemover::remove(const automaton::Automaton& automaton) { return dispatch(automaton.getData()); diff --git a/alib2algo/src/automaton/simplify/UselessStatesRemover.h b/alib2algo/src/automaton/simplify/UselessStatesRemover.h index fbf21b2335f99c2fb6ed133e0fee51341a1dc291..7ea18e0297f2065983a468237d35be7416aee2e6 100644 --- a/alib2algo/src/automaton/simplify/UselessStatesRemover.h +++ b/alib2algo/src/automaton/simplify/UselessStatesRemover.h @@ -19,6 +19,7 @@ #include <automaton/FSM/MultiInitialStateNFA.h> #include <automaton/FSM/NFA.h> #include <automaton/FSM/DFA.h> +#include <automaton/TA/DFTA.h> namespace automaton { @@ -37,6 +38,8 @@ public: static automaton::DFA < SymbolType, StateType > remove( const automaton::DFA < SymbolType, StateType > & fsm ); template < class SymbolType, class StateType > static automaton::MultiInitialStateNFA < SymbolType, StateType > remove( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm ); + template < class SymbolType, class RankType, class StateType > + static automaton::DFTA < SymbolType, RankType, StateType > remove( const automaton::DFTA < SymbolType, RankType, StateType > & dfta ); }; template < class T, class SymbolType, class StateType > @@ -130,6 +133,44 @@ automaton::MultiInitialStateNFA < SymbolType, StateType > UselessStatesRemover:: return M; } +template < class SymbolType, class RankType, class StateType > +automaton::DFTA < SymbolType, RankType, StateType > UselessStatesRemover::remove( const automaton::DFTA < SymbolType, RankType, StateType > & dfta ) { + automaton::DFTA < SymbolType, RankType, StateType > res; + res.setInputAlphabet(dfta.getInputAlphabet()); + res.setStates(dfta.getFinalStates()); + res.setFinalStates(dfta.getFinalStates()); + + typedef std::pair < const std::pair < std::ranked_symbol < SymbolType, RankType >, std::vector < StateType > >, StateType > Transition; + + std::map<StateType, std::set<const Transition *>> transitionsToState; + for(const auto & transition : dfta.getTransitions()) { + transitionsToState[transition.second].insert(&transition); + } + + std::deque<StateType> queue; + for (const auto & state : dfta.getFinalStates()) { + queue.push_back(state); + } + + while(!queue.empty()) { + std::set<const Transition *> & transitions = transitionsToState[queue.front()]; + queue.pop_front(); + for (const Transition * transitionPt : transitions) { + const Transition &transition = *transitionPt; + + for (const auto & state : transition.first.second) { + if (res.addState(state)) { + queue.push_back(state); + } + } + + res.addTransition(transition.first.first, transition.first.second, transition.second); + } + } + + return res; +} + } /* namespace simplify */ } /* namespace automaton */ diff --git a/alib2algo/test-src/automaton/simplify/trimTest.cpp b/alib2algo/test-src/automaton/simplify/trimTest.cpp index 6c17786b04a6ea273287ecb6e0ef9d5591dca9bb..e2d813d8d34d84c32290744febbd0a860ec745c2 100644 --- a/alib2algo/test-src/automaton/simplify/trimTest.cpp +++ b/alib2algo/test-src/automaton/simplify/trimTest.cpp @@ -6,6 +6,7 @@ #include "automaton/FSM/DFA.h" #include "grammar/Regular/RightRG.h" +#include "automaton/TA/DFTA.h" CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( trimTest, "automaton" ); CPPUNIT_TEST_SUITE_REGISTRATION( trimTest ); @@ -17,47 +18,99 @@ void trimTest::tearDown() { } void trimTest::testTrimAutomaton() { - automaton::DFA < > automaton(DefaultStateType(1)); - - automaton.addState(DefaultStateType(1)); - automaton.addState(DefaultStateType(2)); - automaton.addState(DefaultStateType(3)); - automaton.addInputSymbol(DefaultSymbolType("a")); - automaton.addInputSymbol(DefaultSymbolType("b")); - - automaton.addTransition(DefaultStateType(1), DefaultSymbolType("a"), DefaultStateType(2)); - automaton.addTransition(DefaultStateType(2), DefaultSymbolType("b"), DefaultStateType(1)); - automaton.addTransition(DefaultStateType(3), DefaultSymbolType("b"), DefaultStateType(1)); - - automaton.addFinalState(DefaultStateType(1)); - - automaton::DFA<> trimed = automaton::simplify::Trim::trim(automaton); - - CPPUNIT_ASSERT(trimed.getStates().size() == 2); + automaton::DFA < > automaton(DefaultStateType(1)); + + automaton.addState(DefaultStateType(1)); + automaton.addState(DefaultStateType(2)); + automaton.addState(DefaultStateType(3)); + automaton.addInputSymbol(DefaultSymbolType("a")); + automaton.addInputSymbol(DefaultSymbolType("b")); + + automaton.addTransition(DefaultStateType(1), DefaultSymbolType("a"), DefaultStateType(2)); + automaton.addTransition(DefaultStateType(2), DefaultSymbolType("b"), DefaultStateType(1)); + automaton.addTransition(DefaultStateType(3), DefaultSymbolType("b"), DefaultStateType(1)); + + automaton.addFinalState(DefaultStateType(1)); + + automaton::DFA<> trimed = automaton::simplify::Trim::trim(automaton); + + CPPUNIT_ASSERT(trimed.getStates().size() == 2); } void trimTest::testTrimGrammar() { - grammar::RightRG < > rrGrammar(DefaultSymbolType(1)); - - rrGrammar.addNonterminalSymbol(DefaultSymbolType(1)); - rrGrammar.addNonterminalSymbol(DefaultSymbolType(2)); - rrGrammar.addNonterminalSymbol(DefaultSymbolType(3)); - rrGrammar.addNonterminalSymbol(DefaultSymbolType(4)); - rrGrammar.addNonterminalSymbol(DefaultSymbolType(5)); - rrGrammar.addNonterminalSymbol(DefaultSymbolType(6)); - rrGrammar.addTerminalSymbol(DefaultSymbolType("a")); - rrGrammar.addTerminalSymbol(DefaultSymbolType("b")); - - rrGrammar.addRule(DefaultSymbolType(1), std::make_pair(DefaultSymbolType("a"), DefaultSymbolType(2))); - rrGrammar.addRule(DefaultSymbolType(2), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(3))); - rrGrammar.addRule(DefaultSymbolType(3), DefaultSymbolType("a")); - - rrGrammar.addRule(DefaultSymbolType(4), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(5))); - rrGrammar.addRule(DefaultSymbolType(5), DefaultSymbolType("a")); - rrGrammar.addRule(DefaultSymbolType(5), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(2))); - rrGrammar.addRule(DefaultSymbolType(6), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(6))); - - grammar::RightRG < > trimed = grammar::simplify::Trim::trim(rrGrammar); - - CPPUNIT_ASSERT(trimed.getNonterminalAlphabet().size() == 3); + grammar::RightRG < > rrGrammar(DefaultSymbolType(1)); + + rrGrammar.addNonterminalSymbol(DefaultSymbolType(1)); + rrGrammar.addNonterminalSymbol(DefaultSymbolType(2)); + rrGrammar.addNonterminalSymbol(DefaultSymbolType(3)); + rrGrammar.addNonterminalSymbol(DefaultSymbolType(4)); + rrGrammar.addNonterminalSymbol(DefaultSymbolType(5)); + rrGrammar.addNonterminalSymbol(DefaultSymbolType(6)); + rrGrammar.addTerminalSymbol(DefaultSymbolType("a")); + rrGrammar.addTerminalSymbol(DefaultSymbolType("b")); + + rrGrammar.addRule(DefaultSymbolType(1), std::make_pair(DefaultSymbolType("a"), DefaultSymbolType(2))); + rrGrammar.addRule(DefaultSymbolType(2), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(3))); + rrGrammar.addRule(DefaultSymbolType(3), DefaultSymbolType("a")); + + rrGrammar.addRule(DefaultSymbolType(4), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(5))); + rrGrammar.addRule(DefaultSymbolType(5), DefaultSymbolType("a")); + rrGrammar.addRule(DefaultSymbolType(5), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(2))); + rrGrammar.addRule(DefaultSymbolType(6), std::make_pair(DefaultSymbolType("b"), DefaultSymbolType(6))); + + grammar::RightRG < > trimed = grammar::simplify::Trim::trim(rrGrammar); + + CPPUNIT_ASSERT(trimed.getNonterminalAlphabet().size() == 3); +} + +void trimTest::testTrimDFTA() { + automaton::DFTA < > automaton; + + std::vector<DefaultStateType> q; + for (int i = 0; i <= 11; ++i) { + DefaultStateType state (i); + q.push_back(state); + automaton.addState(state); + } + automaton.addFinalState(q[2]); + automaton.addFinalState(q[11]); + + const std::ranked_symbol < > a ("a", 2); + const std::ranked_symbol < > b ("b", 1); + const std::ranked_symbol < > c ("c", 0); + automaton.addInputSymbol(a); + automaton.addInputSymbol(b); + automaton.addInputSymbol(c); + + automaton.addTransition(c, {}, q[0]); + automaton.addTransition(a, {q[0], q[0]}, q[1]); + automaton.addTransition(b, {q[1]}, q[2]); + + //unreachable and useless + automaton.addTransition(a, {q[3], q[4]}, q[5]); + automaton.addTransition(b, {q[5]}, q[6]); + + //useless + automaton.addTransition(a, {q[2], q[2]}, q[7]); + automaton.addTransition(a, {q[7], q[7]}, q[8]); + + //unreachable + automaton.addTransition(a, {q[9], q[9]}, q[10]); + automaton.addTransition(a, {q[10], q[10]}, q[11]); + + automaton::DFTA<> trimed = automaton::simplify::Trim::trim(automaton); + + automaton::DFTA<> correct; + correct.addState(q[0]); + correct.addState(q[1]); + correct.addState(q[2]); + correct.addFinalState(q[2]); + correct.addInputSymbol(a); + correct.addInputSymbol(b); + correct.addInputSymbol(c); + correct.addTransition(c, {}, q[0]); + correct.addTransition(a, {q[0], q[0]}, q[1]); + correct.addTransition(b, {q[1]}, q[2]); + + CPPUNIT_ASSERT(trimed == correct); } diff --git a/alib2algo/test-src/automaton/simplify/trimTest.h b/alib2algo/test-src/automaton/simplify/trimTest.h index b23af79d17e8e2423e455379987f8891074d5550..79b6736ce63b0ac4fbba61bfc8e53192f969b449 100644 --- a/alib2algo/test-src/automaton/simplify/trimTest.h +++ b/alib2algo/test-src/automaton/simplify/trimTest.h @@ -3,19 +3,20 @@ #include <cppunit/extensions/HelperMacros.h> -class trimTest : public CppUnit::TestFixture -{ - CPPUNIT_TEST_SUITE( trimTest ); - CPPUNIT_TEST( testTrimAutomaton ); - CPPUNIT_TEST( testTrimGrammar ); - CPPUNIT_TEST_SUITE_END(); +class trimTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE( trimTest ); + CPPUNIT_TEST( testTrimAutomaton ); + CPPUNIT_TEST( testTrimGrammar ); + CPPUNIT_TEST( testTrimDFTA ); + CPPUNIT_TEST_SUITE_END(); public: - void setUp(); - void tearDown(); + void setUp(); + void tearDown(); - void testTrimAutomaton(); - void testTrimGrammar(); + void testTrimAutomaton(); + void testTrimGrammar(); + void testTrimDFTA(); }; -#endif // TRIM_TEST_H_ +#endif // TRIM_TEST_H_ diff --git a/atrim2/src/atrim.cpp b/atrim2/src/atrim.cpp index 06f914e4beb3d5bf0a9536324cad3efb46dcb658..cce17143a26494a0ead90b8956be3dbf0849ba2b 100644 --- a/atrim2/src/atrim.cpp +++ b/atrim2/src/atrim.cpp @@ -65,10 +65,10 @@ int main(int argc, char* argv[]) { TCLAP::CmdLine cmd("Removes unreachable and useless states from FSM, productive and unreachable nonterminals from CFG. Simplifies representation of RE", ' ', "0.01"); - TCLAP::SwitchArg useless( "u", "useless", "Removes useless states (or symbols). (works with FSM or CFG)" ); + TCLAP::SwitchArg useless( "u", "useless", "Removes useless states (or symbols). (works with FSM, FTA or CFG)" ); cmd.add( useless ); - TCLAP::SwitchArg unreachable( "r", "unreachable", "Removes unreachable states (or symbols). (works with FSM or CFG)" ); + TCLAP::SwitchArg unreachable( "r", "unreachable", "Removes unreachable states (or symbols). (works with FSM, FTA or CFG)" ); cmd.add( unreachable ); TCLAP::SwitchArg simplify( "s", "simplify", "Simplifies representation. (works with RE only)" );