From bb5c4fdc3cff2fe768def0b46043451a0fc4fc7c Mon Sep 17 00:00:00 2001 From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz> Date: Tue, 7 Nov 2017 09:41:37 +0100 Subject: [PATCH] add minimize generator --- .../src/automaton/MinimizeGenerator.cpp | 61 +++++ .../src/automaton/MinimizeGenerator.h | 26 +++ .../src/automaton/RandomAutomatonFactory2.cpp | 68 ++++++ .../src/automaton/RandomAutomatonFactory2.h | 210 ++++++++++++++++++ 4 files changed, 365 insertions(+) create mode 100644 alib2algo_experimental/src/automaton/MinimizeGenerator.cpp create mode 100644 alib2algo_experimental/src/automaton/MinimizeGenerator.h create mode 100644 alib2algo_experimental/src/automaton/RandomAutomatonFactory2.cpp create mode 100644 alib2algo_experimental/src/automaton/RandomAutomatonFactory2.h diff --git a/alib2algo_experimental/src/automaton/MinimizeGenerator.cpp b/alib2algo_experimental/src/automaton/MinimizeGenerator.cpp new file mode 100644 index 0000000000..ba482c6fe0 --- /dev/null +++ b/alib2algo_experimental/src/automaton/MinimizeGenerator.cpp @@ -0,0 +1,61 @@ +/* + * RandomAutomatonFactory.cpp + * + * Created on: 6. 11. 2017 + * Author: Jan Travnicek + */ + +#include "MinimizeGenerator.h" +#include "RandomAutomatonFactory2.h" +#include <registration/AlgoRegistration.hpp> +#include <automaton/simplify/UnreachableStatesRemover.h> +#include <automaton/simplify/UselessStatesRemover.h> +#include <automaton/simplify/Minimize.h> + +#include <exception/CommonException.h> + +namespace automaton { + +namespace generate { + +automaton::DFA < char, unsigned > MinimizeGenerator::generateMinimizeDFA ( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, size_t alphabetSize, bool randomizedAlphabet, double density, size_t expectedSteps ) { + size_t limit = 10000; + while ( limit -- ) { + automaton::DFA < char, unsigned > automaton = RandomAutomatonFactory2::generateDFA ( statesMinimal, statesDuplicates, statesUnreachable, statesUseless, alphabetSize, randomizedAlphabet, density ); + + size_t states = automaton.getStates ( ).size ( ); + automaton::DFA < char, unsigned > unreachable = automaton::simplify::UnreachableStatesRemover::remove ( automaton ); + if ( unreachable.getStates ( ).size ( ) + statesUnreachable != states ) + continue; + + states = unreachable.getStates ( ).size ( ); + automaton::DFA < char, unsigned > useless = automaton::simplify::UselessStatesRemover::remove ( unreachable ); + if ( useless.getStates ( ).size ( ) + statesUseless != states ) + continue; + + size_t steps; + states = useless.getStates ( ).size ( ); + automaton::DFA < char, unsigned > minimal = automaton::simplify::Minimize::minimize ( useless, steps ); + if ( ( expectedSteps != 0 && steps != expectedSteps ) || minimal.getStates ( ).size ( ) + statesDuplicates != states ) + continue; + + states = minimal.getStates ( ).size ( ); + if ( statesMinimal != states ) + continue; + + return automaton; + } + + throw exception::CommonException ( "Generating automaton for minimization failed." ); +} + +} /* namespace generate */ + +} /* namespace automaton */ + +namespace { + +auto GenerateDFA = registration::AbstractRegister < automaton::generate::MinimizeGenerator, automaton::DFA < char, unsigned >, size_t, size_t, size_t, size_t, size_t, bool, double, size_t > ( automaton::generate::MinimizeGenerator::generateMinimizeDFA, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "statesMinimal", "statesDuplicates", "statesUnreachable", "statesUseless", "alphabetSize", "randomizedAlphabet", "density", "steps" ); + +} /* namespace */ + diff --git a/alib2algo_experimental/src/automaton/MinimizeGenerator.h b/alib2algo_experimental/src/automaton/MinimizeGenerator.h new file mode 100644 index 0000000000..a6d96e1052 --- /dev/null +++ b/alib2algo_experimental/src/automaton/MinimizeGenerator.h @@ -0,0 +1,26 @@ +/* + * MinimizeGenerator.h + * + * Created on: 6. 11. 2017 + * Author: Jan Travnicek + */ + +#ifndef MINIMIZE_GENERATOR_H_ +#define MINIMIZE_GENERATOR_H_ + +#include <automaton/FSM/DFA.h> + +namespace automaton { + +namespace generate { + +class MinimizeGenerator { +public: + static automaton::DFA < char, unsigned > generateMinimizeDFA ( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, size_t alphabetSize, bool randomizedAlphabet, double density, size_t expectedSteps ); +}; + +} /* namespace generate */ + +} /* namespace automaton */ + +#endif /* MINIMIZE_GENERATOR_H_ */ diff --git a/alib2algo_experimental/src/automaton/RandomAutomatonFactory2.cpp b/alib2algo_experimental/src/automaton/RandomAutomatonFactory2.cpp new file mode 100644 index 0000000000..a3f84fdff9 --- /dev/null +++ b/alib2algo_experimental/src/automaton/RandomAutomatonFactory2.cpp @@ -0,0 +1,68 @@ +/* + * RandomAutomatonFactory.cpp + * + * Created on: 5. 11. 2017 + * Author: Jan Travnicek + */ + +#include "RandomAutomatonFactory2.h" +#include <registration/AlgoRegistration.hpp> + +namespace automaton { + +namespace generate { + +size_t RandomAutomatonFactory2::randomSourceState ( size_t statesMinimal, size_t visited, size_t depleted, const ext::deque < bool > & VStates, const ext::deque < bool > & DStates ) { + size_t y = ext::random_devices::semirandom() % ( visited - depleted ) + 1; // select y-th accessible state + + for( size_t i = 0, cnt = 0; i < statesMinimal; i++ ) { + if( VStates [ i ] == true && DStates [ i ] == false ) + cnt ++; + + if( cnt == y ) + return i; + } + + return -1; +} + +size_t RandomAutomatonFactory2::randomTargetState ( size_t statesMinimal, size_t visited, const ext::deque < bool > & VStates ) { + size_t z = ext::random_devices::semirandom() % ( statesMinimal - visited ) + 1; // select z-th inaccessible state + + for( size_t i = 0, cnt = 0; i < statesMinimal; i++ ) { + if( VStates[ i ] == false ) + cnt ++; + + if( cnt == z ) + return i; + } + + return -1; +} + +automaton::DFA < char, unsigned > RandomAutomatonFactory2::generateDFA( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, size_t alphabetSize, bool randomizedAlphabet, double density ) { + if ( alphabetSize > 26 ) + throw exception::CommonException("Too big alphabet."); + + ext::vector < char > symbols; + for ( int i = 0; i < 26; i++ ) + symbols.push_back ( i + 'a' ); + + if ( randomizedAlphabet ) + shuffle ( symbols.begin ( ), symbols.end ( ), ext::random_devices::semirandom ); + + ext::deque < char > alphabet ( symbols.begin ( ), symbols.begin ( ) + alphabetSize ); + + return RandomAutomatonFactory2::NonminimalDFA ( statesMinimal, statesDuplicates, statesUnreachable, statesUseless, alphabet, density ); +} + +} /* namespace generate */ + +} /* namespace automaton */ + +namespace { + +auto GenerateDFA = registration::AbstractRegister < automaton::generate::RandomAutomatonFactory2, automaton::DFA < char, unsigned >, size_t, size_t, size_t, size_t, size_t, bool, double > ( automaton::generate::RandomAutomatonFactory2::generateDFA, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "statesMinimal", "statesDuplicates", "statesUnreachable", "statesUseless", "alphabetSize", "randomizedAlphabet", "density" ); + +} /* namespace */ + diff --git a/alib2algo_experimental/src/automaton/RandomAutomatonFactory2.h b/alib2algo_experimental/src/automaton/RandomAutomatonFactory2.h new file mode 100644 index 0000000000..741d7a60d9 --- /dev/null +++ b/alib2algo_experimental/src/automaton/RandomAutomatonFactory2.h @@ -0,0 +1,210 @@ +/* + * RandomAutomatonFactory2.h + * + * Created on: 5. 11. 2017 + * Author: Jan Travnicek + */ + +#ifndef RANDOM_AUTOMATON_FACTORY_2_H_ +#define RANDOM_AUTOMATON_FACTORY_2_H_ + +#include <deque> +#include <set> +#include <algorithm> +#include <random> + +#include <exception/CommonException.h> + +#include <automaton/FSM/DFA.h> + +namespace automaton { + +namespace generate { + +class RandomAutomatonFactory2 { +public: + template < class SymbolType > + static automaton::DFA < SymbolType, unsigned > generateDFA ( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, ext::set < SymbolType> alphabet, double density ); + static automaton::DFA < char, unsigned > generateDFA ( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, size_t alphabetSize, bool randomizedAlphabet, double density ); + +private: + static size_t randomSourceState ( size_t statesMinimal, size_t visited, size_t depleted, const ext::deque < bool > & Vstates, const ext::deque < bool > & DStates ); + static size_t randomTargetState ( size_t statesMinimal, size_t visited, const ext::deque < bool > & VStates ); + template < class SymbolType > + static size_t randomSymbol ( unsigned sourceState, const ext::deque < SymbolType > & alphabet, const automaton::DFA < SymbolType, unsigned > & automaton ); + template < class SymbolType > + static void addTransition ( automaton::DFA < SymbolType, unsigned > & automaton, unsigned source, SymbolType symbol, unsigned target, const ext::deque < ext::vector < unsigned > > & duplicates ); + template < class SymbolType > + static automaton::DFA < SymbolType, unsigned > NonminimalDFA( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, const ext::deque < SymbolType > & alphabet, double density ); +}; + +template < class SymbolType > +automaton::DFA < SymbolType, unsigned > RandomAutomatonFactory2::generateDFA( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, ext::set < SymbolType > alphabet, double density ) { + ext::deque < SymbolType> alphabet2; + + for( const auto & s : alphabet ) + alphabet2.push_back ( s ); + + return RandomAutomatonFactory2::NonminimalDFA ( statesMinimal, statesDuplicates, statesUnreachable, statesUseless, alphabet, density ); +} + +template < class SymbolType > +size_t RandomAutomatonFactory2::randomSymbol ( unsigned sourceState, const ext::deque < SymbolType > & alphabet, const automaton::DFA < SymbolType, unsigned > & automaton ) { + size_t x = ext::random_devices::semirandom() % ( alphabet.size( ) - automaton.getTransitionsFromState ( sourceState ).size ( ) ) + 1; + for( size_t i = 0, cnt = 0; i < alphabet.size ( ); i++ ) { + if ( automaton.getTransitions ( ).find ( std::make_pair ( sourceState, alphabet [ i ] ) ) == automaton.getTransitions ( ).end ( ) ) + cnt ++; + + if( cnt == x ) + return i; + } + + return -1; +} + +template < class SymbolType > +void RandomAutomatonFactory2::addTransition ( automaton::DFA < SymbolType, unsigned > & automaton, unsigned source, SymbolType symbol, unsigned target, const ext::deque < ext::vector < unsigned > > & duplicates ) { + for ( unsigned duplicateSource : duplicates [ source ] ) { + unsigned duplicateTarget = duplicates [ target ] [ ext::random_devices::semirandom() % ( duplicates [ target ].size ( ) ) ]; + automaton.addTransition( duplicateSource, symbol, duplicateTarget ); + } +} + +template < class SymbolType > +automaton::DFA < SymbolType, unsigned > RandomAutomatonFactory2::NonminimalDFA( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, const ext::deque < SymbolType > & alphabet, double density ) __attribute__ ((optnone)) { + if( alphabet.size( ) <= 0 ) + throw exception::CommonException( "Alphabet size must be greater than 0." ); + + ext::deque < bool > VStates; + ext::deque < bool > FStates; + ext::deque < bool > DStates; + ext::deque < ext::vector < unsigned > > duplicates; + + size_t visited; + size_t finals = 0; + size_t depleted = 0; + + automaton::DFA < SymbolType, unsigned > automaton ( 0 ); + + for( const auto & s : alphabet ) + automaton.addInputSymbol( s ); + + /* states partitioning -- | statesMinimal | statesUseless | statesUnreachable | statesDuplicates | */ + + for( unsigned i = 0; i < statesMinimal + statesUseless + statesUnreachable + statesDuplicates; i ++ ) { + VStates.push_back( false ); + DStates.push_back( false ); + FStates.push_back( false ); + duplicates.push_back ( ext::vector < unsigned > { i } ); + automaton.addState( i ); + } + + for ( unsigned i = 0; i < statesDuplicates; ++ i ) { + size_t a = ext::random_devices::semirandom() % ( statesMinimal ); + duplicates [ a ].push_back ( i + statesMinimal + statesUseless + statesUnreachable ); + } + + // ---- base + if( statesMinimal == 0 ) { + return automaton; + } else if( statesMinimal == 1 ) { + addTransition( automaton, 0, alphabet[ ext::random_devices::semirandom() % alphabet.size( ) ], 0, duplicates ); + + visited = 1; + VStates[ 0 ] = true; + DStates[ 0 ] = automaton.getTransitionsFromState ( 0 ).size ( ) == alphabet.size ( ); + if ( DStates[ 0 ] == true ) + depleted ++; + } else { + size_t x = ( ext::random_devices::semirandom() % ( statesMinimal - 1 ) ) + 1; + addTransition( automaton, 0, alphabet[ ext::random_devices::semirandom() % alphabet.size( ) ], x, duplicates ); + visited = 2; + + VStates[ 0 ] = true; + VStates[ x ] = true; + + DStates[ 0 ] = automaton.getTransitionsFromState ( 0 ).size ( ) == alphabet.size ( ); + if ( DStates[ 0 ] == true ) + depleted ++; + } + + // ---- make statesMinimal reachable + while( visited != statesMinimal ) { + size_t a = randomSourceState ( statesMinimal, visited, depleted, VStates, DStates ); + size_t b = randomTargetState ( statesMinimal, visited, VStates ); + size_t c = randomSymbol ( a, alphabet, automaton ); + + addTransition( automaton, a, alphabet[ c ], b, duplicates ); + + visited ++; + VStates[ b ] = true; + DStates[ a ] = automaton.getTransitionsFromState ( a ).size ( ) == alphabet.size ( ); + if ( DStates[ a ] == true ) + depleted ++; + } + + // ---- make statesUseless reachable + while( visited != statesMinimal + statesUseless ) { + size_t a = randomSourceState ( statesMinimal + statesUseless, visited, depleted, VStates, DStates ); + size_t b = randomTargetState ( statesMinimal + statesUseless, visited, VStates ); + size_t c = randomSymbol ( a, alphabet, automaton ); + + addTransition( automaton, a, alphabet[ c ], b, duplicates ); + + visited ++; + VStates[ b ] = true; + DStates[ a ] = automaton.getTransitionsFromState ( a ).size ( ) == alphabet.size ( ); + if ( DStates[ a ] == true ) + depleted ++; + } + + unsigned last = 0; + for ( unsigned i = 0; i < statesMinimal; ++ i ) { + if ( automaton.getTransitionsFromState ( i ).size ( ) == 0 ) { + size_t c = randomSymbol ( i, alphabet, automaton ); + addTransition ( automaton, i, alphabet [ c ], last, duplicates ); + last = i; + } + } + + for ( unsigned i = 0; i < statesMinimal / 2; ++ i ) { + size_t a = randomSourceState ( statesMinimal, visited - statesUseless, finals, VStates, FStates ); + FStates [ a ] = true; + finals ++; + + for ( unsigned s : duplicates [ a ] ) + automaton.addFinalState ( s ); + } + + visited += statesUnreachable; + for ( unsigned i = 0; i < statesUnreachable; ++ i ) { + VStates [ statesMinimal + statesUseless + i ] = true; + } + + double mnn100 = 100.0 / alphabet.size( ) / ( statesMinimal + statesUseless + statesUnreachable + statesDuplicates ); + while( automaton.getTransitions ( ).size( ) * mnn100 < density ) { + size_t a = randomSourceState ( statesMinimal + statesUseless + statesUnreachable, visited, depleted, VStates, DStates ); + size_t b; + if ( a < statesMinimal ) + b = ext::random_devices::semirandom() % ( statesMinimal ); + else if ( a < statesMinimal + statesUseless ) + b = ext::random_devices::semirandom() % ( statesUseless ) + statesMinimal; + else + b = ext::random_devices::semirandom() % ( statesMinimal + statesUseless + statesUnreachable ); + + size_t c = randomSymbol ( a, alphabet, automaton ); + + addTransition( automaton, a, alphabet [ c ], b, duplicates ); + DStates[ a ] = automaton.getTransitionsFromState ( a ).size ( ) == alphabet.size ( ); + if ( DStates[ a ] == true ) + depleted ++; + } + + return automaton; +} + +} /* namespace generate */ + +} /* namespace automaton */ + +#endif /* RANDOM_AUTOMATON_FACTORY_2_H_ */ -- GitLab