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:
philipp 2021-11-04 00:24:17 +01:00
parent 6ebe3d7447
commit 98ebbea17e
12 changed files with 3376 additions and 3038 deletions

View file

@ -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,
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;
}
}

View file

@ -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
strategies_to_aig(const std::vector<const_twa_graph_ptr>& strat_vec,
const char* mode);
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,
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>& m_vec,
const char* mode);
SPOT_API 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<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
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);
/// @}

View file

@ -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());
mmc->copy_ap_of(mm);
mmc->copy_acceptance_of(mm);
orig_is_split = false;
}
auto mmc = make_twa_graph(mm, twa::prop_set::all());
mmc->copy_ap_of(mm);
mmc->copy_acceptance_of(mm);
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;
@ -567,25 +751,20 @@ namespace spot
std::vector<bool> done(mm->num_states(), false);
todo.emplace(init);
while (!todo.empty())
{
auto current = todo.top();
todo.pop();
done[current] = true;
for (auto& e : mm->out(current))
{
auto repr_dst = repr[e.dst];
e.dst = repr_dst;
if (!done[repr_dst])
todo.emplace(repr_dst);
auto current = todo.top();
todo.pop();
done[current] = true;
for (auto& e : mm->out(current))
{
auto repr_dst = repr[e.dst];
e.dst = repr_dst;
if (!done[repr_dst])
todo.emplace(repr_dst);
}
}
}
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,64 +2941,14 @@ 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,
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)
void cstr_split_mealy(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_env_states = minmach->num_states();
// For each minimal letter, create the (input) condition that it represents
@ -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)
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;
}
// Always keep machines split
assert(is_split_mealy_specialization(mm, mmw));
return std::const_pointer_cast<twa_graph>(mmw);
};
// 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,
const_twa_graph_ptr right,
bool verbose)
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)
{

View file

@ -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,
const_twa_graph_ptr right,
bool verbose = false);
SPOT_API bool
is_split_mealy_specialization(const_twa_graph_ptr left,
const_twa_graph_ptr right,
bool verbose = false);
}

View file

@ -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)
@ -1253,41 +1109,41 @@ namespace spot
*vs << "trying to create strategy directly for " << f << '\n';
auto ret_sol_maybe = [&vs]()
{
if (vs)
*vs << "direct strategy might exist but was not found.\n";
return strategy_like_t{
strategy_like_t::realizability_code::UNKNOWN,
nullptr,
bddfalse};
};
{
if (vs)
*vs << "direct strategy might exist but was not found.\n";
return mealy_like{
mealy_like::realizability_code::UNKNOWN,
nullptr,
bddfalse};
};
auto ret_sol_none = [&vs]()
{
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};
};
auto ret_sol_exists = [&vs](auto strat)
{
if (vs)
{
*vs << "direct strategy was found.\n"
<< "direct strat has " << strat->num_states()
<< " states and " << strat->num_sets() << " colors\n";
}
return strategy_like_t{
strategy_like_t::realizability_code::REALIZABLE_REGULAR,
strat,
bddfalse};
};
if (vs)
{
*vs << "direct strategy was found.\n"
<< "direct strat has " << strat->num_states()
<< " states and " << strat->num_sets() << " colors\n";
}
return mealy_like{
mealy_like::realizability_code::REALIZABLE_REGULAR,
strat,
bddfalse};
};
formula_2_inout_props form2props(output_aps);
auto output_aps_set = std::set<std::string>(output_aps.begin(),
output_aps.end());
output_aps.end());
formula f_g = formula::tt(), f_left, f_right;

View file

@ -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);