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

Minimization #476

Open
wants to merge 7 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 14 additions & 3 deletions include/mata/nfa/nfa.hh
Original file line number Diff line number Diff line change
Expand Up @@ -669,12 +669,23 @@ Nfa complement(const Nfa& aut, const utils::OrdVector<Symbol>& symbols,
/**
* @brief Compute minimal deterministic automaton.
*
* @param[in] aut Automaton whose minimal version to compute.
* @param[in] aut NFA whose minimal version to compute.
* @param[in] params Optional parameters to control the minimization algorithm:
* - "algorithm": "brzozowski"
* - "algorithm": "brzozowski" (Default: "brzozowski")
* @return Minimal deterministic automaton.
*/
Nfa minimize(const Nfa &aut, const ParameterMap& params = { { "algorithm", "brzozowski" } });
Nfa make_minimal_dfa(const Nfa &nfa, const ParameterMap& params = { { "algorithm", "brzozowski" } });

/**
* @brief Compute minimal deterministic automaton.
*
* @param[in] aut DFA whose minimal version to compute. Trimming on the copy of the automaton
* will be performed if the automaton is not already trimmed.
* @param[in] params Optional parameters to control the minimization algorithm:
* - "algorithm": "hopcroft", "brzozowski" (Default: "hopcroft")
* @return Minimal deterministic automaton.
*/
Nfa minimize(const Nfa &dfa, const ParameterMap& params = { { "algorithm", "hopcroft" } });

/**
* @brief Determinize automaton.
Expand Down
4 changes: 3 additions & 1 deletion include/mata/nfa/plumbing.hh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ inline void complement(
{ "minimize", "false"}}) { *result = complement(aut, alphabet, params);
}

inline void minimize(Nfa* res, const Nfa &aut) { *res = minimize(aut); }
inline void minimize(Nfa* res, const Nfa &dfa, const ParameterMap& params = {{ "algorithm", "hopcroft" }}) { *res = minimize(dfa, params); }

inline void make_minimal_dfa(Nfa* res, const Nfa &nfa, const ParameterMap& params = {{ "algorithm", "brzozowski" }}) { *res = make_minimal_dfa(nfa, params); }

inline void determinize(Nfa* result, const Nfa& aut, std::unordered_map<StateSet, State> *subset_map = nullptr) {
*result = determinize(aut, subset_map);
Expand Down
49 changes: 38 additions & 11 deletions src/nfa/operations.cc
Original file line number Diff line number Diff line change
Expand Up @@ -927,27 +927,54 @@ Nfa mata::nfa::algorithms::minimize_brzozowski(const Nfa& aut) {
return determinize(revert(determinize(revert(aut))));
}

Nfa mata::nfa::minimize(
const Nfa& aut,
const ParameterMap& params)
{
Nfa result;
// setting the default algorithm
decltype(algorithms::minimize_brzozowski)* algo = algorithms::minimize_brzozowski;
Nfa mata::nfa::make_minimal_dfa(const Nfa& nfa, const ParameterMap& params) {
if (!haskey(params, "algorithm")) {
throw std::runtime_error(std::to_string(__func__) +
" requires setting the \"algorithm\" key in the \"params\" argument; "
"received: " + std::to_string(params));
}

// Setting the algorithm. Default is Brzozowski.
const std::string& str_algo = params.at("algorithm");
if ("brzozowski" == str_algo) { /* default */ }
decltype(algorithms::minimize_brzozowski)* algo = algorithms::minimize_brzozowski;
if (str_algo == "brzozowski") { /* default */ }
else {
throw std::runtime_error(std::string(__func__) +
" received an unknown value for the \"algorithm\" key: " + str_algo);
}

return algo(nfa);
}

Nfa mata::nfa::minimize(const Nfa &dfa, const ParameterMap& params)
{
if (!haskey(params, "algorithm")) {
throw std::runtime_error(std::to_string(__func__) +
" received an unknown value of the \"algorithm\" key: " + str_algo);
" requires setting the \"algorithm\" key in the \"params\" argument; "
"received: " + std::to_string(params));
}

return algo(aut);
// Setting the algorithm. Default is Hopcroft.
const std::string& str_algo = params.at("algorithm");
decltype(algorithms::minimize_hopcroft)* algo = algorithms::minimize_hopcroft;
if (str_algo == "hopcroft") {
/* default */;
} else if (str_algo == "brzozowski") {
algo = algorithms::minimize_brzozowski;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be named make_minimal_dfa_brzozowski()? To show it can be used for NFAs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this will break the naming convention for minimizazion algorithms.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but Brzozowski does not perform minimization as per the definition given by Lukáš. But this is debatable. What does @jurajsic think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know, but I realized that this change, that minimize now HAVE to take DFA, might break noodler (and anyone using mata, if such people exists), because before it was assumed it takes nfa and calls brzozowski.

} else {
throw std::runtime_error(std::string(__func__) +
" received an unknown value for the \"algorithm\" key: " + str_algo);
}

// Hopcroft algorithm does not work with non-trimmed automata.
if (str_algo == "hopcroft") {
const BoolVector is_used = dfa.get_useful_states();
bool is_trimmed = std::all_of(is_used.begin(), is_used.end(), [](bool b) { return b; });
if (!is_trimmed) {
return algo(Nfa(dfa).trim());
}
}
return algo(dfa);
}

// Anonymous namespace for the Hopcroft minimization algorithm.
Expand Down Expand Up @@ -1295,7 +1322,7 @@ Nfa mata::nfa::algorithms::minimize_hopcroft(const Nfa& dfa_trimmed) {
for (const SymbolPost &symbol_post : dfa_trimmed.delta[q]) {
assert(symbol_post.targets.size() == 1);
const State target = brp.set_idx[*symbol_post.targets.begin()];
mut_state_post.push_back(SymbolPost{ symbol_post.symbol, StateSet{ target } });
mut_state_post.emplace_back(SymbolPost{ symbol_post.symbol, StateSet{ target } });
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests-integration/src/test-unary-op.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) {

// minimization test
Nfa aut_min(aut);
aut_min = mata::nfa::minimize(aut_min);
aut_min = mata::nfa::make_minimal_dfa(aut_min);
std::cout << "minimize:" << (mata::nfa::are_equivalent(aut, aut_min) ? "ok" : "fail") << std::endl;

return EXIT_SUCCESS;
Expand Down
6 changes: 3 additions & 3 deletions tests-integration/src/unary-operations.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ int main(int argc, char *argv[]) {
TIME_END(reduce);

Nfa minimized_aut;
TIME_BEGIN(minimize);
TIME_BEGIN(make_minimal_dfa);
// > START OF PROFILED CODE
// Only minimize and its callees will be measured
mata::nfa::plumbing::minimize(&minimized_aut, aut);
mata::nfa::plumbing::make_minimal_dfa(&minimized_aut, aut);
// > END OF PROFILED CODE
TIME_END(minimize);
TIME_END(make_minimal_dfa);

Nfa det_aut;
TIME_BEGIN(determinize);
Expand Down
2 changes: 1 addition & 1 deletion tests/nfa/nfa-plumbing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ TEST_CASE("Mata::nfa::Plumbing") {

SECTION("Mata::nfa::Plumbing::minimize") {
FILL_WITH_AUT_A(lhs);
mata::nfa::plumbing::minimize(&result, lhs);
mata::nfa::plumbing::make_minimal_dfa(&result, lhs);
CHECK(!result.is_lang_empty());
}

Expand Down
2 changes: 1 addition & 1 deletion tests/nfa/nfa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ TEST_CASE("mata::nfa::minimize() for profiling", "[.profiling],[minimize]") {
aut.delta.add(3, 110, 3);
aut.delta.add(3, 111, 3);
aut.delta.add(3, 114, 3);
minimize(&result, aut);
make_minimal_dfa(&result, aut);
}

TEST_CASE("mata::nfa::construct() correct calls")
Expand Down
Loading