introduce delay_branching_here

This is motivated by an example sent by Edmond Irani Liu,
that will be tested in next patch.

* spot/twaalgos/dbranch.cc, spot/twaalgos/dbranch.hh: New files.
* python/spot/impl.i, spot/twaalgos/Makefile.am: Add them.
* spot/twaalgos/translate.cc: Call delay_branching_here
unconditionally.
* spot/twa/twagraph.cc (defrag_states): Do not assume
that games are alternating.
* tests/core/genltl.test: Adjust expected numbers.
* tests/python/dbranch.py: New file.
* tests/Makefile.am: Add it.
This commit is contained in:
Alexandre Duret-Lutz 2022-09-21 15:40:12 +02:00
parent aa7992c65f
commit 3efab05cf2
10 changed files with 378 additions and 24 deletions

View file

@ -1,5 +1,5 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2008-2018, 2020-2021 Laboratoire de Recherche et
## Copyright (C) 2008-2018, 2020-2022 Laboratoire de Recherche et
## Développement de l'Epita (LRDE).
## Copyright (C) 2003-2005 Laboratoire d'Informatique de Paris 6
## (LIP6), département Systèmes Répartis Coopératifs (SRC), Université
@ -42,6 +42,7 @@ twaalgos_HEADERS = \
contains.hh \
copy.hh \
cycles.hh \
dbranch.hh \
degen.hh \
determinize.hh \
dot.hh \
@ -115,6 +116,7 @@ libtwaalgos_la_SOURCES = \
compsusp.cc \
contains.cc \
cycles.cc \
dbranch.cc \
degen.cc \
determinize.cc \
dot.cc \

163
spot/twaalgos/dbranch.cc Normal file
View file

@ -0,0 +1,163 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2022 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 "config.h"
#include <spot/twaalgos/dbranch.hh>
#include <spot/misc/bddlt.hh>
#include <spot/priv/robin_hood.hh>
#include <unordered_map>
#include <utility>
#include <stack>
namespace spot
{
namespace
{
typedef std::pair<bdd, acc_cond::mark_t> bdd_color;
struct bdd_color_hash
{
size_t
operator()(const bdd_color& bc) const noexcept
{
return bc.first.id() ^ bc.second.hash();
}
};
template<bool is_game>
bool delay_branching_aux(const twa_graph_ptr& aut, std::vector<bool>* owner)
{
unsigned ns = aut->num_states();
// number of predecessors of each state
std::vector<unsigned> pred_count(ns, 0);
unsigned init = aut->get_init_state_number();
pred_count[init] = 2; // pretend the initial state has too many
// predecessors, so it does not get fused.
// for each state, number of successors that have a single predecessors
std::vector<unsigned> succ_cand(ns, 0);
for (auto& e: aut->edges())
for (unsigned d: aut->univ_dests(e))
{
// Note that e.dst might be a destination group in
// alternating automata.
unsigned pc = ++pred_count[d];
succ_cand[e.src] += (pc == 1) - (pc == 2);
}
bool changed = false;
typedef robin_hood::unordered_map<bdd_color, unsigned,
bdd_color_hash> hashmap_t;
hashmap_t first_dest[1 + is_game];
auto& g = aut->get_graph();
// setup a DFS
std::vector<bool> seen(ns);
std::stack<unsigned> todo;
auto push_state = [&](unsigned state)
{
todo.push(state);
seen[state] = true;
};
push_state(init);
while (!todo.empty())
{
unsigned src = todo.top();
todo.pop();
if (succ_cand[src] < 2) // nothing to merge
{
for (auto& e: aut->out(src))
for (unsigned d: aut->univ_dests(e))
if (!seen[d])
push_state(d);
continue;
}
first_dest[0].clear();
if constexpr (is_game)
first_dest[1].clear();
auto it = g.out_iteraser(src);
while (it)
{
unsigned canddst = it->dst;
for (unsigned d: aut->univ_dests(canddst))
if (!seen[d])
push_state(d);
if (aut->is_univ_dest(canddst) || pred_count[canddst] != 1)
{
++it;
continue;
}
if (it->cond == bddfalse)
{
it.erase();
continue;
}
unsigned mapidx = is_game ? (*owner)[canddst] : 0;
auto [it2, inserted] =
first_dest[mapidx].emplace(bdd_color{it->cond, it->acc},
canddst);
if (inserted)
{
++it;
continue;
}
unsigned mergedst = it2->second;
// we have to merge canddst into mergedst. This is as
// simple as:
// 1) connecting their list of transitions
unsigned& mergedfirst = g.state_storage(mergedst).succ;
unsigned& mergedlast = g.state_storage(mergedst).succ_tail;
unsigned& candfirst = g.state_storage(canddst).succ;
unsigned& candlast = g.state_storage(canddst).succ_tail;
if (mergedlast)
aut->edge_storage(mergedlast).next_succ = candfirst;
else // mergedst had now successor
mergedfirst = candfirst;
mergedlast = candlast;
// 2) updating the source of the merged transitions
for (unsigned e2 = candfirst; e2 != 0;)
{
auto& edge = aut->edge_storage(e2);
edge.src = mergedst;
e2 = edge.next_succ;
}
// 3) deleting the edge to canddst.
candfirst = candlast = 0;
it.erase();
// 4) updating succ_cand
succ_cand[mergedst] += succ_cand[canddst];
succ_cand[canddst] = 0;
changed = true;
}
}
return changed;
}
}
bool delay_branching_here(const twa_graph_ptr& aut)
{
if (aut->prop_universal())
return false;
auto owner = aut->get_named_prop<std::vector<bool>>("state-player");
if (SPOT_UNLIKELY(owner))
return delay_branching_aux<true>(aut, owner);
else
return delay_branching_aux<false>(aut, nullptr);
}
}

36
spot/twaalgos/dbranch.hh Normal file
View file

@ -0,0 +1,36 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2022 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/>.
#pragma once
#include <spot/twa/twagraph.hh>
namespace spot
{
/// \ingroup twa_algorithms
/// \brief Merge states to delay
///
/// If a state (x) has two outgoing transitions (x,l,m,y) and
/// (x,l,m,z) going to states (x) and (y) that have no other
/// incoming edges, then (y) and (z) can be merged (keeping the
/// union of their outgoing destinations).
///
/// \return true iff the automaton was modified.
SPOT_API bool delay_branching_here(const twa_graph_ptr& aut);
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013-2018, 2020-2021 Laboratoire de Recherche et
// Copyright (C) 2013-2018, 2020-2022 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -29,6 +29,7 @@
#include <spot/twaalgos/product.hh>
#include <spot/twaalgos/sccinfo.hh>
#include <spot/twaalgos/hoa.hh>
#include <spot/twaalgos/dbranch.hh>
namespace spot
{
@ -401,6 +402,11 @@ namespace spot
aut = ltl_to_tgba_fm(r, simpl_->get_dict(), exprop,
true, false, false, nullptr, nullptr,
unambiguous);
if (delay_branching_here(aut))
{
aut->purge_unreachable_states();
aut->merge_edges();
}
}
aut = this->postprocessor::run(aut, r);