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