diff --git a/alib2algo/src/automaton/convert/ToRTEStateElimination.cpp b/alib2algo/src/automaton/convert/ToRTEStateElimination.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5317429c933f329ace873f6ece898fcd299b01d7 --- /dev/null +++ b/alib2algo/src/automaton/convert/ToRTEStateElimination.cpp @@ -0,0 +1,20 @@ +/* + * ToRTEStateElimination.cpp + * + * Created on: 13. 3. 2019 + * Author: Tomas Pecka + */ + +#include "ToRTEStateElimination.h" +#include <registration/AlgoRegistration.hpp> + +namespace automaton { + +namespace convert { + +auto ToRTEStateEliminationDFTA = registration::AbstractRegister< ToRTEStateElimination, rte::FormalRTE< ext::variant< DefaultSymbolType, DefaultStateType > >, const automaton::DFTA<>& > ( ToRTEStateElimination::convert, "automaton" ); +auto ToRTEStateEliminationNFTA = registration::AbstractRegister< ToRTEStateElimination, rte::FormalRTE< ext::variant< DefaultSymbolType, DefaultStateType > >, const automaton::NFTA<>& > ( ToRTEStateElimination::convert, "automaton" ); + +} /* namespace convert */ + +} /* namespace automaton */ diff --git a/alib2algo/src/automaton/convert/ToRTEStateElimination.h b/alib2algo/src/automaton/convert/ToRTEStateElimination.h new file mode 100644 index 0000000000000000000000000000000000000000..ca79e54640812654894d15db844b6b61297f5776 --- /dev/null +++ b/alib2algo/src/automaton/convert/ToRTEStateElimination.h @@ -0,0 +1,245 @@ +/* + * ToRTEStateElimination.h + * + * This file is part of Algorithms library toolkit. + * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz) + + * Algorithms library toolkit is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * Algorithms library toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with Algorithms library toolkit. If not, see <http://www.gnu.org/licenses/>. + * + * Created on: 13. 3. 2019 + * Author: Tomas Pecka + */ + +#ifndef TO_RTE_STATE_ELIMINATION_H_ +#define TO_RTE_STATE_ELIMINATION_H_ + +#include <algorithm> +#include <numeric> + +#include <rte/formal/FormalRTE.h> + +#include <automaton/Automaton.h> +#include <automaton/TA/DFTA.h> +#include <automaton/TA/ExtendedNFTA.h> +#include <automaton/TA/NFTA.h> + +#include <rte/formal/FormalRTEElements.h> + +#include <common/createUnique.hpp> + +#include <label/FinalStateLabel.h> + +#include <rte/simplify/RTEOptimize.h> + +namespace automaton { + +namespace convert { + +/** + * Converts a finite tree automaton to a regular tree expression using using the State Elimination algorithm (Travnicek: Not yet published) + * This algorithm returns the regular tree expression as rte::FormalRTE. + */ +class ToRTEStateElimination { +public: + /** + * Performs conversion. + * @tparam T type of the finite tree automaton + * @tparam SymbolType the type of symbol of the automaton's input ranked alphabet + * @tparam StateType the type of states of the automaton + * @param automaton finite tree automaton to convert + * @return formal regular tree expression equivalent to the original automaton + */ + template < class T, class SymbolType = DefaultSymbolType, class StateType = DefaultStateType > + static rte::FormalRTE< ext::variant< SymbolType, StateType > > convert ( const T& automaton ); + +private: + /** + * Helper function to create new final state in the automaton + * @tparam SymbolType the type of symbol of the automaton's input ranked alphabet + * @tparam StateType the type of states of the automaton + * @param automaton extended finite tree automaton + */ + template < class SymbolType, class StateType > + static void extendExtendedNFTA ( automaton::ExtendedNFTA< SymbolType, StateType >& automaton ); + + /** + * Helper function for the elimination of a single state according to the algorithm. + * @tparam SymbolType the type of symbol of the automaton's input ranked alphabet + * @tparam StateType the type of states of the automaton + * @param extendedAutomaton automaton for the elimination + * @param state a state to eliminate + * @return the @p extendedAutomaton after the elimination of a state @state. + */ + template < class SymbolType, class StateType > + static automaton::ExtendedNFTA< SymbolType, StateType > eliminateState ( const automaton::ExtendedNFTA< SymbolType, StateType >& extendedAutomaton, const StateType& state ); + + /** + * Helper function to create RTE alternation from several transition's rtexps + * @tparam SymbolType the type of symbol of the automaton's input ranked alphabet + * @tparam StateType the type of states of the automaton + * @param transitions transitions with RTEs to alternate + * @return the @p extendedAutomaton after the elimination of a state @state. + */ + template < class SymbolType, class StateType > + static rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > createAlternation ( const ext::vector< ext::pair< ext::pair< rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >, ext::vector< StateType > >, StateType > >& transitions ); +}; + +template < class T, class SymbolType, class StateType > +rte::FormalRTE< ext::variant< SymbolType, StateType > > ToRTEStateElimination::convert ( const T& automaton ) { + if ( automaton.getFinalStates ( ).size ( ) == 0 ) + return rte::FormalRTE< ext::variant< SymbolType, StateType > > ( ); + + // create an automaton, extend it with a new final state + automaton::ExtendedNFTA< SymbolType, StateType > extendedAutomaton ( automaton ); + extendExtendedNFTA ( extendedAutomaton ); + + // eliminate all non-final states + ext::set< StateType > statesToEliminate; + std::set_difference ( extendedAutomaton.getStates ( ).begin ( ), extendedAutomaton.getStates ( ).end ( ), + extendedAutomaton.getFinalStates ( ).begin ( ), extendedAutomaton.getFinalStates ( ).end ( ), + std::inserter ( statesToEliminate, statesToEliminate.begin ( ) ) ); + + for ( const StateType& state : statesToEliminate ) { + extendedAutomaton = eliminateState ( extendedAutomaton, state ); + } + + // step 4 + ext::vector< ext::pair< ext::pair< rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >, ext::vector< StateType > >, StateType > > alt; + for ( const auto& tr : extendedAutomaton.getTransitions ( ) ) + for ( const StateType& targetState : tr.second ) + alt.push_back ( ext::make_pair ( tr.first, targetState ) ); + + return rte::FormalRTE< ext::variant< SymbolType, StateType > > ( rte::simplify::RTEOptimize::optimize ( createAlternation ( alt ) ) ); +} + +template < class SymbolType, class StateType > +rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > ToRTEStateElimination::createAlternation ( const ext::vector< ext::pair< ext::pair< rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >, ext::vector< StateType > >, StateType > >& transitions ) { + if ( transitions.size ( ) == 0 ) + return rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > ( rte::FormalRTEEmpty< ext::variant< SymbolType, StateType > > ( ) ); + if ( transitions.size ( ) == 1 ) + return transitions.at ( 0 ).first.first; + + static auto alt = [] ( const rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >& sum, const ext::pair< ext::pair< rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >, ext::vector< StateType > >, StateType >& transition ) { + return rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > ( rte::FormalRTEAlternation< ext::variant< SymbolType, StateType > > ( sum.getStructure ( ), transition.first.first.getStructure ( ) ) ); + }; + + return std::accumulate ( transitions.begin ( ), transitions.end ( ), rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > ( ), alt ); +} + +template < class SymbolType, class StateType > +automaton::ExtendedNFTA< SymbolType, StateType > ToRTEStateElimination::eliminateState ( const automaton::ExtendedNFTA< SymbolType, StateType >& automaton, const StateType& q ) { + // create state symbol in RTE's K alphabet + const rte::FormalRTESymbolSubst< ext::variant< SymbolType, StateType > > stateSymbol ( common::ranked_symbol< ext::variant< SymbolType, StateType > > ( q, 0 ) ); + + // create new automaton, without state Q + automaton::ExtendedNFTA< SymbolType, StateType > newAutomaton ( automaton.getStates ( ), automaton.getInputAlphabet ( ), automaton.getFinalStates ( ) ); + newAutomaton.removeState ( q ); // preserve all states but q (the one to eliminate) + + // divide transitions into the following groups: + // - loop(Q) - Q is in sources AND Q is a target + // - incoming(Q) - Q is NOT in sources AND Q is a target + // - outgoing(Q) - Q is in sources AND Q is NOT a target + // - not_take_part(Q) - Q is NOT in sources AND Q is NOT a target + // + // also identify prev(incoming(Q)), loop(incoming(Q)) + ext::vector< ext::pair< ext::pair< rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >, ext::vector< StateType > >, StateType > > loop, incoming, outgoing; + ext::set< StateType > prev_loop, prev_incoming; + + for ( const auto& transition : automaton.getTransitions ( ) ) { + for ( const StateType& target : transition.second ) { + const ext::vector< StateType >& src_states = transition.first.second; + + const bool is_source = std::find ( src_states.begin ( ), src_states.end ( ), q ) != src_states.end ( ); + const bool is_target = target == q; + + if ( is_source && is_target ) { // loop + loop.push_back ( ext::make_pair ( transition.first, target ) ); + prev_loop.insert ( transition.first.second.begin ( ), transition.first.second.end ( ) ); + } else if ( ! is_source && is_target ) { // incoming + incoming.push_back ( ext::make_pair ( transition.first, target ) ); + prev_incoming.insert ( transition.first.second.begin ( ), transition.first.second.end ( ) ); + } else if ( is_source && ! is_target ) { //outgoing + outgoing.push_back ( ext::make_pair ( transition.first, target ) ); + } else /* ( ! is_source && ! is_target ) */ { // not_take_part + newAutomaton.addTransition ( transition.first.first, transition.first.second, target ); + } + } + } + + const rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > rte_loop = createAlternation ( loop ), + rte_incoming = createAlternation ( incoming ); + + // eliminate all incoming and outgoing transitions as follows: + // for every outgoing transition: + // - do the SUBST(outgoing, SUBST(iter(loops), incoming)) operation + for ( const auto& transition : outgoing ) { + const rte::FormalRTEStructure< ext::variant< SymbolType, StateType > >& tr_rte = transition.first.first; + const ext::vector< StateType >& tr_src_states = transition.first.second; + const StateType& tr_target = transition.second; + + // by eliminating Q, the new transition T will be in the from ( merge(prev_loop, prev_incoming, prev_outgoing) ) -> outgoing.target + // The order of prev_states is not important as the order is present in the RTE structure since the first conversion to ExtendedNFTA + ext::set< StateType > prevStates; + prevStates.insert ( prev_loop.begin ( ), prev_loop.end ( ) ); + prevStates.insert ( prev_incoming.begin ( ), prev_incoming.end ( ) ); + prevStates.insert ( tr_src_states.begin ( ), tr_src_states.end ( ) ); + prevStates.erase ( q ); + + rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > rte ( + rte::FormalRTESubstitution< ext::variant< SymbolType, StateType > > ( + tr_rte.getStructure ( ), // outgoing rte + rte::FormalRTESubstitution< ext::variant< SymbolType, StateType > > ( + rte::FormalRTEIteration< ext::variant< SymbolType, StateType > > ( rte_loop.getStructure ( ), stateSymbol ), + rte_incoming.getStructure ( ), stateSymbol ), + stateSymbol ) ); + + // TATA pattern + /* + rte::FormalRTEStructure < ext::variant < SymbolType, StateType > > rte ( + rte::FormalRTESubstitution < ext::variant < SymbolType, StateType > > ( + rte::FormalRTESubstitution < ext::variant < SymbolType, StateType > > ( + tr_rte.getStructure ( ), // outgoing rte + rte::FormalRTEIteration < ext::variant < SymbolType, StateType > > ( rte_loop.getStructure ( ), stateSymbol ), + stateSymbol ), + rte_incoming.getStructure ( ), + stateSymbol ) ); + */ + + newAutomaton.addTransition ( rte::simplify::RTEOptimize::optimize ( rte ), ext::vector< StateType > ( prevStates.begin ( ), prevStates.end ( ) ), tr_target ); + } + + return newAutomaton; +} + +template < class SymbolType, class StateType > +void ToRTEStateElimination::extendExtendedNFTA ( automaton::ExtendedNFTA< SymbolType, StateType >& automaton ) { + // create new state with final label + const StateType f = common::createUnique ( label::FinalStateLabel::instance< StateType > ( ), automaton.getStates ( ) ); + automaton.addState ( f ); + + // create transitions from all final states to the new state, the transition contains only reference (RTE's K symbol) to the previous final state + for ( const StateType& state : automaton.getFinalStates ( ) ) { + const rte::FormalRTEStructure< ext::variant< SymbolType, StateType > > expr ( rte::FormalRTESymbolSubst< ext::variant< SymbolType, StateType > > ( common::ranked_symbol< ext::variant< SymbolType, StateType > > ( state, 0 ) ) ); + automaton.addTransition ( expr, { state }, f ); + } + + // new state is the only final + automaton.setFinalStates ( { f } ); +} + +} /* namespace convert */ + +} /* namespace automaton */ + +#endif /* TO_RTE_STATE_ELIMINATION_H_ */ diff --git a/alib2integrationtest/test-src/tests/conversionsTest2.cpp b/alib2integrationtest/test-src/tests/conversionsTest2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5aac5b356bf959733d586601851deca0e8e461a --- /dev/null +++ b/alib2integrationtest/test-src/tests/conversionsTest2.cpp @@ -0,0 +1,66 @@ +#include <alib/vector> +#include <catch2/catch.hpp> + +#include "testing/TestFiles.hpp" +#include "testing/TimeoutAqlTest.hpp" + +const unsigned RAND_STATES = 15; +const unsigned RAND_ALPHABET = 5; +const unsigned RAND_RANK = 4; +const double RAND_DENSITY = 5; +const size_t ITERATIONS = 50; + +const std::string qGenNFTA ( ) { + std::ostringstream oss; + oss << "execute automaton::generate::RandomTreeAutomatonFactory "; + oss << "(size_t)" << rand ( ) % RAND_STATES + 1 << " "; + oss << "(size_t)" << rand ( ) % RAND_ALPHABET + 1 << " "; + oss << "(size_t)" << rand ( ) % RAND_RANK << " "; + oss << "(bool)true "; + oss << "(double)\"" << RAND_DENSITY << "\""; + return oss.str ( ); +} + +TEST_CASE ( "FTA-RTE conversions test", "[integration]" ) { + static const std::string qMinimize ( "automaton::determinize::Determinize - | automaton::simplify::Trim - | automaton::simplify::Minimize - | automaton::simplify::Normalize -" ); + + SECTION ( "RTE Files test" ) { + for ( const std::string& inputFile : TestFiles::Get ( "/rte/rte*.xml$" ) ) { + ext::vector< std::string > qs = { + "execute < " + inputFile + " | rte::convert::ToFTAGlushkov - > $gen", + "quit compare::AutomatonCompare <( $gen | " + qMinimize + " )" + " <( $gen | automaton::convert::ToRTEStateElimination - | rte::convert::ToFTAGlushkov - | " + qMinimize + ")" }; + + TimeoutAqlTest ( 10s, qs ); + } + } + + SECTION ( "RTE Random tests" ) { + for ( size_t i = 0; i < ITERATIONS; i++ ) { + ext::vector< std::string > qs = { + qGenNFTA ( ) + " > $gen", + "quit compare::AutomatonCompare <( $gen | " + qMinimize + " )" + " <( $gen | automaton::convert::ToRTEStateElimination - | rte::convert::ToFTAGlushkov - | " + qMinimize + ")" }; + + TimeoutAqlTest ( 10s, qs ); + } + } + + // ------------------------------------------------------------------------------------------------------ + + SECTION ( "DFTA Files test" ) { + auto files = GENERATE ( + TestFiles::Get ( "/automaton/DFTA.*.xml$" ), + TestFiles::Get ( "/automaton/NFTA.*.xml$" ) ); + + for ( const std::string& file : files ) { + ext::vector< std::string > qs = { + "execute < " + file + " > $fta1", + "execute $fta1 | automaton::convert::ToRTEStateElimination - | rte::convert::ToFTAGlushkov - > $fta2", + "execute $fta1 | " + qMinimize + " > $m1", + "execute $fta2 | " + qMinimize + " > $m2", + "quit compare::AutomatonCompare $m1 $m2", + }; + + TimeoutAqlTest ( 10s, qs ); + } + } +}