From 878ad0a3f155a804365fa309f55938659d3c3ea5 Mon Sep 17 00:00:00 2001 From: koniksedy Date: Wed, 20 Nov 2024 15:16:16 +0100 Subject: [PATCH 1/9] Tabakov-Vardi generator --- include/mata/nfa/builder.hh | 12 +++++ src/nfa/builder.cc | 44 +++++++++++++++++ tests/nfa/builder.cc | 99 +++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) diff --git a/include/mata/nfa/builder.hh b/include/mata/nfa/builder.hh index 0ad27fae0..0ebf1f912 100644 --- a/include/mata/nfa/builder.hh +++ b/include/mata/nfa/builder.hh @@ -45,6 +45,18 @@ Nfa create_empty_string_nfa(); */ Nfa create_sigma_star_nfa(Alphabet* alphabet = new OnTheFlyAlphabet{}); +/** + * Creates Tabakov-Vardi random NFA. + * + * @param num_of_states Number of states in the automaton. + * @param alphabet_size Size of the alphabet. + * @param transition_density If the density is 1, the automaton will have @p num_of_states transition for each symbol. + * Source and target states are chosen randomly. + * @param final_state_density Density of final states in the automaton. If the density is 1, every state will be final. + * + */ +Nfa create_tabakov_vardi_nfa(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density); + /** Loads an automaton from Parsed object */ // TODO this function should the same thing as the one taking IntermediateAut or be deleted Nfa construct(const mata::parser::ParsedSection& parsec, Alphabet* alphabet, NameStateMap* state_map = nullptr); diff --git a/src/nfa/builder.cc b/src/nfa/builder.cc index c5bcf497b..598302b69 100644 --- a/src/nfa/builder.cc +++ b/src/nfa/builder.cc @@ -4,6 +4,8 @@ #include "mata/parser/mintermization.hh" #include +#include +#include using namespace mata::nfa; using mata::nfa::Nfa; @@ -217,6 +219,48 @@ Nfa builder::create_sigma_star_nfa(mata::Alphabet* alphabet) { return nfa; } +Nfa builder::create_tabakov_vardi_nfa(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density) { + if (transition_density < 0 || static_cast(transition_density) > num_of_states) { + // Maximum of num_of_states^2 unique transitions for one symbol can be created. + throw std::runtime_error("Transition density must be in range [0, num_of_states]"); + } + if (final_state_density < 0 || final_state_density > 1) { + // Maximum of num_of_states final states can be created. + throw std::runtime_error("Final state density must be in range [0, 1]"); + } + + Nfa nfa{ num_of_states, StateSet{ 0 }, StateSet{ 0 }, new OnTheFlyAlphabet{} }; + + // Initialize the random number generator + std::random_device rd; // Seed for the random number engine + std::mt19937 gen(rd()); // Mersenne Twister engine + std::uniform_int_distribution state_dis(0, num_of_states - 1); + std::uniform_int_distribution symbol_dis(0, static_cast(alphabet_size - 1)); + + // Create final states + const size_t num_of_final_states{ static_cast(std::round(static_cast(num_of_states) * final_state_density)) }; + while (nfa.final.size() < num_of_final_states) { + nfa.final.insert(state_dis(gen)); + } + + // Create transitions + const size_t num_of_transitions_per_symbol{ static_cast(std::round(static_cast(num_of_states) * transition_density)) }; + for (Symbol symbol{ 0 }; symbol < alphabet_size; symbol++) { + size_t num_of_added_transitions = 0; + while (num_of_added_transitions < num_of_transitions_per_symbol) { + const State src_state{ state_dis(gen) }; + const State tgt_state{ state_dis(gen) }; + if (nfa.delta.contains(src_state, symbol, tgt_state)) { + continue; + } + nfa.delta.add(src_state, symbol, tgt_state); + num_of_added_transitions++; + } + } + + return nfa; +} + Nfa builder::parse_from_mata(std::istream& nfa_stream) { const std::string nfa_str = "NFA"; parser::Parsed parsed{ parser::parse_mf(nfa_stream) }; diff --git a/tests/nfa/builder.cc b/tests/nfa/builder.cc index 609c86086..541ec861c 100644 --- a/tests/nfa/builder.cc +++ b/tests/nfa/builder.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -141,3 +142,101 @@ TEST_CASE("parse_from_mata()") { } } } + +TEST_CASE("Create Tabakov-Vardi NFA") { + size_t num_of_states; + size_t alphabet_size; + float transition_density; + float final_state_density; + + SECTION("EMPTY") { + num_of_states = 0; + alphabet_size = 0; + transition_density = 0; + final_state_density = 0; + + Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + CHECK(nfa.num_of_states() == 1); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == 1); + CHECK(nfa.delta.empty()); + } + + SECTION("10-5-0.5-0.5") { + num_of_states = 10; + alphabet_size = 5; + transition_density = 0.5; + final_state_density = 0.5; + + Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + CHECK(nfa.num_of_states() == num_of_states); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); + CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size); + CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + } + + SECTION("Max final") { + num_of_states = 10; + alphabet_size = 5; + transition_density = 0.5; + final_state_density = 1; + + Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + CHECK(nfa.num_of_states() == num_of_states); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == num_of_states); + CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size); + CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + } + + SECTION("Max transitions") { + num_of_states = 10; + alphabet_size = 5; + transition_density = 10; + final_state_density = 0.5; + + Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + CHECK(nfa.num_of_states() == num_of_states); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); + CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size); + CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + } + + SECTION("Throw runtime_error. transition_density < 0") { + num_of_states = 10; + alphabet_size = 5; + transition_density = static_cast(-0.1); + final_state_density = 0.5; + + CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + } + + SECTION("Throw runtime_error. transition_density > num_of_states") { + num_of_states = 10; + alphabet_size = 5; + transition_density = 11; + final_state_density = 0.5; + + CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + } + + SECTION("Throw runtime_error. final_state_density < 0") { + num_of_states = 10; + alphabet_size = 5; + transition_density = 0.5; + final_state_density = static_cast(-0.1); + + CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + } + + SECTION("Throw runtime_error. final_state_density > 1") { + num_of_states = 10; + alphabet_size = 5; + transition_density = 0.5; + final_state_density = static_cast(1.1); + + CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + } +} From 3c1a8d208d4238743f5ab618e9450f02ec16e1aa Mon Sep 17 00:00:00 2001 From: koniksedy Date: Wed, 20 Nov 2024 15:25:01 +0100 Subject: [PATCH 2/9] big test --- tests/nfa/builder.cc | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/nfa/builder.cc b/tests/nfa/builder.cc index 541ec861c..b75bb1770 100644 --- a/tests/nfa/builder.cc +++ b/tests/nfa/builder.cc @@ -172,7 +172,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); - CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size); + CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); } @@ -186,7 +186,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == num_of_states); - CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size); + CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); } @@ -200,10 +200,25 @@ TEST_CASE("Create Tabakov-Vardi NFA") { CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); - CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size); + CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); } + SECTION("BIG") { + num_of_states = 1000; + alphabet_size = 100; + transition_density = 5; + final_state_density = 1; + + Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + CHECK(nfa.num_of_states() == num_of_states); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == num_of_states); + CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); + CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + + } + SECTION("Throw runtime_error. transition_density < 0") { num_of_states = 10; alphabet_size = 5; From cec63073257c6bce0f008beb1deff2b52d6c6cc4 Mon Sep 17 00:00:00 2001 From: koniksedy Date: Wed, 20 Nov 2024 17:22:55 +0100 Subject: [PATCH 3/9] renaming --- include/mata/nfa/builder.hh | 5 +++-- src/nfa/builder.cc | 20 ++++++++++---------- tests/nfa/builder.cc | 18 +++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/include/mata/nfa/builder.hh b/include/mata/nfa/builder.hh index 0ebf1f912..48ec23ebf 100644 --- a/include/mata/nfa/builder.hh +++ b/include/mata/nfa/builder.hh @@ -51,11 +51,12 @@ Nfa create_sigma_star_nfa(Alphabet* alphabet = new OnTheFlyAlphabet{}); * @param num_of_states Number of states in the automaton. * @param alphabet_size Size of the alphabet. * @param transition_density If the density is 1, the automaton will have @p num_of_states transition for each symbol. - * Source and target states are chosen randomly. + * Source and target states are chosen randomly. The value must be in range [0, num_of_states]. * @param final_state_density Density of final states in the automaton. If the density is 1, every state will be final. + * The value must be in range [0, 1]. * */ -Nfa create_tabakov_vardi_nfa(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density); +Nfa create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density); /** Loads an automaton from Parsed object */ // TODO this function should the same thing as the one taking IntermediateAut or be deleted diff --git a/src/nfa/builder.cc b/src/nfa/builder.cc index 598302b69..481125ab9 100644 --- a/src/nfa/builder.cc +++ b/src/nfa/builder.cc @@ -219,7 +219,7 @@ Nfa builder::create_sigma_star_nfa(mata::Alphabet* alphabet) { return nfa; } -Nfa builder::create_tabakov_vardi_nfa(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density) { +Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density) { if (transition_density < 0 || static_cast(transition_density) > num_of_states) { // Maximum of num_of_states^2 unique transitions for one symbol can be created. throw std::runtime_error("Transition density must be in range [0, num_of_states]"); @@ -234,27 +234,27 @@ Nfa builder::create_tabakov_vardi_nfa(const size_t num_of_states, const size_t a // Initialize the random number generator std::random_device rd; // Seed for the random number engine std::mt19937 gen(rd()); // Mersenne Twister engine - std::uniform_int_distribution state_dis(0, num_of_states - 1); - std::uniform_int_distribution symbol_dis(0, static_cast(alphabet_size - 1)); + std::uniform_int_distribution state_rand_dis(0, num_of_states - 1); + std::uniform_int_distribution symbol_rand_dis(0, static_cast(alphabet_size - 1)); // Create final states const size_t num_of_final_states{ static_cast(std::round(static_cast(num_of_states) * final_state_density)) }; while (nfa.final.size() < num_of_final_states) { - nfa.final.insert(state_dis(gen)); + nfa.final.insert(state_rand_dis(gen)); } // Create transitions const size_t num_of_transitions_per_symbol{ static_cast(std::round(static_cast(num_of_states) * transition_density)) }; - for (Symbol symbol{ 0 }; symbol < alphabet_size; symbol++) { + for (Symbol symbol{ 0 }; symbol < alphabet_size; ++symbol) { size_t num_of_added_transitions = 0; while (num_of_added_transitions < num_of_transitions_per_symbol) { - const State src_state{ state_dis(gen) }; - const State tgt_state{ state_dis(gen) }; - if (nfa.delta.contains(src_state, symbol, tgt_state)) { + const State source{ state_rand_dis(gen) }; + const State target{ state_rand_dis(gen) }; + if (nfa.delta.contains(source, symbol, target)) { continue; } - nfa.delta.add(src_state, symbol, tgt_state); - num_of_added_transitions++; + nfa.delta.add(source, symbol, target); + ++num_of_added_transitions; } } diff --git a/tests/nfa/builder.cc b/tests/nfa/builder.cc index b75bb1770..d539b4f55 100644 --- a/tests/nfa/builder.cc +++ b/tests/nfa/builder.cc @@ -155,7 +155,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 0; final_state_density = 0; - Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); CHECK(nfa.num_of_states() == 1); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == 1); @@ -168,7 +168,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 0.5; final_state_density = 0.5; - Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); @@ -182,7 +182,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 0.5; final_state_density = 1; - Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == num_of_states); @@ -196,7 +196,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 10; final_state_density = 0.5; - Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); @@ -210,7 +210,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 5; final_state_density = 1; - Nfa nfa = mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == num_of_states); @@ -225,7 +225,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = static_cast(-0.1); final_state_density = 0.5; - CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); } SECTION("Throw runtime_error. transition_density > num_of_states") { @@ -234,7 +234,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 11; final_state_density = 0.5; - CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); } SECTION("Throw runtime_error. final_state_density < 0") { @@ -243,7 +243,7 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 0.5; final_state_density = static_cast(-0.1); - CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); } SECTION("Throw runtime_error. final_state_density > 1") { @@ -252,6 +252,6 @@ TEST_CASE("Create Tabakov-Vardi NFA") { transition_density = 0.5; final_state_density = static_cast(1.1); - CHECK_THROWS_AS(mata::nfa::builder::create_tabakov_vardi_nfa(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); } } From 08033f89a93da70a8d881c9ba5f24b55a7f32fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=A0ed=C3=BD?= <55767047+koniksedy@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:49:08 +0100 Subject: [PATCH 4/9] Update include/mata/nfa/builder.hh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Chocholatý --- include/mata/nfa/builder.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mata/nfa/builder.hh b/include/mata/nfa/builder.hh index 48ec23ebf..213b4cc4d 100644 --- a/include/mata/nfa/builder.hh +++ b/include/mata/nfa/builder.hh @@ -50,7 +50,7 @@ Nfa create_sigma_star_nfa(Alphabet* alphabet = new OnTheFlyAlphabet{}); * * @param num_of_states Number of states in the automaton. * @param alphabet_size Size of the alphabet. - * @param transition_density If the density is 1, the automaton will have @p num_of_states transition for each symbol. + * @param transition_density If the density is 1, the automaton will have @p num_of_states transitions for each symbol. * Source and target states are chosen randomly. The value must be in range [0, num_of_states]. * @param final_state_density Density of final states in the automaton. If the density is 1, every state will be final. * The value must be in range [0, 1]. From a0e1bac48fab468857a97d7c3cec61983d98b50d Mon Sep 17 00:00:00 2001 From: koniksedy Date: Sun, 24 Nov 2024 17:55:33 +0100 Subject: [PATCH 5/9] renaming + new unique state/transition generator --- include/mata/nfa/builder.hh | 12 ++--- src/nfa/builder.cc | 41 +++++++++------- tests/nfa/builder.cc | 94 ++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 57 deletions(-) diff --git a/include/mata/nfa/builder.hh b/include/mata/nfa/builder.hh index 213b4cc4d..8ceefe0de 100644 --- a/include/mata/nfa/builder.hh +++ b/include/mata/nfa/builder.hh @@ -50,13 +50,13 @@ Nfa create_sigma_star_nfa(Alphabet* alphabet = new OnTheFlyAlphabet{}); * * @param num_of_states Number of states in the automaton. * @param alphabet_size Size of the alphabet. - * @param transition_density If the density is 1, the automaton will have @p num_of_states transitions for each symbol. - * Source and target states are chosen randomly. The value must be in range [0, num_of_states]. - * @param final_state_density Density of final states in the automaton. If the density is 1, every state will be final. - * The value must be in range [0, 1]. - * + * @param states_transitions_ratio_per_symbol Ratio between number of transitions and number of states for each symbol. + * The value must be in range [0, num_of_states]. A value of 1 means that there will be num_of_states transitions for each symbol. + * A value of num_of_states means that there will be a transition between every pair of states for each symbol. + * @param final_state_density Density of final states in the automaton. The value must be in range (0, 1]. The state 0 is always initial. + * If the density is 1, every state will be final. */ -Nfa create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density); +Nfa create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const double states_trans_ratio_per_symbol, const double final_state_density); /** Loads an automaton from Parsed object */ // TODO this function should the same thing as the one taking IntermediateAut or be deleted diff --git a/src/nfa/builder.cc b/src/nfa/builder.cc index 481125ab9..46ab570f6 100644 --- a/src/nfa/builder.cc +++ b/src/nfa/builder.cc @@ -219,14 +219,17 @@ Nfa builder::create_sigma_star_nfa(mata::Alphabet* alphabet) { return nfa; } -Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const float transition_density, const float final_state_density) { - if (transition_density < 0 || static_cast(transition_density) > num_of_states) { +Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const double states_trans_ratio_per_symbol, const double final_state_density) { + if (num_of_states == 0) { + return Nfa(); + } + if (states_trans_ratio_per_symbol < 0 || static_cast(states_trans_ratio_per_symbol) > num_of_states) { // Maximum of num_of_states^2 unique transitions for one symbol can be created. throw std::runtime_error("Transition density must be in range [0, num_of_states]"); } - if (final_state_density < 0 || final_state_density > 1) { + if (final_state_density <= 0 || final_state_density > 1) { // Maximum of num_of_states final states can be created. - throw std::runtime_error("Final state density must be in range [0, 1]"); + throw std::runtime_error("Final state density must be in range (0, 1]"); } Nfa nfa{ num_of_states, StateSet{ 0 }, StateSet{ 0 }, new OnTheFlyAlphabet{} }; @@ -234,30 +237,32 @@ Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const s // Initialize the random number generator std::random_device rd; // Seed for the random number engine std::mt19937 gen(rd()); // Mersenne Twister engine - std::uniform_int_distribution state_rand_dis(0, num_of_states - 1); - std::uniform_int_distribution symbol_rand_dis(0, static_cast(alphabet_size - 1)); + + // Unique final state generator + std::vector states(num_of_states); + std::iota(states.begin(), states.end(), 0); + std::shuffle(states.begin() + 1, states.end(), gen); // Starting from 1, because 0 is allways final state. // Create final states const size_t num_of_final_states{ static_cast(std::round(static_cast(num_of_states) * final_state_density)) }; - while (nfa.final.size() < num_of_final_states) { - nfa.final.insert(state_rand_dis(gen)); + for (size_t i = 0; i < num_of_final_states; ++i) { + nfa.final.insert(states[i]); } + // Unique transition generator + std::vector one_dimensional_transition_matrix(num_of_states * num_of_states); + std::iota(one_dimensional_transition_matrix.begin(), one_dimensional_transition_matrix.end(), 0); + // Create transitions - const size_t num_of_transitions_per_symbol{ static_cast(std::round(static_cast(num_of_states) * transition_density)) }; + const size_t num_of_transitions_per_symbol{ static_cast(std::round(static_cast(num_of_states) * states_trans_ratio_per_symbol)) }; for (Symbol symbol{ 0 }; symbol < alphabet_size; ++symbol) { - size_t num_of_added_transitions = 0; - while (num_of_added_transitions < num_of_transitions_per_symbol) { - const State source{ state_rand_dis(gen) }; - const State target{ state_rand_dis(gen) }; - if (nfa.delta.contains(source, symbol, target)) { - continue; - } + std::shuffle(one_dimensional_transition_matrix.begin(), one_dimensional_transition_matrix.end(), gen); + for (size_t i = 0; i < num_of_transitions_per_symbol; ++i) { + const State source{ one_dimensional_transition_matrix[i] / num_of_states }; + const State target{ one_dimensional_transition_matrix[i] % num_of_states }; nfa.delta.add(source, symbol, target); - ++num_of_added_transitions; } } - return nfa; } diff --git a/tests/nfa/builder.cc b/tests/nfa/builder.cc index d539b4f55..78e61c031 100644 --- a/tests/nfa/builder.cc +++ b/tests/nfa/builder.cc @@ -146,112 +146,140 @@ TEST_CASE("parse_from_mata()") { TEST_CASE("Create Tabakov-Vardi NFA") { size_t num_of_states; size_t alphabet_size; - float transition_density; - float final_state_density; + double states_trans_ratio_per_symbol; + double final_state_density; SECTION("EMPTY") { num_of_states = 0; alphabet_size = 0; - transition_density = 0; + states_trans_ratio_per_symbol = 0; final_state_density = 0; - Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); - CHECK(nfa.num_of_states() == 1); - CHECK(nfa.initial.size() == 1); - CHECK(nfa.final.size() == 1); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); + CHECK(nfa.num_of_states() == 0); + CHECK(nfa.initial.size() == 0); + CHECK(nfa.final.size() == 0); CHECK(nfa.delta.empty()); } SECTION("10-5-0.5-0.5") { num_of_states = 10; alphabet_size = 5; - transition_density = 0.5; + states_trans_ratio_per_symbol = 0.5; final_state_density = 0.5; - Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); + CHECK(nfa.num_of_states() == num_of_states); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == 5); + CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); + CHECK(nfa.delta.num_of_transitions() == 25); + } + + SECTION("Min final") { + num_of_states = 10; + alphabet_size = 5; + states_trans_ratio_per_symbol = 0.5; + final_state_density = 0.0001; + + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); - CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); + CHECK(nfa.final.size() == 1); CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); - CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + CHECK(nfa.delta.num_of_transitions() == 25); } SECTION("Max final") { num_of_states = 10; alphabet_size = 5; - transition_density = 0.5; + states_trans_ratio_per_symbol = 0.5; final_state_density = 1; - Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == num_of_states); CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); - CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + CHECK(nfa.delta.num_of_transitions() == 25); + } + + SECTION("Min transitions") { + num_of_states = 10; + alphabet_size = 5; + states_trans_ratio_per_symbol = 0; + final_state_density = 0.5; + + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); + CHECK(nfa.num_of_states() == num_of_states); + CHECK(nfa.initial.size() == 1); + CHECK(nfa.final.size() == 5); + CHECK(nfa.delta.get_used_symbols().size() == 0); + CHECK(nfa.delta.num_of_transitions() == 0); } SECTION("Max transitions") { num_of_states = 10; alphabet_size = 5; - transition_density = 10; + states_trans_ratio_per_symbol = 10; final_state_density = 0.5; - Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); - CHECK(nfa.final.size() == static_cast(std::round(final_state_density * static_cast(num_of_states)))); + CHECK(nfa.final.size() == 5); CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); - CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + CHECK(nfa.delta.num_of_transitions() == 500); } SECTION("BIG") { - num_of_states = 1000; + num_of_states = 200; alphabet_size = 100; - transition_density = 5; + states_trans_ratio_per_symbol = 5; final_state_density = 1; - Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density); + Nfa nfa = mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density); CHECK(nfa.num_of_states() == num_of_states); CHECK(nfa.initial.size() == 1); CHECK(nfa.final.size() == num_of_states); CHECK(nfa.delta.get_used_symbols().size() == alphabet_size); - CHECK(nfa.delta.num_of_transitions() == static_cast(std::round(transition_density * static_cast(num_of_states))) * alphabet_size); + CHECK(nfa.delta.num_of_transitions() == 100000); } SECTION("Throw runtime_error. transition_density < 0") { num_of_states = 10; alphabet_size = 5; - transition_density = static_cast(-0.1); + states_trans_ratio_per_symbol = static_cast(-0.1); final_state_density = 0.5; - CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density), std::runtime_error); } SECTION("Throw runtime_error. transition_density > num_of_states") { num_of_states = 10; alphabet_size = 5; - transition_density = 11; + states_trans_ratio_per_symbol = 11; final_state_density = 0.5; - CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density), std::runtime_error); } - SECTION("Throw runtime_error. final_state_density < 0") { + SECTION("Throw runtime_error. final_state_density = 0") { num_of_states = 10; alphabet_size = 5; - transition_density = 0.5; - final_state_density = static_cast(-0.1); + states_trans_ratio_per_symbol = 0.5; + final_state_density = 0; - CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density), std::runtime_error); } SECTION("Throw runtime_error. final_state_density > 1") { num_of_states = 10; alphabet_size = 5; - transition_density = 0.5; - final_state_density = static_cast(1.1); + states_trans_ratio_per_symbol = 0.5; + final_state_density = static_cast(1.1); - CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, transition_density, final_state_density), std::runtime_error); + CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density), std::runtime_error); } } From 6f591e6220246b7e03fa000c9e3ae0cfe2c113f1 Mon Sep 17 00:00:00 2001 From: koniksedy Date: Sun, 24 Nov 2024 18:04:03 +0100 Subject: [PATCH 6/9] fin_state_density range changed to [0;1] --- include/mata/nfa/builder.hh | 2 +- src/nfa/builder.cc | 2 +- tests/nfa/builder.cc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mata/nfa/builder.hh b/include/mata/nfa/builder.hh index 8ceefe0de..3976384bb 100644 --- a/include/mata/nfa/builder.hh +++ b/include/mata/nfa/builder.hh @@ -53,7 +53,7 @@ Nfa create_sigma_star_nfa(Alphabet* alphabet = new OnTheFlyAlphabet{}); * @param states_transitions_ratio_per_symbol Ratio between number of transitions and number of states for each symbol. * The value must be in range [0, num_of_states]. A value of 1 means that there will be num_of_states transitions for each symbol. * A value of num_of_states means that there will be a transition between every pair of states for each symbol. - * @param final_state_density Density of final states in the automaton. The value must be in range (0, 1]. The state 0 is always initial. + * @param final_state_density Density of final states in the automaton. The value must be in range [0, 1]. The state 0 is always final. * If the density is 1, every state will be final. */ Nfa create_random_nfa_tabakov_vardi(const size_t num_of_states, const size_t alphabet_size, const double states_trans_ratio_per_symbol, const double final_state_density); diff --git a/src/nfa/builder.cc b/src/nfa/builder.cc index 46ab570f6..1a9ca5ee7 100644 --- a/src/nfa/builder.cc +++ b/src/nfa/builder.cc @@ -227,7 +227,7 @@ Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const s // Maximum of num_of_states^2 unique transitions for one symbol can be created. throw std::runtime_error("Transition density must be in range [0, num_of_states]"); } - if (final_state_density <= 0 || final_state_density > 1) { + if (final_state_density < 0 || final_state_density > 1) { // Maximum of num_of_states final states can be created. throw std::runtime_error("Final state density must be in range (0, 1]"); } diff --git a/tests/nfa/builder.cc b/tests/nfa/builder.cc index 78e61c031..bb9dc8638 100644 --- a/tests/nfa/builder.cc +++ b/tests/nfa/builder.cc @@ -265,11 +265,11 @@ TEST_CASE("Create Tabakov-Vardi NFA") { CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density), std::runtime_error); } - SECTION("Throw runtime_error. final_state_density = 0") { + SECTION("Throw runtime_error. final_state_density < 0") { num_of_states = 10; alphabet_size = 5; states_trans_ratio_per_symbol = 0.5; - final_state_density = 0; + final_state_density = static_cast(-0.1); CHECK_THROWS_AS(mata::nfa::builder::create_random_nfa_tabakov_vardi(num_of_states, alphabet_size, states_trans_ratio_per_symbol, final_state_density), std::runtime_error); } From 35ba8255c4e4d82a0fec3a1d8a6da2e32889e14d Mon Sep 17 00:00:00 2001 From: koniksedy Date: Mon, 25 Nov 2024 10:37:17 +0100 Subject: [PATCH 7/9] paper --- include/mata/nfa/builder.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mata/nfa/builder.hh b/include/mata/nfa/builder.hh index 3976384bb..9cc804c87 100644 --- a/include/mata/nfa/builder.hh +++ b/include/mata/nfa/builder.hh @@ -47,6 +47,7 @@ Nfa create_sigma_star_nfa(Alphabet* alphabet = new OnTheFlyAlphabet{}); /** * Creates Tabakov-Vardi random NFA. + * The implementation is based on the paper "Experimental Evaluation of Classical Automata Constructions" by Tabakov and Vardi. * * @param num_of_states Number of states in the automaton. * @param alphabet_size Size of the alphabet. From 0e69ac024c07be10bec56ef2f9a080a842467430 Mon Sep 17 00:00:00 2001 From: koniksedy Date: Mon, 25 Nov 2024 10:55:37 +0100 Subject: [PATCH 8/9] float -> double + off-by-one potential error fix --- src/nfa/builder.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nfa/builder.cc b/src/nfa/builder.cc index 1a9ca5ee7..7b2764feb 100644 --- a/src/nfa/builder.cc +++ b/src/nfa/builder.cc @@ -244,7 +244,7 @@ Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const s std::shuffle(states.begin() + 1, states.end(), gen); // Starting from 1, because 0 is allways final state. // Create final states - const size_t num_of_final_states{ static_cast(std::round(static_cast(num_of_states) * final_state_density)) }; + const size_t num_of_final_states{ static_cast(std::round(static_cast(num_of_states) * final_state_density)) }; for (size_t i = 0; i < num_of_final_states; ++i) { nfa.final.insert(states[i]); } @@ -254,7 +254,7 @@ Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const s std::iota(one_dimensional_transition_matrix.begin(), one_dimensional_transition_matrix.end(), 0); // Create transitions - const size_t num_of_transitions_per_symbol{ static_cast(std::round(static_cast(num_of_states) * states_trans_ratio_per_symbol)) }; + const size_t num_of_transitions_per_symbol{ std::min(static_cast(std::round(static_cast(num_of_states) * states_trans_ratio_per_symbol)), one_dimensional_transition_matrix.size()) }; for (Symbol symbol{ 0 }; symbol < alphabet_size; ++symbol) { std::shuffle(one_dimensional_transition_matrix.begin(), one_dimensional_transition_matrix.end(), gen); for (size_t i = 0; i < num_of_transitions_per_symbol; ++i) { From 49fc1531eca2dd627a42e11732d9bc2bf21bdd8e Mon Sep 17 00:00:00 2001 From: koniksedy Date: Mon, 25 Nov 2024 11:03:16 +0100 Subject: [PATCH 9/9] comment --- src/nfa/builder.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nfa/builder.cc b/src/nfa/builder.cc index 7b2764feb..36c063c49 100644 --- a/src/nfa/builder.cc +++ b/src/nfa/builder.cc @@ -254,6 +254,8 @@ Nfa builder::create_random_nfa_tabakov_vardi(const size_t num_of_states, const s std::iota(one_dimensional_transition_matrix.begin(), one_dimensional_transition_matrix.end(), 0); // Create transitions + // Using std::min because, in some universe, casting and rounding might cause the number of transitions to exceed the number of possible transitions by 1 + // and then an access to the non-existing element of one_dimensional_transition_matrix would occur. const size_t num_of_transitions_per_symbol{ std::min(static_cast(std::round(static_cast(num_of_states) * states_trans_ratio_per_symbol)), one_dimensional_transition_matrix.size()) }; for (Symbol symbol{ 0 }; symbol < alphabet_size; ++symbol) { std::shuffle(one_dimensional_transition_matrix.begin(), one_dimensional_transition_matrix.end(), gen);