Automatic mass renaming. * src/bin/autfilt.cc, src/bin/common_aoutput.cc, src/bin/common_aoutput.hh, src/bin/common_conv.cc, src/bin/common_conv.hh, src/bin/common_output.hh, src/bin/dstar2tgba.cc, src/bin/ltlcross.cc, src/bin/ltldo.cc, src/bin/randaut.cc, src/dstarparse/dra2ba.cc, src/dstarparse/dstar2tgba.cc, src/dstarparse/dstarparse.yy, src/dstarparse/nra2nba.cc, src/dstarparse/nsa2tgba.cc, src/dstarparse/public.hh, src/graphtest/tgbagraph.cc, src/hoaparse/hoaparse.yy, src/hoaparse/public.hh, src/ltlvisit/contain.hh, src/ltlvisit/exclusive.cc, src/ltlvisit/exclusive.hh, src/priv/accmap.hh, src/taalgos/minimize.cc, src/tgba/fwd.hh, src/tgba/tgba.cc, src/tgba/tgbagraph.cc, src/tgba/tgbagraph.hh, src/tgba/tgbasafracomplement.cc, src/tgba/tgbasafracomplement.hh, src/tgbaalgos/are_isomorphic.cc, src/tgbaalgos/are_isomorphic.hh, src/tgbaalgos/canonicalize.cc, src/tgbaalgos/canonicalize.hh, src/tgbaalgos/cleanacc.cc, src/tgbaalgos/cleanacc.hh, src/tgbaalgos/complete.cc, src/tgbaalgos/complete.hh, src/tgbaalgos/compsusp.cc, src/tgbaalgos/compsusp.hh, src/tgbaalgos/cycles.hh, src/tgbaalgos/degen.cc, src/tgbaalgos/degen.hh, src/tgbaalgos/dotty.cc, src/tgbaalgos/dtbasat.cc, src/tgbaalgos/dtbasat.hh, src/tgbaalgos/dtgbacomp.cc, src/tgbaalgos/dtgbacomp.hh, src/tgbaalgos/dtgbasat.cc, src/tgbaalgos/dtgbasat.hh, src/tgbaalgos/dupexp.cc, src/tgbaalgos/dupexp.hh, src/tgbaalgos/emptiness.cc, src/tgbaalgos/emptiness.hh, src/tgbaalgos/hoa.cc, src/tgbaalgos/isdet.cc, src/tgbaalgos/isdet.hh, src/tgbaalgos/lbtt.cc, src/tgbaalgos/ltl2tgba_fm.cc, src/tgbaalgos/ltl2tgba_fm.hh, src/tgbaalgos/mask.cc, src/tgbaalgos/mask.hh, src/tgbaalgos/minimize.cc, src/tgbaalgos/minimize.hh, src/tgbaalgos/neverclaim.cc, src/tgbaalgos/postproc.cc, src/tgbaalgos/postproc.hh, src/tgbaalgos/powerset.cc, src/tgbaalgos/powerset.hh, src/tgbaalgos/product.cc, src/tgbaalgos/product.hh, src/tgbaalgos/randomgraph.cc, src/tgbaalgos/randomgraph.hh, src/tgbaalgos/randomize.cc, src/tgbaalgos/randomize.hh, src/tgbaalgos/relabel.cc, src/tgbaalgos/relabel.hh, src/tgbaalgos/remfin.cc, src/tgbaalgos/remfin.hh, src/tgbaalgos/remprop.cc, src/tgbaalgos/remprop.hh, src/tgbaalgos/safety.cc, src/tgbaalgos/safety.hh, src/tgbaalgos/sbacc.cc, src/tgbaalgos/sbacc.hh, src/tgbaalgos/sccfilter.cc, src/tgbaalgos/sccfilter.hh, src/tgbaalgos/sccinfo.cc, src/tgbaalgos/sccinfo.hh, src/tgbaalgos/simulation.cc, src/tgbaalgos/simulation.hh, src/tgbaalgos/stats.cc, src/tgbaalgos/stats.hh, src/tgbaalgos/stripacc.cc, src/tgbaalgos/stripacc.hh, src/tgbaalgos/stutter.cc, src/tgbaalgos/stutter.hh, src/tgbaalgos/totgba.cc, src/tgbaalgos/totgba.hh, src/tgbaalgos/translate.cc, src/tgbaalgos/translate.hh, src/tgbatest/checkpsl.cc, src/tgbatest/complementation.cc, src/tgbatest/emptchk.cc, src/tgbatest/ltl2tgba.cc, src/tgbatest/randtgba.cc, wrap/python/spot_impl.i, wrap/python/tests/automata-io.ipynb, wrap/python/tests/automata.ipynb, wrap/python/tests/piperead.ipynb, wrap/python/tests/testingaut.ipynb: Rename tgba_digraph as twa_graph.
353 lines
11 KiB
C++
353 lines
11 KiB
C++
// -*- coding: utf-8 -*-
|
|
// Copyright (C) 2013, 2014 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#include "public.hh"
|
|
#include "tgba/tgbamask.hh"
|
|
#include "tgbaalgos/scc.hh"
|
|
#include "tgbaalgos/reachiter.hh"
|
|
#include "tgbaalgos/gtec/gtec.hh"
|
|
#include "tgbaalgos/sccfilter.hh"
|
|
#include "tgbaalgos/dotty.hh"
|
|
|
|
namespace spot
|
|
{
|
|
|
|
// IMPORTANT NOTE: If you attempt to follow Krishnan et
|
|
// al. (ISAAC'94) paper while reading this code, make sure you
|
|
// understand the difference between their Rabin acceptance
|
|
// definition and the one we are using.
|
|
//
|
|
// Here, a cycle is accepting in a Rabin automaton if there exists
|
|
// an acceptance pair (Lᵢ, Uᵢ) such that some states from Lᵢ are
|
|
// visited while no states from Uᵢ are visited. This is the
|
|
// same definition used by ltl2dstar.
|
|
//
|
|
// In the Krishnan et al. paper, a cycle is accepting in a Rabin
|
|
// automaton if there exists an acceptance pair (Lᵢ, Uᵢ) such that
|
|
// some states from Lᵢ are visited and all visited states belongs to
|
|
// Uᵢ. In other words, you can switch from one definition to
|
|
// the other by complementing the Uᵢ set.
|
|
//
|
|
// This is a source of confusion; you have been warned.
|
|
|
|
|
|
// This function is defined in nra2nba.cc, and used only here.
|
|
SPOT_LOCAL
|
|
twa_graph_ptr nra_to_nba(const const_dstar_aut_ptr& nra,
|
|
const const_tgba_ptr& aut);
|
|
|
|
namespace
|
|
{
|
|
typedef std::list<const state*> state_list;
|
|
|
|
// The functions that take aut and dra as first arguments are
|
|
// either called on the main automaton, in which case dra->aut ==
|
|
// aut, or it is called on a sub-automaton in which case aut is a
|
|
// masked version of dra->aut. So we should always explore the
|
|
// automaton aut, but because the state of aut are states of
|
|
// dra->aut, we can use dra->aut to get labels, and dra->acccs to
|
|
// retrive acceptances.
|
|
|
|
static bool
|
|
filter_states(const const_tgba_ptr& aut,
|
|
const const_dstar_aut_ptr& dra,
|
|
const state_list& sl,
|
|
state_list& final,
|
|
state_list& nonfinal);
|
|
|
|
static bool
|
|
filter_scc(const const_tgba_ptr& aut,
|
|
const const_dstar_aut_ptr& dra,
|
|
state_list& final,
|
|
state_list& nonfinal)
|
|
{
|
|
// Iterate over all non-trivial SCCs.
|
|
scc_map sm(aut);
|
|
sm.build_map();
|
|
for (unsigned scc_max = sm.scc_count(), scc = 0;
|
|
scc < scc_max; ++scc)
|
|
{
|
|
if (sm.trivial(scc))
|
|
{
|
|
nonfinal.push_back(sm.one_state_of(scc));
|
|
continue;
|
|
}
|
|
|
|
// Get the list of states of that SCC
|
|
const std::list<const state*>& sl = sm.states_of(scc);
|
|
assert(!sl.empty());
|
|
if (!filter_states(aut, dra, sl, final, nonfinal))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
filter_states(const const_tgba_ptr& aut,
|
|
const const_dstar_aut_ptr& dra,
|
|
const state_list& sl,
|
|
state_list& final,
|
|
state_list& nonfinal)
|
|
{
|
|
// Check whether the SCC composed of all states in sl contains
|
|
// non-accepting cycles.
|
|
//
|
|
// A cycle is accepting (in a Rabin automaton) if there exists
|
|
// an acceptance pair (Lᵢ, Uᵢ) such that some states from Lᵢ
|
|
// are visited while no states from Uᵢ are visited.
|
|
//
|
|
// Consequently, a cycle is non-accepting if for all acceptance
|
|
// pairs (Lᵢ, Uᵢ), either no states from Lᵢ are visited or some
|
|
// states from Uᵢ are visited. (This corresponds to an
|
|
// accepting cycle with Streett acceptance.)
|
|
//
|
|
// Now we consider the SCC as one large cycle and check its
|
|
// intersection with all Lᵢs and Uᵢs. Let l=[l₁,l₂,...] and
|
|
// u=[u₁,u₂,...] be bitvectors where bit lᵢ (resp. uᵢ)
|
|
// indicates that Lᵢ (resp. Uᵢ) has been visited in the SCC.
|
|
state_list::const_iterator it = sl.begin();
|
|
int num = dra->aut->state_number(*it++);
|
|
bitvect* l = dra->accsets->at(num * 2).clone();
|
|
bitvect* u = dra->accsets->at(num * 2 + 1).clone();
|
|
for (; it != sl.end(); ++it)
|
|
{
|
|
num = dra->aut->state_number(*it);
|
|
*l |= dra->accsets->at(num * 2);
|
|
*u |= dra->accsets->at(num * 2 + 1);
|
|
}
|
|
// If we have l&!u = [0,0,...] that means that the cycle formed
|
|
// by the entire SCC is not accepting. However that does not
|
|
// necessarily imply that all cycles in the SCC are also
|
|
// non-accepting. We may have a smaller cycle that is
|
|
// accepting, but which becomes non-accepting when extended with
|
|
// more states.
|
|
*l -= *u;
|
|
delete u;
|
|
if (l->is_fully_clear())
|
|
{
|
|
delete l;
|
|
// Check whether the SCC is accepting. We do that by simply
|
|
// converting that SCC into a TGBA and running our emptiness
|
|
// check. This is not a really smart implementation and
|
|
// could be improved.
|
|
{
|
|
state_set keep(sl.begin(), sl.end());
|
|
auto masked = build_tgba_mask_keep(dra->aut, keep, sl.front());
|
|
if (!nra_to_nba(dra, masked)->is_empty())
|
|
// This SCC is not DBA-realizable.
|
|
return false;
|
|
}
|
|
for (state_list::const_iterator i = sl.begin();
|
|
i != sl.end(); ++i)
|
|
nonfinal.push_back(*i);
|
|
return true;
|
|
}
|
|
// The bits sets in *l corresponds to Lᵢs that have been seen
|
|
// without seeing the matching Uᵢ. In this SCC, any state in Lᵢ
|
|
// is therefore final. Otherwise we do not know: it is possible
|
|
// that there is a non-accepting cycle in the SCC that do not
|
|
// visit Lᵢ.
|
|
state_set unknown;
|
|
for (it = sl.begin(); it != sl.end(); ++it)
|
|
{
|
|
num = dra->aut->state_number(*it);
|
|
bitvect* l2 = dra->accsets->at(num * 2).clone();
|
|
*l2 &= *l;
|
|
if (!l2->is_fully_clear())
|
|
{
|
|
final.push_back(*it);
|
|
}
|
|
else
|
|
{
|
|
unknown.insert(*it);
|
|
}
|
|
delete l2;
|
|
}
|
|
delete l;
|
|
// Check whether it is possible to build non-accepting cycles
|
|
// using only the "unknown" states.
|
|
while (!unknown.empty())
|
|
{
|
|
//std::cerr << "unknown\n";
|
|
// Build a sub-automaton for just the unknown states,
|
|
// starting from any state in the SCC.
|
|
auto scc_mask = build_tgba_mask_keep(aut, unknown, *unknown.begin());
|
|
state_list local_final;
|
|
state_list local_nonfinal;
|
|
bool dbarealizable =
|
|
filter_scc(scc_mask, dra, local_final, local_nonfinal);
|
|
if (!dbarealizable)
|
|
return false;
|
|
for (state_list::const_iterator i = local_final.begin();
|
|
i != local_final.end(); ++i)
|
|
unknown.erase(*i);
|
|
final.splice(final.end(), local_final);
|
|
for (state_list::const_iterator i = local_nonfinal.begin();
|
|
i != local_nonfinal.end(); ++i)
|
|
unknown.erase(*i);
|
|
nonfinal.splice(nonfinal.end(), local_nonfinal);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
class dra_to_ba_worker: public tgba_reachable_iterator_depth_first
|
|
{
|
|
public:
|
|
dra_to_ba_worker(const const_dstar_aut_ptr& a,
|
|
const state_set& final,
|
|
const scc_map& sm,
|
|
const std::vector<bool>& realizable):
|
|
tgba_reachable_iterator_depth_first(a->aut),
|
|
in_(a),
|
|
out_(make_twa_graph(a->aut->get_dict())),
|
|
final_(final),
|
|
num_states_(a->aut->num_states()),
|
|
sm_(sm),
|
|
realizable_(realizable)
|
|
{
|
|
out_->copy_ap_of(a->aut);
|
|
out_->prop_state_based_acc();
|
|
acc_ = out_->set_buchi();
|
|
out_->new_states(num_states_ * (a->accpair_count + 1));
|
|
out_->set_init_state(a->aut->get_init_state_number());
|
|
|
|
}
|
|
|
|
twa_graph_ptr
|
|
result()
|
|
{
|
|
return out_;
|
|
}
|
|
|
|
void
|
|
process_link(const state* sin, int,
|
|
const state* sout, int,
|
|
const tgba_succ_iterator* si)
|
|
{
|
|
int in = in_->aut->state_number(sin);
|
|
int out = in_->aut->state_number(sout);
|
|
unsigned in_scc = sm_.scc_of_state(sin);
|
|
|
|
bdd cond = si->current_condition();
|
|
unsigned t = out_->new_transition(in, out, cond);
|
|
|
|
if (realizable_[in_scc])
|
|
{
|
|
if (final_.find(sin) != final_.end())
|
|
out_->trans_data(t).acc = acc_;
|
|
}
|
|
else if (sm_.scc_of_state(sout) == in_scc)
|
|
{
|
|
// Create one clone of the SCC per accepting pair,
|
|
// removing states from the Ui part of the (Li, Ui) pairs.
|
|
// (Or the Ei part of Löding's (Ei, Fi) pairs.)
|
|
bitvect& l = in_->accsets->at(2 * in);
|
|
bitvect& u = in_->accsets->at(2 * in + 1);
|
|
for (size_t i = 0; i < in_->accpair_count; ++i)
|
|
{
|
|
int shift = num_states_ * (i + 1);
|
|
// In the Ui set. (Löding's Ei set.)
|
|
if (!u.get(i))
|
|
{
|
|
// Transition t1 is a non-deterministic jump
|
|
// from the original automaton to the i-th clone.
|
|
//
|
|
// Transition t2 constructs the clone.
|
|
//
|
|
// Löding creates transition t1 regardless of the
|
|
// acceptance set. We restrict it to the non-Li
|
|
// states. Both his definition and this
|
|
// implementation create more transitions than needed:
|
|
// we do not need more than one transition per
|
|
// accepting cycle.
|
|
out_->new_transition(in, out + shift, cond);
|
|
|
|
// Acceptance transitions are those in the Li
|
|
// set. (Löding's Fi set.)
|
|
out_->new_acc_transition(in + shift, out + shift, cond,
|
|
l.get(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
const const_dstar_aut_ptr& in_;
|
|
twa_graph_ptr out_;
|
|
const state_set& final_;
|
|
size_t num_states_;
|
|
acc_cond::mark_t acc_;
|
|
const scc_map& sm_;
|
|
const std::vector<bool>& realizable_;
|
|
};
|
|
|
|
}
|
|
|
|
|
|
twa_graph_ptr dra_to_ba(const const_dstar_aut_ptr& dra, bool* dba)
|
|
{
|
|
assert(dra->type == Rabin);
|
|
|
|
state_list final;
|
|
state_list nonfinal;
|
|
|
|
// Iterate over all non-trivial SCCs.
|
|
scc_map sm(dra->aut);
|
|
sm.build_map();
|
|
unsigned scc_max = sm.scc_count();
|
|
|
|
bool dba_realizable = true;
|
|
std::vector<bool> realizable(scc_max);
|
|
|
|
for (unsigned scc = 0; scc < scc_max; ++scc)
|
|
{
|
|
if (sm.trivial(scc))
|
|
{
|
|
realizable[scc] = true;
|
|
continue;
|
|
}
|
|
|
|
// Get the list of states of that SCC
|
|
const std::list<const state*>& sl = sm.states_of(scc);
|
|
assert(!sl.empty());
|
|
bool scc_realizable = filter_states(dra->aut, dra, sl, final, nonfinal);
|
|
dba_realizable &= scc_realizable;
|
|
realizable[scc] = scc_realizable;
|
|
//std::cerr << "realizable[" << scc << "] = " << scc_realizable << '\n';
|
|
}
|
|
|
|
if (dba)
|
|
*dba = dba_realizable;
|
|
|
|
// for (state_list::const_iterator i = final.begin();
|
|
// i != final.end(); ++i)
|
|
// std::cerr << dra->aut->get_label(*i) << " is final\n";
|
|
// for (state_list::const_iterator i = nonfinal.begin();
|
|
// i != nonfinal.end(); ++i)
|
|
// std::cerr << dra->aut->get_label(*i) << " is non-final\n";
|
|
|
|
state_set fs(final.begin(), final.end());
|
|
dra_to_ba_worker w(dra, fs, sm, realizable);
|
|
w.run();
|
|
return scc_filter_states(w.result());
|
|
}
|
|
|
|
}
|