From c75612e989e13781119e780ad780e673139ec30d Mon Sep 17 00:00:00 2001 From: Tomas Pecka <tomas.pecka@fit.cvut.cz> Date: Sun, 8 Mar 2020 12:00:51 +0100 Subject: [PATCH] algo: Add algorithm for existence of a synchronizing word on DFA --- .../properties/SynchronizingWordExistence.cpp | 19 ++++ .../properties/SynchronizingWordExistence.h | 96 +++++++++++++++++++ .../properties/SynchronizingWordExistence.cpp | 58 +++++++++++ 3 files changed, 173 insertions(+) create mode 100644 alib2algo/src/automaton/properties/SynchronizingWordExistence.cpp create mode 100644 alib2algo/src/automaton/properties/SynchronizingWordExistence.h create mode 100644 alib2algo/test-src/automaton/properties/SynchronizingWordExistence.cpp diff --git a/alib2algo/src/automaton/properties/SynchronizingWordExistence.cpp b/alib2algo/src/automaton/properties/SynchronizingWordExistence.cpp new file mode 100644 index 0000000000..7adb81dbfb --- /dev/null +++ b/alib2algo/src/automaton/properties/SynchronizingWordExistence.cpp @@ -0,0 +1,19 @@ +/* + * SynchronizingWordExistence.cpp + * + * Created on: 8. 3. 2020 + * Author: Tomas Pecka + */ + +#include "SynchronizingWordExistence.h" +#include <registration/AlgoRegistration.hpp> + +namespace { + +auto SynchronizingWordExistenceDFA = registration::AbstractRegister < automaton::properties::SynchronizingWordExistence, bool, const automaton::DFA < > & > ( automaton::properties::SynchronizingWordExistence::exists, "dfa" ).setDocumentation ( +"Checks whether the given DFA has a synchronizing word.\n\ +\n\ +@param dfa automaton\n\ +@return boolean indicating whether the automaton has a synchronizing word" ); + +} /* namespace */ diff --git a/alib2algo/src/automaton/properties/SynchronizingWordExistence.h b/alib2algo/src/automaton/properties/SynchronizingWordExistence.h new file mode 100644 index 0000000000..1cf13953a9 --- /dev/null +++ b/alib2algo/src/automaton/properties/SynchronizingWordExistence.h @@ -0,0 +1,96 @@ +/* + * SynchronizingWordExistence.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: 8. 3. 2020 + * Author: Tomas Pecka + */ + +#ifndef SYNCHRONIZING_WORD_EXISTENCE_H_ +#define SYNCHRONIZING_WORD_EXISTENCE_H_ + +#include <alib/set> +#include <queue> + +#include <automaton/FSM/DFA.h> +#include <automaton/transform/AutomataIntersectionCartesianProduct.h> + +namespace automaton::properties { + +/** + * Algorithm determining the existence of a synchronizing word on an automaton. + */ +class SynchronizingWordExistence { +public: + /** + * Checks whether the given DFA has a synchronizing word. + * + * @param dfa automaton + * @return boolean indicating whether the automaton has a synchronizing word + */ + template < class SymbolType, class StateType > + static bool exists ( const automaton::DFA < SymbolType, StateType > & dfa ); +}; + +template < class SymbolType, class StateType > +bool SynchronizingWordExistence::exists ( const automaton::DFA < SymbolType, StateType > & dfa ) { + /* + * It must hold that (1) <=> (2) + * (1) DFA is synchronizing + * (2) Foreach q1, q2 in Q, exists a word w such that \delta(q1, w) = \delta(q2, w) + */ + + /* Therefore the algorithm is as follows: + * Create a parallel run of the automaton with itseld + * All pairs (q1, q2) \in Q x Q MUST have a path to some state-pair (q, q). + * -> For every pair (q,q) run a BFS search and check that all pairs (q1, q2) are visited + */ + + automaton::DFA < SymbolType, ext::pair < StateType, StateType > > cart = automaton::transform::AutomataIntersectionCartesianProduct:: intersection ( dfa, dfa ); + + std::queue < ext::pair < StateType, StateType > > q; + ext::set < ext::pair < StateType, StateType > > visited; + + for ( const ext::pair < StateType, StateType > state: cart.getStates ( ) ) { + if ( state.first != state.second ) + continue; + + visited.insert ( state ); + q.push ( state ); + } + + while ( ! q.empty ( ) ) { + const ext::pair < StateType, StateType > cstate = std::move ( q.front ( ) ); + q.pop ( ); + + for ( const auto transition : cart.getTransitionsToState ( cstate ) ) { + const auto & srcState = transition.first.first; + + if ( visited.count ( srcState ) == 0 ) { + visited.insert ( srcState ); + q.push ( srcState ); + } + } + } + + return visited == cart.getStates ( ); +} + +} /* namespace automaton::properties */ + +#endif /* SYNCHRONIZING_WORD_EXISTENCE_H_ */ diff --git a/alib2algo/test-src/automaton/properties/SynchronizingWordExistence.cpp b/alib2algo/test-src/automaton/properties/SynchronizingWordExistence.cpp new file mode 100644 index 0000000000..c43d722c37 --- /dev/null +++ b/alib2algo/test-src/automaton/properties/SynchronizingWordExistence.cpp @@ -0,0 +1,58 @@ +#include <catch2/catch.hpp> + +#include <automaton/FSM/DFA.h> + +#include "automaton/properties/SynchronizingWordExistence.h" + +TEST_CASE ( "Synchronizing Word Existence", "[unit][algo][automaton]" ) { + SECTION ( "Test 1" ) { + DefaultStateType q1 = DefaultStateType ("q1"); + DefaultStateType q2 = DefaultStateType ("q2"); + DefaultStateType q3 = DefaultStateType ("q3"); + + DefaultSymbolType a = DefaultSymbolType ("a"); + DefaultSymbolType b = DefaultSymbolType ("b"); + + automaton::DFA< > automaton(q1); + automaton.addInputSymbols({a, b}); + automaton.setStates({q1, q2, q3}); + + automaton.addTransition(q1, a, q1); + automaton.addTransition(q2, a, q2); + automaton.addTransition(q3, a, q3); + automaton.addTransition(q1, b, q2); + automaton.addTransition(q2, b, q3); + automaton.addTransition(q3, b, q1); + + CHECK(! automaton::properties::SynchronizingWordExistence::exists(automaton)); + } + + SECTION ( "Test 2" ) { + DefaultStateType q0 = DefaultStateType ("q0"); + DefaultStateType q1 = DefaultStateType ("q1"); + DefaultStateType q2 = DefaultStateType ("q2"); + DefaultStateType q3 = DefaultStateType ("q3"); + DefaultStateType q4 = DefaultStateType ("q4"); + + DefaultSymbolType a = DefaultSymbolType ("a"); + DefaultSymbolType b = DefaultSymbolType ("b"); + + automaton::DFA< > automaton(q0); + automaton.addInputSymbols({a, b}); + automaton.setStates({q0, q1, q2, q3, q4}); + + automaton.addTransition(q0, a, q1); + automaton.addTransition(q1, a, q1); + automaton.addTransition(q2, a, q2); + automaton.addTransition(q3, a, q3); + automaton.addTransition(q4, a, q4); + + automaton.addTransition(q0, b, q1); + automaton.addTransition(q1, b, q2); + automaton.addTransition(q2, b, q3); + automaton.addTransition(q3, b, q4); + automaton.addTransition(q4, b, q0); + + CHECK(automaton::properties::SynchronizingWordExistence::exists(automaton)); + } +} -- GitLab