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:
parent
7b28f1ffb5
commit
96e2da8666
11 changed files with 467 additions and 60 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue