spot/src/tgbaalgos/powerset.cc
Alexandre Duret-Lutz 2c764fb3c7 Store membership to acceptance sets using bitsets, not BDDs.
This is a huge patch, that took over a month to complete.  The bit sets
are currently restricted to what 'unsigned can store', but it should be
easy to extend it to 'uint64_t' should we need it.

* NEWS: Update.
* src/tgba/acc.hh: New file.
* src/tgbatest/acc.cc, src/tgbatest/acc.test: Test it.
* src/tgba/tgbakvcomplement.cc, src/tgba/tgbakvcomplement.hh,
src/tgba/tgbasgba.cc, src/tgba/tgbasgba.hh: Delete.  The KV
complementation is too slow to be used in practice, and I somehow broke
it during the conversion to bitsets.  The tgba->sgba conversion was only
used for the KV complementation, and should be better redone on
tgba_digraph_ptr should it be needed again.
* src/bin/ltlcross.cc, src/dstarparse/dra2ba.cc,
src/dstarparse/nsa2tgba.cc, src/graphtest/tgbagraph.cc,
src/graphtest/tgbagraph.test, src/kripke/fairkripke.cc,
src/kripke/fairkripke.hh, src/kripke/kripke.cc, src/kripke/kripke.hh,
src/kripke/kripkeexplicit.cc, src/kripke/kripkeexplicit.hh,
src/misc/hash.hh, src/neverparse/neverclaimparse.yy, src/priv/accmap.hh,
src/ta/ta.cc, src/ta/ta.hh, src/ta/taexplicit.cc, src/ta/taexplicit.hh,
src/ta/taproduct.cc, src/ta/taproduct.hh, src/ta/tgta.cc,
src/ta/tgta.hh, src/ta/tgtaexplicit.cc, src/ta/tgtaexplicit.hh,
src/ta/tgtaproduct.cc, src/ta/tgtaproduct.hh, src/taalgos/dotty.cc,
src/taalgos/emptinessta.cc, src/taalgos/minimize.cc,
src/taalgos/tgba2ta.cc, src/tgba/Makefile.am, src/tgba/fwd.hh,
src/tgba/taatgba.cc, src/tgba/taatgba.hh, src/tgba/tgba.cc,
src/tgba/tgba.hh, src/tgba/tgbagraph.cc, src/tgba/tgbagraph.hh,
src/tgba/tgbamask.cc, src/tgba/tgbamask.hh, src/tgba/tgbaproduct.cc,
src/tgba/tgbaproduct.hh, src/tgba/tgbaproxy.cc, src/tgba/tgbaproxy.hh,
src/tgba/tgbasafracomplement.cc, src/tgba/tgbasafracomplement.hh,
src/tgbaalgos/bfssteps.cc, src/tgbaalgos/complete.cc,
src/tgbaalgos/compsusp.cc, src/tgbaalgos/degen.cc,
src/tgbaalgos/dotty.cc, src/tgbaalgos/dtbasat.cc,
src/tgbaalgos/dtgbacomp.cc, src/tgbaalgos/dtgbasat.cc,
src/tgbaalgos/dupexp.cc, src/tgbaalgos/emptiness.cc,
src/tgbaalgos/emptiness.hh, src/tgbaalgos/gtec/ce.cc,
src/tgbaalgos/gtec/gtec.cc, src/tgbaalgos/gtec/gtec.hh,
src/tgbaalgos/gtec/sccstack.cc, src/tgbaalgos/gtec/sccstack.hh,
src/tgbaalgos/gv04.cc, src/tgbaalgos/hoaf.cc,
src/tgbaalgos/isweakscc.cc, src/tgbaalgos/lbtt.cc,
src/tgbaalgos/ltl2tgba_fm.cc, src/tgbaalgos/magic.cc,
src/tgbaalgos/ndfs_result.hxx, src/tgbaalgos/neverclaim.cc,
src/tgbaalgos/postproc.cc, src/tgbaalgos/powerset.cc,
src/tgbaalgos/randomgraph.cc, src/tgbaalgos/randomgraph.hh,
src/tgbaalgos/reducerun.cc, src/tgbaalgos/replayrun.cc,
src/tgbaalgos/safety.cc, src/tgbaalgos/save.cc, src/tgbaalgos/scc.cc,
src/tgbaalgos/scc.hh, src/tgbaalgos/sccfilter.cc,
src/tgbaalgos/sccinfo.cc, src/tgbaalgos/sccinfo.hh,
src/tgbaalgos/se05.cc, src/tgbaalgos/simulation.cc,
src/tgbaalgos/simulation.hh, src/tgbaalgos/stats.cc,
src/tgbaalgos/stripacc.cc, src/tgbaalgos/tau03.cc,
src/tgbaalgos/tau03opt.cc, src/tgbaalgos/weight.cc,
src/tgbaalgos/weight.hh, src/tgbaparse/tgbaparse.yy,
src/tgbatest/Makefile.am, src/tgbatest/complementation.cc,
src/tgbatest/complementation.test, src/tgbatest/degenlskip.test,
src/tgbatest/det.test, src/tgbatest/dstar.test, src/tgbatest/emptchk.cc,
src/tgbatest/explpro2.test, src/tgbatest/explpro3.test,
src/tgbatest/explpro4.test, src/tgbatest/explprod.test,
src/tgbatest/ltl2tgba.cc, src/tgbatest/ltl2tgba.test,
src/tgbatest/maskacc.cc, src/tgbatest/maskacc.test,
src/tgbatest/neverclaimread.test, src/tgbatest/randtgba.cc,
src/tgbatest/readsave.test, src/tgbatest/sim.test,
src/tgbatest/sim2.test, src/tgbatest/spotlbtt.test,
src/tgbatest/tgbaread.test, src/tgbatest/tripprod.test,
iface/dve2/dve2.cc: Adjust or use to the new acceptance interface.
2014-10-06 10:18:57 +02:00

347 lines
9.1 KiB
C++

// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2013, 2014 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <set>
#include <deque>
#include <iterator>
#include <vector>
#include "powerset.hh"
#include "misc/hash.hh"
#include "tgbaalgos/powerset.hh"
#include "bdd.h"
#include "tgbaalgos/scc.hh"
#include "tgbaalgos/cycles.hh"
#include "tgbaalgos/gtec/gtec.hh"
#include "tgba/tgbaproduct.hh"
#include "tgba/bddprint.hh"
#include "tgbaalgos/dotty.hh"
#include "tgbaalgos/gtec/gtec.hh"
#include "tgbaalgos/sccfilter.hh"
#include "tgbaalgos/ltl2tgba_fm.hh"
#include "tgbaalgos/dtgbacomp.hh"
#include "ltlast/unop.hh"
namespace spot
{
tgba_digraph_ptr
tgba_powerset(const const_tgba_ptr& aut, power_map& pm, bool merge)
{
typedef power_map::power_state power_state;
typedef std::map<power_map::power_state, int> power_set;
typedef std::deque<power_map::power_state> todo_list;
power_set seen;
todo_list todo;
auto res = make_tgba_digraph(aut->get_dict());
res->copy_ap_of(aut);
{
power_state ps;
const state* s = pm.canonicalize(aut->get_init_state());
ps.insert(s);
todo.push_back(ps);
unsigned num = res->new_state();
seen[ps] = num;
pm.map_[num] = ps;
}
while (!todo.empty())
{
power_state src = todo.front();
todo.pop_front();
// Compute all variables occurring on outgoing arcs.
bdd all_vars = bddtrue;
for (auto s: src)
for (auto i: aut->succ(s))
all_vars &= bdd_support(i->current_condition());
bdd all_conds = bddtrue;
// Iterate over all possible conditions
while (all_conds != bddfalse)
{
bdd cond = bdd_satoneset(all_conds, all_vars, bddtrue);
all_conds -= cond;
// Construct the set of all states reachable via COND.
power_state dest;
for (auto s: src)
for (auto si: aut->succ(s))
if ((cond >> si->current_condition()) == bddtrue)
dest.insert(pm.canonicalize(si->current_state()));
if (dest.empty())
continue;
// Add that transition.
power_set::const_iterator i = seen.find(dest);
int dest_num;
if (i != seen.end())
{
dest_num = i->second;
}
else
{
dest_num = res->new_state();
seen[dest] = dest_num;
pm.map_[dest_num] = dest;
todo.push_back(dest);
}
res->new_transition(seen[src], dest_num, cond);
}
}
if (merge)
res->merge_transitions();
return res;
}
tgba_digraph_ptr
tgba_powerset(const const_tgba_ptr& aut)
{
power_map pm;
return tgba_powerset(aut, pm);
}
namespace
{
class fix_scc_acceptance: protected enumerate_cycles
{
public:
typedef dfs_stack::const_iterator cycle_iter;
typedef tgba_graph_trans_data trans;
typedef std::set<trans*> trans_set;
typedef std::vector<trans_set> set_set;
protected:
const_tgba_ptr ref_;
power_map& refmap_;
trans_set reject_; // set of rejecting transitions
set_set accept_; // set of cycles that are accepting
trans_set all_; // all non rejecting transitions
unsigned threshold_; // maximum count of enumerated cycles
unsigned cycles_left_; // count of cycles left to explore
public:
fix_scc_acceptance(const scc_map& sm, const_tgba_ptr ref,
power_map& refmap, unsigned threshold)
: enumerate_cycles(sm), ref_(ref), refmap_(refmap),
threshold_(threshold)
{
}
bool fix_scc(const int m)
{
reject_.clear();
accept_.clear();
cycles_left_ = threshold_;
run(m);
// std::cerr << "SCC #" << m << '\n';
// std::cerr << "REJECT: ";
// print_set(std::cerr, reject_) << '\n';
// std::cerr << "ALL: ";
// print_set(std::cerr, all_) << '\n';
// for (set_set::const_iterator j = accept_.begin();
// j != accept_.end(); ++j)
// {
// std::cerr << "ACCEPT: ";
// print_set(std::cerr, *j) << '\n';
// }
auto acc = aut_->acc().all_sets();
for (auto i: all_)
i->acc = acc;
return threshold_ != 0 && cycles_left_ == 0;
}
bool is_cycle_accepting(cycle_iter begin, trans_set& ts) const
{
auto a = std::static_pointer_cast<tgba_digraph>
(std::const_pointer_cast<tgba>(aut_));
// Build an automaton representing this loop.
auto loop_a = make_tgba_digraph(aut_->get_dict());
int loop_size = std::distance(begin, dfs_.end());
loop_a->new_states(loop_size);
int n;
cycle_iter i;
for (n = 1, i = begin; n <= loop_size; ++n, ++i)
{
trans* t = &a->trans_data(i->succ);
loop_a->new_transition(n - 1, n % loop_size, t->cond);
if (reject_.find(t) == reject_.end())
ts.insert(t);
}
assert(i == dfs_.end());
const state* loop_a_init = loop_a->get_init_state();
assert(loop_a->state_number(loop_a_init) == 0);
// Check if the loop is accepting in the original automaton.
bool accepting = false;
// Iterate on each original state corresponding to the
// start of the loop in the determinized automaton.
const power_map::power_state& ps =
refmap_.states_of(a->state_number(begin->ts->first));
for (auto s: ps)
{
// Check the product between LOOP_A, and ORIG_A starting
// in S.
if (!product_at(loop_a, ref_, loop_a_init, s)->is_empty())
{
accepting = true;
break;
}
}
loop_a_init->destroy();
return accepting;
}
std::ostream&
print_set(std::ostream& o, const trans_set& s) const
{
o << "{ ";
for (auto i: s)
o << i << ' ';
o << '}';
return o;
}
virtual bool
cycle_found(const state* start)
{
cycle_iter i = dfs_.begin();
while (i->ts->first != start)
++i;
trans_set ts;
bool is_acc = is_cycle_accepting(i, ts);
do
{
// std::cerr << aut_->format_state(i->ts->first) << ' ';
++i;
}
while (i != dfs_.end());
// std::cerr << " acc=" << is_acc << " (";
// bdd_print_accset(std::cerr, aut_->get_dict(), s) << ") ";
// print_set(std::cerr, ts) << '\n';
if (is_acc)
{
accept_.push_back(ts);
all_.insert(ts.begin(), ts.end());
}
else
{
for (auto t: ts)
{
reject_.insert(t);
for (auto& j: accept_)
j.erase(t);
all_.erase(t);
}
}
// Abort this algorithm if we have seen too much cycles, i.e.,
// when cycle_left_ *reaches* 0. (If cycle_left_ == 0, that
// means we had no limit.)
return (cycles_left_ == 0) || --cycles_left_;
}
};
static bool
fix_dba_acceptance(tgba_digraph_ptr det,
const_tgba_ptr ref, power_map& refmap,
unsigned threshold)
{
det->copy_acceptance_conditions_of(ref);
scc_map sm(det);
sm.build_map();
unsigned scc_count = sm.scc_count();
fix_scc_acceptance fsa(sm, ref, refmap, threshold);
for (unsigned m = 0; m < scc_count; ++m)
if (!sm.trivial(m))
if (fsa.fix_scc(m))
return true;
return false;
}
}
tgba_digraph_ptr
tba_determinize(const const_tgba_ptr& aut,
unsigned threshold_states, unsigned threshold_cycles)
{
power_map pm;
// Do not merge transitions in the deterministic automaton. If we
// add two self-loops labeled by "a" and "!a", we do not want
// these to be merged as "1" before the acceptance has been fixed.
auto det = tgba_powerset(aut, pm, false);
if ((threshold_states > 0)
&& (pm.map_.size() > pm.states_.size() * threshold_states))
return nullptr;
if (fix_dba_acceptance(det, aut, pm, threshold_cycles))
return nullptr;
det->merge_transitions();
return det;
}
tgba_digraph_ptr
tba_determinize_check(const tgba_digraph_ptr& aut,
unsigned threshold_states,
unsigned threshold_cycles,
const ltl::formula* f,
const_tgba_digraph_ptr neg_aut)
{
if (f == 0 && neg_aut == 0)
return 0;
if (aut->acc().num_sets() > 1)
return 0;
auto det = tba_determinize(aut, threshold_states, threshold_cycles);
if (!det)
return nullptr;
if (neg_aut == nullptr)
{
const ltl::formula* neg_f =
ltl::unop::instance(ltl::unop::Not, f->clone());
neg_aut = ltl_to_tgba_fm(neg_f, aut->get_dict());
neg_f->destroy();
// Remove useless SCCs.
neg_aut = scc_filter(neg_aut, true);
}
if (product(det, neg_aut)->is_empty())
// Complement the DBA.
if (product(aut, dtgba_complement(det))->is_empty())
// Finally, we are now sure that it was safe
// to determinize the automaton.
return det;
return aut;
}
}