Renaming and clean up
"Strategy" was used for mealy machines and game strategies a like. Introduced the notion of mealy machine in three different flavors: mealy machine: twa_graph with synthesis-outputs separated mealy machine: mealy machine and all transitions have conditions of the form (bdd over inputs)&(bdd over outputs) split mealy machine: mealy machine that alternates between env and player states. Needs state-players * bin/ltlsynt.cc: renaming * python/spot/impl.i: Add vector for const_twa_graph_ptr * spot/twaalgos/aiger.cc, spot/twaalgos/aiger.hh: Adapting functions * spot/twaalgos/mealy_machine.cc, spot/twaalgos/mealy_machine.hh: Add test functions and propagate properties correctly. Adjust for names * spot/twaalgos/synthesis.cc, spot/twaalgos/synthesis.hh: Removing unnecessary functions and adapt to new names * tests/python/aiger.py, tests/python/_mealy.ipynb, tests/python/mealy.py, tests/python/synthesis.ipynb: Adjust
This commit is contained in:
parent
6ebe3d7447
commit
98ebbea17e
12 changed files with 3376 additions and 3038 deletions
106
bin/ltlsynt.cc
106
bin/ltlsynt.cc
|
|
@ -36,6 +36,7 @@
|
|||
#include <spot/twaalgos/game.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/minimize.hh>
|
||||
#include <spot/twaalgos/mealy_machine.hh>
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/twaalgos/synthesis.hh>
|
||||
#include <spot/twaalgos/translate.hh>
|
||||
|
|
@ -344,7 +345,7 @@ namespace
|
|||
|
||||
auto sub_f = sub_form.begin();
|
||||
auto sub_o = sub_outs_str.begin();
|
||||
std::vector<spot::strategy_like_t> strategies;
|
||||
std::vector<spot::mealy_like> mealy_machines;
|
||||
|
||||
auto print_game = want_game ?
|
||||
[](const spot::twa_graph_ptr& game)->void
|
||||
|
|
@ -359,27 +360,27 @@ namespace
|
|||
|
||||
for (; sub_f != sub_form.end(); ++sub_f, ++sub_o)
|
||||
{
|
||||
spot::strategy_like_t strat
|
||||
spot::mealy_like m_like
|
||||
{
|
||||
spot::strategy_like_t::realizability_code::UNKNOWN,
|
||||
spot::mealy_like::realizability_code::UNKNOWN,
|
||||
nullptr,
|
||||
bddfalse
|
||||
};
|
||||
// If we want to print a game,
|
||||
// we never use the direct approach
|
||||
if (!want_game)
|
||||
strat =
|
||||
m_like =
|
||||
spot::try_create_direct_strategy(*sub_f, *sub_o, *gi);
|
||||
|
||||
switch (strat.success)
|
||||
switch (m_like.success)
|
||||
{
|
||||
case spot::strategy_like_t::realizability_code::UNREALIZABLE:
|
||||
case spot::mealy_like::realizability_code::UNREALIZABLE:
|
||||
{
|
||||
std::cout << "UNREALIZABLE" << std::endl;
|
||||
safe_tot_time();
|
||||
return 1;
|
||||
}
|
||||
case spot::strategy_like_t::realizability_code::UNKNOWN:
|
||||
case spot::mealy_like::realizability_code::UNKNOWN:
|
||||
{
|
||||
auto arena = spot::ltl_to_game(*sub_f, *sub_o, *gi);
|
||||
if (gi->bv)
|
||||
|
|
@ -404,55 +405,75 @@ namespace
|
|||
// only if we need it
|
||||
if (!opt_real)
|
||||
{
|
||||
spot::strategy_like_t sl;
|
||||
sl.success =
|
||||
spot::strategy_like_t::realizability_code::REALIZABLE_REGULAR;
|
||||
sl.strat_like = spot::create_strategy(arena, *gi);
|
||||
sl.glob_cond = bddfalse;
|
||||
strategies.push_back(sl);
|
||||
spot::mealy_like ml;
|
||||
ml.success =
|
||||
spot::mealy_like::realizability_code::REALIZABLE_REGULAR;
|
||||
ml.mealy_like =
|
||||
spot::solved_game_to_separated_mealy(arena, *gi);
|
||||
ml.glob_cond = bddfalse;
|
||||
mealy_machines.push_back(ml);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case spot::strategy_like_t::realizability_code::REALIZABLE_REGULAR:
|
||||
case spot::mealy_like::realizability_code::REALIZABLE_REGULAR:
|
||||
{
|
||||
// the direct approach yielded a strategy
|
||||
// which can now be minimized
|
||||
// We minimize only if we need it
|
||||
assert(strat.strat_like && "Expected success but found no strat!");
|
||||
assert(m_like.mealy_like && "Expected success but found no mealy!");
|
||||
if (!opt_real)
|
||||
{
|
||||
spot::stopwatch sw_min;
|
||||
sw_min.start();
|
||||
unsigned simplify = gi->minimize_lvl;
|
||||
bool do_split = 3 <= simplify;
|
||||
if (do_split)
|
||||
strat.strat_like =
|
||||
split_2step(strat.strat_like,
|
||||
spot::get_synthesis_outputs(strat.strat_like),
|
||||
spot::stopwatch sw_direct;
|
||||
sw_direct.start();
|
||||
|
||||
if ((0 < gi->minimize_lvl) && (gi->minimize_lvl < 3))
|
||||
// Uses reduction or not,
|
||||
// both work with mealy machines (non-separated)
|
||||
reduce_mealy_here(m_like.mealy_like, gi->minimize_lvl == 2);
|
||||
|
||||
auto delta = sw_direct.stop();
|
||||
|
||||
sw_direct.start();
|
||||
// todo better algo here?
|
||||
m_like.mealy_like =
|
||||
split_2step(m_like.mealy_like,
|
||||
spot::get_synthesis_outputs(m_like.mealy_like),
|
||||
false);
|
||||
minimize_strategy_here(strat.strat_like, simplify);
|
||||
if (do_split)
|
||||
strat.strat_like = spot::unsplit_2step(strat.strat_like);
|
||||
auto delta = sw_min.stop();
|
||||
if (gi->bv)
|
||||
gi->bv->split_time += sw_direct.stop();
|
||||
|
||||
sw_direct.start();
|
||||
if (gi->minimize_lvl >= 3)
|
||||
{
|
||||
sw_direct.start();
|
||||
// actual minimization, works on split mealy
|
||||
m_like.mealy_like = minimize_mealy(m_like.mealy_like,
|
||||
gi->minimize_lvl - 4);
|
||||
delta = sw_direct.stop();
|
||||
}
|
||||
|
||||
// Unsplit to have separated mealy
|
||||
m_like.mealy_like = unsplit_mealy(m_like.mealy_like);
|
||||
|
||||
if (gi->bv)
|
||||
gi->bv->strat2aut_time += delta;
|
||||
if (gi->verbose_stream)
|
||||
*gi->verbose_stream << "final strategy has "
|
||||
<< strat.strat_like->num_states()
|
||||
<< m_like.mealy_like->num_states()
|
||||
<< " states and "
|
||||
<< strat.strat_like->num_edges()
|
||||
<< m_like.mealy_like->num_edges()
|
||||
<< " edges\n"
|
||||
<< "minimization took " << delta
|
||||
<< " seconds\n";
|
||||
}
|
||||
SPOT_FALLTHROUGH;
|
||||
}
|
||||
case spot::strategy_like_t::realizability_code::REALIZABLE_DTGBA:
|
||||
case spot::mealy_like::realizability_code::REALIZABLE_DTGBA:
|
||||
if (!opt_real)
|
||||
strategies.push_back(strat);
|
||||
mealy_machines.push_back(m_like);
|
||||
break;
|
||||
default:
|
||||
error(2, 0, "unexpected success code during strategy generation "
|
||||
error(2, 0, "unexpected success code during mealy machine generation "
|
||||
"(please send bug report)");
|
||||
}
|
||||
}
|
||||
|
|
@ -472,8 +493,9 @@ namespace
|
|||
}
|
||||
// If we reach this line
|
||||
// a strategy was found for each subformula
|
||||
assert(strategies.size() == sub_form.size()
|
||||
&& "Strategies are missing");
|
||||
assert(mealy_machines.size() == sub_form.size()
|
||||
&& "There are subformula for which no mealy like object"
|
||||
"has been created.");
|
||||
|
||||
spot::aig_ptr saig = nullptr;
|
||||
spot::twa_graph_ptr tot_strat = nullptr;
|
||||
|
|
@ -485,7 +507,7 @@ namespace
|
|||
spot::stopwatch sw2;
|
||||
if (gi->bv)
|
||||
sw2.start();
|
||||
saig = spot::strategies_to_aig(strategies, opt_print_aiger,
|
||||
saig = spot::mealy_machines_to_aig(mealy_machines, opt_print_aiger,
|
||||
input_aps,
|
||||
sub_outs_str);
|
||||
if (gi->bv)
|
||||
|
|
@ -507,14 +529,14 @@ namespace
|
|||
else
|
||||
{
|
||||
assert(std::all_of(
|
||||
strategies.begin(), strategies.end(),
|
||||
[](const auto& sl)
|
||||
{return sl.success ==
|
||||
spot::strategy_like_t::realizability_code::REALIZABLE_REGULAR; })
|
||||
mealy_machines.begin(), mealy_machines.end(),
|
||||
[](const auto& ml)
|
||||
{return ml.success ==
|
||||
spot::mealy_like::realizability_code::REALIZABLE_REGULAR; })
|
||||
&& "ltlsynt: Cannot handle TGBA as strategy.");
|
||||
tot_strat = strategies.front().strat_like;
|
||||
for (size_t i = 1; i < strategies.size(); ++i)
|
||||
tot_strat = spot::product(tot_strat, strategies[i].strat_like);
|
||||
tot_strat = mealy_machines.front().mealy_like;
|
||||
for (size_t i = 1; i < mealy_machines.size(); ++i)
|
||||
tot_strat = spot::product(tot_strat, mealy_machines[i].mealy_like);
|
||||
printer.print(tot_strat, timer_printer_dummy);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -461,6 +461,8 @@ static void handle_any_exception()
|
|||
%include <spot/misc/trival.hh>
|
||||
|
||||
%implicitconv std::vector<spot::formula>;
|
||||
%implicitconv std::vector<spot::twa_graph_ptr>;
|
||||
%implicitconv std::vector<spot::const_twa_graph_ptr>;
|
||||
%implicitconv spot::formula;
|
||||
%implicitconv std::vector<bool>;
|
||||
|
||||
|
|
@ -670,6 +672,7 @@ def state_is_accepting(self, src) -> "bool":
|
|||
%template(scc_info_scc_edges) spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_all>;
|
||||
%template(scc_info_inner_scc_edges) spot::internal::scc_edges<spot::digraph<spot::twa_graph_state, spot::twa_graph_edge_data> const, spot::internal::keep_inner_scc>;
|
||||
%template(vector_twa_graph) std::vector<spot::twa_graph_ptr>;
|
||||
%template(vector_const_twa_graph) std::vector<spot::const_twa_graph_ptr>;
|
||||
%include <spot/twaalgos/strength.hh>
|
||||
%include <spot/twaalgos/sccfilter.hh>
|
||||
%include <spot/twaalgos/stats.hh>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <spot/twa/twagraph.hh>
|
||||
#include <spot/misc/bddlt.hh>
|
||||
#include <spot/misc/minato.hh>
|
||||
#include <spot/twaalgos/mealy_machine.hh>
|
||||
#include <spot/twaalgos/synthesis.hh>
|
||||
|
||||
#define STR(x) #x
|
||||
|
|
@ -1201,12 +1202,20 @@ namespace spot
|
|||
};
|
||||
|
||||
std::vector<bdd> outbddvec;
|
||||
outbddvec.reserve(output_names_.size());
|
||||
for (auto& ao : output_names_)
|
||||
outbddvec.push_back(bdd_ithvar(aut->register_ap(ao)));
|
||||
// Also register the ins
|
||||
for (auto& ai : input_names_)
|
||||
aut->register_ap(ai);
|
||||
|
||||
// Set the named prop
|
||||
set_synthesis_outputs(aut,
|
||||
std::accumulate(outbddvec.begin(), outbddvec.end(),
|
||||
(bdd) bddtrue,
|
||||
[](const bdd& b1, const bdd& b2) -> bdd
|
||||
{return b1 & b2; }));
|
||||
|
||||
std::vector<bdd> outcondbddvec;
|
||||
for (auto ov : outputs_)
|
||||
outcondbddvec.push_back(aigvar2bdd(ov));
|
||||
|
|
@ -1387,8 +1396,6 @@ namespace
|
|||
{
|
||||
assert(e.cond != bddfalse);
|
||||
bdd bout = bdd_exist(e.cond, all_inputs);
|
||||
assert(((bout & bdd_existcomp(e.cond, all_inputs)) == e.cond) &&
|
||||
"Precondition (in) & (out) == cond violated");
|
||||
// low is good, dc is ok, high is bad
|
||||
this_outc[astrat.first->edge_number(e)] =
|
||||
bdd_satoneshortest(bout, 0, 1, 5);
|
||||
|
|
@ -1447,9 +1454,9 @@ namespace
|
|||
const std::vector<std::string>& unused_ins = {},
|
||||
const std::vector<std::string>& unused_outs = {})
|
||||
{
|
||||
// The aiger circuit cannot encode the acceptance condition
|
||||
// Test that the acceptance condition is true
|
||||
for (auto&& astrat : strat_vec)
|
||||
// The aiger circuit can currently noly encode separated mealy machines
|
||||
|
||||
for (const auto& astrat : strat_vec)
|
||||
if (!astrat.first->acc().is_t())
|
||||
{
|
||||
std::cerr << "Acc cond must be true not " << astrat.first->acc()
|
||||
|
|
@ -1457,6 +1464,10 @@ namespace
|
|||
throw std::runtime_error("Cannot turn automaton into "
|
||||
"aiger circuit");
|
||||
}
|
||||
// This checks the acc again, but does more and is to
|
||||
// costly for non-debug mode
|
||||
assert(std::all_of(strat_vec.begin(), strat_vec.end(),
|
||||
[](const auto& p){ return is_separated_mealy(p.first); }));
|
||||
|
||||
// get the propositions
|
||||
std::vector<std::string> input_names;
|
||||
|
|
@ -1840,56 +1851,40 @@ namespace spot
|
|||
{
|
||||
|
||||
aig_ptr
|
||||
strategy_to_aig(const const_twa_graph_ptr& aut, const char* mode)
|
||||
mealy_machine_to_aig(const const_twa_graph_ptr& m, const char* mode)
|
||||
{
|
||||
if (!aut)
|
||||
throw std::runtime_error("aut cannot be null");
|
||||
if (!m)
|
||||
throw std::runtime_error("mealy_machine_to_aig(): "
|
||||
"m cannot be null");
|
||||
|
||||
return auts_to_aiger({{aut, get_synthesis_outputs(aut)}}, mode);
|
||||
return auts_to_aiger({{m, get_synthesis_outputs(m)}}, mode);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
strategies_to_aig(const std::vector<const_twa_graph_ptr>& strat_vec,
|
||||
const char *mode)
|
||||
mealy_machine_to_aig(const mealy_like& m, const char* mode)
|
||||
{
|
||||
std::for_each(strat_vec.begin()+1, strat_vec.end(),
|
||||
[usedbdd = strat_vec.at(0)->get_dict()](const auto& s)
|
||||
{
|
||||
if (usedbdd != s->get_dict())
|
||||
throw std::runtime_error("All strategies have to "
|
||||
"share a bdd_dict!\n");
|
||||
});
|
||||
if (m.success != mealy_like::realizability_code::REALIZABLE_REGULAR)
|
||||
throw std::runtime_error("mealy_machine_to_aig(): "
|
||||
"Can only handle regular mealy machine, TBD");
|
||||
|
||||
std::vector<std::pair<const_twa_graph_ptr, bdd>> new_vec;
|
||||
new_vec.reserve(strat_vec.size());
|
||||
|
||||
bdd all_outputs = bddtrue;
|
||||
for (auto& astrat : strat_vec)
|
||||
{
|
||||
bdd this_outputs = get_synthesis_outputs(astrat);
|
||||
// Check if outs do not overlap
|
||||
if (bdd_and(bdd_not(this_outputs), all_outputs) == bddfalse)
|
||||
throw std::runtime_error("\"outs\" of strategies are not "
|
||||
"distinct!.\n");
|
||||
all_outputs &= this_outputs;
|
||||
new_vec.emplace_back(astrat, this_outputs);
|
||||
}
|
||||
return auts_to_aiger(new_vec, mode);
|
||||
return mealy_machine_to_aig(m.mealy_like, mode);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
strategy_to_aig(const twa_graph_ptr &aut, const char *mode,
|
||||
mealy_machine_to_aig(const twa_graph_ptr &m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs)
|
||||
{
|
||||
if (!aut)
|
||||
throw std::runtime_error("aut cannot be null");
|
||||
if (!m)
|
||||
throw std::runtime_error("mealy_machine_to_aig(): "
|
||||
"m cannot be null");
|
||||
|
||||
// Make sure ins and outs are disjoint
|
||||
{
|
||||
std::vector<std::string> all_ap = ins;
|
||||
all_ap.insert(all_ap.end(), outs.begin(), outs.end());
|
||||
check_double_names(all_ap, "Atomic propositions appears in input "
|
||||
check_double_names(all_ap, "mealy_machine_to_aig(): "
|
||||
"Atomic propositions appears in input "
|
||||
"and output propositions; ");
|
||||
}
|
||||
// Check if there exist outs or ins that are not used by the
|
||||
|
|
@ -1900,7 +1895,7 @@ namespace spot
|
|||
std::vector<std::string> unused_ins;
|
||||
{
|
||||
std::set<std::string> used_aps;
|
||||
for (const auto& f : aut->ap())
|
||||
for (const auto& f : m->ap())
|
||||
used_aps.insert(f.ap_name());
|
||||
for (const auto& ao : outs)
|
||||
if (!used_aps.count(ao))
|
||||
|
|
@ -1910,25 +1905,90 @@ namespace spot
|
|||
unused_ins.push_back(ai);
|
||||
}
|
||||
// todo Some additional checks?
|
||||
return auts_to_aiger({{aut, get_synthesis_outputs(aut)}}, mode,
|
||||
return auts_to_aiger({{m, get_synthesis_outputs(m)}}, mode,
|
||||
unused_ins, unused_outs);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
mealy_machine_to_aig(mealy_like& m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs)
|
||||
{
|
||||
if (m.success != mealy_like::realizability_code::REALIZABLE_REGULAR)
|
||||
throw std::runtime_error("mealy_machine_to_aig(): "
|
||||
"Can only handle regular mealy machine, TBD");
|
||||
|
||||
return mealy_machine_to_aig(m.mealy_like, mode, ins, outs);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
mealy_machines_to_aig(const std::vector<const_twa_graph_ptr>& m_vec,
|
||||
const char *mode)
|
||||
{
|
||||
std::for_each(m_vec.begin()+1, m_vec.end(),
|
||||
[usedbdd = m_vec.at(0)->get_dict()](const auto& s)
|
||||
{
|
||||
if (usedbdd != s->get_dict())
|
||||
throw std::runtime_error("mealy_machines_to_aig(): "
|
||||
"All machines have to "
|
||||
"share a bdd_dict!\n");
|
||||
});
|
||||
|
||||
std::vector<std::pair<const_twa_graph_ptr, bdd>> new_vec;
|
||||
new_vec.reserve(m_vec.size());
|
||||
|
||||
bdd all_outputs = bddtrue;
|
||||
for (auto& am : m_vec)
|
||||
{
|
||||
bdd this_outputs = get_synthesis_outputs(am);
|
||||
// Check if outs do not overlap
|
||||
if (bdd_and(bdd_not(this_outputs), all_outputs) == bddfalse)
|
||||
throw std::runtime_error("mealy_machines_to_aig(): "
|
||||
"\"outs\" of the machines are not "
|
||||
"distinct!.\n");
|
||||
all_outputs &= this_outputs;
|
||||
new_vec.emplace_back(am, this_outputs);
|
||||
}
|
||||
return auts_to_aiger(new_vec, mode);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
mealy_machines_to_aig(const std::vector<mealy_like>& m_vec,
|
||||
const char *mode)
|
||||
{
|
||||
if (std::any_of(m_vec.cbegin(), m_vec.cend(),
|
||||
[](const auto& m)
|
||||
{return m.success !=
|
||||
mealy_like::realizability_code::REALIZABLE_REGULAR; }))
|
||||
throw std::runtime_error("mealy_machines_to_aig(): "
|
||||
"Can only handle regular mealy machine for "
|
||||
"the moment, TBD");
|
||||
auto new_vec = std::vector<const_twa_graph_ptr>();
|
||||
new_vec.reserve(m_vec.size());
|
||||
std::transform(m_vec.cbegin(), m_vec.cend(),
|
||||
std::back_inserter(new_vec),
|
||||
[](const auto& m){return m.mealy_like; });
|
||||
|
||||
return mealy_machines_to_aig(new_vec, mode);
|
||||
}
|
||||
|
||||
// Note: This ignores the named property
|
||||
aig_ptr
|
||||
strategies_to_aig(const std::vector<twa_graph_ptr>& strat_vec,
|
||||
mealy_machines_to_aig(const std::vector<twa_graph_ptr>& m_vec,
|
||||
const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs)
|
||||
{
|
||||
if (strat_vec.size() != outs.size())
|
||||
throw std::runtime_error("Expected as many outs as strategies!\n");
|
||||
if (m_vec.size() != outs.size())
|
||||
throw std::runtime_error("mealy_machines_to_aig(): "
|
||||
"Expected as many outs as strategies!\n");
|
||||
|
||||
std::for_each(strat_vec.begin()+1, strat_vec.end(),
|
||||
[usedbdd = strat_vec.at(0)->get_dict()](auto&& it)
|
||||
std::for_each(m_vec.begin()+1, m_vec.end(),
|
||||
[usedbdd = m_vec.at(0)->get_dict()](auto&& it)
|
||||
{
|
||||
if (usedbdd != it->get_dict())
|
||||
throw std::runtime_error("All strategies have to "
|
||||
throw std::runtime_error("mealy_machines_to_aig(): "
|
||||
"All strategies have to "
|
||||
"share a bdd_dict!\n");
|
||||
});
|
||||
|
||||
|
|
@ -1944,17 +2004,17 @@ namespace spot
|
|||
}
|
||||
|
||||
std::vector<std::pair<const_twa_graph_ptr, bdd>> new_vec;
|
||||
new_vec.reserve(strat_vec.size());
|
||||
new_vec.reserve(m_vec.size());
|
||||
|
||||
std::set<std::string> used_aps;
|
||||
|
||||
for (size_t i = 0; i < strat_vec.size(); ++i)
|
||||
for (size_t i = 0; i < m_vec.size(); ++i)
|
||||
{
|
||||
for (const auto& f : strat_vec[i]->ap())
|
||||
for (const auto& f : m_vec[i]->ap())
|
||||
used_aps.insert(f.ap_name());
|
||||
// todo Some additional checks?
|
||||
new_vec.emplace_back(strat_vec[i],
|
||||
get_synthesis_outputs(strat_vec[i]));
|
||||
new_vec.emplace_back(m_vec[i],
|
||||
get_synthesis_outputs(m_vec[i]));
|
||||
}
|
||||
|
||||
// Check if there exist outs or ins that are not used by the
|
||||
|
|
@ -1975,15 +2035,15 @@ namespace spot
|
|||
}
|
||||
|
||||
aig_ptr
|
||||
strategies_to_aig(const std::vector<strategy_like_t>& strat_vec,
|
||||
mealy_machines_to_aig(const std::vector<mealy_like>& strat_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs)
|
||||
{
|
||||
// todo extend to TGBA and possibly others
|
||||
const unsigned ns = strat_vec.size();
|
||||
std::vector<twa_graph_ptr> strategies;
|
||||
strategies.reserve(ns);
|
||||
std::vector<twa_graph_ptr> m_machines;
|
||||
m_machines.reserve(ns);
|
||||
std::vector<std::vector<std::string>> outs_used;
|
||||
outs_used.reserve(ns);
|
||||
|
||||
|
|
@ -1991,27 +2051,28 @@ namespace spot
|
|||
{
|
||||
switch (strat_vec[i].success)
|
||||
{
|
||||
case strategy_like_t::realizability_code::UNREALIZABLE:
|
||||
throw std::runtime_error("strategies_to_aig(): Partial strat is "
|
||||
"not feasible!");
|
||||
case strategy_like_t::realizability_code::UNKNOWN:
|
||||
throw std::runtime_error("strategies_to_aig(): Partial strat has "
|
||||
"unknown status!");
|
||||
case strategy_like_t::realizability_code::REALIZABLE_REGULAR:
|
||||
case mealy_like::realizability_code::UNREALIZABLE:
|
||||
throw std::runtime_error("mealy_machines_to_aig(): One of the "
|
||||
"mealy like machines is not realizable.");
|
||||
case mealy_like::realizability_code::UNKNOWN:
|
||||
throw std::runtime_error("mealy_machines_to_aig(): One of the"
|
||||
"mealy like objects has "
|
||||
"status \"unkwnown\"");
|
||||
case mealy_like::realizability_code::REALIZABLE_REGULAR:
|
||||
{
|
||||
strategies.push_back(strat_vec[i].strat_like);
|
||||
m_machines.push_back(strat_vec[i].mealy_like);
|
||||
outs_used.push_back(outs[i]);
|
||||
break;
|
||||
}
|
||||
case strategy_like_t::realizability_code::REALIZABLE_DTGBA:
|
||||
throw std::runtime_error("strategies_to_aig(): TGBA not "
|
||||
"yet supported.");
|
||||
case mealy_like::realizability_code::REALIZABLE_DTGBA:
|
||||
throw std::runtime_error("mealy_machines_to_aig(): TGBA not "
|
||||
"yet supported - TBD");
|
||||
default:
|
||||
throw std::runtime_error("strategies_to_aig(): Unknown "
|
||||
throw std::runtime_error("mealy_machines_to_aig(): Unknown "
|
||||
"success identifier.");
|
||||
}
|
||||
}
|
||||
return strategies_to_aig(strategies, mode, ins, outs_used);
|
||||
return mealy_machines_to_aig(m_machines, mode, ins, outs_used);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
|
|
@ -2073,7 +2134,7 @@ namespace spot
|
|||
print_aiger(std::ostream& os, const const_twa_graph_ptr& aut,
|
||||
const char* mode)
|
||||
{
|
||||
print_aiger(os, strategy_to_aig(aut, mode));
|
||||
print_aiger(os, mealy_machine_to_aig(aut, mode));
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
namespace spot
|
||||
{
|
||||
// Forward for synthesis
|
||||
struct strategy_like_t;
|
||||
struct mealy_like;
|
||||
|
||||
class aig;
|
||||
|
||||
|
|
@ -415,8 +415,8 @@ namespace spot
|
|||
};
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief Convert a strategy into an aig relying on the transformation
|
||||
/// described by \a mode.
|
||||
/// \brief Convert a mealy (like) machine into an aig relying on
|
||||
/// the transformation described by \a mode.
|
||||
/// \param mode This param has to be of the form
|
||||
/// `ite|isop|both [+dc][+ud][+sub0|+sub1|+sub2]`
|
||||
/// Where `ite` means encoded via if-then-else normal form,
|
||||
|
|
@ -437,9 +437,16 @@ namespace spot
|
|||
/// outs are guaranteed to appear in the aiger circuit.
|
||||
///@{
|
||||
SPOT_API aig_ptr
|
||||
strategy_to_aig(const const_twa_graph_ptr& aut, const char* mode);
|
||||
mealy_machine_to_aig(const const_twa_graph_ptr& m, const char* mode);
|
||||
SPOT_API aig_ptr
|
||||
strategy_to_aig(const twa_graph_ptr& aut, const char *mode,
|
||||
mealy_machine_to_aig(const twa_graph_ptr& m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs);
|
||||
|
||||
SPOT_API aig_ptr
|
||||
mealy_machine_to_aig(const mealy_like& m, const char* mode);
|
||||
SPOT_API aig_ptr
|
||||
mealy_machine_to_aig(mealy_like& m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs);
|
||||
///@}
|
||||
|
|
@ -459,15 +466,18 @@ namespace spot
|
|||
/// guaranteed to appear in the aiger circuit.
|
||||
/// @{
|
||||
SPOT_API aig_ptr
|
||||
strategies_to_aig(const std::vector<const_twa_graph_ptr>& strat_vec,
|
||||
mealy_machines_to_aig(const std::vector<const_twa_graph_ptr>& m_vec,
|
||||
const char* mode);
|
||||
SPOT_API aig_ptr
|
||||
strategies_to_aig(const std::vector<twa_graph_ptr>& strat_vec,
|
||||
mealy_machines_to_aig(const std::vector<mealy_like>& m_vec,
|
||||
const char* mode);
|
||||
SPOT_API aig_ptr
|
||||
mealy_machines_to_aig(const std::vector<twa_graph_ptr>& m_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs);
|
||||
SPOT_API aig_ptr
|
||||
strategies_to_aig(const std::vector<strategy_like_t>& strat_vec,
|
||||
mealy_machines_to_aig(const std::vector<mealy_like>& m_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <spot/misc/timer.hh>
|
||||
#include <spot/misc/minato.hh>
|
||||
#include <spot/twaalgos/game.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/synthesis.hh>
|
||||
|
||||
#include <picosat/picosat.h>
|
||||
|
|
@ -52,11 +53,245 @@
|
|||
# define dotimeprint while (0) std::cerr
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_deterministic_(const std::vector<bdd>& ins)
|
||||
{
|
||||
const unsigned n_ins = ins.size();
|
||||
for (unsigned idx1 = 0; idx1 < n_ins - 1; ++idx1)
|
||||
for (unsigned idx2 = idx1 + 1; idx2 < n_ins; ++idx2)
|
||||
if (bdd_have_common_assignment(ins[idx1], ins[idx2]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
bool
|
||||
is_mealy(const const_twa_graph_ptr& m)
|
||||
{
|
||||
if (!m->acc().is_t())
|
||||
{
|
||||
trace << "is_mealy(): Mealy machines must have "
|
||||
"true acceptance condition.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m->get_named_prop<bdd>("synthesis-outputs"))
|
||||
{
|
||||
trace << "is_mealy(): \"synthesis-outputs\" not found!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
is_separated_mealy(const const_twa_graph_ptr& m)
|
||||
{
|
||||
if (!is_mealy(m))
|
||||
return false;
|
||||
|
||||
auto outs = get_synthesis_outputs(m);
|
||||
|
||||
for (const auto& e : m->edges())
|
||||
if ((bdd_exist(e.cond, outs) & bdd_existcomp(e.cond, outs)) != e.cond)
|
||||
{
|
||||
trace << "is_separated_mealy_machine(): Edge number "
|
||||
<< m->edge_number(e) << " from " << e.src << " to " << e.dst
|
||||
<< " with " << e.cond << " is not separated!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
is_split_mealy(const const_twa_graph_ptr& m)
|
||||
{
|
||||
if (!is_mealy(m))
|
||||
return false;
|
||||
|
||||
if (m->get_named_prop<region_t>("state-player") == nullptr)
|
||||
{
|
||||
trace << "is_split_mealy(): Split mealy machine must define the named "
|
||||
"property \"state-player\"!\n";
|
||||
}
|
||||
auto sp = get_state_players(m);
|
||||
|
||||
if (sp.size() != m->num_states())
|
||||
{
|
||||
trace << "\"state-player\" has not the same size as the "
|
||||
"automaton!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sp[m->get_init_state_number()])
|
||||
{
|
||||
trace << "is_split_mealy(): Initial state must be owned by env!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto outs = get_synthesis_outputs(m);
|
||||
|
||||
for (const auto& e : m->edges())
|
||||
{
|
||||
if (sp[e.src] == sp[e.dst])
|
||||
{
|
||||
trace << "is_split_mealy(): Edge number "
|
||||
<< m->edge_number(e) << " from " << e.src << " to " << e.dst
|
||||
<< " is not alternating!\n";
|
||||
return false;
|
||||
}
|
||||
if (sp[e.src] && (bdd_exist(e.cond, outs) != bddtrue))
|
||||
{
|
||||
trace << "is_split_mealy(): Edge number "
|
||||
<< m->edge_number(e) << " from " << e.src << " to " << e.dst
|
||||
<< " depends on inputs, but should be labeled by outputs!\n";
|
||||
return false;
|
||||
}
|
||||
if (!sp[e.src] && (bdd_existcomp(e.cond, outs) != bddtrue))
|
||||
{
|
||||
trace << "is_split_mealy(): Edge number "
|
||||
<< m->edge_number(e) << " from " << e.src << " to " << e.dst
|
||||
<< " depends on outputs, but should be labeled by inputs!\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
is_input_deterministic_mealy(const const_twa_graph_ptr& m)
|
||||
{
|
||||
if (!is_mealy(m))
|
||||
return false;
|
||||
|
||||
const unsigned N = m->num_states();
|
||||
|
||||
auto sp_ptr = m->get_named_prop<region_t>("state-player");
|
||||
// Unsplit mealy -> sp_ptr == nullptr
|
||||
if (sp_ptr && !is_split_mealy(m))
|
||||
return false;
|
||||
|
||||
auto outs = get_synthesis_outputs(m);
|
||||
|
||||
std::vector<bdd> ins;
|
||||
for (unsigned s = 0; s < N; ++s)
|
||||
{
|
||||
if (sp_ptr && (*sp_ptr)[s])
|
||||
continue;
|
||||
ins.clear();
|
||||
for (const auto& e : m->out(s))
|
||||
ins.push_back(sp_ptr ? e.cond
|
||||
: bdd_exist(e.cond, outs));
|
||||
if (!is_deterministic_(ins))
|
||||
{
|
||||
trace << "is_input_deterministic_mealy(): State number "
|
||||
<< s << " is not input determinisc!\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
split_separated_mealy_here(const twa_graph_ptr& m)
|
||||
{
|
||||
assert(is_mealy(m));
|
||||
|
||||
auto output_bdd = get_synthesis_outputs(m);
|
||||
|
||||
struct dst_cond_color_t
|
||||
{
|
||||
std::pair<unsigned, int> dst_cond;
|
||||
acc_cond::mark_t color;
|
||||
};
|
||||
|
||||
auto hasher = [](const dst_cond_color_t& dcc) noexcept
|
||||
{
|
||||
return dcc.color.hash() ^ pair_hash()(dcc.dst_cond);
|
||||
};
|
||||
auto equal = [](const dst_cond_color_t& dcc1,
|
||||
const dst_cond_color_t& dcc2) noexcept
|
||||
{
|
||||
return (dcc1.dst_cond == dcc2.dst_cond)
|
||||
&& (dcc1.color == dcc2.color);
|
||||
};
|
||||
|
||||
std::unordered_map<dst_cond_color_t, unsigned,
|
||||
decltype(hasher),
|
||||
decltype(equal)> player_map(m->num_edges(),
|
||||
hasher, equal);
|
||||
|
||||
auto get_ps = [&](unsigned dst, const bdd& ocond,
|
||||
acc_cond::mark_t color)
|
||||
{
|
||||
dst_cond_color_t key{std::make_pair(dst, ocond.id()),
|
||||
color};
|
||||
auto [it, inserted] =
|
||||
player_map.try_emplace(key, m->num_states());
|
||||
if (!inserted)
|
||||
return it->second;
|
||||
unsigned ns = m->new_state();
|
||||
assert(ns == it->second);
|
||||
m->new_edge(ns, dst, ocond, color);
|
||||
return ns;
|
||||
};
|
||||
|
||||
unsigned ne = m->edge_vector().size();
|
||||
for (unsigned eidx = 1; eidx < ne; ++eidx)
|
||||
{
|
||||
const auto& e = m->edge_storage(eidx);
|
||||
if (e.next_succ == eidx) // dead edge
|
||||
continue;
|
||||
bdd incond = bdd_exist(e.cond, output_bdd);
|
||||
bdd outcond = bdd_existcomp(e.cond, output_bdd);
|
||||
assert(((incond&outcond) == e.cond) && "Precondition violated");
|
||||
// Create new state and trans
|
||||
// this may invalidate "e".
|
||||
unsigned new_dst = get_ps(e.dst, outcond, e.acc);
|
||||
// redirect
|
||||
auto& e2 = m->edge_storage(eidx);
|
||||
e2.dst = new_dst;
|
||||
e2.cond = incond;
|
||||
}
|
||||
|
||||
auto* sp_ptr = m->get_or_set_named_prop<region_t>("state-player");
|
||||
|
||||
sp_ptr->resize(m->num_states());
|
||||
std::fill(sp_ptr->begin(), sp_ptr->end(), false);
|
||||
for (const auto& eit : player_map)
|
||||
(*sp_ptr)[eit.second] = true;
|
||||
//Done
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
split_separated_mealy(const const_twa_graph_ptr& m)
|
||||
{
|
||||
assert(is_mealy((m)));
|
||||
auto m2 = make_twa_graph(m, twa::prop_set::all());
|
||||
m2->copy_acceptance_of(m);
|
||||
set_synthesis_outputs(m2, get_synthesis_outputs(m));
|
||||
split_separated_mealy_here(m2);
|
||||
return m2;
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
unsplit_mealy(const const_twa_graph_ptr& m)
|
||||
{
|
||||
assert(is_split_mealy(m));
|
||||
return unsplit_2step(m);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// Anonymous for minimize_mealy_fast
|
||||
// Anonymous for reduce_mealy
|
||||
using namespace spot;
|
||||
|
||||
// Used to get the signature of the state.
|
||||
|
|
@ -478,84 +713,33 @@ namespace
|
|||
|
||||
namespace spot
|
||||
{
|
||||
twa_graph_ptr minimize_mealy_fast(const const_twa_graph_ptr& mm,
|
||||
bool use_inclusion)
|
||||
twa_graph_ptr reduce_mealy(const const_twa_graph_ptr& mm,
|
||||
bool output_assignment)
|
||||
{
|
||||
bool orig_is_split = true;
|
||||
bdd* outsptr = nullptr;
|
||||
bdd outs = bddfalse;
|
||||
assert(is_mealy(mm));
|
||||
if (mm->get_named_prop<std::vector<bool>>("state-player"))
|
||||
throw std::runtime_error("reduce_mealy(): "
|
||||
"Only works on unsplit machines.\n");
|
||||
|
||||
twa_graph_ptr mmc;
|
||||
if (mm->get_named_prop<std::vector<bool>>("state-player") != nullptr)
|
||||
{
|
||||
outsptr = mm->get_named_prop<bdd>("synthesis-outputs");
|
||||
assert(outsptr && "If the original strat is split, "
|
||||
"we need \"synthesis-outputs\".");
|
||||
outs = *outsptr;
|
||||
mmc = unsplit_2step(mm);
|
||||
}
|
||||
else
|
||||
{
|
||||
mmc = make_twa_graph(mm, twa::prop_set::all());
|
||||
auto mmc = make_twa_graph(mm, twa::prop_set::all());
|
||||
mmc->copy_ap_of(mm);
|
||||
mmc->copy_acceptance_of(mm);
|
||||
orig_is_split = false;
|
||||
}
|
||||
set_synthesis_outputs(mmc, get_synthesis_outputs(mm));
|
||||
|
||||
minimize_mealy_fast_here(mmc, use_inclusion);
|
||||
|
||||
// Split if the initial aut was split
|
||||
if (orig_is_split)
|
||||
{
|
||||
mmc = split_2step(mmc, outs, false);
|
||||
bool orig_init_player =
|
||||
mm->get_named_prop<std::vector<bool>>("state-player")
|
||||
->at(mm->get_init_state_number());
|
||||
alternate_players(mmc, orig_init_player, false);
|
||||
}
|
||||
// Try to set outputs
|
||||
if (outsptr)
|
||||
mmc->set_named_prop("synthesis-outputs", new bdd(outs));
|
||||
reduce_mealy_here(mmc, output_assignment);
|
||||
|
||||
assert(is_mealy(mmc));
|
||||
return mmc;
|
||||
}
|
||||
|
||||
void minimize_mealy_fast_here(twa_graph_ptr& mm, bool use_inclusion)
|
||||
void reduce_mealy_here(twa_graph_ptr& mm, bool output_assignment)
|
||||
{
|
||||
|
||||
bool orig_is_split = false;
|
||||
bdd* outsptr = nullptr;
|
||||
bdd outs = bddfalse;
|
||||
|
||||
auto sp_ptr = mm->get_named_prop<std::vector<bool>>("state-player");
|
||||
|
||||
bool init_player = sp_ptr ? sp_ptr->at(mm->get_init_state_number())
|
||||
: false;
|
||||
assert(is_mealy(mm));
|
||||
|
||||
// Only consider infinite runs
|
||||
mm->purge_dead_states();
|
||||
|
||||
if (mm->get_named_prop<std::vector<bool>>("state-player") != nullptr)
|
||||
{
|
||||
// Check if done
|
||||
if (std::count(sp_ptr->begin(), sp_ptr->end(), false) == 1)
|
||||
return;
|
||||
outsptr = mm->get_named_prop<bdd>("synthesis-outputs");
|
||||
assert(outsptr && "If the original strat is split, "
|
||||
"we need \"synthesis-outputs\".");
|
||||
outs = *outsptr;
|
||||
init_player =
|
||||
mm->get_named_prop<std::vector<bool>>("state-player")->at(
|
||||
mm->get_init_state_number());
|
||||
mm = unsplit_2step(mm);
|
||||
orig_is_split = true;
|
||||
}
|
||||
else
|
||||
// Check if done
|
||||
if (mm->num_states() == 1)
|
||||
return;
|
||||
|
||||
auto repr = get_repres(mm, use_inclusion);
|
||||
auto repr = get_repres(mm, output_assignment);
|
||||
if (repr[0] == -1U)
|
||||
return;
|
||||
|
||||
|
|
@ -580,12 +764,7 @@ namespace spot
|
|||
}
|
||||
}
|
||||
mm->purge_unreachable_states();
|
||||
if (orig_is_split)
|
||||
{
|
||||
mm = split_2step(mm, outs, false);
|
||||
alternate_players(mm, init_player, false);
|
||||
mm->set_named_prop("synthesis-outputs", new bdd(outs));
|
||||
}
|
||||
assert(is_mealy(mm));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -639,7 +818,7 @@ namespace
|
|||
template <class CONT, class PRED>
|
||||
size_t find_first_index_of(const CONT& c, PRED pred)
|
||||
{
|
||||
size_t s = c.size();
|
||||
const size_t s = c.size();
|
||||
for (unsigned i = 0; i < s; ++i)
|
||||
if (pred(c[i]))
|
||||
return i;
|
||||
|
|
@ -874,8 +1053,8 @@ namespace
|
|||
|
||||
std::vector<bool> spnew(n_new, true);
|
||||
std::fill(spnew.begin(), spnew.begin()+n_env, false);
|
||||
omm->set_named_prop("state-player",
|
||||
new std::vector<bool>(std::move(spnew)));
|
||||
set_state_players(omm, std::move(spnew));
|
||||
set_synthesis_outputs(omm, get_synthesis_outputs(mm));
|
||||
|
||||
// Make sure we have a proper strategy,
|
||||
// that is each player state has only one successor
|
||||
|
|
@ -1217,7 +1396,7 @@ namespace
|
|||
#ifdef TRACE
|
||||
trace << "We found " << n_group << " groups.\n";
|
||||
for (unsigned s = 0; s < n_env; ++s)
|
||||
trace << s << " : " << which_group[s] << '\n';
|
||||
trace << s << " : " << which_group.at(s) << '\n';
|
||||
#endif
|
||||
return std::make_pair(n_group, which_group);
|
||||
}
|
||||
|
|
@ -1640,7 +1819,7 @@ namespace
|
|||
split_mmw->get_graph().chain_edges_();
|
||||
#ifdef TRACE
|
||||
trace << "Orig split aut\n";
|
||||
print_hoa(std::cerr, split_mmw) << '\n';
|
||||
print_hoa(std::cerr, mmw) << '\n';
|
||||
{
|
||||
auto ss = make_twa_graph(split_mmw, twa::prop_set::all());
|
||||
for (unsigned group = 0; group < red.n_groups; ++group)
|
||||
|
|
@ -1652,10 +1831,9 @@ namespace
|
|||
bdd_ithvar(ss->register_ap("g"+std::to_string(group)
|
||||
+"e"+std::to_string(i))));
|
||||
}
|
||||
const unsigned ns = ss->num_states();
|
||||
for (unsigned s = 0; s < ns; ++s)
|
||||
for (unsigned s = 0; s < n_env; ++s)
|
||||
{
|
||||
if (red.which_group[s] != group)
|
||||
if (red.which_group.at(s) != group)
|
||||
continue;
|
||||
for (auto& e : ss->out(s))
|
||||
e.cond =
|
||||
|
|
@ -1676,8 +1854,8 @@ namespace
|
|||
for (const auto& idx_vec : red.bisim_letters[i])
|
||||
trace << red.minimal_letters_vec[i][idx_vec.front()].id() << '\n';
|
||||
trace << "ids in the edge of the group\n";
|
||||
for (unsigned s = 0; s < split_mmw->num_states(); ++s)
|
||||
if (red.which_group[s] == i)
|
||||
for (unsigned s = 0; s < n_env; ++s)
|
||||
if (red.which_group.at(s) == i)
|
||||
for (const auto& e : split_mmw->out(s))
|
||||
trace << e.src << "->" << e.dst << ':' << e.cond.id() << '\n';
|
||||
}
|
||||
|
|
@ -2763,57 +2941,7 @@ namespace
|
|||
return gmm2cond;
|
||||
}
|
||||
|
||||
void cstr_unsplit(twa_graph_ptr& minmach,
|
||||
const reduced_alphabet_t& red,
|
||||
const std::vector<std::pair<unsigned,
|
||||
std::vector<bool>>>&
|
||||
x_in_class,
|
||||
std::unordered_map<iaj_t, bdd,
|
||||
decltype(iaj_hash),
|
||||
decltype(iaj_eq)>& used_ziaj_map)
|
||||
{
|
||||
const unsigned n_groups = red.n_groups;
|
||||
// For each minimal letter, create the (input) condition that it represents
|
||||
// This has to be created for each minimal letters
|
||||
// and might be shared between alphabets
|
||||
std::vector<std::vector<bdd>> gmm2cond
|
||||
= comp_represented_cond(red);
|
||||
// Use a new map to reduce the number of edges created
|
||||
// iaj.i: src_class or 0
|
||||
// iaj.a: id of out condition
|
||||
// iaj.j: dst_class
|
||||
std::unordered_map<iaj_t, bdd,
|
||||
decltype(iaj_hash), decltype(iaj_eq)>
|
||||
edge_map(2*used_ziaj_map.size(), iaj_hash, iaj_eq);
|
||||
|
||||
for (unsigned group = 0; group < n_groups; ++group)
|
||||
{
|
||||
edge_map.clear();
|
||||
|
||||
// Attention the a in used_ziaj corresponds to the index
|
||||
for (const auto& [iaj, outcond] : used_ziaj_map)
|
||||
{
|
||||
if (x_in_class[iaj.i].first != group)
|
||||
continue;
|
||||
|
||||
const bdd& repr_cond = gmm2cond[group].at(iaj.a);
|
||||
|
||||
// Check if there already exists a suitable edge
|
||||
auto [itedge, inserted] =
|
||||
edge_map.try_emplace({iaj.i, (unsigned) outcond.id(), iaj.j},
|
||||
repr_cond);
|
||||
if (!inserted)
|
||||
itedge->second |= repr_cond;
|
||||
}
|
||||
// Create the actual edges
|
||||
|
||||
for (const auto& ep : edge_map)
|
||||
minmach->new_edge(ep.first.i, ep.first.j,
|
||||
ep.second & bdd_from_int((int) ep.first.a));
|
||||
}
|
||||
}
|
||||
|
||||
void cstr_keep_split(twa_graph_ptr& minmach,
|
||||
void cstr_split_mealy(twa_graph_ptr& minmach,
|
||||
const reduced_alphabet_t& red,
|
||||
const std::vector<std::pair<unsigned,
|
||||
std::vector<bool>>>&
|
||||
|
|
@ -2895,11 +3023,10 @@ namespace
|
|||
}
|
||||
|
||||
// Set the state-player
|
||||
std::vector<bool> sp(minmach->num_states(), true);
|
||||
auto* sp = new std::vector<bool>(minmach->num_states(), true);
|
||||
for (unsigned i = 0; i < n_env_states; ++i)
|
||||
sp[i] = false;
|
||||
minmach->set_named_prop("state-player",
|
||||
new std::vector<bool>(std::move(sp)));
|
||||
(*sp)[i] = false;
|
||||
minmach->set_named_prop("state-player", sp);
|
||||
}
|
||||
|
||||
// This function refines the sat constraints in case the
|
||||
|
|
@ -3185,7 +3312,6 @@ namespace
|
|||
const reduced_alphabet_t& red,
|
||||
const part_sol_t& psol,
|
||||
const unsigned n_env,
|
||||
bool keep_split,
|
||||
stopwatch& sw)
|
||||
{
|
||||
const auto& psolv = psol.psol;
|
||||
|
|
@ -3432,10 +3558,7 @@ namespace
|
|||
continue; //retry
|
||||
}
|
||||
|
||||
if (keep_split)
|
||||
cstr_keep_split(minmach, red, x_in_class, used_ziaj_map);
|
||||
else
|
||||
cstr_unsplit(minmach, red, x_in_class, used_ziaj_map);
|
||||
cstr_split_mealy(minmach, red, x_in_class, used_ziaj_map);
|
||||
|
||||
// todo: What is the impact of chosing one of the possibilities
|
||||
minmach->set_init_state(init_class_v.front());
|
||||
|
|
@ -3447,26 +3570,17 @@ namespace
|
|||
namespace spot
|
||||
{
|
||||
twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm,
|
||||
int premin, bool keep_split)
|
||||
int premin)
|
||||
{
|
||||
assert(is_split_mealy(mm));
|
||||
|
||||
stopwatch sw;
|
||||
sw.start();
|
||||
|
||||
if ((premin < -1) || (premin > 1))
|
||||
throw std::runtime_error("premin has to be -1, 0 or 1");
|
||||
|
||||
if (!mm->acc().is_t())
|
||||
throw std::runtime_error("Mealy machine needs true acceptance!\n");
|
||||
|
||||
auto orig_spref = get_state_players(mm);
|
||||
if (orig_spref.size() != mm->num_states())
|
||||
throw std::runtime_error("Inconsistent \"state-player\"");
|
||||
|
||||
if (std::any_of(mm->edges().begin(), mm->edges().end(),
|
||||
[&](const auto& e){return orig_spref[e.src]
|
||||
== orig_spref[e.dst]; }))
|
||||
throw std::runtime_error("Arena is not alternating!");
|
||||
|
||||
|
||||
// Check if finite traces exist
|
||||
// If so, deactivate fast minimization
|
||||
|
|
@ -3491,10 +3605,18 @@ namespace spot
|
|||
if (premin == -1)
|
||||
return mm;
|
||||
else
|
||||
return minimize_mealy_fast(mm, premin == 1);
|
||||
{
|
||||
// We have a split machine -> unsplit then resplit,
|
||||
// as reduce mealy works on separated
|
||||
auto mms = unsplit_mealy(mm);
|
||||
reduce_mealy_here(mms, premin == 1);
|
||||
split_separated_mealy_here(mms);
|
||||
return mms;
|
||||
}
|
||||
};
|
||||
|
||||
const_twa_graph_ptr mmw = do_premin();
|
||||
assert(is_split_mealy(mmw));
|
||||
dotimeprint << "Done premin " << sw.stop() << '\n';
|
||||
|
||||
// 0 -> "Env" next is input props
|
||||
|
|
@ -3508,6 +3630,7 @@ namespace spot
|
|||
// second black : player-states
|
||||
unsigned n_env = -1u;
|
||||
std::tie(mmw, n_env) = reorganize_mm(mmw, spref);
|
||||
assert(is_split_mealy(mmw));
|
||||
#ifdef TRACE
|
||||
print_hoa(std::cerr, mmw);
|
||||
#endif
|
||||
|
|
@ -3530,15 +3653,9 @@ namespace spot
|
|||
|
||||
auto early_exit = [&]()
|
||||
{
|
||||
if (keep_split)
|
||||
// Always keep machines split
|
||||
assert(is_split_mealy_specialization(mm, mmw));
|
||||
return std::const_pointer_cast<twa_graph>(mmw);
|
||||
else
|
||||
{
|
||||
auto mmw_u = unsplit_2step(mmw);
|
||||
// todo correct unsplit to avoid this
|
||||
mmw_u->purge_unreachable_states();
|
||||
return mmw_u;
|
||||
}
|
||||
};
|
||||
|
||||
// If the partial solution has the same number of
|
||||
|
|
@ -3576,7 +3693,6 @@ namespace spot
|
|||
minmachine = try_build_min_machine(mm_pb, mmw,
|
||||
reduced_alphabet,
|
||||
partsol, n_env,
|
||||
keep_split,
|
||||
sw);
|
||||
dotimeprint << "Done try_build " << n_classes
|
||||
<< " - " << sw.stop() << '\n';
|
||||
|
|
@ -3591,35 +3707,32 @@ namespace spot
|
|||
// Set state players!
|
||||
if (!minmachine)
|
||||
return early_exit();
|
||||
// Try to set outputs
|
||||
if (bdd* outptr = mm->get_named_prop<bdd>("synthesis-outputs"))
|
||||
minmachine->set_named_prop("synthesis-outputs", new bdd(*outptr));
|
||||
set_synthesis_outputs(minmachine, get_synthesis_outputs(mm));
|
||||
dotimeprint << "Done minimizing - " << minmachine->num_states()
|
||||
<< " - " << sw.stop() << '\n';
|
||||
|
||||
assert(is_split_mealy_specialization(mm, minmachine));
|
||||
return minmachine;
|
||||
}
|
||||
}
|
||||
|
||||
namespace spot
|
||||
{
|
||||
bool is_mealy_specialization(const_twa_graph_ptr left,
|
||||
bool is_split_mealy_specialization(const_twa_graph_ptr left,
|
||||
const_twa_graph_ptr right,
|
||||
bool verbose)
|
||||
{
|
||||
assert(is_split_mealy(left));
|
||||
assert(is_split_mealy(right));
|
||||
|
||||
auto& spl = get_state_players(left);
|
||||
auto& spr = get_state_players(right);
|
||||
|
||||
const unsigned initl = left->get_init_state_number();
|
||||
const unsigned initr = right->get_init_state_number();
|
||||
|
||||
if (spl[initl])
|
||||
throw std::runtime_error("left: Mealy machine must have an "
|
||||
"environment controlled initial state.");
|
||||
if (spr[initr])
|
||||
throw std::runtime_error("right: Mealy machine must have an "
|
||||
"environment controlled initial state.");
|
||||
|
||||
#ifndef NDEBUG
|
||||
// todo
|
||||
auto check_out = [](const const_twa_graph_ptr& aut,
|
||||
const auto& sp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,48 +23,109 @@
|
|||
|
||||
namespace spot
|
||||
{
|
||||
/// \brief Minimizes an (in)completely specified mealy machine
|
||||
/// todo
|
||||
/// Comment je faire au mieux pour expliquer mealy dans les doc
|
||||
|
||||
/// \brief Checks whether or not the automaton is a mealy machine
|
||||
///
|
||||
/// \param m The automaton to be verified
|
||||
/// \note A mealy machine must have the named property \"synthesis-outputs\"
|
||||
/// and have a \"true\" as acceptance condition
|
||||
SPOT_API bool
|
||||
is_mealy(const const_twa_graph_ptr& m);
|
||||
|
||||
/// \brief Checks whether or not the automaton is a separated mealy machine
|
||||
///
|
||||
/// \param m The automaton to be verified
|
||||
/// \note A separated mealy machine is a mealy machine machine with all
|
||||
/// transitions having the form (in)&(out) where in[out] is a bdd over
|
||||
/// input[output] propositions of m
|
||||
SPOT_API bool
|
||||
is_separated_mealy(const const_twa_graph_ptr& m);
|
||||
|
||||
/// \brief Checks whether or not the automaton is a split mealy machine
|
||||
///
|
||||
/// \param m The automaton to be verified
|
||||
/// \note A split mealy machine is a mealy machine machine with the named
|
||||
/// property \"state-player\". Moreover the underlying automaton
|
||||
/// must be alternating between the player and the env. Transitions
|
||||
/// leaving env[player] states can only be labeled by
|
||||
/// input[output] propositions.
|
||||
SPOT_API bool
|
||||
is_split_mealy(const const_twa_graph_ptr& m);
|
||||
|
||||
/// \brief Checks whether or not a mealy machine is input deterministic
|
||||
///
|
||||
/// \param m The automaton to be verified
|
||||
/// \note works all mealy machines, no matter whether they are split
|
||||
/// or separated or neither of neither of them.
|
||||
/// \note A machine is input deterministic if none of the states
|
||||
/// has two outgoing transitions that can agree on a assignement
|
||||
/// of the input propositions.
|
||||
SPOT_API bool
|
||||
is_input_deterministic_mealy(const const_twa_graph_ptr& m);
|
||||
|
||||
|
||||
/// \brief make each transition in a separated mealy machine a
|
||||
/// 2-step transition.
|
||||
///
|
||||
/// \param m separated mealy machine to be split
|
||||
/// \return returns the equivalent split mealy machine if not done inplace
|
||||
/// @{
|
||||
SPOT_API twa_graph_ptr
|
||||
split_separated_mealy(const const_twa_graph_ptr& m);
|
||||
|
||||
SPOT_API void
|
||||
split_separated_mealy_here(const twa_graph_ptr& m);
|
||||
/// @}
|
||||
|
||||
/// \brief the inverse of split_separated_mealy
|
||||
SPOT_API twa_graph_ptr
|
||||
unsplit_mealy(const const_twa_graph_ptr& m);
|
||||
|
||||
/// \brief reduce an (in)completely specified mealy machine
|
||||
/// Based on signature inclusion or equality. This is not guaranteed
|
||||
/// to find the minimal number of states but is usually faster.
|
||||
/// This also comes at another drawback:
|
||||
/// All applicable sequences have to be infinite. Finite
|
||||
/// traces are disregarded
|
||||
/// \Note The graph does not have to be split into env and player
|
||||
/// states as for minimize_mealy
|
||||
SPOT_API
|
||||
void minimize_mealy_fast_here(twa_graph_ptr& mm,
|
||||
bool use_inclusion = false);
|
||||
/// \param mm The mealy machine to be minimized, has to be unsplit
|
||||
/// \param output_assignment Whether or not to use output assignment
|
||||
/// \return A specialization of \c mm. Note that if mm is separated,
|
||||
/// the returned machine is separated as well.
|
||||
/// \note See todo TACAS22 Effective reductions of mealy machines
|
||||
/// @{
|
||||
SPOT_API twa_graph_ptr
|
||||
reduce_mealy(const const_twa_graph_ptr& mm,
|
||||
bool output_assignment = false);
|
||||
|
||||
/// \brief Like minimize_mealy_fast_here
|
||||
SPOT_API
|
||||
twa_graph_ptr minimize_mealy_fast(const const_twa_graph_ptr& mm,
|
||||
bool use_inclusion = false);
|
||||
SPOT_API void
|
||||
reduce_mealy_here(twa_graph_ptr& mm,
|
||||
bool output_assignment = false);
|
||||
/// @}
|
||||
|
||||
/// \brief Minimizes an (in)completely specified mealy machine
|
||||
/// The approach is basically described in \cite abel2015memin
|
||||
/// \param premin Use minimize_mealy_fast before applying the
|
||||
/// \brief Minimizes a split (in)completely specified mealy machine
|
||||
/// The approach is described in \todo TACAS
|
||||
/// \param premin Use reduce_mealy before applying the
|
||||
/// main algorithm if demanded AND
|
||||
/// the original machine has no finite trace.
|
||||
/// -1 : Do not use;
|
||||
/// 0 : Use without inclusion;
|
||||
/// 1 : Use with inclusion
|
||||
/// \param keep_split Whether or not to keep the automaton states
|
||||
/// partitioned into player and env states
|
||||
/// \pre Graph must be split into env states and player states,
|
||||
/// such that they alternate. Edges leaving env states must be labeled
|
||||
/// with input proposition and edges leaving player states must be
|
||||
/// labeled with output propositions.
|
||||
SPOT_API
|
||||
twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm,
|
||||
int premin = -1,
|
||||
bool keep_split = true);
|
||||
/// 0 : Use without output assignment;
|
||||
/// 1 : Use with output assignment
|
||||
/// \return Returns a split mealy machines which is a minimal
|
||||
/// speciliazation of the original machine
|
||||
SPOT_API twa_graph_ptr
|
||||
minimize_mealy(const const_twa_graph_ptr& mm, int premin = -1);
|
||||
|
||||
|
||||
/// \brief Test if the mealy machine \a right is a specialization of
|
||||
/// the mealy machine \a left. That is all input sequences valid for left
|
||||
/// \brief Test if the split mealy machine \a right is a specialization of
|
||||
/// the split mealy machine \a left.
|
||||
///
|
||||
/// That is all input sequences valid for left
|
||||
/// must be applicable for right and the induced sequence of output signals
|
||||
/// of right must imply the ones of left
|
||||
SPOT_API bool is_mealy_specialization(const_twa_graph_ptr left,
|
||||
SPOT_API bool
|
||||
is_split_mealy_specialization(const_twa_graph_ptr left,
|
||||
const_twa_graph_ptr right,
|
||||
bool verbose = false);
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
// Helper function/structures for split_2step
|
||||
namespace{
|
||||
using namespace spot;
|
||||
// Computes and stores the restriction
|
||||
// of each cond to the input domain and the support
|
||||
// This is useful as it avoids recomputation
|
||||
|
|
@ -47,9 +48,9 @@ namespace{
|
|||
struct small_cacher_t
|
||||
{
|
||||
//e to e_in and support
|
||||
std::unordered_map<bdd, std::pair<bdd, bdd>, spot::bdd_hash> cond_hash_;
|
||||
std::unordered_map<bdd, std::pair<bdd, bdd>, bdd_hash> cond_hash_;
|
||||
|
||||
void fill(const spot::const_twa_graph_ptr& aut, bdd output_bdd)
|
||||
void fill(const const_twa_graph_ptr& aut, bdd output_bdd)
|
||||
{
|
||||
cond_hash_.reserve(aut->num_edges()/5+1);
|
||||
// 20% is about lowest number of different edge conditions
|
||||
|
|
@ -79,28 +80,28 @@ namespace{
|
|||
// of the state.
|
||||
struct e_info_t
|
||||
{
|
||||
e_info_t(const spot::twa_graph::edge_storage_t& e,
|
||||
e_info_t(const twa_graph::edge_storage_t& e,
|
||||
const small_cacher_t& sm)
|
||||
: dst(e.dst),
|
||||
econd(e.cond),
|
||||
einsup(sm[e.cond]),
|
||||
acc(e.acc)
|
||||
{
|
||||
pre_hash = (spot::wang32_hash(dst)
|
||||
^ std::hash<spot::acc_cond::mark_t>()(acc))
|
||||
* spot::fnv<size_t>::prime;
|
||||
pre_hash = (wang32_hash(dst)
|
||||
^ std::hash<acc_cond::mark_t>()(acc))
|
||||
* fnv<size_t>::prime;
|
||||
}
|
||||
|
||||
inline size_t hash() const
|
||||
{
|
||||
return spot::wang32_hash(spot::bdd_hash()(econdout)) ^ pre_hash;
|
||||
return wang32_hash(bdd_hash()(econdout)) ^ pre_hash;
|
||||
}
|
||||
|
||||
unsigned dst;
|
||||
bdd econd;
|
||||
mutable bdd econdout;
|
||||
std::pair<bdd, bdd> einsup;
|
||||
spot::acc_cond::mark_t acc;
|
||||
acc_cond::mark_t acc;
|
||||
size_t pre_hash;
|
||||
};
|
||||
// We define an order between the edges to avoid creating multiple
|
||||
|
|
@ -129,6 +130,313 @@ namespace{
|
|||
< std::tie(rhs->dst, rhs->acc, r_id);
|
||||
}
|
||||
}less_info_ptr;
|
||||
|
||||
// Improved apply strat, that reduces the number of edges/states
|
||||
// while keeping the needed edge-properties
|
||||
// Note, this only deals with deterministic strategies
|
||||
// Note, assumes that env starts playing
|
||||
twa_graph_ptr
|
||||
apply_strategy(const twa_graph_ptr& arena,
|
||||
bool unsplit, bool keep_acc)
|
||||
{
|
||||
const auto& win = get_state_winners(arena);
|
||||
const auto& strat = get_strategy(arena);
|
||||
const auto& sp = get_state_players(arena);
|
||||
auto outs = get_synthesis_outputs(arena);
|
||||
|
||||
if (!win[arena->get_init_state_number()])
|
||||
throw std::runtime_error("Player does not win initial state, strategy "
|
||||
"is not applicable");
|
||||
|
||||
assert((sp[arena->get_init_state_number()] == false)
|
||||
&& "Env needs to have first turn!");
|
||||
|
||||
assert(std::none_of(arena->edges().begin(), arena->edges().end(),
|
||||
[&sp](const auto& e)
|
||||
{ bool same_player = sp.at(e.src) == sp.at(e.dst);
|
||||
if (same_player)
|
||||
std::cerr << e.src << " and " << e.dst << " belong to same "
|
||||
<< "player, arena not alternating!\n";
|
||||
return same_player; }));
|
||||
|
||||
auto strat_split = make_twa_graph(arena->get_dict());
|
||||
strat_split->copy_ap_of(arena);
|
||||
if (keep_acc)
|
||||
strat_split->copy_acceptance_of(arena);
|
||||
else
|
||||
strat_split->acc().set_acceptance(acc_cond::acc_code::t());
|
||||
|
||||
std::stack<unsigned> todo;
|
||||
todo.push(arena->get_init_state_number());
|
||||
|
||||
struct dca{
|
||||
unsigned dst;
|
||||
unsigned condvar;
|
||||
acc_cond::mark_t acc;
|
||||
};
|
||||
struct dca_hash
|
||||
{
|
||||
size_t operator()(const dca& d) const noexcept
|
||||
{
|
||||
return pair_hash()(std::make_pair(d.dst, d.condvar))
|
||||
^ wang32_hash(d.acc.hash());
|
||||
}
|
||||
};
|
||||
struct dca_equal
|
||||
{
|
||||
bool operator()(const dca& d1, const dca& d2) const noexcept
|
||||
{
|
||||
return std::tie(d1.dst, d1.condvar, d1.acc)
|
||||
== std::tie(d2.dst, d2.condvar, d2.acc);
|
||||
}
|
||||
};
|
||||
|
||||
//env dst + player cond + acc -> p stat
|
||||
std::unordered_map<dca,
|
||||
unsigned,
|
||||
dca_hash,
|
||||
dca_equal> p_map;
|
||||
|
||||
constexpr unsigned unseen_mark = std::numeric_limits<unsigned>::max();
|
||||
std::vector<unsigned> env_map(arena->num_states(), unseen_mark);
|
||||
strat_split->set_init_state(strat_split->new_state());
|
||||
env_map[arena->get_init_state_number()] =
|
||||
strat_split->get_init_state_number();
|
||||
|
||||
// The states in the new graph are qualified local
|
||||
// Get a local environment state
|
||||
auto get_sel = [&](unsigned s)
|
||||
{
|
||||
if (SPOT_UNLIKELY(env_map[s] == unseen_mark))
|
||||
env_map[s] = strat_split->new_state();
|
||||
return env_map[s];
|
||||
};
|
||||
|
||||
// local dst
|
||||
// Check if there exists already a local player state
|
||||
// that has the same dst, cond and (if keep_acc is true) acc
|
||||
auto get_spl = [&](unsigned dst, const bdd& cond, acc_cond::mark_t acc)
|
||||
{
|
||||
dca d{dst, (unsigned) cond.id(), acc};
|
||||
auto it = p_map.find(d);
|
||||
if (it != p_map.end())
|
||||
return it->second;
|
||||
unsigned ns = strat_split->new_state();
|
||||
p_map[d] = ns;
|
||||
strat_split->new_edge(ns, dst, cond, acc);
|
||||
return ns;
|
||||
};
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
unsigned src_env = todo.top();
|
||||
unsigned src_envl = get_sel(src_env);
|
||||
todo.pop();
|
||||
// All env edges
|
||||
for (const auto& e_env : arena->out(src_env))
|
||||
{
|
||||
// Get the corresponding strat
|
||||
const auto& e_strat = arena->edge_storage(strat[e_env.dst]);
|
||||
// Check if already explored
|
||||
if (env_map[e_strat.dst] == unseen_mark)
|
||||
todo.push(e_strat.dst);
|
||||
unsigned dst_envl = get_sel(e_strat.dst);
|
||||
// The new env edge, player is constructed automatically
|
||||
auto used_acc = keep_acc ? e_strat.acc : acc_cond::mark_t({});
|
||||
strat_split->new_edge(src_envl,
|
||||
get_spl(dst_envl, e_strat.cond, used_acc),
|
||||
e_env.cond, used_acc);
|
||||
}
|
||||
}
|
||||
// All states exists, we can try to further merge them
|
||||
// Specialized merge
|
||||
std::vector<bool> spnew(strat_split->num_states(), false);
|
||||
for (const auto& p : p_map)
|
||||
spnew[p.second] = true;
|
||||
|
||||
// Sorting edges in place
|
||||
auto comp_edge = [](const auto& e1, const auto& e2)
|
||||
{
|
||||
return std::tuple(e1.dst, e1.acc, e1.cond.id())
|
||||
< std::tuple(e2.dst, e2.acc, e2.cond.id());
|
||||
};
|
||||
// todo, replace with sort_edges_of_ in graph
|
||||
auto sort_edges_of =
|
||||
[&, &split_graph = strat_split->get_graph()](unsigned s)
|
||||
{
|
||||
static std::vector<unsigned> edge_nums;
|
||||
edge_nums.clear();
|
||||
|
||||
auto eit = strat_split->out(s);
|
||||
const auto eit_e = eit.end();
|
||||
// 0 Check if it is already sorted
|
||||
if (std::is_sorted(eit.begin(), eit_e, comp_edge))
|
||||
return false; // No change
|
||||
// 1 Get all edges numbers and sort them
|
||||
std::transform(eit.begin(), eit_e, std::back_inserter(edge_nums),
|
||||
[&](const auto& e)
|
||||
{ return strat_split->edge_number(e); });
|
||||
std::sort(edge_nums.begin(), edge_nums.end(),
|
||||
[&](unsigned ne1, unsigned ne2)
|
||||
{ return comp_edge(strat_split->edge_storage(ne1),
|
||||
strat_split->edge_storage(ne2)); });
|
||||
// 2 Correct the order
|
||||
auto& sd = split_graph.state_storage(s);
|
||||
sd.succ = edge_nums.front();
|
||||
sd.succ_tail = edge_nums.back();
|
||||
const unsigned n_edges_p = edge_nums.size()-1;
|
||||
for (unsigned i = 0; i < n_edges_p; ++i)
|
||||
split_graph.edge_storage(edge_nums[i]).next_succ = edge_nums[i+1];
|
||||
split_graph.edge_storage(edge_nums.back()).next_succ = 0; //terminal
|
||||
// All nicely chained
|
||||
return true;
|
||||
};
|
||||
auto merge_edges_of = [&](unsigned s)
|
||||
{
|
||||
// Call this after sort edges of
|
||||
// Mergeable edges are nicely chained
|
||||
bool changed = false;
|
||||
auto eit = strat_split->out_iteraser(s);
|
||||
unsigned idx_last = strat_split->edge_number(*eit);
|
||||
++eit;
|
||||
while (eit)
|
||||
{
|
||||
auto& e_last = strat_split->edge_storage(idx_last);
|
||||
if (std::tie(e_last.dst, e_last.acc)
|
||||
== std::tie(eit->dst, eit->acc))
|
||||
{
|
||||
//Same dest and acc -> or condition
|
||||
e_last.cond |= eit->cond;
|
||||
eit.erase();
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx_last = strat_split->edge_number(*eit);
|
||||
++eit;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
};
|
||||
auto merge_able = [&](unsigned s1, unsigned s2)
|
||||
{
|
||||
auto eit1 = strat_split->out(s1);
|
||||
auto eit2 = strat_split->out(s2);
|
||||
// Note: No self-loops here
|
||||
return std::equal(eit1.begin(), eit1.end(),
|
||||
eit2.begin(), eit2.end(),
|
||||
[](const auto& e1, const auto& e2)
|
||||
{
|
||||
return std::tuple(e1.dst, e1.acc, e1.cond.id())
|
||||
== std::tuple(e2.dst, e2.acc, e2.cond.id());
|
||||
});
|
||||
};
|
||||
|
||||
const unsigned n_sstrat = strat_split->num_states();
|
||||
std::vector<unsigned> remap(n_sstrat, -1u);
|
||||
bool changed_any;
|
||||
std::vector<unsigned> to_check;
|
||||
to_check.reserve(n_sstrat);
|
||||
// First iteration -> all env states are candidates
|
||||
for (unsigned i = 0; i < n_sstrat; ++i)
|
||||
if (!spnew[i])
|
||||
to_check.push_back(i);
|
||||
|
||||
while (true)
|
||||
{
|
||||
changed_any = false;
|
||||
std::for_each(to_check.begin(), to_check.end(),
|
||||
[&](unsigned s){ sort_edges_of(s); merge_edges_of(s); });
|
||||
// Check if we can merge env states
|
||||
for (unsigned s1 : to_check)
|
||||
for (unsigned s2 = 0; s2 < n_sstrat; ++s2)
|
||||
{
|
||||
if (spnew[s2] || (s1 == s2))
|
||||
continue; // Player state or s1 == s2
|
||||
if ((remap[s2] < s2))
|
||||
continue; //s2 is already remapped
|
||||
if (merge_able(s1, s2))
|
||||
{
|
||||
changed_any = true;
|
||||
if (s1 < s2)
|
||||
remap[s2] = s1;
|
||||
else
|
||||
remap[s1] = s2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed_any)
|
||||
break;
|
||||
// Redirect changed targets and set possibly mergeable states
|
||||
to_check.clear();
|
||||
for (auto& e : strat_split->edges())
|
||||
if (remap[e.dst] != -1u)
|
||||
{
|
||||
e.dst = remap[e.dst];
|
||||
to_check.push_back(e.src);
|
||||
assert(spnew[e.src]);
|
||||
}
|
||||
|
||||
// Now check the player states
|
||||
// We can skip sorting -> only one edge
|
||||
// todo change when multistrat
|
||||
changed_any = false;
|
||||
for (unsigned s1 : to_check)
|
||||
for (unsigned s2 = 0; s2 < n_sstrat; ++s2)
|
||||
{
|
||||
if (!spnew[s2] || (s1 == s2))
|
||||
continue; // Env state or s1 == s2
|
||||
if ((remap[s2] < s2))
|
||||
continue; //s2 is already remapped
|
||||
if (merge_able(s1, s2))
|
||||
{
|
||||
changed_any = true;
|
||||
if (s1 < s2)
|
||||
remap[s2] = s1;
|
||||
else
|
||||
remap[s1] = s2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed_any)
|
||||
break;
|
||||
// Redirect changed targets and set possibly mergeable states
|
||||
to_check.clear();
|
||||
for (auto& e : strat_split->edges())
|
||||
if (remap[e.dst] != -1u)
|
||||
{
|
||||
e.dst = remap[e.dst];
|
||||
to_check.push_back(e.src);
|
||||
assert(!spnew[e.src]);
|
||||
}
|
||||
}
|
||||
|
||||
// Defrag and alternate
|
||||
if (remap[strat_split->get_init_state_number()] != -1u)
|
||||
strat_split->set_init_state(remap[strat_split->get_init_state_number()]);
|
||||
unsigned st = 0;
|
||||
for (auto& s: remap)
|
||||
if (s == -1U)
|
||||
s = st++;
|
||||
else
|
||||
s = -1U;
|
||||
|
||||
strat_split->defrag_states(remap, st);
|
||||
|
||||
alternate_players(strat_split, false, false);
|
||||
// What we do now depends on whether we unsplit or not
|
||||
if (unsplit)
|
||||
{
|
||||
auto final = unsplit_2step(strat_split);
|
||||
set_synthesis_outputs(final, outs);
|
||||
return final;
|
||||
}
|
||||
|
||||
set_synthesis_outputs(strat_split, outs);
|
||||
return strat_split;
|
||||
}
|
||||
}
|
||||
|
||||
namespace spot
|
||||
|
|
@ -142,7 +450,7 @@ namespace spot
|
|||
split->copy_acceptance_of(aut);
|
||||
split->new_states(aut->num_states());
|
||||
split->set_init_state(aut->get_init_state_number());
|
||||
split->set_named_prop<bdd>("synthesis-output", new bdd(output_bdd));
|
||||
split->set_named_prop<bdd>("synthesis-outputs", new bdd(output_bdd));
|
||||
|
||||
auto [is_unsat, unsat_mark] = aut->acc().unsat_mark();
|
||||
if (!is_unsat && complete_env)
|
||||
|
|
@ -318,89 +626,13 @@ namespace spot
|
|||
if (sink_env != -1u)
|
||||
owner->at(sink_env) = false;
|
||||
split->set_named_prop("state-player", owner);
|
||||
split->set_named_prop("synthesis-outputs",
|
||||
new bdd(output_bdd));
|
||||
|
||||
// Done
|
||||
return split;
|
||||
}
|
||||
|
||||
void
|
||||
split_2step_fast_here(const twa_graph_ptr& aut, const bdd& output_bdd)
|
||||
{
|
||||
struct dst_cond_color_t
|
||||
{
|
||||
std::pair<unsigned, int> dst_cond;
|
||||
acc_cond::mark_t color;
|
||||
};
|
||||
|
||||
auto hasher = [](const dst_cond_color_t& dcc) noexcept
|
||||
{
|
||||
return dcc.color.hash() ^ pair_hash()(dcc.dst_cond);
|
||||
};
|
||||
auto equal = [](const dst_cond_color_t& dcc1,
|
||||
const dst_cond_color_t& dcc2) noexcept
|
||||
{
|
||||
return (dcc1.dst_cond == dcc2.dst_cond)
|
||||
&& (dcc1.color == dcc2.color);
|
||||
};
|
||||
|
||||
std::unordered_map<dst_cond_color_t, unsigned,
|
||||
decltype(hasher),
|
||||
decltype(equal)> player_map(aut->num_edges(),
|
||||
hasher, equal);
|
||||
|
||||
auto get_ps = [&](unsigned dst, const bdd& ocond,
|
||||
acc_cond::mark_t color)
|
||||
{
|
||||
dst_cond_color_t key{std::make_pair(dst, ocond.id()),
|
||||
color};
|
||||
auto [it, inserted] =
|
||||
player_map.try_emplace(key, aut->num_states());
|
||||
if (!inserted)
|
||||
return it->second;
|
||||
unsigned ns = aut->new_state();
|
||||
assert(ns == it->second);
|
||||
aut->new_edge(ns, dst, ocond, color);
|
||||
return ns;
|
||||
};
|
||||
|
||||
unsigned ne = aut->edge_vector().size();
|
||||
for (unsigned eidx = 1; eidx < ne; ++eidx)
|
||||
{
|
||||
const auto& e = aut->edge_storage(eidx);
|
||||
if (e.next_succ == eidx) // dead edge
|
||||
continue;
|
||||
bdd incond = bdd_exist(e.cond, output_bdd);
|
||||
bdd outcond = bdd_existcomp(e.cond, output_bdd);
|
||||
assert(((incond&outcond) == e.cond) && "Precondition violated");
|
||||
// Create new state and trans
|
||||
// this may invalidate "e".
|
||||
unsigned new_dst = get_ps(e.dst, outcond, e.acc);
|
||||
// redirect
|
||||
auto& e2 = aut->edge_storage(eidx);
|
||||
e2.dst = new_dst;
|
||||
e2.cond = incond;
|
||||
}
|
||||
|
||||
auto* sp_ptr =
|
||||
aut->get_or_set_named_prop<std::vector<bool>>("state-player");
|
||||
|
||||
sp_ptr->resize(aut->num_states());
|
||||
std::fill(sp_ptr->begin(), sp_ptr->end(), false);
|
||||
for (auto& eit : player_map)
|
||||
(*sp_ptr)[eit.second] = true;
|
||||
//Done
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
split_2step_fast(const const_twa_graph_ptr& aut, const bdd& output_bdd)
|
||||
{
|
||||
auto aut2 = make_twa_graph(aut, twa::prop_set::all());
|
||||
aut2->copy_acceptance_of(aut);
|
||||
aut2->set_named_prop<bdd>("synthesis-output", new bdd(output_bdd));
|
||||
split_2step_fast_here(aut2, output_bdd);
|
||||
return aut2;
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
unsplit_2step(const const_twa_graph_ptr& aut)
|
||||
{
|
||||
|
|
@ -453,321 +685,6 @@ namespace spot
|
|||
return out;
|
||||
}
|
||||
|
||||
// Improved apply strat, that reduces the number of edges/states
|
||||
// while keeping the needed edge-properties
|
||||
// Note, this only deals with deterministic strategies
|
||||
// Note, assumes that env starts playing
|
||||
spot::twa_graph_ptr
|
||||
apply_strategy(const spot::twa_graph_ptr& arena,
|
||||
bool unsplit, bool keep_acc)
|
||||
{
|
||||
const auto& win = get_state_winners(arena);
|
||||
const auto& strat = get_strategy(arena);
|
||||
const auto& sp = get_state_players(arena);
|
||||
auto outs = get_synthesis_outputs(arena);
|
||||
|
||||
if (!win[arena->get_init_state_number()])
|
||||
throw std::runtime_error("Player does not win initial state, strategy "
|
||||
"is not applicable");
|
||||
|
||||
assert((sp[arena->get_init_state_number()] == false)
|
||||
&& "Env needs to have first turn!");
|
||||
|
||||
assert(std::none_of(arena->edges().begin(), arena->edges().end(),
|
||||
[&sp](const auto& e)
|
||||
{ bool same_player = sp.at(e.src) == sp.at(e.dst);
|
||||
if (same_player)
|
||||
std::cerr << e.src << " and " << e.dst << " belong to same "
|
||||
<< "player, arena not alternating!\n";
|
||||
return same_player; }));
|
||||
|
||||
auto strat_split = spot::make_twa_graph(arena->get_dict());
|
||||
strat_split->copy_ap_of(arena);
|
||||
if (keep_acc)
|
||||
strat_split->copy_acceptance_of(arena);
|
||||
|
||||
std::stack<unsigned> todo;
|
||||
todo.push(arena->get_init_state_number());
|
||||
|
||||
struct dca{
|
||||
unsigned dst;
|
||||
unsigned condvar;
|
||||
acc_cond::mark_t acc;
|
||||
};
|
||||
struct dca_hash
|
||||
{
|
||||
size_t operator()(const dca& d) const noexcept
|
||||
{
|
||||
return pair_hash()(std::make_pair(d.dst, d.condvar))
|
||||
^ wang32_hash(d.acc.hash());
|
||||
}
|
||||
};
|
||||
struct dca_equal
|
||||
{
|
||||
bool operator()(const dca& d1, const dca& d2) const noexcept
|
||||
{
|
||||
return std::tie(d1.dst, d1.condvar, d1.acc)
|
||||
== std::tie(d2.dst, d2.condvar, d2.acc);
|
||||
}
|
||||
};
|
||||
|
||||
//env dst + player cond + acc -> p stat
|
||||
std::unordered_map<dca,
|
||||
unsigned,
|
||||
dca_hash,
|
||||
dca_equal> p_map;
|
||||
|
||||
constexpr unsigned unseen_mark = std::numeric_limits<unsigned>::max();
|
||||
std::vector<unsigned> env_map(arena->num_states(), unseen_mark);
|
||||
strat_split->set_init_state(strat_split->new_state());
|
||||
env_map[arena->get_init_state_number()] =
|
||||
strat_split->get_init_state_number();
|
||||
|
||||
// The states in the new graph are qualified local
|
||||
// Get a local environment state
|
||||
auto get_sel = [&](unsigned s)
|
||||
{
|
||||
if (SPOT_UNLIKELY(env_map[s] == unseen_mark))
|
||||
env_map[s] = strat_split->new_state();
|
||||
return env_map[s];
|
||||
};
|
||||
|
||||
// local dst
|
||||
// Check if there exists already a local player state
|
||||
// that has the same dst, cond and (if keep_acc is true) acc
|
||||
auto get_spl = [&](unsigned dst, const bdd& cond, acc_cond::mark_t acc)
|
||||
{
|
||||
dca d{dst, (unsigned) cond.id(), acc};
|
||||
auto it = p_map.find(d);
|
||||
if (it != p_map.end())
|
||||
return it->second;
|
||||
unsigned ns = strat_split->new_state();
|
||||
p_map[d] = ns;
|
||||
strat_split->new_edge(ns, dst, cond, acc);
|
||||
return ns;
|
||||
};
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
unsigned src_env = todo.top();
|
||||
unsigned src_envl = get_sel(src_env);
|
||||
todo.pop();
|
||||
// All env edges
|
||||
for (const auto& e_env : arena->out(src_env))
|
||||
{
|
||||
// Get the corresponding strat
|
||||
const auto& e_strat = arena->edge_storage(strat[e_env.dst]);
|
||||
// Check if already explored
|
||||
if (env_map[e_strat.dst] == unseen_mark)
|
||||
todo.push(e_strat.dst);
|
||||
unsigned dst_envl = get_sel(e_strat.dst);
|
||||
// The new env edge, player is constructed automatically
|
||||
auto used_acc = keep_acc ? e_strat.acc : acc_cond::mark_t({});
|
||||
strat_split->new_edge(src_envl,
|
||||
get_spl(dst_envl, e_strat.cond, used_acc),
|
||||
e_env.cond, used_acc);
|
||||
}
|
||||
}
|
||||
// All states exists, we can try to further merge them
|
||||
// Specialized merge
|
||||
std::vector<bool> spnew(strat_split->num_states(), false);
|
||||
for (const auto& p : p_map)
|
||||
spnew[p.second] = true;
|
||||
|
||||
// Sorting edges in place
|
||||
auto comp_edge = [](const auto& e1, const auto& e2)
|
||||
{
|
||||
return std::tuple(e1.dst, e1.acc, e1.cond.id())
|
||||
< std::tuple(e2.dst, e2.acc, e2.cond.id());
|
||||
};
|
||||
auto sort_edges_of =
|
||||
[&, &split_graph = strat_split->get_graph()](unsigned s)
|
||||
{
|
||||
static std::vector<unsigned> edge_nums;
|
||||
edge_nums.clear();
|
||||
|
||||
auto eit = strat_split->out(s);
|
||||
const auto eit_e = eit.end();
|
||||
// 0 Check if it is already sorted
|
||||
if (std::is_sorted(eit.begin(), eit_e, comp_edge))
|
||||
return false; // No change
|
||||
// 1 Get all edges numbers and sort them
|
||||
std::transform(eit.begin(), eit_e, std::back_inserter(edge_nums),
|
||||
[&](const auto& e)
|
||||
{ return strat_split->edge_number(e); });
|
||||
std::sort(edge_nums.begin(), edge_nums.end(),
|
||||
[&](unsigned ne1, unsigned ne2)
|
||||
{ return comp_edge(strat_split->edge_storage(ne1),
|
||||
strat_split->edge_storage(ne2)); });
|
||||
// 2 Correct the order
|
||||
auto& sd = split_graph.state_storage(s);
|
||||
sd.succ = edge_nums.front();
|
||||
sd.succ_tail = edge_nums.back();
|
||||
const unsigned n_edges_p = edge_nums.size()-1;
|
||||
for (unsigned i = 0; i < n_edges_p; ++i)
|
||||
split_graph.edge_storage(edge_nums[i]).next_succ = edge_nums[i+1];
|
||||
split_graph.edge_storage(edge_nums.back()).next_succ = 0; //terminal
|
||||
// All nicely chained
|
||||
return true;
|
||||
};
|
||||
auto merge_edges_of = [&](unsigned s)
|
||||
{
|
||||
// Call this after sort edges of
|
||||
// Mergeable edges are nicely chained
|
||||
bool changed = false;
|
||||
auto eit = strat_split->out_iteraser(s);
|
||||
unsigned idx_last = strat_split->edge_number(*eit);
|
||||
++eit;
|
||||
while (eit)
|
||||
{
|
||||
auto& e_last = strat_split->edge_storage(idx_last);
|
||||
if (std::tie(e_last.dst, e_last.acc)
|
||||
== std::tie(eit->dst, eit->acc))
|
||||
{
|
||||
//Same dest and acc -> or condition
|
||||
e_last.cond |= eit->cond;
|
||||
eit.erase();
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx_last = strat_split->edge_number(*eit);
|
||||
++eit;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
};
|
||||
auto merge_able = [&](unsigned s1, unsigned s2)
|
||||
{
|
||||
auto eit1 = strat_split->out(s1);
|
||||
auto eit2 = strat_split->out(s2);
|
||||
// Note: No self-loops here
|
||||
return std::equal(eit1.begin(), eit1.end(),
|
||||
eit2.begin(), eit2.end(),
|
||||
[](const auto& e1, const auto& e2)
|
||||
{
|
||||
return std::tuple(e1.dst, e1.acc, e1.cond.id())
|
||||
== std::tuple(e2.dst, e2.acc, e2.cond.id());
|
||||
});
|
||||
};
|
||||
|
||||
const unsigned n_sstrat = strat_split->num_states();
|
||||
std::vector<unsigned> remap(n_sstrat, -1u);
|
||||
bool changed_any;
|
||||
std::vector<unsigned> to_check;
|
||||
to_check.reserve(n_sstrat);
|
||||
// First iteration -> all env states are candidates
|
||||
for (unsigned i = 0; i < n_sstrat; ++i)
|
||||
if (!spnew[i])
|
||||
to_check.push_back(i);
|
||||
|
||||
while (true)
|
||||
{
|
||||
changed_any = false;
|
||||
std::for_each(to_check.begin(), to_check.end(),
|
||||
[&](unsigned s){ sort_edges_of(s); merge_edges_of(s); });
|
||||
// Check if we can merge env states
|
||||
for (unsigned s1 : to_check)
|
||||
for (unsigned s2 = 0; s2 < n_sstrat; ++s2)
|
||||
{
|
||||
if (spnew[s2] || (s1 == s2))
|
||||
continue; // Player state or s1 == s2
|
||||
if ((remap[s2] < s2))
|
||||
continue; //s2 is already remapped
|
||||
if (merge_able(s1, s2))
|
||||
{
|
||||
changed_any = true;
|
||||
if (s1 < s2)
|
||||
remap[s2] = s1;
|
||||
else
|
||||
remap[s1] = s2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed_any)
|
||||
break;
|
||||
// Redirect changed targets and set possibly mergeable states
|
||||
to_check.clear();
|
||||
for (auto& e : strat_split->edges())
|
||||
if (remap[e.dst] != -1u)
|
||||
{
|
||||
e.dst = remap[e.dst];
|
||||
to_check.push_back(e.src);
|
||||
assert(spnew[e.src]);
|
||||
}
|
||||
|
||||
// Now check the player states
|
||||
// We can skip sorting -> only one edge
|
||||
// todo change when multistrat
|
||||
changed_any = false;
|
||||
for (unsigned s1 : to_check)
|
||||
for (unsigned s2 = 0; s2 < n_sstrat; ++s2)
|
||||
{
|
||||
if (!spnew[s2] || (s1 == s2))
|
||||
continue; // Env state or s1 == s2
|
||||
if ((remap[s2] < s2))
|
||||
continue; //s2 is already remapped
|
||||
if (merge_able(s1, s2))
|
||||
{
|
||||
changed_any = true;
|
||||
if (s1 < s2)
|
||||
remap[s2] = s1;
|
||||
else
|
||||
remap[s1] = s2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed_any)
|
||||
break;
|
||||
// Redirect changed targets and set possibly mergeable states
|
||||
to_check.clear();
|
||||
for (auto& e : strat_split->edges())
|
||||
if (remap[e.dst] != -1u)
|
||||
{
|
||||
e.dst = remap[e.dst];
|
||||
to_check.push_back(e.src);
|
||||
assert(!spnew[e.src]);
|
||||
}
|
||||
}
|
||||
|
||||
// Defrag and alternate
|
||||
if (remap[strat_split->get_init_state_number()] != -1u)
|
||||
strat_split->set_init_state(remap[strat_split->get_init_state_number()]);
|
||||
unsigned st = 0;
|
||||
for (auto& s: remap)
|
||||
if (s == -1U)
|
||||
s = st++;
|
||||
else
|
||||
s = -1U;
|
||||
|
||||
strat_split->defrag_states(remap, st);
|
||||
|
||||
alternate_players(strat_split, false, false);
|
||||
// What we do now depends on whether we unsplit or not
|
||||
if (unsplit)
|
||||
{
|
||||
auto final = unsplit_2step(strat_split);
|
||||
set_synthesis_outputs(final, outs);
|
||||
return final;
|
||||
}
|
||||
// Keep the splitted version
|
||||
set_state_winners(strat_split, region_t(strat_split->num_states(), true));
|
||||
|
||||
std::vector<unsigned> new_strat(strat_split->num_states(), -1u);
|
||||
|
||||
for (unsigned i = 0; i < strat_split->num_states(); ++i)
|
||||
{
|
||||
if (!sp[i])
|
||||
continue;
|
||||
new_strat[i] = strat_split->edge_number(*strat_split->out(i).begin());
|
||||
}
|
||||
set_strategy(strat_split, std::move(new_strat));
|
||||
set_synthesis_outputs(strat_split, outs);
|
||||
return strat_split;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, synthesis_info::algo s)
|
||||
{
|
||||
using algo = synthesis_info::algo;
|
||||
|
|
@ -1059,7 +976,7 @@ namespace spot
|
|||
} //end switch solver
|
||||
// Set the named properties
|
||||
assert(dpa->get_named_prop<std::vector<bool>>("state-player")
|
||||
&& "DPA has no state-players");
|
||||
&& "DPA has no \"state-player\"");
|
||||
set_synthesis_outputs(dpa, outs);
|
||||
return dpa;
|
||||
}
|
||||
|
|
@ -1087,78 +1004,15 @@ namespace spot
|
|||
return ltl_to_game(parse_formula(f), all_outs, gi);
|
||||
}
|
||||
|
||||
void
|
||||
minimize_strategy_here(twa_graph_ptr& strat, int min_lvl)
|
||||
{
|
||||
if (!strat->acc().is_t())
|
||||
throw std::runtime_error("minimize_strategy_here(): strategy is expected "
|
||||
"to accept all runs! Otherwise it does not "
|
||||
"correspond to a mealy machine and can not be "
|
||||
"optimized this way.");
|
||||
// 0 -> No minimization
|
||||
// 1 -> Regular monitor/DFA minimization
|
||||
// 2 -> Mealy minimization based on language inclusion
|
||||
// with output assignement
|
||||
// 3 -> Exact Mealy minimization using SAT
|
||||
// 4 -> Monitor min then exact minimization
|
||||
// 5 -> Mealy minimization based on language inclusion then exact
|
||||
// minimization
|
||||
bdd outs = get_synthesis_outputs(strat);
|
||||
|
||||
switch (min_lvl)
|
||||
{
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
{
|
||||
minimize_mealy_fast_here(strat, false);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
minimize_mealy_fast_here(strat, true);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
strat = minimize_mealy(strat, -1);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
strat = minimize_mealy(strat, 0);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
strat = minimize_mealy(strat, 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unknown minimization option");
|
||||
}
|
||||
set_synthesis_outputs(strat, outs);
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
minimize_strategy(const_twa_graph_ptr& strat, int min_lvl)
|
||||
{
|
||||
auto strat_dup = spot::make_twa_graph(strat, spot::twa::prop_set::all());
|
||||
set_synthesis_outputs(strat_dup,
|
||||
get_synthesis_outputs(strat));
|
||||
if (auto sp_ptr = strat->get_named_prop<region_t>("state-player"))
|
||||
set_state_players(strat_dup, *sp_ptr);
|
||||
minimize_strategy_here(strat_dup, min_lvl);
|
||||
return strat_dup;
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
create_strategy(twa_graph_ptr arena, synthesis_info& gi)
|
||||
solved_game_to_separated_mealy(twa_graph_ptr arena, synthesis_info& gi)
|
||||
{
|
||||
assert(arena->get_named_prop<region_t>("state-player")
|
||||
&& "Need prop \"state-player\"");
|
||||
if (!arena)
|
||||
throw std::runtime_error("Arena can not be null");
|
||||
|
||||
spot::stopwatch sw;
|
||||
stopwatch sw;
|
||||
|
||||
if (gi.bv)
|
||||
sw.start();
|
||||
|
|
@ -1168,32 +1022,34 @@ namespace spot
|
|||
|
||||
// If we use minimizations 0,1 or 2 -> unsplit
|
||||
const bool do_unsplit = gi.minimize_lvl < 3;
|
||||
twa_graph_ptr strat_aut = apply_strategy(arena, do_unsplit, false);
|
||||
auto m = apply_strategy(arena, do_unsplit, false);
|
||||
|
||||
strat_aut->prop_universal(true);
|
||||
m->prop_universal(true);
|
||||
|
||||
minimize_strategy_here(strat_aut, gi.minimize_lvl);
|
||||
if ((0 < gi.minimize_lvl) && (gi.minimize_lvl < 3))
|
||||
reduce_mealy_here(m, gi.minimize_lvl == 2);
|
||||
else if (gi.minimize_lvl >= 3)
|
||||
m = minimize_mealy(m, gi.minimize_lvl - 4);
|
||||
|
||||
assert(do_unsplit
|
||||
|| strat_aut->get_named_prop<std::vector<bool>>("state-player"));
|
||||
if (!do_unsplit)
|
||||
strat_aut = unsplit_2step(strat_aut);
|
||||
m = unsplit_mealy(m);
|
||||
|
||||
if (gi.bv)
|
||||
{
|
||||
gi.bv->strat2aut_time += sw.stop();
|
||||
gi.bv->nb_strat_states += strat_aut->num_states();
|
||||
gi.bv->nb_strat_edges += strat_aut->num_edges();
|
||||
gi.bv->nb_strat_states += m->num_states();
|
||||
gi.bv->nb_strat_edges += m->num_edges();
|
||||
}
|
||||
|
||||
return strat_aut;
|
||||
assert(is_separated_mealy(m));
|
||||
return m;
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
create_strategy(twa_graph_ptr arena)
|
||||
solved_game_to_separated_mealy(twa_graph_ptr arena)
|
||||
{
|
||||
synthesis_info dummy;
|
||||
return create_strategy(arena, dummy);
|
||||
return solved_game_to_separated_mealy(arena, dummy);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1241,7 +1097,7 @@ namespace spot
|
|||
};
|
||||
}
|
||||
|
||||
strategy_like_t
|
||||
mealy_like
|
||||
try_create_direct_strategy(formula f,
|
||||
const std::vector<std::string>& output_aps,
|
||||
synthesis_info &gi)
|
||||
|
|
@ -1256,8 +1112,8 @@ namespace spot
|
|||
{
|
||||
if (vs)
|
||||
*vs << "direct strategy might exist but was not found.\n";
|
||||
return strategy_like_t{
|
||||
strategy_like_t::realizability_code::UNKNOWN,
|
||||
return mealy_like{
|
||||
mealy_like::realizability_code::UNKNOWN,
|
||||
nullptr,
|
||||
bddfalse};
|
||||
};
|
||||
|
|
@ -1265,8 +1121,8 @@ namespace spot
|
|||
{
|
||||
if (vs)
|
||||
*vs << "no strategy exists.\n";
|
||||
return strategy_like_t{
|
||||
strategy_like_t::realizability_code::UNREALIZABLE,
|
||||
return mealy_like{
|
||||
mealy_like::realizability_code::UNREALIZABLE,
|
||||
nullptr,
|
||||
bddfalse};
|
||||
};
|
||||
|
|
@ -1279,8 +1135,8 @@ namespace spot
|
|||
<< "direct strat has " << strat->num_states()
|
||||
<< " states and " << strat->num_sets() << " colors\n";
|
||||
}
|
||||
return strategy_like_t{
|
||||
strategy_like_t::realizability_code::REALIZABLE_REGULAR,
|
||||
return mealy_like{
|
||||
mealy_like::realizability_code::REALIZABLE_REGULAR,
|
||||
strat,
|
||||
bddfalse};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,29 +58,13 @@ namespace spot
|
|||
split_2step(const const_twa_graph_ptr& aut,
|
||||
const bdd& output_bdd, bool complete_env);
|
||||
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief make each transition a 2-step transition.
|
||||
///
|
||||
/// This algorithm is only applicable if all transitions of the
|
||||
/// graph have the form p -- ins & outs --> q.
|
||||
/// That is they are a conjunction of a condition over the input
|
||||
/// propositions ins and a condition over the output propositions outs
|
||||
///
|
||||
/// \param aut automaton to be transformed
|
||||
/// \param output_bdd conjunction of all output AP, all APs not present
|
||||
/// are treated as inputs
|
||||
/// @{
|
||||
SPOT_API void
|
||||
split_2step_fast_here(const twa_graph_ptr& aut, const bdd& output_bdd);
|
||||
|
||||
SPOT_API twa_graph_ptr
|
||||
split_2step_fast(const const_twa_graph_ptr& aut, const bdd& output_bdd);
|
||||
/// @}
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief the reverse of split_2step
|
||||
/// \brief the inverse of split_2step
|
||||
///
|
||||
/// \pre aut is an alternating arena
|
||||
/// \post All edge conditions in the returned automaton are of the form
|
||||
/// ins&outs, with ins/outs being a condition over the input/output
|
||||
/// propositions
|
||||
/// \note This function relies on the named property "state-player"
|
||||
SPOT_API twa_graph_ptr
|
||||
unsplit_2step(const const_twa_graph_ptr& aut);
|
||||
|
|
@ -174,57 +158,23 @@ namespace spot
|
|||
/// @}
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief Takes a solved game and restricts the automaton to the
|
||||
/// winning strategy of the player
|
||||
///
|
||||
/// \param arena twa_graph with named properties "state-player",
|
||||
/// "strategy" and "state-winner"
|
||||
/// \param unsplit Whether or not to unsplit the automaton
|
||||
/// \param keep_acc Whether or not keep the acceptance condition
|
||||
/// \return the resulting twa_graph
|
||||
SPOT_API spot::twa_graph_ptr
|
||||
apply_strategy(const spot::twa_graph_ptr& arena,
|
||||
bool unsplit, bool keep_acc);
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief Minimizes a strategy. Strategies are infact
|
||||
/// Mealy machines. So we can use techniques designed for them
|
||||
///
|
||||
/// \param strat The machine to minimize
|
||||
/// \param min_lvl How to minimize the machine:
|
||||
/// 0: No minimization, 1: Minimizing it like an DFA,
|
||||
/// means the language is preserved, 2: Inclusion with
|
||||
/// output assignement, 3: Exact minimization using
|
||||
/// SAT solving, 4: SAT solving with DFA minimization as
|
||||
/// preprocessing, 5: SAT solving using inclusion based
|
||||
/// minimization with output assignment as preprocessing
|
||||
/// \note min_lvl 1 and 2 work more efficiently on UNSPLIT strategies,
|
||||
/// whereas min_lvl 3, 4 and 5 mandate a SPLIT strategy
|
||||
/// \brief creates a separated mealy machine from a solved game
|
||||
/// taking into account the options given in \a gi.
|
||||
/// This concerns in particular whether or not the machine is to be reduced
|
||||
/// and how.
|
||||
/// @{
|
||||
SPOT_API void
|
||||
minimize_strategy_here(twa_graph_ptr& strat, int min_lvl);
|
||||
|
||||
SPOT_API twa_graph_ptr
|
||||
minimize_strategy(const_twa_graph_ptr& strat, int min_lvl);
|
||||
solved_game_to_separated_mealy(twa_graph_ptr arena, synthesis_info& gi);
|
||||
SPOT_API twa_graph_ptr
|
||||
solved_game_to_separated_mealy(twa_graph_ptr arena);
|
||||
/// @}
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief creates a strategy from a solved game taking into account the
|
||||
/// options given in \a gi
|
||||
/// @{
|
||||
SPOT_API twa_graph_ptr
|
||||
create_strategy(twa_graph_ptr arena, synthesis_info& gi);
|
||||
SPOT_API twa_graph_ptr
|
||||
create_strategy(twa_graph_ptr arena);
|
||||
/// @}
|
||||
|
||||
/// \ingroup synthesis
|
||||
/// \brief A struct that represents different types of strategy like
|
||||
/// \brief A struct that represents different types of mealy like
|
||||
/// objects
|
||||
struct SPOT_API
|
||||
strategy_like_t
|
||||
mealy_like
|
||||
{
|
||||
|
||||
enum class realizability_code
|
||||
{
|
||||
UNREALIZABLE,
|
||||
|
|
@ -235,7 +185,7 @@ namespace spot
|
|||
};
|
||||
|
||||
realizability_code success;
|
||||
twa_graph_ptr strat_like;
|
||||
twa_graph_ptr mealy_like;
|
||||
bdd glob_cond;
|
||||
};
|
||||
|
||||
|
|
@ -272,7 +222,7 @@ namespace spot
|
|||
/// \param f The formula to synthesize a strategy for
|
||||
/// \param output_aps A vector with the name of all output properties.
|
||||
/// All APs not named in this vector are treated as inputs
|
||||
SPOT_API strategy_like_t
|
||||
SPOT_API mealy_like
|
||||
try_create_direct_strategy(formula f,
|
||||
const std::vector<std::string>& output_aps,
|
||||
synthesis_info& gi);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "99baf49c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
|
@ -13,7 +12,6 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "066489fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Test the Mealy printer."
|
||||
|
|
@ -22,7 +20,6 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "08d7d512",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
|
@ -32,7 +29,6 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "4e5b66f4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -53,7 +49,6 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "ce2ff962",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -140,7 +135,7 @@
|
|||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f3d8c04f720> >"
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f300caabba0> >"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
|
|
@ -155,17 +150,15 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "3bcbeef2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = spot.apply_strategy(g, True, False)"
|
||||
"x = spot.solved_game_to_separated_mealy(g)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "47d298f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -220,7 +213,7 @@
|
|||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f3d8c04f630> >"
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f300c179300> >"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
|
|
@ -235,7 +228,6 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f0d098ac",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
|
@ -245,7 +237,6 @@
|
|||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "adf92ac7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
|
@ -294,7 +285,7 @@
|
|||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f3d8c04f630> >"
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f300c179300> >"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
|
|
@ -305,96 +296,6 @@
|
|||
"source": [
|
||||
"x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "6e8cd892",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/svg+xml": [
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
|
||||
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
|
||||
"<!-- Generated by graphviz version 2.43.0 (0)\n",
|
||||
" -->\n",
|
||||
"<!-- Pages: 1 -->\n",
|
||||
"<svg width=\"247pt\" height=\"204pt\"\n",
|
||||
" viewBox=\"0.00 0.00 247.00 204.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
|
||||
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1.0 1.0) rotate(0) translate(4 200)\">\n",
|
||||
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-200 243,-200 243,4 -4,4\"/>\n",
|
||||
"<text text-anchor=\"start\" x=\"8\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\">Inf(</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"29\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#6a3d9a\">❸</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"45\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\">) | (Fin(</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"87\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#ff7f00\">❷</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"103\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\">) & (Inf(</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"149\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#ff4da0\">❶</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"165\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\">) | Fin(</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"203\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#1f78b4\">⓿</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"219\" y=\"-181.8\" font-family=\"Lato\" font-size=\"14.00\">)))</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"64\" y=\"-167.8\" font-family=\"Lato\" font-size=\"14.00\">[parity max odd 4]</text>\n",
|
||||
"<!-- I -->\n",
|
||||
"<!-- 0 -->\n",
|
||||
"<g id=\"node2\" class=\"node\">\n",
|
||||
"<title>0</title>\n",
|
||||
"<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"123\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"123\" y=\"-14.3\" font-family=\"Lato\" font-size=\"14.00\">0</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- I->0 -->\n",
|
||||
"<g id=\"edge1\" class=\"edge\">\n",
|
||||
"<title>I->0</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M64.17,-18C66.01,-18 82.75,-18 97.75,-18\"/>\n",
|
||||
"<polygon fill=\"black\" stroke=\"black\" points=\"104.9,-18 97.9,-21.15 101.4,-18 97.9,-18 97.9,-18 97.9,-18 101.4,-18 97.9,-14.85 104.9,-18 104.9,-18\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- 0->0 -->\n",
|
||||
"<g id=\"edge2\" class=\"edge\">\n",
|
||||
"<title>0->0</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M119.4,-35.78C118.79,-45.31 119.99,-54 123,-54 125.21,-54 126.44,-49.32 126.7,-43.05\"/>\n",
|
||||
"<polygon fill=\"black\" stroke=\"black\" points=\"126.6,-35.78 129.85,-42.74 126.65,-39.28 126.7,-42.78 126.7,-42.78 126.7,-42.78 126.65,-39.28 123.55,-42.83 126.6,-35.78 126.6,-35.78\"/>\n",
|
||||
"<polygon fill=\"#e9f4fb\" stroke=\"transparent\" points=\"72,-77 72,-96 115,-96 115,-77 72,-77\"/>\n",
|
||||
"<text text-anchor=\"start\" x=\"74\" y=\"-82.8\" font-family=\"Lato\" font-size=\"14.00\">!a & !c</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"119\" y=\"-82.8\" font-family=\"Lato\" font-size=\"14.00\">/</text>\n",
|
||||
"<polygon fill=\"#ffe5f1\" stroke=\"transparent\" points=\"130,-77 130,-96 174,-96 174,-77 130,-77\"/>\n",
|
||||
"<text text-anchor=\"start\" x=\"132\" y=\"-82.8\" font-family=\"Lato\" font-size=\"14.00\">!b & !d</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"115\" y=\"-61.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#6a3d9a\">❸</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- 0->0 -->\n",
|
||||
"<g id=\"edge3\" class=\"edge\">\n",
|
||||
"<title>0->0</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M117.81,-35.4C113.5,-60.54 115.23,-98 123,-98 130.08,-98 132.14,-66.97 129.21,-42.44\"/>\n",
|
||||
"<polygon fill=\"black\" stroke=\"black\" points=\"128.19,-35.4 132.31,-41.88 128.69,-38.86 129.19,-42.33 129.19,-42.33 129.19,-42.33 128.69,-38.86 126.07,-42.78 128.19,-35.4 128.19,-35.4\"/>\n",
|
||||
"<polygon fill=\"#e9f4fb\" stroke=\"transparent\" points=\"86,-121 86,-140 115,-140 115,-121 86,-121\"/>\n",
|
||||
"<text text-anchor=\"start\" x=\"88\" y=\"-126.8\" font-family=\"Lato\" font-size=\"14.00\">a | c</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"119\" y=\"-126.8\" font-family=\"Lato\" font-size=\"14.00\">/</text>\n",
|
||||
"<polygon fill=\"#ffe5f1\" stroke=\"transparent\" points=\"130,-121 130,-140 161,-140 161,-121 130,-121\"/>\n",
|
||||
"<text text-anchor=\"start\" x=\"132\" y=\"-126.8\" font-family=\"Lato\" font-size=\"14.00\">b | d</text>\n",
|
||||
"<text text-anchor=\"start\" x=\"115.5\" y=\"-105.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#6a3d9a\">❸</text>\n",
|
||||
"</g>\n",
|
||||
"</g>\n",
|
||||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<spot.twa_graph; proxy of <Swig Object of type 'std::shared_ptr< spot::twa_graph > *' at 0x7f3d76e561e0> >"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"spot.apply_strategy(g, True, True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5948f923",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
|
@ -413,7 +314,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.2"
|
||||
"version": "3.8.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
|||
|
|
@ -3339,9 +3339,10 @@ for strat_string, (ins_str, outs_str) in strats:
|
|||
for ss in [""] + [f"+sub{ii}" for ii in range(3)]:
|
||||
for ddx in ["", "+dc"]:# todo prob here
|
||||
for uud in ["", "+ud"]:
|
||||
aig = spot.strategy_to_aig(strat, m+ss+ddx+uud)
|
||||
aig = spot.mealy_machine_to_aig(strat, m+ss+ddx+uud)
|
||||
strat2_s = aig.as_automaton(True)
|
||||
if not spot.is_mealy_specialization(strat_s, strat2_s):
|
||||
if not spot.is_split_mealy_specialization(strat_s,
|
||||
strat2_s):
|
||||
print(f"Mode is {m+ss+ddx+uud}")
|
||||
print(f"""Strat is \n{strat_s.to_str("hoa")}""")
|
||||
print(f"""Aig as aut is \n{strat2_s.to_str("hoa")}""")
|
||||
|
|
@ -3361,7 +3362,7 @@ for aout in outs_str:
|
|||
outs &= buddy.bdd_ithvar(strat.register_ap(aout))
|
||||
|
||||
spot.set_synthesis_outputs(strat, outs)
|
||||
aig = spot.strategy_to_aig(strat, "isop")
|
||||
aig = spot.mealy_machine_to_aig(strat, "isop")
|
||||
|
||||
ins = [True, False, False, False, True, True, True, True, True, True]
|
||||
exp_latches = [(False, False),
|
||||
|
|
|
|||
|
|
@ -43,9 +43,10 @@ spot.set_synthesis_outputs(a, o1&o2)
|
|||
|
||||
b = spot.minimize_mealy(a)
|
||||
assert(list(spot.get_state_players(b)).count(False) == 2)
|
||||
assert(spot.is_mealy_specialization(a, b))
|
||||
assert(spot.is_split_mealy_specialization(a, b))
|
||||
|
||||
test_auts = [("""HOA: v1
|
||||
test_auts = [
|
||||
("""HOA: v1
|
||||
States: 22
|
||||
Start: 0
|
||||
AP: 6 "i0" "i1" "i2" "i3" "o0" "o1"
|
||||
|
|
@ -360,13 +361,6 @@ for (mealy_str, nenv_min) in test_auts:
|
|||
|
||||
mealy = spot.automaton(mealy_str)
|
||||
mealy.merge_edges()
|
||||
mealy_min_ks = spot.minimize_mealy(mealy, -1, True)
|
||||
mealy_min_us = spot.minimize_mealy(mealy, -1, False)
|
||||
|
||||
n_e = sum([s == 0 for s in spot.get_state_players(mealy_min_ks)])
|
||||
assert(n_e == nenv_min)
|
||||
assert(mealy_min_us.num_states() == nenv_min)
|
||||
assert(spot.is_mealy_specialization(mealy, mealy_min_ks, True))
|
||||
|
||||
outs = buddy.bddtrue
|
||||
ins = buddy.bddtrue
|
||||
|
|
@ -378,8 +372,20 @@ for (mealy_str, nenv_min) in test_auts:
|
|||
ins = ins & buddy.bdd_ithvar(mealy.register_ap(aap.ap_name()))
|
||||
else:
|
||||
assert("""Aps must start with either "i" or "o".""")
|
||||
mealy_min_us_s = spot.split_2step(mealy_min_us, outs, False)
|
||||
assert(spot.is_mealy_specialization(mealy, mealy_min_us_s, True))
|
||||
|
||||
spot.set_synthesis_outputs(mealy, outs)
|
||||
|
||||
mealy_min_ks = spot.minimize_mealy(mealy, -1)
|
||||
|
||||
n_e = sum([s == 0 for s in spot.get_state_players(mealy_min_ks)])
|
||||
assert(n_e == nenv_min)
|
||||
assert(spot.is_split_mealy_specialization(mealy, mealy_min_ks))
|
||||
|
||||
# Test un- and resplit
|
||||
tmp = spot.unsplit_2step(mealy_min_ks)
|
||||
mealy_min_rs = spot.split_2step(tmp, spot.get_synthesis_outputs(tmp), False)
|
||||
assert(spot.is_split_mealy_specialization(mealy, mealy_min_rs, True))
|
||||
assert(spot.are_equivalent(mealy_min_ks, mealy_min_rs))
|
||||
|
||||
|
||||
# Testing bisimulation (with output assignment)
|
||||
|
|
@ -498,13 +504,23 @@ State: 16
|
|||
--END--""")
|
||||
|
||||
# Build an equivalent deterministic monitor
|
||||
min_equiv = spot.minimize_mealy_fast(aut, False)
|
||||
# ;
|
||||
# ;
|
||||
#
|
||||
spot.set_synthesis_outputs(aut,
|
||||
buddy.bdd_ithvar(
|
||||
aut.register_ap("u02alarm29control02alarm29control"))\
|
||||
& buddy.bdd_ithvar(
|
||||
aut.register_ap("u02alarm29control0f1d2alarm29turn2on1b"))\
|
||||
& buddy.bdd_ithvar(
|
||||
aut.register_ap("u02alarm29control0f1d2alarm29turn2off1b")))
|
||||
min_equiv = spot.reduce_mealy(aut, False)
|
||||
assert min_equiv.num_states() == 6
|
||||
assert spot.are_equivalent(min_equiv, aut)
|
||||
|
||||
# Build an automaton that recognizes a subset of the language of the original
|
||||
# automaton
|
||||
min_sub = spot.minimize_mealy_fast(aut, True)
|
||||
min_sub = spot.reduce_mealy(aut, True)
|
||||
assert min_sub.num_states() == 5
|
||||
prod = spot.product(spot.complement(aut), min_sub)
|
||||
assert spot.generic_emptiness_check(prod)
|
||||
|
|
@ -531,6 +547,8 @@ State: 3
|
|||
--END--
|
||||
""")
|
||||
|
||||
spot.set_synthesis_outputs(aut, buddy.bdd_ithvar(aut.register_ap("b")))
|
||||
|
||||
exp = """HOA: v1
|
||||
States: 1
|
||||
Start: 0
|
||||
|
|
@ -544,7 +562,7 @@ State: 0
|
|||
--END--"""
|
||||
|
||||
# An example that shows that we should not build a tree when we use inclusion.
|
||||
res = spot.minimize_mealy_fast(aut, True)
|
||||
res = spot.reduce_mealy(aut, True)
|
||||
assert res.to_str() == exp
|
||||
|
||||
aut = spot.automaton("""
|
||||
|
|
@ -569,6 +587,8 @@ State: 3
|
|||
--END--
|
||||
""")
|
||||
|
||||
spot.set_synthesis_outputs(aut, buddy.bdd_ithvar(aut.register_ap("b")))
|
||||
|
||||
exp = """HOA: v1
|
||||
States: 2
|
||||
Start: 0
|
||||
|
|
@ -585,7 +605,7 @@ State: 1
|
|||
[0&1] 1
|
||||
--END--"""
|
||||
|
||||
res = spot.minimize_mealy_fast(aut, True)
|
||||
res = spot.reduce_mealy(aut, True)
|
||||
assert res.to_str() == exp
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue