// -*- coding: utf-8 -*-
// Copyright (C) 2017 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#include
#include
#include
#include
#include
#include
#include
#include
namespace spot
{
namespace
{
typedef std::pair pair_state_nca;
// Helper function that returns the called 'augmented subset construction'
// of the given automaton, i.e. the product of the automaton with its
// powerset construction.
//
// 'aut_power' is the automaton that will be used for the powerset
// construction and 'aut_prod' is the one that will be used for the
// product. They may be confusing in the sense that why the same automaton
// is not used for the product and the powerset construction. Actually,
// when dealing with an automaton A with Rabin acceptance, it is firstly
// converted into an automaton B with Streett-like acceptance. The powerset
// construction of B happens to be isomorphic with the powerset construction
// of A. Therefore, you would like to use A (which is smaller than B) for
// the powerset construction and B for the product.
static
twa_graph_ptr
aug_subset_cons(const const_twa_graph_ptr& aut_prod,
const const_twa_graph_ptr& aut_power,
bool named_states,
struct power_map& pmap)
{
twa_graph_ptr res = product(aut_prod, tgba_powerset(aut_power, pmap));
if (named_states)
{
const product_states* res_map = res->get_named_prop
("product-states");
auto v = new std::vector;
res->set_named_prop("state-names", v);
auto get_st_name =
[&](const pair_state_nca& x)
{
std::stringstream os;
os << x.first << ",{";
bool not_first = false;
for (auto& a : pmap.states_of(x.second))
{
if (not_first)
os << ',';
else
not_first = true;
os << a;
}
os << '}';
return os.str();
};
unsigned num_states = res->num_states();
for (unsigned i = 0; i < num_states; ++i)
v->emplace_back(get_st_name((*res_map)[i]));
}
return res;
}
class nsa_to_nca_converter final
{
protected:
struct power_map pmap_; // Sets of sts (powerset cons.).
const_twa_graph_ptr aut_; // The given automaton.
bool state_based_; // Is it state based?
std::vector pairs_; // All pairs of the acc. cond.
unsigned nb_pairs_; // Nb pair in the acc. cond.
bool named_states_; // Name states for display?
twa_graph_ptr res_; // The augmented subset const.
product_states* res_map_; // States of the aug. sub. cons.
scc_info si_; // SCC information.
unsigned nb_states_; // Number of states.
unsigned was_rabin_; // Was it Rabin before Streett?
std::vector* orig_states_; // Match old Rabin st. from new.
unsigned orig_num_st_; // Rabin original nb states.
// Keep information of states that are wanted to be seen infinitely
// often (cf Header).
void save_inf_nca_st(unsigned s, acc_cond::mark_t m,
vect_nca_info* nca_info)
{
if (was_rabin_ && m)
{
for (unsigned p = 0; p < nb_pairs_; ++p)
if (pairs_[p].fin || m & pairs_[p].inf)
{
const pair_state_nca& st = (*res_map_)[s];
auto bv = make_bitvect(orig_num_st_);
for (unsigned state : pmap_.states_of(st.second))
bv->set(state);
assert(!was_rabin_
|| ((int)(*orig_states_)[st.first] >= 0));
unsigned state = was_rabin_ ? (*orig_states_)[st.first]
: st.first;
unsigned clause_nb = was_rabin_ ? p / 2 : p;
nca_info->push_back(new nca_st_info(clause_nb, state, bv));
}
}
else if (!was_rabin_)
{
const pair_state_nca& st = (*res_map_)[s];
auto bv = make_bitvect(aut_->num_states());
for (unsigned state : pmap_.states_of(st.second))
bv->set(state);
nca_info->push_back(new nca_st_info(0, st.first, bv));
}
}
// Helper function that marks states that we want to see finitely often
// and save some information about states that we want to see infinitely
// often (cf Header).
void set_marks_using(std::vector& nca_is_inf_state,
vect_nca_info* nca_info)
{
for (unsigned s = 0; s < nb_states_; ++s)
{
unsigned src_scc = si_.scc_of(s);
if (nca_is_inf_state[s])
{
acc_cond::mark_t m = 0u;
for (auto& e : res_->out(s))
{
if (nca_info && e.acc && (si_.scc_of(e.dst) == src_scc
|| state_based_))
m |= e.acc;
e.acc = 0u;
}
if (nca_info)
save_inf_nca_st(s, m, nca_info);
}
else
{
for (auto& e : res_->out(s))
{
if (si_.scc_of(e.dst) == src_scc || state_based_)
e.acc = acc_cond::mark_t({0});
else
e.acc = 0u;
}
}
}
}
public:
nsa_to_nca_converter(const const_twa_graph_ptr ref_prod,
const const_twa_graph_ptr ref_power,
std::vector& pairs,
bool named_states = false,
bool was_rabin = false,
unsigned orig_num_st = 0)
: aut_(ref_prod),
state_based_((bool)aut_->prop_state_acc()),
pairs_(pairs),
nb_pairs_(pairs.size()),
named_states_(named_states),
res_(aug_subset_cons(ref_prod, ref_power, named_states_, pmap_)),
res_map_(res_->get_named_prop("product-states")),
si_(scc_info(res_)),
nb_states_(res_->num_states()),
was_rabin_(was_rabin),
orig_num_st_(orig_num_st)
{
if (was_rabin)
orig_states_ = ref_prod->get_named_prop>
("original-states");
}
~nsa_to_nca_converter()
{}
twa_graph_ptr run(vect_nca_info* nca_info)
{
std::vector nca_is_inf_state; // Accepting or rejecting sts.
nca_is_inf_state.resize(nb_states_, false);
// Iterate over all SCCs and check for accepting states. A state 's'
// is accepting if there is a cycle containing 's' that visits
// finitely often all acceptance sets marked as Fin or infinitely
// often acceptance sets marked by Inf.
unsigned nb_scc = si_.scc_count();
for (unsigned scc = 0; scc < nb_scc; ++scc)
for (unsigned st : si_.states_on_acc_cycle_of(scc))
nca_is_inf_state[st] = true;
set_marks_using(nca_is_inf_state, nca_info);
res_->prop_state_acc(state_based_);
res_->set_co_buchi();
res_->merge_edges();
return res_;
}
};
}
twa_graph_ptr
nsa_to_nca(const const_twa_graph_ptr& ref,
bool named_states,
vect_nca_info* nca_info)
{
twa_graph_ptr ref_tmp = ref->acc().is_parity() ? to_generalized_streett(ref)
: nullptr;
std::vector pairs;
if (!(ref_tmp ? ref_tmp : ref)->acc().is_streett_like(pairs))
throw std::runtime_error("nsa_to_nca() only works with Streett-like or "
"Parity acceptance condition");
nsa_to_nca_converter nca_converter(ref_tmp ? ref_tmp : ref,
ref_tmp ? ref_tmp : ref,
pairs,
named_states,
false);
return nca_converter.run(nca_info);
}
twa_graph_ptr
to_dca(const const_twa_graph_ptr& aut, bool named_states)
{
std::vector pairs;
if (aut->acc().is_streett_like(pairs) || aut->acc().is_parity())
return nsa_to_nca(aut, named_states);
else
throw std::runtime_error("to_dca() only works with Streett-like or Parity"
" acceptance condition");
}
}