From 9c4a6b19164ece7a36251cb71141165d3bfdeb83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <peckato1@fit.cvut.cz> Date: Thu, 11 Sep 2014 16:40:26 +0200 Subject: [PATCH] algo: StateElimination --- aconversions2/src/ConversionHandler.cpp | 58 +++- aconversions2/src/ConversionHandler.h | 7 +- .../fa2re/formal/StateEliminationFormal.cpp | 292 ++++++++++++++++++ .../fa2re/formal/StateEliminationFormal.h | 62 ++++ .../unbounded/StateEliminationUnbounded.cpp | 272 ++++++++++++++++ .../unbounded/StateEliminationUnbounded.h | 62 ++++ tests.aconversion.sh | 28 +- 7 files changed, 755 insertions(+), 26 deletions(-) create mode 100644 alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp create mode 100644 alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h create mode 100644 alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp create mode 100644 alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h diff --git a/aconversions2/src/ConversionHandler.cpp b/aconversions2/src/ConversionHandler.cpp index a3c7a957a7..f7367ae991 100644 --- a/aconversions2/src/ConversionHandler.cpp +++ b/aconversions2/src/ConversionHandler.cpp @@ -7,7 +7,8 @@ #include "ConversionHandler.h" -//#include "conversions/fa2re/StateElimination.h" +#include "conversions/fa2re/unbounded/StateEliminationUnbounded.h" +#include "conversions/fa2re/formal/StateEliminationFormal.h" #include "conversions/fa2re/BrzozowskiAlgebraic.h" #include "conversions/re2fa/Glushkov.h" @@ -54,7 +55,7 @@ void ConversionHandler::convert( void ) { if( m_source == FINITE_AUTOMATON ) convertFSM( ); - else if( m_source == REGULAR_EXPRESSION ) + else if( isRegExp(m_source) ) convertRE( ); else if( isGrammar( m_source ) ) convertRG( ); @@ -68,7 +69,7 @@ void ConversionHandler::convertFSM( void ) { if( m_target == FINITE_AUTOMATON ) throw exception::AlibException( "ConversionHandler: No valid conversion from FSM to FSM." ); - else if( m_target == REGULAR_EXPRESSION ) + else if( isRegExp( m_target ) ) convertFSMtoRE( ); else if( isGrammar( m_target ) ) convertFSMtoRG( ); @@ -80,7 +81,7 @@ void ConversionHandler::convertRE( void ) { if( m_target == FINITE_AUTOMATON ) convertREtoFSM( ); - else if( m_target == REGULAR_EXPRESSION ) + else if( isRegExp( m_target ) ) throw exception::AlibException( "ConversionHandler: No valid conversion from RE to RE." ); else if( isGrammar( m_target ) ) convertREtoRG( ); @@ -92,7 +93,7 @@ void ConversionHandler::convertRG( void ) { if( m_target == FINITE_AUTOMATON ) convertRGtoFSM( ); - else if( m_target == REGULAR_EXPRESSION ) + else if( isRegExp( m_target ) ) convertRGtoRE( ); else if( isGrammar( m_target ) ) convertRGtoRG( ); @@ -102,8 +103,40 @@ void ConversionHandler::convertRG( void ) // ---------------------------------------------------------------------------- -void ConversionHandler::convertFSMtoRE( void ) +void ConversionHandler::convertFSMtoRE(void) { + switch( m_target ) + { + case REGULAR_EXPRESSION_FORMAL: + convertFSMtoREF(); + break; + default: + convertFSMtoREU(); + break; + } +} + +void ConversionHandler::convertFSMtoREF(void) +{ + const automaton::Automaton automaton = alib::DataFactory::fromTokens<automaton::Automaton>(m_tokens); + + switch( m_algorithm ) + { + case BRZOZOWSKI_ALGEBRAIC: + break; + case STATE_ELIMINATION: + default: { + alib::DataFactory::toStdout(fa2re::StateEliminationFormal::convert(automaton)); + break; + } + } + +} + +void ConversionHandler::convertFSMtoREU( void ) +{ + const automaton::Automaton automaton = alib::DataFactory::fromTokens<automaton::Automaton>(m_tokens); + switch( m_algorithm ) { case BRZOZOWSKI_ALGEBRAIC: { @@ -123,9 +156,7 @@ void ConversionHandler::convertFSMtoRE( void ) } case STATE_ELIMINATION: default: { - /*fa2re::StateElimination conv( fsm ); - regexp::UnboundedRegExp re = conv.convert(); - alib::DataFactory::toStdout(re);*/ + alib::DataFactory::toStdout(fa2re::StateEliminationUnbounded::convert(automaton)); break; } } @@ -368,7 +399,9 @@ ConversionHandler::TFormalism ConversionHandler::parseFormalismFromString( const return FINITE_AUTOMATON; if( target == "re" || target == "regexp" || target == "regex" ) - return REGULAR_EXPRESSION; + return REGULAR_EXPRESSION_UNBOUNDED; + if (target == "formalre" || target == "formalregexp" || target == "formalregex") + return REGULAR_EXPRESSION_FORMAL; if( target == "rrg" || target == "rg" || target == "grammar" ) return RIGHT_REGULAR_GRAMMAR; @@ -387,7 +420,10 @@ ConversionHandler::TFormalism ConversionHandler::parseFormalismFromTokens( void return FINITE_AUTOMATON; if( xmlMark == "regexp" || xmlMark == "unboundedRegexp" ) - return REGULAR_EXPRESSION; + return REGULAR_EXPRESSION_UNBOUNDED; + + if(xmlMark == "formalRegExp") + return REGULAR_EXPRESSION_FORMAL; if( xmlMark == "RightRG" ) return RIGHT_REGULAR_GRAMMAR; diff --git a/aconversions2/src/ConversionHandler.h b/aconversions2/src/ConversionHandler.h index 8c1a383754..c8ce539e0f 100644 --- a/aconversions2/src/ConversionHandler.h +++ b/aconversions2/src/ConversionHandler.h @@ -69,13 +69,16 @@ public: LEFT_LINEAR_GRAMMAR, RIGHT_LINEAR_GRAMMAR, - REGULAR_EXPRESSION, + REGULAR_EXPRESSION_UNBOUNDED, + REGULAR_EXPRESSION_FORMAL, UNKNOWN, }; #define isGrammar(x) ( (x) == REGULAR_GRAMMAR || (x) == LEFT_REGULAR_GRAMMAR || (x) == RIGHT_REGULAR_GRAMMAR \ || (x) == LINEAR_GRAMMAR || (x) == LEFT_LINEAR_GRAMMAR || (x) == RIGHT_LINEAR_GRAMMAR ) + #define isRegExp(x) ( (x) == REGULAR_EXPRESSION_FORMAL || (x) == REGULAR_EXPRESSION_UNBOUNDED ) + ConversionHandler( std::list<sax::Token> & tokens, const std::string & target, const std::string & algorithm, std::ostream & out ); void convert( void ); @@ -86,6 +89,8 @@ private: void convertFSM( void ); void convertFSMtoRE( void ); + void convertFSMtoREF( void ); + void convertFSMtoREU( void ); void convertFSMtoRG( void ); void convertFSMtoLRG( void ); void convertFSMtoRRG( void ); diff --git a/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp new file mode 100644 index 0000000000..b7302c366a --- /dev/null +++ b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp @@ -0,0 +1,292 @@ +/* + * StateEliminationFormal.cpp + * + * Created on: 9. 2. 2014 + * Author: Tomas Pecka + */ + +#include "StateEliminationFormal.h" + +#include <regexp/formal/FormalRegExp.h> +#include <label/StringLabel.h> +#include <exception/AlibException.h> + +#include "../../../regexp/RegExpOptimize.h" + +namespace fa2re +{ + +template<class T> +regexp::RegExp StateEliminationFormal::convert(const T& automaton) +{ + if(automaton.getFinalStates().size() == 0) + return regexp::RegExp{regexp::FormalRegExp(regexp::FormalRegExpEmpty())}; + + // steps 1 + 2 + automaton::ExtendedNFA extendedAutomaton = constructExtendedNFA(automaton); + extendExtendedNFA(extendedAutomaton); + + // step 3 - Exterminate! + // select all states that are neither final nor initial + std::set<automaton::State> statesToEliminate = extendedAutomaton.getStates(); + statesToEliminate.erase(*extendedAutomaton.getInitialStates().begin()); + statesToEliminate.erase(*extendedAutomaton.getFinalStates().begin()); + + for(const auto& state : statesToEliminate) + extendedAutomaton = eliminateState(extendedAutomaton, state); + + // step 4 + //regexp::RegExpOptimize opt; + + regexp::FormalRegExpConcatenation concat( + std::move(*transition(extendedAutomaton, *extendedAutomaton.getInitialStates().begin(), *extendedAutomaton.getFinalStates().begin())), + regexp::FormalRegExpIteration(std::move(*transition(extendedAutomaton, *extendedAutomaton.getFinalStates().begin(), *extendedAutomaton.getFinalStates().begin()))) + ); + //return regexp::RegExp{opt.optimize(regexp::FormalRegExp(concat))}; + return regexp::RegExp{regexp::FormalRegExp(concat)}; +} + +template<> +regexp::RegExp StateEliminationFormal::convert(const automaton::Automaton& automaton) +{ + regexp::RegExp* out = NULL; + automaton.getData().Accept((void*) &out, StateEliminationFormal::STATE_ELIMINATION); + regexp::RegExp res = std::move(*out); + delete out; + return res; +} + + + +void StateEliminationFormal::Visit(void*, const automaton::UnknownAutomaton&) const +{ + throw exception::AlibException("Unsupported automaton type UnknownAutomaton"); +} + +void StateEliminationFormal::Visit(void* data, const automaton::EpsilonNFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationFormal::Visit(void* data, const automaton::NFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationFormal::Visit(void* data, const automaton::DFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationFormal::Visit(void* data, const automaton::ExtendedNFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationFormal::Visit(void*, const automaton::CompactNFA& ) const +{ + throw exception::AlibException("Unsupported automaton type CompactNFA"); +} + +void StateEliminationFormal::Visit(void*, const automaton::NPDA&) const +{ + throw exception::AlibException("Unsupported automaton type NPDA"); +} + +void StateEliminationFormal::Visit(void*, const automaton::SinglePopNPDA&) const +{ + throw exception::AlibException("Unsupported automaton type SinglePopNPDA"); +} + +void StateEliminationFormal::Visit(void*, const automaton::OneTapeDTM&) const +{ + throw exception::AlibException("Unsupported automaton type OneTapeDTM"); +} + + +automaton::ExtendedNFA StateEliminationFormal::eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& q) +{ + automaton::ExtendedNFA newAutomaton; + newAutomaton.setStates(extendedAutomaton.getStates()); + newAutomaton.removeState(q); // preserve all states but q (the one to eliminate) + newAutomaton.setInputSymbols(extendedAutomaton.getInputAlphabet()); + newAutomaton.setInitialStates(extendedAutomaton.getInitialStates()); // sure that q is neither initial nor final (follows from step 2 - extending ExtendedNFA) + newAutomaton.setFinalStates(extendedAutomaton.getFinalStates()); + + // regexp::RegExpOptimize opt; + + for(const auto& p: newAutomaton.getStates()) + { + for(const auto& r : newAutomaton.getStates()) + { + regexp::FormalRegExpConcatenation concat( + std::move(*transition(extendedAutomaton, p, q)), + regexp::FormalRegExpConcatenation( + regexp::FormalRegExpIteration(std::move(*transition(extendedAutomaton, q, q))), + std::move(*transition(extendedAutomaton, q, r)) + ) + ); + + regexp::FormalRegExpAlternation alt( + std::move(*transition(extendedAutomaton, p, r)), + concat + ); + + // regexp::FormalRegExp transitionRegExp(opt.optimize(regexp::FormalRegExp(alt))); + regexp::FormalRegExp transitionRegExp(alt); + transitionRegExp.setAlphabet(extendedAutomaton.getInputAlphabet()); + newAutomaton.addTransition(p, regexp::RegExp{transitionRegExp}, r); + } + } + + return newAutomaton; +} + +const regexp::FormalRegExpElement* StateEliminationFormal::transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to) +{ + std::vector<regexp::FormalRegExpElement*> alt; + + for(const auto& transition: automaton.getTransitionsFromState(from)) + if(transition.second.count(to) > 0) + alt.push_back(static_cast<const regexp::FormalRegExp&>(transition.first.second.getData()).getRegExp().clone()); + + size_t size = alt.size(); + if(size == 0) return new regexp::FormalRegExpEmpty(); + if(size == 1) return alt.at(0); + + regexp::FormalRegExpAlternation* ret = new regexp::FormalRegExpAlternation(std::move(*alt.at(--size)), std::move(*alt.at(--size))); + while(size) + { + regexp::FormalRegExpElement* left = alt.at(--size); + regexp::FormalRegExpElement* right = ret; + + ret = new regexp::FormalRegExpAlternation(std::move(*left), std::move(*right)); + delete left; + delete right; + } + + return ret; +} + +template<> +automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::NFA& automaton) +{ + automaton::ExtendedNFA extendedAutomaton; + extendedAutomaton.setStates(automaton.getStates()); + extendedAutomaton.setInputSymbols(automaton.getInputAlphabet()); + extendedAutomaton.setInitialStates(automaton.getInitialStates()); + extendedAutomaton.setFinalStates(automaton.getFinalStates()); + + for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State> + { + regexp::FormalRegExp regexp(regexp::FormalRegExpSymbol(transition.first.second)); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + + for(const auto& to : transition.second) + extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to); + } + + return extendedAutomaton; +} + +template<> +automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::DFA& automaton) +{ + automaton::ExtendedNFA extendedAutomaton; + extendedAutomaton.setStates(automaton.getStates()); + extendedAutomaton.setInputSymbols(automaton.getInputAlphabet()); + extendedAutomaton.setInitialStates(std::set<automaton::State>{automaton.getInitialState()}); + extendedAutomaton.setFinalStates(automaton.getFinalStates()); + + for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State> + { + regexp::FormalRegExp regexp(regexp::FormalRegExpSymbol(transition.first.second)); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, transition.second); + } + + return extendedAutomaton; +} + +template<> +automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::EpsilonNFA& automaton) +{ + automaton::ExtendedNFA extendedAutomaton; + extendedAutomaton.setStates(automaton.getStates()); + extendedAutomaton.setInputSymbols(automaton.getInputAlphabet()); + extendedAutomaton.setInitialStates(automaton.getInitialStates()); + extendedAutomaton.setFinalStates(automaton.getFinalStates()); + + for(const auto& transition : automaton.getSymbolTransitions()) // pair<State, symb/eps> -> set<State> + { + regexp::FormalRegExp regexp(regexp::FormalRegExpSymbol(transition.first.second)); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + + for(const auto& to : transition.second) + extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to); + } + for(const auto& transition : automaton.getEpsilonTransitions()) + { + regexp::FormalRegExp regexp; + regexp.setRegExp(regexp::FormalRegExpEpsilon()); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + + for(const auto& to : transition.second) + extendedAutomaton.addTransition(transition.first, regexp::RegExp{regexp}, to); + } + + return extendedAutomaton; +} + +template<> +automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::ExtendedNFA& automaton) +{ + return automaton; +} + +void StateEliminationFormal::extendExtendedNFA(automaton::ExtendedNFA& automaton) +{ + const automaton::State& initState = *automaton.getInitialStates().begin(); + if(automaton.getFinalStates().count(initState) > 0 || automaton.getTransitionsToState(initState).size() > 0 ) + { + const automaton::State q0 = automaton::createUniqueState(initState, automaton.getStates()); + automaton.addState(q0); + + regexp::FormalRegExp regexp; + regexp.setRegExp(regexp::FormalRegExpEpsilon()); + regexp.setAlphabet(automaton.getInputAlphabet()); + automaton.addTransition(q0, regexp::RegExp{regexp}, initState); + + const std::set<automaton::State> automatonInitialStates = automaton.getInitialStates(); + for(const auto& state: automatonInitialStates) + automaton.removeInitialState(state); + automaton.addInitialState(q0); + } + + if(automaton.getFinalStates().size() > 1) + { + const automaton::State f = automaton::createUniqueState(automaton::State(label::Label(label::StringLabel("f"))), automaton.getStates()); + automaton.addState(f); + + const std::set<automaton::State> automatonFinalStates = automaton.getFinalStates(); + for(const auto &state : automatonFinalStates) + { + regexp::FormalRegExp regexp; + regexp.setRegExp(regexp::FormalRegExpEpsilon()); + regexp.setAlphabet(automaton.getInputAlphabet()); + automaton.addTransition(state, regexp::RegExp{regexp}, f); + + automaton.removeFinalState(state); + } + + automaton.addFinalState(f); + } +} + +const StateEliminationFormal StateEliminationFormal::STATE_ELIMINATION; + +} /* namespace fa2re */ diff --git a/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h new file mode 100644 index 0000000000..594ffa7041 --- /dev/null +++ b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h @@ -0,0 +1,62 @@ +/* + * StateEliminationFormal.h + * + * Created on: 9. 2. 2014 + * Author: Tomas Pecka + */ + +#ifndef STATEELIMINATIONFORMAL_H_ +#define STATEELIMINATIONFORMAL_H_ + +#include <regexp/RegExp.h> +#include <regexp/formal/FormalRegExpElements.h> + +#include <automaton/Automaton.h> +#include <automaton/FSM/DFA.h> +#include <automaton/FSM/NFA.h> +#include <automaton/FSM/EpsilonNFA.h> +#include <automaton/FSM/ExtendedNFA.h> + +namespace fa2re +{ + +/** + * Converts FSM to RE using State Elimination algorithm. + * Source: Melichar 2.118 + */ +class StateEliminationFormal : public automaton::VisitableAutomatonBase::const_visitor_type +{ +public: + /** + * Performs conversion. + * @return regular expression equivalent to source NFA. + */ + template<class T> + static regexp::RegExp convert(const T& automaton); + +private: + void Visit(void*, const automaton::UnknownAutomaton& automaton) const; + void Visit(void*, const automaton::EpsilonNFA& automaton) const; + void Visit(void*, const automaton::NFA& automaton) const; + void Visit(void*, const automaton::DFA& automaton) const; + void Visit(void*, const automaton::ExtendedNFA& automaton) const; + void Visit(void*, const automaton::CompactNFA& automaton) const; + void Visit(void*, const automaton::NPDA& automaton) const; + void Visit(void*, const automaton::SinglePopNPDA& automaton) const; + void Visit(void*, const automaton::OneTapeDTM& automaton) const; + + template<class T> + static automaton::ExtendedNFA constructExtendedNFA(const T& automaton); + + static void extendExtendedNFA(automaton::ExtendedNFA& automaton); + + static const regexp::FormalRegExpElement* transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to); + + static automaton::ExtendedNFA eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& state); + + static const StateEliminationFormal STATE_ELIMINATION; +}; + +} /* namespace fa2re */ + +#endif /* STATEELIMINATIONFORMAL_H_ */ diff --git a/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp new file mode 100644 index 0000000000..311a8c74e0 --- /dev/null +++ b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp @@ -0,0 +1,272 @@ +/* + * StateEliminationUnbounded.cpp + * + * Created on: 9. 2. 2014 + * Author: Tomas Pecka + */ + +#include "StateEliminationUnbounded.h" + +#include <regexp/unbounded/UnboundedRegExp.h> +#include <label/StringLabel.h> +#include <exception/AlibException.h> +#include <factory/DataFactory.hpp> + +#include "../../../regexp/RegExpOptimize.h" + +namespace fa2re +{ + +template<class T> +regexp::RegExp StateEliminationUnbounded::convert(const T& automaton) +{ + if(automaton.getFinalStates().size() == 0) + return regexp::RegExp{regexp::UnboundedRegExp(regexp::UnboundedRegExpEmpty())}; + + // steps 1 + 2 + automaton::ExtendedNFA extendedAutomaton = constructExtendedNFA(automaton); + extendExtendedNFA(extendedAutomaton); + + // step 3 - Exterminate! + // select all states that are neither final nor initial + std::set<automaton::State> statesToEliminate = extendedAutomaton.getStates(); + statesToEliminate.erase(*extendedAutomaton.getInitialStates().begin()); + statesToEliminate.erase(*extendedAutomaton.getFinalStates().begin()); + + for(const auto& state : statesToEliminate) + extendedAutomaton = eliminateState(extendedAutomaton, state); + + // step 4 + regexp::RegExpOptimize opt; + + regexp::UnboundedRegExpConcatenation concat; + concat.appendElement(std::move(*transition(extendedAutomaton, *extendedAutomaton.getInitialStates().begin(), *extendedAutomaton.getFinalStates().begin()))); + concat.appendElement(regexp::UnboundedRegExpIteration(std::move(*transition(extendedAutomaton, *extendedAutomaton.getFinalStates().begin(), *extendedAutomaton.getFinalStates().begin())))); + return regexp::RegExp{opt.optimize(regexp::UnboundedRegExp(concat))}; +} + +template<> +regexp::RegExp StateEliminationUnbounded::convert(const automaton::Automaton& automaton) +{ + regexp::RegExp* out = NULL; + automaton.getData().Accept((void*) &out, StateEliminationUnbounded::STATE_ELIMINATION); + regexp::RegExp res = std::move(*out); + delete out; + return res; +} + + + +void StateEliminationUnbounded::Visit(void*, const automaton::UnknownAutomaton&) const +{ + throw exception::AlibException("Unsupported automaton type UnknownAutomaton"); +} + +void StateEliminationUnbounded::Visit(void* data, const automaton::EpsilonNFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationUnbounded::Visit(void* data, const automaton::NFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationUnbounded::Visit(void* data, const automaton::DFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationUnbounded::Visit(void* data, const automaton::ExtendedNFA& automaton) const +{ + regexp::RegExp* & out = *((regexp::RegExp**) data); + out = new regexp::RegExp(convert(automaton)); +} + +void StateEliminationUnbounded::Visit(void*, const automaton::CompactNFA& ) const +{ + throw exception::AlibException("Unsupported automaton type CompactNFA"); +} + +void StateEliminationUnbounded::Visit(void*, const automaton::NPDA&) const +{ + throw exception::AlibException("Unsupported automaton type NPDA"); +} + +void StateEliminationUnbounded::Visit(void*, const automaton::SinglePopNPDA&) const +{ + throw exception::AlibException("Unsupported automaton type SinglePopNPDA"); +} + +void StateEliminationUnbounded::Visit(void*, const automaton::OneTapeDTM&) const +{ + throw exception::AlibException("Unsupported automaton type OneTapeDTM"); +} + + +automaton::ExtendedNFA StateEliminationUnbounded::eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& q) +{ + automaton::ExtendedNFA newAutomaton; + newAutomaton.setStates(extendedAutomaton.getStates()); + newAutomaton.removeState(q); // preserve all states but q (the one to eliminate) + newAutomaton.setInputSymbols(extendedAutomaton.getInputAlphabet()); + newAutomaton.setInitialStates(extendedAutomaton.getInitialStates()); // sure that q is neither initial nor final (follows from step 2 - extending ExtendedNFA) + newAutomaton.setFinalStates(extendedAutomaton.getFinalStates()); + + regexp::RegExpOptimize opt; + + for(const auto& p: newAutomaton.getStates()) + { + for(const auto& r : newAutomaton.getStates()) + { + regexp::UnboundedRegExpConcatenation concat; + concat.appendElement(std::move(*transition(extendedAutomaton, p, q))); + concat.appendElement(regexp::UnboundedRegExpIteration(std::move(*transition(extendedAutomaton, q, q)))); + concat.appendElement(std::move(*transition(extendedAutomaton, q, r))); + + regexp::UnboundedRegExpAlternation alt; + alt.appendElement(std::move(*transition(extendedAutomaton, p, r))); + alt.appendElement(concat); + + regexp::UnboundedRegExp transitionRegExp(opt.optimize(regexp::UnboundedRegExp(alt))); + transitionRegExp.setAlphabet(extendedAutomaton.getInputAlphabet()); + newAutomaton.addTransition(p, regexp::RegExp{transitionRegExp}, r); + } + } + + return newAutomaton; +} + +const regexp::UnboundedRegExpElement* StateEliminationUnbounded::transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to) +{ + regexp::RegExpOptimize opt; + regexp::UnboundedRegExpAlternation ret; + + for(const auto& transition: automaton.getTransitionsFromState(from)) + if(transition.second.count(to) > 0) + ret.appendElement(static_cast<const regexp::UnboundedRegExp&>(transition.first.second.getData()).getRegExp()); + + return opt.optimize(regexp::UnboundedRegExp(ret)).getRegExp().clone(); +} + +template<> +automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::NFA& automaton) +{ + automaton::ExtendedNFA extendedAutomaton; + extendedAutomaton.setStates(automaton.getStates()); + extendedAutomaton.setInputSymbols(automaton.getInputAlphabet()); + extendedAutomaton.setInitialStates(automaton.getInitialStates()); + extendedAutomaton.setFinalStates(automaton.getFinalStates()); + + for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State> + { + regexp::UnboundedRegExp regexp(regexp::UnboundedRegExpSymbol(transition.first.second)); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + + for(const auto& to : transition.second) + extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to); + } + + return extendedAutomaton; +} + +template<> +automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::DFA& automaton) +{ + automaton::ExtendedNFA extendedAutomaton; + extendedAutomaton.setStates(automaton.getStates()); + extendedAutomaton.setInputSymbols(automaton.getInputAlphabet()); + extendedAutomaton.setInitialStates(std::set<automaton::State>{automaton.getInitialState()}); + extendedAutomaton.setFinalStates(automaton.getFinalStates()); + + for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State> + { + regexp::UnboundedRegExp regexp(regexp::UnboundedRegExpSymbol(transition.first.second)); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, transition.second); + } + + return extendedAutomaton; +} + +template<> +automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::EpsilonNFA& automaton) +{ + automaton::ExtendedNFA extendedAutomaton; + extendedAutomaton.setStates(automaton.getStates()); + extendedAutomaton.setInputSymbols(automaton.getInputAlphabet()); + extendedAutomaton.setInitialStates(automaton.getInitialStates()); + extendedAutomaton.setFinalStates(automaton.getFinalStates()); + + for(const auto& transition : automaton.getSymbolTransitions()) // pair<State, symb/eps> -> set<State> + { + regexp::UnboundedRegExp regexp(regexp::UnboundedRegExpSymbol(transition.first.second)); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + + for(const auto& to : transition.second) + extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to); + } + for(const auto& transition : automaton.getEpsilonTransitions()) + { + regexp::UnboundedRegExp regexp; + regexp.setRegExp(regexp::UnboundedRegExpEpsilon()); + regexp.setAlphabet(extendedAutomaton.getInputAlphabet()); + + for(const auto& to : transition.second) + extendedAutomaton.addTransition(transition.first, regexp::RegExp{regexp}, to); + } + + return extendedAutomaton; +} + +template<> +automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::ExtendedNFA& automaton) +{ + return automaton; +} + +void StateEliminationUnbounded::extendExtendedNFA(automaton::ExtendedNFA& automaton) +{ + const automaton::State& initState = *automaton.getInitialStates().begin(); + if(automaton.getFinalStates().count(initState) > 0 || automaton.getTransitionsToState(initState).size() > 0 ) + { + const automaton::State q0 = automaton::createUniqueState(initState, automaton.getStates()); + automaton.addState(q0); + + regexp::UnboundedRegExp regexp; + regexp.setRegExp(regexp::UnboundedRegExpEpsilon()); + regexp.setAlphabet(automaton.getInputAlphabet()); + automaton.addTransition(q0, regexp::RegExp{regexp}, initState); + + const std::set<automaton::State> automatonInitialStates = automaton.getInitialStates(); + for(const auto& state: automatonInitialStates) + automaton.removeInitialState(state); + automaton.addInitialState(q0); + } + + if(automaton.getFinalStates().size() > 1) + { + const automaton::State f = automaton::createUniqueState(automaton::State(label::Label(label::StringLabel("f"))), automaton.getStates()); + automaton.addState(f); + + const std::set<automaton::State> automatonFinalStates = automaton.getFinalStates(); + for(const auto &state : automatonFinalStates) + { + regexp::UnboundedRegExp regexp; + regexp.setRegExp(regexp::UnboundedRegExpEpsilon()); + regexp.setAlphabet(automaton.getInputAlphabet()); + automaton.addTransition(state, regexp::RegExp{regexp}, f); + + automaton.removeFinalState(state); + } + + automaton.addFinalState(f); + } +} + +const StateEliminationUnbounded StateEliminationUnbounded::STATE_ELIMINATION; + +} /* namespace fa2re */ diff --git a/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h new file mode 100644 index 0000000000..f5e081814c --- /dev/null +++ b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h @@ -0,0 +1,62 @@ +/* + * StateEliminationUnbounded.h + * + * Created on: 9. 2. 2014 + * Author: Tomas Pecka + */ + +#ifndef STATEELIMINATIONUNBOUNDED_H_ +#define STATEELIMINATIONUNBOUNDED_H_ + +#include <regexp/RegExp.h> +#include <regexp/unbounded/UnboundedRegExpElements.h> + +#include <automaton/Automaton.h> +#include <automaton/FSM/DFA.h> +#include <automaton/FSM/NFA.h> +#include <automaton/FSM/EpsilonNFA.h> +#include <automaton/FSM/ExtendedNFA.h> + +namespace fa2re +{ + +/** + * Converts FSM to RE using State Elimination algorithm. + * Source: Melichar 2.118 + */ +class StateEliminationUnbounded : public automaton::VisitableAutomatonBase::const_visitor_type +{ +public: + /** + * Performs conversion. + * @return regular expression equivalent to source NFA. + */ + template<class T> + static regexp::RegExp convert(const T& automaton); + +private: + void Visit(void*, const automaton::UnknownAutomaton& automaton) const; + void Visit(void*, const automaton::EpsilonNFA& automaton) const; + void Visit(void*, const automaton::NFA& automaton) const; + void Visit(void*, const automaton::DFA& automaton) const; + void Visit(void*, const automaton::ExtendedNFA& automaton) const; + void Visit(void*, const automaton::CompactNFA& automaton) const; + void Visit(void*, const automaton::NPDA& automaton) const; + void Visit(void*, const automaton::SinglePopNPDA& automaton) const; + void Visit(void*, const automaton::OneTapeDTM& automaton) const; + + template<class T> + static automaton::ExtendedNFA constructExtendedNFA(const T& automaton); + + static void extendExtendedNFA(automaton::ExtendedNFA& automaton); + + static const regexp::UnboundedRegExpElement* transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to); + + static automaton::ExtendedNFA eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& state); + + static const StateEliminationUnbounded STATE_ELIMINATION; +}; + +} /* namespace fa2re */ + +#endif /* STATEELIMINATIONUNBOUNDED_H_ */ diff --git a/tests.aconversion.sh b/tests.aconversion.sh index a433ccd025..73ecbc2d2d 100755 --- a/tests.aconversion.sh +++ b/tests.aconversion.sh @@ -85,7 +85,7 @@ function runTest2 { fi } -# $1 - aconversion sequence +# $1 - aconversion2 sequence function runTest { RES_GOOD=0 RES_FAIL=0 @@ -167,22 +167,22 @@ runTest "./aconversions2 -t LRG | ./aconversions2 -t RRG | ./aconversions2 -t FA runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t FA -a brzozowski" runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t FA -a thompson" runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t FA -a glushkov " -#runTest "./aconversion -t RE -a elimination | ./aconversion -t FA -a brzozowski" -#runTest "./aconversion -t RE -a elimination | ./aconversion -t FA -a thompson" -#runTest "./aconversion -t RE -a elimination | ./aconversion -t FA -a glushkov" +runTest "./aconversions2 -t RE -a elimination | ./aconversions2 -t FA -a brzozowski" +runTest "./aconversions2 -t RE -a elimination | ./aconversions2 -t FA -a thompson" +runTest "./aconversions2 -t RE -a elimination | ./aconversions2 -t FA -a glushkov" # FA -> RE -> RRG -> LRG -> FA # covers: FA -> RE (Brz. algebraic, elimination), RE -> RRG ( Brz. derivation, Glushkov), RRG -> LRG, LRG -> FA -# runTest "./aconversion -t RE -a algebraic | ./aconversion -t RRG -a brzozowski | ./aconversion -t LRG | ./aconversion -t FA" -# runTest "./aconversion -t RE -a algebraic | ./aconversion -t RRG -a glushkov | ./aconversion -t LRG | ./aconversion -t FA" -# runTest "./aconversion -t RE -a elimination | ./aconversion -t RRG -a brzozowski | ./aconversion -t LRG | ./aconversion -t FA" -# runTest "./aconversion -t RE -a elimination | ./aconversion -t RRG -a glushkov | ./aconversion -t LRG | ./aconversion -t FA" +runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t RRG -a brzozowski | ./aconversion2 -t LRG | ./aconversion2 -t FA" +runTest "./aconversions2 -ts2-t RE -a algebraic | ./aconversions2 -t RRG -a glushkov | ./aconversion2 -t LRG | ./aconversion2 -t FA" +runTest "./aconversions2 -ts2-t RE -a elimination | ./aconversions2 -t RRG -a brzozowski | ./aconversion2 -t LRG | ./aconversion2 -t FA" +runTest "./aconversions2 -ts2-t RE -a elimination | ./aconversions2 -t RRG -a glushkov | ./aconversion2 -t LRG | ./aconversion2 -t FA" # FA -> RRG -> RE -> FA # covers: FA -> RRG, FA -> LRG, RRG -> RE, LRG -> RE, RE -> FA (Brz. derivation, Thompson, Glushkov) -# runTest "./aconversion -t RRG | ./aconversion -t RE | ./aconversion -t FA -a brzozowski" -# runTest "./aconversion -t LRG | ./aconversion -t RE | ./aconversion -t FA -a brzozowski" -# runTest "./aconversion -t RRG | ./aconversion -t RE | ./aconversion -t FA -a thompson" -# runTest "./aconversion -t LRG | ./aconversion -t RE | ./aconversion -t FA -a thompson" -# runTest "./aconversion -t RRG | ./aconversion -t RE | ./aconversion -t FA -a glushkov" -# runTest "./aconversion -t LRG | ./aconversion -t RE | ./aconversion -t FA -a glushkov" +# runTest "./aconversion2 -t RRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a brzozowski" +# runTest "./aconversion2 -t LRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a brzozowski" +# runTest "./aconversion2 -t RRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a thompson" +# runTest "./aconversion2 -t LRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a thompson" +# runTest "./aconversion2 -t RRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a glushkov" +# runTest "./aconversion2 -t LRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a glushkov" -- GitLab