Adding option to solve parity games globally

Parity games have been solved semi-locally so far.
We deduced a strategy for the reachable part of the arena
This lead to some inconsistencies when not all state were
rachable.
Now you can chose to solve parity games truely globally.

* spot/twaalgos/game.cc, spot/twaalgos/game.hh: Here
* tests/python/games.ipynb: Test
This commit is contained in:
Philipp Schlehuber 2023-03-09 22:41:44 +01:00
parent 146942953a
commit e7e23d5ffc
3 changed files with 1258 additions and 60 deletions

View file

@ -149,12 +149,38 @@ namespace spot
{ {
public: public:
bool solve(const twa_graph_ptr& arena) bool solve(const twa_graph_ptr& arena, bool solve_globally)
{ {
// todo check if reordering states according to scc is worth it // todo check if reordering states according to scc is worth it
set_up(arena); set_up(arena);
// Start recursive zielonka in a bottom-up fashion on each scc // Start recursive zielonka in a bottom-up fashion on each scc
subgame_info_t subgame_info; subgame_info_t subgame_info;
while (true)
{
// If we solve globally,
auto maybe_useful = [&](unsigned scc_idx){
if (info_->is_useful_scc(scc_idx))
return true;
if (!solve_globally)
return false;
// Check if we have an out-edge to a winning state
// in another scc
return std::any_of(
info_->states_of(scc_idx).begin(),
info_->states_of(scc_idx).end(),
[&](unsigned s){
return std::any_of(
arena->out(s).begin(),
arena->out(s).end(),
[&](const auto& e){
assert ((subgame_[e.dst] == unseen_mark)
|| (info_->scc_of(e.dst) != scc_idx));
return (info_->scc_of(e.dst) != scc_idx)
&& w_.winner(e.dst);
});
});
};
for (c_scc_idx_ = 0; c_scc_idx_ < info_->scc_count(); ++c_scc_idx_) for (c_scc_idx_ = 0; c_scc_idx_ < info_->scc_count(); ++c_scc_idx_)
{ {
// Testing // Testing
@ -169,7 +195,7 @@ namespace spot
return true; return true;
}()); }());
// Useless SCCs are winning for player 0. // Useless SCCs are winning for player 0.
if (!info_->is_useful_scc(c_scc_idx_)) if (!maybe_useful(c_scc_idx_))
{ {
// This scc also gets its own subgame // This scc also gets its own subgame
++rd_; ++rd_;
@ -207,15 +233,49 @@ namespace spot
} }
} }
} }
// Every state needs a winner if (!solve_globally)
assert(std::all_of(w_.has_winner_.cbegin(), w_.has_winner_.cend(), break;
[](bool b)
{ return b; })); // Update the scc_info and continue
unsigned new_init
= std::distance(subgame_.begin(),
std::find(subgame_.begin(), subgame_.end(),
unseen_mark));
if (new_init == arena->num_states())
break; // All states have been solved
// Compute new sccs
scc_info::edge_filter ef
= [](const twa_graph::edge_storage_t&,
unsigned dst, void* subgame){
const auto& sg = *static_cast<std::vector<unsigned>*>(subgame);
return sg[dst] == unseen_mark ?
scc_info::edge_filter_choice::keep :
scc_info::edge_filter_choice::ignore;
};
info_ = std::make_unique<scc_info>(arena, new_init, ef, &subgame_);
}
// Every state needs a winner (solve_globally)
// Or only those reachable
assert((solve_globally
&& std::all_of(w_.has_winner_.cbegin(), w_.has_winner_.cend(),
[](bool b) { return b; }))
|| (!solve_globally
&& [&](){
for (unsigned s = 0; s < arena->num_states(); ++s)
{
if ((info_->scc_of(s) != -1u)
&& !w_.has_winner_.at(s))
return false;
}
return true;
}()));
// Only the states owned by the winner need a strategy // Only the states owned by the winner need a strategy
assert([&]() assert([&]()
{ {
for (unsigned v = 0; v < arena_->num_states(); ++v) for (unsigned v = 0; v < arena_->num_states(); ++v)
{ {
if (!solve_globally && (info_->scc_of(v) == -1u))
continue;
if (((*owner_ptr_)[v] == w_.winner(v)) if (((*owner_ptr_)[v] == w_.winner(v))
&& ((s_[v] <= 0) || (s_[v] > arena_->num_edges()))) && ((s_[v] <= 0) || (s_[v] > arena_->num_edges())))
return false; return false;
@ -817,10 +877,10 @@ namespace spot
} // anonymous } // anonymous
bool solve_parity_game(const twa_graph_ptr& arena) bool solve_parity_game(const twa_graph_ptr& arena, bool solve_globally)
{ {
parity_game pg; parity_game pg;
return pg.solve(arena); return pg.solve(arena, solve_globally);
} }
bool solve_game(const twa_graph_ptr& arena) bool solve_game(const twa_graph_ptr& arena)

View file

@ -70,13 +70,19 @@ namespace spot
/// This computes the winning strategy and winning region using /// This computes the winning strategy and winning region using
/// Zielonka's recursive algorithm. \cite zielonka.98.tcs /// Zielonka's recursive algorithm. \cite zielonka.98.tcs
/// ///
/// By default only a 'local' strategy is computed:
/// Only the part of the arena reachable from the init state is considered.
/// If you want to compute a strategy for ALL states, set
/// \a solve_globally to true
///
/// Also includes some inspiration from Oink. /// Also includes some inspiration from Oink.
/// \cite vandijk.18.tacas /// \cite vandijk.18.tacas
/// ///
/// Returns the player winning in the initial state, and sets /// Returns the player winning in the initial state, and sets
/// the state-winner and strategy named properties. /// the state-winner and strategy named properties.
SPOT_API SPOT_API
bool solve_parity_game(const twa_graph_ptr& arena); bool solve_parity_game(const twa_graph_ptr& arena,
bool solve_globally = false);
/// \ingroup games /// \ingroup games
/// \brief Solve a safety game. /// \brief Solve a safety game.

File diff suppressed because it is too large Load diff