Skip to content

Commit

Permalink
Tabakov-Vardi generator
Browse files Browse the repository at this point in the history
  • Loading branch information
koniksedy committed Nov 20, 2024
1 parent 65cb9c6 commit 878ad0a
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
12 changes: 12 additions & 0 deletions include/mata/nfa/builder.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
44 changes: 44 additions & 0 deletions src/nfa/builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "mata/parser/mintermization.hh"

#include <fstream>
#include <random>
#include <cmath>

using namespace mata::nfa;
using mata::nfa::Nfa;
Expand Down Expand Up @@ -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<size_t>(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> state_dis(0, num_of_states - 1);
std::uniform_int_distribution<Symbol> symbol_dis(0, static_cast<Symbol>(alphabet_size - 1));

// Create final states
const size_t num_of_final_states{ static_cast<size_t>(std::round(static_cast<float>(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<size_t>(std::round(static_cast<float>(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) };
Expand Down
99 changes: 99 additions & 0 deletions tests/nfa/builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <unordered_set>
#include <fstream>
#include <cmath>

#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
Expand Down Expand Up @@ -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<size_t>(std::round(final_state_density * static_cast<float>(num_of_states))));
CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size);
CHECK(nfa.delta.num_of_transitions() == static_cast<size_t>(std::round(transition_density * static_cast<float>(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<size_t>(std::round(transition_density * static_cast<float>(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<size_t>(std::round(final_state_density * static_cast<float>(num_of_states))));
CHECK(nfa.delta.get_used_symbols().size() <= alphabet_size);
CHECK(nfa.delta.num_of_transitions() == static_cast<size_t>(std::round(transition_density * static_cast<float>(num_of_states))) * alphabet_size);
}

SECTION("Throw runtime_error. transition_density < 0") {
num_of_states = 10;
alphabet_size = 5;
transition_density = static_cast<float>(-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<float>(-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<float>(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);
}
}

0 comments on commit 878ad0a

Please sign in to comment.