Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tabakov-Vardi generator #468

Merged
merged 9 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
koniksedy marked this conversation as resolved.
Show resolved Hide resolved
koniksedy marked this conversation as resolved.
Show resolved Hide resolved
* 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.
koniksedy marked this conversation as resolved.
Show resolved Hide resolved
*
*/
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);
koniksedy marked this conversation as resolved.
Show resolved Hide resolved

/** 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) {
koniksedy marked this conversation as resolved.
Show resolved Hide resolved
// 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)) };
jurajsic marked this conversation as resolved.
Show resolved Hide resolved
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) };
koniksedy marked this conversation as resolved.
Show resolved Hide resolved
if (nfa.delta.contains(src_state, symbol, tgt_state)) {
continue;
}
nfa.delta.add(src_state, symbol, tgt_state);
num_of_added_transitions++;
koniksedy marked this conversation as resolved.
Show resolved Hide resolved
}
}

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
114 changes: 114 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>
koniksedy marked this conversation as resolved.
Show resolved Hide resolved

#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
Expand Down Expand Up @@ -141,3 +142,116 @@ 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;
jurajsic marked this conversation as resolved.
Show resolved Hide resolved
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("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<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);
}
}
Loading