implement conversion to GRA and GSA

Fixes #174.

* spot/twaalgos/totgba.hh, spot/twaalgos/totgba.cc
(to_generalized_streett, to_generalized_rabin): New functions.
* spot/twa/acc.hh: Declare more methods as static.
* bin/autfilt.cc: Implement --generalized-rabin and
--generalized-streett options.
* NEWS: Mention these.
* tests/core/gragsa.test: New file.
* tests/Makefile.am: Add it.
This commit is contained in:
Alexandre Duret-Lutz 2016-08-04 22:24:30 +02:00
parent 736003057c
commit 14bee1ae7f
7 changed files with 338 additions and 11 deletions

View file

@ -1056,42 +1056,42 @@ namespace spot
bool check_fin_acceptance() const;
public:
acc_code inf(mark_t mark) const
static acc_code inf(mark_t mark)
{
return acc_code::inf(mark);
}
acc_code inf(std::initializer_list<unsigned> vals) const
static acc_code inf(std::initializer_list<unsigned> vals)
{
return inf(mark_t(vals.begin(), vals.end()));
}
acc_code inf_neg(mark_t mark) const
static acc_code inf_neg(mark_t mark)
{
return acc_code::inf_neg(mark);
}
acc_code inf_neg(std::initializer_list<unsigned> vals) const
static acc_code inf_neg(std::initializer_list<unsigned> vals)
{
return inf_neg(mark_t(vals.begin(), vals.end()));
}
acc_code fin(mark_t mark) const
static acc_code fin(mark_t mark)
{
return acc_code::fin(mark);
}
acc_code fin(std::initializer_list<unsigned> vals) const
static acc_code fin(std::initializer_list<unsigned> vals)
{
return fin(mark_t(vals.begin(), vals.end()));
}
acc_code fin_neg(mark_t mark) const
static acc_code fin_neg(mark_t mark)
{
return acc_code::fin_neg(mark);
}
acc_code fin_neg(std::initializer_list<unsigned> vals) const
static acc_code fin_neg(std::initializer_list<unsigned> vals)
{
return fin_neg(mark_t(vals.begin(), vals.end()));
}

View file

@ -329,7 +329,7 @@ namespace spot
// Handle false specifically. We want the output
// an automaton with Acceptance: t, that has a single
// state without successor.
if (cnf.size() == 2 && cnf.back().op == acc_cond::acc_op::Fin)
if (cnf.is_f())
{
assert(cnf.front().mark == 0U);
res = make_twa_graph(aut->get_dict());
@ -357,4 +357,203 @@ namespace spot
}
return res;
}
namespace
{
// If the DNF is
// Fin(1)&Inf(2)&Inf(4) | Fin(2)&Fin(3)&Inf(1) |
// Inf(1)&Inf(3) | Inf(1)&Inf(2) | Fin(4)
// this returns the following vector of pairs:
// [({1}, {2,4})
// ({2,3}, {1}),
// ({}, {1,3}),
// ({}, {2}),
// ({4}, t)]
static std::vector<std::pair<acc_cond::mark_t, acc_cond::mark_t>>
split_dnf_acc(const acc_cond::acc_code& acc)
{
std::vector<std::pair<acc_cond::mark_t, acc_cond::mark_t>> res;
if (acc.empty())
{
res.emplace_back(0U, 0U);
return res;
}
auto pos = &acc.back();
if (pos->op == acc_cond::acc_op::Or)
--pos;
auto start = &acc.front();
while (pos > start)
{
if (pos->op == acc_cond::acc_op::Fin)
{
// We have only a Fin term, without Inf. In this case
// only, the Fin() may encode a disjunction of sets.
for (auto s: pos[-1].mark.sets())
res.emplace_back(acc_cond::mark_t({s}), 0U);
pos -= pos->size + 1;
}
else
{
// We have a conjunction of Fin and Inf sets.
auto end = pos - pos->size - 1;
acc_cond::mark_t fin = 0U;
acc_cond::mark_t inf = 0U;
while (pos > end)
{
switch (pos->op)
{
case acc_cond::acc_op::And:
--pos;
break;
case acc_cond::acc_op::Fin:
fin |= pos[-1].mark;
assert(pos[-1].mark.count() == 1);
pos -= 2;
break;
case acc_cond::acc_op::Inf:
inf |= pos[-1].mark;
pos -= 2;
break;
case acc_cond::acc_op::FinNeg:
case acc_cond::acc_op::InfNeg:
case acc_cond::acc_op::Or:
SPOT_UNREACHABLE();
break;
}
}
assert(pos == end);
res.emplace_back(fin, inf);
}
}
return res;
}
static twa_graph_ptr
to_generalized_rabin_aux(const const_twa_graph_ptr& aut,
bool share_inf, bool complement)
{
auto res = cleanup_acceptance(aut);
auto oldacc = res->get_acceptance();
if (complement)
res->set_acceptance(res->acc().num_sets(), oldacc.complement());
{
std::vector<unsigned> pairs;
if (res->acc().is_generalized_rabin(pairs))
{
if (complement)
res->set_acceptance(res->acc().num_sets(), oldacc);
return res;
}
}
auto dnf = res->get_acceptance().to_dnf();
if (dnf.is_f())
{
if (complement)
res->set_acceptance(0, acc_cond::acc_code::t());
return res;
}
auto v = split_dnf_acc(dnf);
// Decide how we will rename each input set.
//
// inf_rename is only used if hoa_style=false, to
// reuse previously used Inf sets.
unsigned ns = res->num_sets();
std::vector<acc_cond::mark_t> rename(ns);
std::vector<unsigned> inf_rename(ns);
unsigned next_set = 0;
// The output acceptance conditions.
acc_cond::acc_code code =
complement ? acc_cond::acc_code::t() : acc_cond::acc_code::f();
for (auto& i: v)
{
unsigned fin_set = 0U;
if (!complement)
{
for (auto s: i.first.sets())
rename[s].set(next_set);
fin_set = next_set++;
}
acc_cond::mark_t infsets = 0U;
if (share_inf)
for (auto s: i.second.sets())
{
unsigned n = inf_rename[s];
if (n == 0)
n = inf_rename[s] = next_set++;
rename[s].set(n);
infsets.set(n);
}
else // HOA style
{
for (auto s: i.second.sets())
{
unsigned n = next_set++;
rename[s].set(n);
infsets.set(n);
}
}
// The definition of Streett wants the Fin first in clauses,
// so we do the same for generalized Streett since HOA does
// not specify anything. See
// https://github.com/adl/hoaf/issues/62
if (complement)
{
for (auto s: i.first.sets())
rename[s].set(next_set);
fin_set = next_set++;
auto pair = acc_cond::inf({fin_set});
pair |= acc_cond::acc_code::fin(infsets);
pair &= std::move(code);
code = std::move(pair);
}
else
{
auto pair = acc_cond::acc_code::inf(infsets);
pair &= acc_cond::fin({fin_set});
pair |= std::move(code);
code = std::move(pair);
}
}
// Fix the automaton
res->set_acceptance(next_set, code);
for (auto& e: res->edges())
{
acc_cond::mark_t m = 0U;
for (auto s: e.acc.sets())
m |= rename[s];
e.acc = m;
}
return res;
}
}
twa_graph_ptr
to_generalized_rabin(const const_twa_graph_ptr& aut,
bool share_inf)
{
return to_generalized_rabin_aux(aut, share_inf, false);
}
twa_graph_ptr
to_generalized_streett(const const_twa_graph_ptr& aut,
bool share_fin)
{
return to_generalized_rabin_aux(aut, share_fin, true);
}
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Laboratoire de Recherche et Développement
// Copyright (C) 2015, 2016 Laboratoire de Recherche et Développement
// de l'Epita.
//
// This file is part of Spot, a model checking library.
@ -38,4 +38,32 @@ namespace spot
/// less than the number of pairs used by IN.
SPOT_API twa_graph_ptr
streett_to_generalized_buchi_maybe(const const_twa_graph_ptr& in);
/// \brief Take an automaton with any acceptance condition and return
/// an equivalent Generalized Rabin automaton.
///
/// This works by putting the acceptance condition in disjunctive
/// normal form, and then merging all the
/// Fin(x1)&Fin(x2)&...&Fin(xn) that may occur in clauses into a
/// single Fin(X).
///
/// The acceptance-set numbers used by Inf may appear in
/// multiple clauses if \a share_inf is set.
SPOT_API twa_graph_ptr
to_generalized_rabin(const const_twa_graph_ptr& aut,
bool share_inf = false);
/// \brief Take an automaton with any acceptance condition and return
/// an equivalent Generalized Streett automaton.
///
/// This works by putting the acceptance condition in cunjunctive
/// normal form, and then merging all the
/// Inf(x1)|Inf(x2)|...|Inf(xn) that may occur in clauses into a
/// single Inf(X).
///
/// The acceptance-set numbers used by Fin may appear in
/// multiple clauses if \a share_fin is set.
SPOT_API twa_graph_ptr
to_generalized_streett(const const_twa_graph_ptr& aut,
bool share_fin = false);
}