diff --git a/alib2algo/src/automaton/generate/RandomTreeAutomatonFactory.cpp b/alib2algo/src/automaton/generate/RandomTreeAutomatonFactory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45b6f6aef4b036aa32b369c8478fe0cdc8b700f5 --- /dev/null +++ b/alib2algo/src/automaton/generate/RandomTreeAutomatonFactory.cpp @@ -0,0 +1,93 @@ +/* + * RandomAutomatonFactory.cpp + * + * Created on: 27. 3. 2014 + * Author: Tomas Pecka + */ + +#include "RandomTreeAutomatonFactory.h" +#include <registration/AlgoRegistration.hpp> + +namespace automaton { + +namespace generate { + +automaton::NFTA < std::string, DefaultRankType, unsigned > RandomTreeAutomatonFactory::generateNFTA( size_t statesCount, size_t alphabetSize, size_t maxRank, bool randomizedAlphabet, double density ) { + if(alphabetSize > 26) + throw exception::CommonException("Too big alphabet."); + + if(alphabetSize < 1) + throw exception::CommonException("Too small alphabet."); + + ext::deque < std::string > alphabet; + for(char i = 'a'; i <= 'z'; i++) + alphabet.push_back ( std::string ( 1, i ) ); + + if(randomizedAlphabet) + shuffle(alphabet.begin(), alphabet.end(), ext::random_devices::semirandom); + + alphabet.resize ( alphabetSize ); + + ext::deque < common::ranked_symbol < std::string, DefaultRankType > > rankedAlphabet; + + for ( std::string && symbol : ext::make_mover ( alphabet ) ) { + rankedAlphabet.push_back ( common::ranked_symbol < std::string, DefaultRankType > ( std::move ( symbol ), ext::random_devices::semirandom() % ( maxRank + 1 ) ) ); + } + + bool containsNullary = false; + for ( common::ranked_symbol < std::string, DefaultRankType > & rankedSymbol : rankedAlphabet ) { + containsNullary |= rankedSymbol.getRank ( ) == 0; + } + + if ( ! containsNullary ) { + int c = ext::random_devices::semirandom() % rankedAlphabet.size ( ); + rankedAlphabet [ c ] = common::ranked_symbol ( std::move ( rankedAlphabet [ c ].getSymbol ( ) ), 0u ); + } + + return RandomTreeAutomatonFactory::LeslieConnectedNFTA( statesCount, rankedAlphabet, density ); +} + +unsigned RandomTreeAutomatonFactory::ithAccessibleState ( const ext::deque < bool > & VStates, size_t i ) { + i ++; + for( size_t j = 0; j < VStates.size ( ); j++ ) { + if( VStates[ j ] == true ) + i --; + + if( i == 0 ) + return i; + } + throw std::logic_error ( "Not enough states in deque of visited states" ); +} + +unsigned RandomTreeAutomatonFactory::ithInaccessibleState ( const ext::deque < bool > & VStates, size_t i ) { + i ++; + for( size_t j = 0; j < VStates.size ( ); j++ ) { + if( VStates[ j ] == false ) + i --; + + if( i == 0 ) + return j; + } + throw std::logic_error ( "Not enough states in deque of visited states" ); +} + +auto GenerateNFTA1 = registration::AbstractRegister < RandomTreeAutomatonFactory, automaton::NFTA < DefaultSymbolType, DefaultRankType, unsigned >, size_t, const ext::set < common::ranked_symbol < DefaultSymbolType, DefaultRankType > > &, double > ( RandomTreeAutomatonFactory::generateNFTA, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "statesCount", "alphabet", "density" ).setDocumentation ( +"Generates a random finite automaton.\n\ +@param statesCount number of states in the generated automaton\n\ +@param alphabet Input alphabet of the automaton\n\ +@param density density of the transition function\n\ +@return random nondeterministic finite automaton" ); + +auto GenerateNFTA2 = registration::AbstractRegister < RandomTreeAutomatonFactory, automaton::NFTA < std::string, DefaultRankType, unsigned >, size_t, size_t, size_t, bool, double > ( RandomTreeAutomatonFactory::generateNFTA, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "statesCount", "alphabetSize", "maxRank", "randomizedAlphabet", "density" ).setDocumentation ( +"Generates a random finite automaton.\n\ +\n\ +@param statesCount number of states in the generated automaton\n\ +@param alphabetSize size of the alphabet (1-26)\n\ +@param maxRank the maximum rank in the randomly generated alphabet\n\ +@param randomizedAlphabet selects random symbols from a-z range if true\n\ +@param density density of the transition function (0-1). 1 means every possible transition is created\n\ +@return random nondeterministic finite automaton" ); + +} /* namespace generate */ + +} /* namespace automaton */ diff --git a/alib2algo/src/automaton/generate/RandomTreeAutomatonFactory.h b/alib2algo/src/automaton/generate/RandomTreeAutomatonFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..2ed02081f913d193ee4d5ca501b9f9c219f9d773 --- /dev/null +++ b/alib2algo/src/automaton/generate/RandomTreeAutomatonFactory.h @@ -0,0 +1,233 @@ +/* + * RandomAutomatonFactory.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: 27. 3. 2014 + * Author: Tomas Pecka + */ + +#ifndef RANDOM_AUTOMATON_FACTORY_H_ +#define RANDOM_AUTOMATON_FACTORY_H_ + +#include <alib/deque> +#include <alib/set> +#include <alib/algorithm> +#include <alib/random> +#include <alib/string> + +#include <exception/CommonException.h> + +#include <automaton/TA/NFTA.h> + +namespace automaton { + +namespace generate { + +/** + * Generator of random automata. + * + * \details + * The underlying generation algorithm is from Leslie, T: Efficient Approaches to Subset Construction, 1995. + */ +class RandomTreeAutomatonFactory { +public: + /** + * Generates a random finite automaton. + * \tparam SymbolType the type of terminal symbols of the random automaton + * + * \param statesCount number of states in the generated automaton + * \param alphabet Input alphabet of the automaton + * \param density density of the transition function + * + * \return random nondeterministic finite automaton + */ + template < class SymbolType, class RankType > + static automaton::NFTA < SymbolType, RankType, unsigned > generateNFTA ( size_t statesCount, const ext::set < common::ranked_symbol < SymbolType, RankType > > & alphabet, double density ); + + /** + * \overload + * + * Generates a random finite automaton. + * + * \param statesCount number of states in the generated automaton + * \param alphabetSize size of the alphabet (1-26) + * \param maxRank the maximum rank in the randomly generated alphabet + * \param randomizedAlphabet selects random symbols from a-z range if true + * \param density density of the transition function (0-1). 1 means every possible transition is created + * + * \return random nondeterministic finite automaton + */ + static automaton::NFTA < std::string, DefaultRankType, unsigned > generateNFTA ( size_t statesCount, size_t alphabetSize, size_t maxRank, bool randomizedAlphabet, double density ); + +private: + /** + * Selects ith accessible state form \p VStates. + * + * \param VStates the states to select from + * \param i the specification which accessble state to select + * + * \return ith accessible state based on VStates + */ + static unsigned ithAccessibleState ( const ext::deque < bool > & VStates, size_t i ); + + /** + * Selects ith inaccessible state form \p VStates. + * + * \param VStates the states to select from + * \param i the specification which inaccessble state to select + * + * \return ith inaccessible state based on VStates + */ + static unsigned ithInaccessibleState ( const ext::deque < bool > & VStates, size_t i ); + + /** + * Leslie's connected NFTA algorithm + * + * \tparam SymbolType the type of terminal symbols of the random automaton + * + * \param n number of states + * \param alphabet input alphabet + * \param density density of the transition function (0-1). 1 means every possible transition is created + * + * \return the actual random nondeterministic automaton + */ + template < class SymbolType, class RankType > + static automaton::NFTA < SymbolType, RankType, unsigned > LeslieConnectedNFTA ( size_t n, const ext::deque < common::ranked_symbol < SymbolType, RankType > > & alphabet, double density ); +}; + +template < class SymbolType, class RankType > +automaton::NFTA < SymbolType, RankType, unsigned > RandomTreeAutomatonFactory::generateNFTA ( size_t statesCount, const ext::set < common::ranked_symbol < SymbolType, RankType > > & alphabet, double density ) { + ext::deque < common::ranked_symbol < SymbolType, RankType > > alphabet2 ( alphabet.begin ( ), alphabet.end ( ) ); + return RandomTreeAutomatonFactory::LeslieConnectedNFTA ( statesCount, alphabet2, density ); +} + +template < class SymbolType, class RankType > +automaton::NFTA < SymbolType, RankType, unsigned > RandomTreeAutomatonFactory::LeslieConnectedNFTA ( size_t n, const ext::deque < common::ranked_symbol < SymbolType, RankType > > & alphabet, double density ) { + if( alphabet.size( ) <= 0 ) + throw exception::CommonException( "Alphabet size must be greater than 0." ); + + ext::deque < common::ranked_symbol < SymbolType, RankType > > nullaryAlphabet; + for ( const common::ranked_symbol < SymbolType, RankType > & symbol : alphabet ) + if ( symbol.getRank ( ) == 0 ) + nullaryAlphabet.push_back ( symbol ); + + if ( nullaryAlphabet.empty ( ) ) + throw exception::CommonException ( "Alphabet does not contain a nullary symbol" ); + + ext::deque<bool> VStates; + ext::deque<unsigned> Q; + int unvisited; + + automaton::NFTA < SymbolType, RankType, unsigned > automaton; + + for( const auto & s : alphabet ) + automaton.addInputSymbol( s ); + + for( size_t i = 0; i < n; i ++ ) { + VStates.push_back( false ); + Q.push_back( i ); + automaton.addState( Q[ i ] ); + } + + if( n == 0 ) { + return automaton; + } else if( n == 1 ) { + automaton.addTransition( nullaryAlphabet[ ext::random_devices::semirandom() % nullaryAlphabet.size( ) ], ext::vector < unsigned > { }, Q[ 0 ] ); + + unvisited = 0; + VStates[ 0 ] = true; + } else { + unsigned x = ext::random_devices::semirandom() % n; + automaton.addTransition( nullaryAlphabet[ ext::random_devices::semirandom() % nullaryAlphabet.size( ) ], ext::vector < unsigned > { }, Q[ x ] ); + unvisited = n - 1; + + VStates[ x ] = true; + } + + while( unvisited != 0 ) { + int c = ext::random_devices::semirandom() % alphabet.size( ); + ext::vector < unsigned > from; + while ( from.size ( ) < alphabet [ c ].getRank ( ) ) { + size_t a = ithAccessibleState ( VStates, ext::random_devices::semirandom() % ( n - unvisited ) ); // select y-th accessible state + from.push_back ( Q[ a ] ); + } + + size_t b = ithInaccessibleState ( VStates, ext::random_devices::semirandom() % unvisited ); // select z-th inaccessible state + + automaton.addTransition( alphabet[ c ], std::move ( from ), Q[ b ] ); + + unvisited -= 1; + VStates[ b ] = true; + } + + for( const unsigned & q : Q ) { + if( automaton.getTransitionsFromState( q ).size( ) == 0 && ext::random_devices::semirandom() % 100 < 90 ) + automaton.addFinalState( q ); + else if( automaton.getTransitionsFromState( q ).size( ) > 0 && ext::random_devices::semirandom() % 100 < 10 ) + automaton.addFinalState( q ); + } + + if( automaton.getFinalStates( ).size( ) == 0 ) { + if( n == 1 ) { + automaton.addFinalState( * automaton.getStates( ).begin ( ) ); + } else { + for( const unsigned & q : Q ) { + if( automaton.getTransitionsFromState( q ).size( ) == 0 ) { + automaton.addFinalState( q ); + break; + } + } + } + } + + size_t accSourceStates = 1; + for ( const common::ranked_symbol < SymbolType, RankType > & symbol : alphabet ) + accSourceStates += symbol.getRank ( ); + + double mnn100 = 100.0 / alphabet.size( ) / accSourceStates / n; //FIXME check the computation of density + while( automaton.getTransitions ( ).size ( ) * mnn100 < density ) { + int a = ext::random_devices::semirandom() % alphabet.size( ); + ext::vector < unsigned > from; + while ( from.size ( ) < alphabet [ a ].getRank ( ) ) { + int y = ext::random_devices::semirandom() % n; + from.push_back ( Q[ y ] ); + } + + int z = ext::random_devices::semirandom() % n; + + automaton.addTransition( alphabet [ a ], std::move ( from ), Q[ z ] ); + } + + ext::map < common::ranked_symbol < SymbolType, RankType >, bool> alphabetUsage; + for( const auto & a : automaton.getInputAlphabet( ) ) + alphabetUsage[ a ] = false; + for( const auto & t : automaton.getTransitions( ) ) + alphabetUsage[ t.first.first ] = true; + + for( const auto & kv : alphabetUsage ) + if( kv.second == false ) + automaton.removeInputSymbol( kv.first ); + + return automaton; +} + +} /* namespace generate */ + +} /* namespace automaton */ + +#endif /* RANDOM_AUTOMATON_FACTORY_H_ */