sat-minimize: some documentation and associated fixes

* doc/org/satmin.org: Document the new DTωA-minimization procedure.
* doc/org/tools.org: Fix link.
* src/bin/autfilt.cc: Pass -S to sat_minimize().
* src/twa/twagraph.hh: (state_acc_sets) New method.
* src/twaalgos/dotty.cc: Use it to correctly display co-Büchi automata.
* src/twaalgos/dtbasat.cc: Set the deterministic property on the result.
* src/twaalgos/dtgbasat.cc: Likewise, and preprocess the input automaton
in sat_minimize().
* src/twaalgos/dtgbasat.hh: Fix documentation, and take the state-based
information as an argument.
* src/twaalgos/postproc.cc: Do not call simulation-based reduction
on non-separated acceptances.
* src/tests/satmin2.test: Use -S rather than 'state-based'.
* NEWS: Update.
This commit is contained in:
Alexandre Duret-Lutz 2015-05-21 19:05:48 +02:00
parent 7b28f1ffb5
commit 96e2da8666
11 changed files with 467 additions and 60 deletions

View file

@ -585,7 +585,7 @@ namespace
if (opt_sat_minimize)
{
aut = spot::sat_minimize(aut, opt_sat_minimize);
aut = spot::sat_minimize(aut, opt_sat_minimize, sbacc);
if (!aut)
return 0;
}

View file

@ -108,7 +108,7 @@ $autfilt --sat-minimize='acc="Fin(0)|Inf(1)"' test.hoa --stats=%s >output
test `cat output` = 1
# How about a state-based DSA?
$autfilt --sat-minimize='state-based,acc="Fin(0)|Inf(1)"' test.hoa \
$autfilt -S --sat-minimize='acc="Fin(0)|Inf(1)"' test.hoa \
--stats=%s > output
test `cat output` = 3

View file

@ -436,6 +436,16 @@ namespace spot
/// Remove all unreachable states.
void purge_unreachable_states();
acc_cond::mark_t state_acc_sets(unsigned s) const
{
assert(has_state_based_acc() || acc_.num_sets() == 0);
for (auto& t: g_.out(s))
// Stop at the first transition, since the remaining should be
// labeled identically.
return t.acc;
return 0U;
}
bool state_is_accepting(unsigned s) const
{
assert(has_state_based_acc() || acc_.num_sets() == 0);

View file

@ -417,7 +417,10 @@ namespace spot
else
os_ << s;
os_ << '"';
if (mark_states_ && aut_->state_is_accepting(s))
// Use state_acc_sets(), not state_is_accepting() because
// on co-Büchi automata we want to mark the rejecting
// states.
if (mark_states_ && aut_->state_acc_sets(s))
os_ << ", peripheries=2";
os_ << "]\n";
}

View file

@ -641,6 +641,7 @@ namespace spot
acc_cond::mark_t acc = a->set_buchi();
if (state_based)
a->prop_state_based_acc();
a->prop_deterministic();
a->new_states(satdict.cand_size);
unsigned last_aut_trans = -1U;

View file

@ -39,6 +39,7 @@
#include "dtbasat.hh"
#include "sccfilter.hh"
#include "sbacc.hh"
#include "postproc.hh"
// If you set the SPOT_TMPKEEP environment variable the temporary
// file used to communicate with the sat solver will be left in
@ -988,6 +989,7 @@ namespace spot
a->copy_ap_of(aut);
if (state_based)
a->prop_state_based_acc();
a->prop_deterministic();
a->set_acceptance(satdict.cand_nacc, satdict.cand_acc);
a->new_states(satdict.cand_size);
@ -1075,7 +1077,6 @@ namespace spot
#endif
a->merge_transitions();
return a;
}
}
@ -1209,7 +1210,7 @@ namespace spot
}
twa_graph_ptr
sat_minimize(twa_graph_ptr a, const char* opt)
sat_minimize(twa_graph_ptr a, const char* opt, bool state_based)
{
option_map om;
auto err = om.parse_options(opt);
@ -1224,20 +1225,22 @@ namespace spot
throw std::runtime_error
("SAT-based minimization only work with deterministic automata");
bool sb = om.get("state-based", 0);
bool dicho = om.get("dichotomy", 0);
int states = om.get("states", -1);
int max_states = om.get("max-states", -1);
int nacc = om.get("gba", -1);
auto accstr = om.get_str("acc");
if (nacc != -1 && !accstr.empty())
throw std::runtime_error("options 'gba' and 'acc' cannot "
"be both passed to sat_minimize()");
// Assume we are going to use the input automaton acceptance...
bool user_supplied_acc = false;
acc_cond::acc_code target_acc = a->get_acceptance();
int nacc = a->acc().num_sets();
if (accstr == "same")
accstr.clear();
// ... unless the user specified otherwise
if (!accstr.empty())
{
user_supplied_acc = true;
target_acc = parse_acc_code(accstr.c_str());
// Just in case we were given something like
// Fin(1) | Inf(3)
@ -1249,50 +1252,96 @@ namespace spot
target_acc = target_acc.strip(a.comp(used), true);
nacc = used.count();
}
else if (nacc != -1)
bool target_is_buchi = false;
{
acc_cond acccond(nacc);
acccond.set_acceptance(target_acc);
target_is_buchi = acccond.is_buchi();
}
if (int preproc = om.get("preproc", 3))
{
target_acc = acc_cond::generalized_buchi(nacc);
postprocessor post;
auto sba = (state_based && a->has_state_based_acc()) ?
postprocessor::SBAcc : postprocessor::Any;
post.set_pref(postprocessor::Deterministic
| postprocessor::Complete
| sba);
post.set_type(postprocessor::Generic);
postprocessor::optimization_level level;
switch (preproc)
{
case 1:
level = postprocessor::Low;
break;
case 2:
level = postprocessor::Medium;
break;
case 3:
level = postprocessor::High;
break;
default:
throw
std::runtime_error("preproc should be a value between 0 and 3.");
}
post.set_level(level);
a = post.run(a, nullptr);
// If we have WDBA, it is necessarily minimal because
// postprocessor always run WDBA minimization in Deterministic
// mode. If the desired output is a Büchi automaton, or not
// desired acceptance was specified, stop here. There is not
// point in minimizing a minimal automaton.
if (a->is_inherently_weak() && a->is_deterministic()
&& (target_is_buchi || !user_supplied_acc))
return a;
}
else
{
nacc = a->acc().num_sets();
tgba_complete_here(a);
}
bool target_is_buchi =
(nacc == 1 && target_acc.size() == 2 &&
target_acc[1].op == acc_cond::acc_op::Inf
&& target_acc[0].mark.has(0));
if (states == -1 && max_states == -1)
{
if (state_based)
max_states = sbacc(a)->num_states();
else
max_states = a->num_states();
// If we have not user-supplied acceptance, the input
// automaton is a valid one, so we start the search with one
// less state.
max_states -= !user_supplied_acc;
}
tgba_complete_here(a);
if (sb && states == -1 && max_states == -1)
max_states = sbacc(a)->num_states();
if (states == -1)
{
auto orig = a;
if (!target_is_buchi || !a->acc().is_buchi())
a = (dicho ? dtgba_sat_minimize_dichotomy : dtgba_sat_minimize)
(a, nacc, target_acc, sb, max_states);
(a, nacc, target_acc, state_based, max_states);
else
a = (dicho ? dtba_sat_minimize_dichotomy : dtba_sat_minimize)
(a, sb, max_states);
(a, state_based, max_states);
if (!a && !user_supplied_acc)
a = orig;
}
else
{
if (!target_is_buchi || !a->acc().is_buchi())
a = dtgba_sat_synthetize(a, nacc, target_acc, states, sb);
a = dtgba_sat_synthetize(a, nacc, target_acc, states, state_based);
else
a = dtba_sat_synthetize(a, states, sb);
a = dtba_sat_synthetize(a, states, state_based);
}
if (a)
{
if (a->acc().is_generalized_buchi())
a = scc_filter(a);
if (state_based)
a = scc_filter_states(a);
else
a->purge_dead_states();
a = scc_filter(a);
}
return a;
}

View file

@ -81,15 +81,16 @@ namespace spot
/// \brief High-level interface to SAT-based minimization
///
/// Minimize the automaton \a aut, using options \a opt.
/// These options are given a comma-separated list of
/// These options are given as a comma-separated list of
/// assignments of the form:
///
/// state-based = 1
/// states = 10
/// acc = generalized-Buchi 2
/// acc = Rabin 3
/// acc = same /* default */
/// states = 10 // synthetize automaton with fixed number of states
/// max-states = 20 // minimize starting from this upper bound
/// acc = "generalized-Buchi 2"
/// acc = "Rabin 3"
/// acc = "same" /* default */
/// dichotomy = 1 // use dichotomy instead of decreasing loop
///
SPOT_API twa_graph_ptr
sat_minimize(twa_graph_ptr aut, const char* opt);
sat_minimize(twa_graph_ptr aut, const char* opt, bool state_based = false);
}

View file

@ -32,6 +32,7 @@
#include "complete.hh"
#include "totgba.hh"
#include "sbacc.hh"
#include "sepsets.hh"
namespace spot
{
@ -93,6 +94,8 @@ namespace spot
twa_graph_ptr
postprocessor::do_simul(const twa_graph_ptr& a, int opt)
{
if (!has_separate_sets(a))
return a;
switch (opt)
{
case 0: