Commit 98ebbea1 authored by Philipp Schlehuber's avatar Philipp Schlehuber
Browse files

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
parent 6ebe3d74
Pipeline #31170 passed with stages
in 167 minutes and 50 seconds
......@@ -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),
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();
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);
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()
<< " states and "
<< strat.strat_like->num_edges()
<< " edges\n"
<< "minimization took " << delta
<< " seconds\n";
<< m_like.mealy_like->num_states()
<< " states and "
<< 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,9 +507,9 @@ namespace
spot::stopwatch sw2;
if (gi->bv)
sw2.start();
saig = spot::strategies_to_aig(strategies, opt_print_aiger,
input_aps,
sub_outs_str);
saig = spot::mealy_machines_to_aig(mealy_machines, opt_print_aiger,
input_aps,
sub_outs_str);
if (gi->bv)
{
gi->bv->aig_time = sw2.stop();
......@@ -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; })
&& "ltlsynt: Can not 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);
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 = 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");
});
std::vector<std::pair<const_twa_graph_ptr, bdd>> new_vec;
new_vec.reserve(strat_vec.size());
if (m.success != mealy_like::realizability_code::REALIZABLE_REGULAR)
throw std::runtime_error("mealy_machine_to_aig(): "
"Can only handle regular mealy machine, TBD");
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,
const char *mode,
const std::vector<std::string>& ins,
const std::vector<std::vector<std::string>>& outs)
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,
const char *mode,
const std::vector<std::string>& ins,
const std::vector<std::vector<std::string>>& outs)
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,11 +437,18 @@ 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,
const std::vector<std::string>& ins,
const std::vector<std::string>& outs);
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);
///@}
/// \ingroup synthesis
......@@ -459,18 +466,21 @@ namespace spot
/// guaranteed to appear in the aiger circuit.
/// @{
SPOT_API aig_ptr