* spot/twaalgos/zlktree.cc, spot/twaalgos/zlktree.hh: Here. * tests/python/zlktree.ipynb, tests/python/zlktree.py: Add tests and examples.
398 lines
14 KiB
C++
398 lines
14 KiB
C++
// -*- coding: utf-8 -*-
|
|
// Copyright (C) 2021 Laboratoire de Recherche et Developpement 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 <iosfwd>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <spot/misc/bitvect.hh>
|
|
#include <spot/twa/twagraph.hh>
|
|
#include <spot/twaalgos/sccinfo.hh>
|
|
|
|
namespace spot
|
|
{
|
|
/// \ingroup twa_acc_transform
|
|
/// \brief Zielonka Tree implementation
|
|
///
|
|
/// This class implements a Zielonka Tree, using
|
|
/// conventions similar to those in \cite casares.21.icalp
|
|
///
|
|
/// The differences is that this tree is built from Emerson-Lei
|
|
/// acceptance conditions, and can be "walked through" with multiple
|
|
/// colors at once.
|
|
class SPOT_API zielonka_tree
|
|
{
|
|
public:
|
|
/// \brief Build a Zielonka tree from the acceptance condition.
|
|
zielonka_tree(const acc_cond& cond);
|
|
|
|
/// \brief The number of branches in the Zielonka tree.
|
|
///
|
|
/// Branch are designated by the node number of their
|
|
/// leaves.
|
|
unsigned num_branches() const
|
|
{
|
|
return num_branches_;
|
|
}
|
|
|
|
/// \brief The number of one branch in the tree.
|
|
///
|
|
/// This returns the branch whose leave is the smallest one.
|
|
unsigned first_branch() const
|
|
{
|
|
return one_branch_;
|
|
}
|
|
|
|
/// \brief Walk through the Zielonka tree.
|
|
///
|
|
/// Given a \a branch number, and a set of \a colors, this returns
|
|
/// a pair (new branch, level), as needed in definition 3.3 of
|
|
/// \cite casares.21.icalp (or definition 3.7 in the full version).
|
|
///
|
|
/// The level correspond to the priority of a minimum parity acceptance
|
|
/// condition, with the parity odd/even as specified by is_even().
|
|
///
|
|
/// This implementation is slightly different from the original
|
|
/// definition since it allows a set of colors to be processed,
|
|
/// and not exactly one color. When multiple colors are given,
|
|
/// the minimum corresponding level is returned. When no color is
|
|
/// given, the branch is not changed and the level returned might
|
|
/// be one more than the depth of that branch (as if the tree had
|
|
/// another layer of leaves labeled by the empty sets, that we
|
|
/// do not store). Whether the depth for no color is the depth
|
|
/// of the node or one more depend on whether the absence of
|
|
/// color had the same parity has the current node.
|
|
std::pair<unsigned, unsigned>
|
|
step(unsigned branch, acc_cond::mark_t colors) const;
|
|
|
|
/// \brief Whether the tree corresponds to a min even or min odd
|
|
/// parity acceptance.
|
|
bool is_even() const
|
|
{
|
|
return is_even_;
|
|
}
|
|
|
|
/// \brief Whether the Zielonka tree has Rabin shape.
|
|
///
|
|
/// The tree has Rabin shape of all accepting (round) nodes have
|
|
/// at most one child.
|
|
bool has_rabin_shape() const
|
|
{
|
|
return has_rabin_shape_;
|
|
}
|
|
|
|
/// \brief Whether the Zielonka tree has Streett shape.
|
|
///
|
|
/// The tree has Streett shape of all rejecting (square) nodes have
|
|
/// at most one child.
|
|
bool has_streett_shape() const
|
|
{
|
|
return has_streett_shape_;
|
|
}
|
|
|
|
/// \brief Whether the Zielonka tree has parity shape.
|
|
///
|
|
/// The tree has parity shape of all nodes have at most one child.
|
|
bool has_parity_shape() const
|
|
{
|
|
return has_streett_shape_ && has_rabin_shape_;
|
|
}
|
|
|
|
/// \brief Render the tree as in GraphViz format.
|
|
void dot(std::ostream&) const;
|
|
|
|
private:
|
|
struct zielonka_node
|
|
{
|
|
unsigned parent;
|
|
unsigned next_sibling = 0;
|
|
unsigned first_child = 0;
|
|
unsigned level;
|
|
acc_cond::mark_t colors;
|
|
};
|
|
std::vector<zielonka_node> nodes_;
|
|
unsigned one_branch_ = 0;
|
|
unsigned num_branches_ = 0;
|
|
bool is_even_;
|
|
bool empty_is_even_;
|
|
bool has_rabin_shape_ = true;
|
|
bool has_streett_shape_ = true;
|
|
};
|
|
|
|
/// \ingroup twa_acc_transform
|
|
/// \brief Paritize an automaton using Zielonka tree.
|
|
///
|
|
/// This corresponds to the application of Section 3 of
|
|
/// \cite casares.21.icalp
|
|
///
|
|
/// The resulting automaton has a parity acceptance that is either
|
|
/// "min odd" or "min even", depending on the original acceptance.
|
|
/// It may uses up to n+1 colors if the input automaton has n
|
|
/// colors. Finally, it is colored, i.e., each output transition
|
|
/// has exactly one color.
|
|
SPOT_API
|
|
twa_graph_ptr zielonka_tree_transform(const const_twa_graph_ptr& aut);
|
|
|
|
|
|
/// \ingroup twa_acc_transform
|
|
/// \brief Options to alter the behavior of acd
|
|
enum class acd_options
|
|
{
|
|
/// Build the ACD, without checking its shape.
|
|
NONE = 0,
|
|
/// Check if the ACD has Rabin shape.
|
|
CHECK_RABIN = 1,
|
|
/// Check if the ACD has Streett shape.
|
|
CHECK_STREETT = 2,
|
|
/// Check if the ACD has Parity shape
|
|
CHECK_PARITY = CHECK_RABIN | CHECK_STREETT,
|
|
/// Abort the construction of the ACD if it does not have the
|
|
/// shape that is tested. When that happens, node_count() is set
|
|
/// to 0.
|
|
ABORT_WRONG_SHAPE = 4,
|
|
};
|
|
|
|
#ifndef SWIG
|
|
inline
|
|
bool operator!(acd_options me)
|
|
{
|
|
return me == acd_options::NONE;
|
|
}
|
|
|
|
inline
|
|
acd_options operator&(acd_options left, acd_options right)
|
|
{
|
|
typedef std::underlying_type_t<acd_options> ut;
|
|
return static_cast<acd_options>(static_cast<ut>(left)
|
|
& static_cast<ut>(right));
|
|
}
|
|
|
|
inline
|
|
acd_options operator|(acd_options left, acd_options right)
|
|
{
|
|
typedef std::underlying_type_t<acd_options> ut;
|
|
return static_cast<acd_options>(static_cast<ut>(left)
|
|
| static_cast<ut>(right));
|
|
}
|
|
#endif
|
|
|
|
/// \ingroup twa_acc_transform
|
|
/// \brief Alternating Cycle Decomposition implementation
|
|
///
|
|
/// This class implements an Alternating Cycle Decomposition
|
|
/// similar to what is described in \cite casares.21.icalp
|
|
///
|
|
/// The differences is that this ACD is built from Emerson-Lei
|
|
/// acceptance conditions, and can be "walked through" with multiple
|
|
/// colors at once.
|
|
class SPOT_API acd
|
|
{
|
|
public:
|
|
/// \brief Build a Alternating Cycle Decomposition an SCC decomposition
|
|
acd(const scc_info& si, acd_options opt = acd_options::NONE);
|
|
acd(const const_twa_graph_ptr& aut, acd_options opt = acd_options::NONE);
|
|
|
|
~acd();
|
|
|
|
/// \brief Step through the ACD.
|
|
///
|
|
/// Given a \a branch number, and an edge, this returns
|
|
/// a pair (new branch, level), as needed in definition 4.6 of
|
|
/// \cite casares.21.icalp (or definition 4.20 in the full version).
|
|
///
|
|
/// The level correspond to the priority of a minimum parity acceptance
|
|
/// condition, with the parity odd/even as specified by is_even().
|
|
std::pair<unsigned, unsigned>
|
|
step(unsigned branch, unsigned edge) const;
|
|
|
|
|
|
/// \brief Return the list of edges covered by node n of the ACD.
|
|
///
|
|
/// This is mostly used for interactive display.
|
|
std::vector<unsigned> edges_of_node(unsigned n) const;
|
|
|
|
/// \brief Return the number of nodes in the the ACD forest.
|
|
unsigned node_count() const
|
|
{
|
|
return nodes_.size();
|
|
}
|
|
|
|
/// Tell whether a node store accepting or rejecting cycles.
|
|
///
|
|
/// This is mostly used for interactive display.
|
|
bool node_acceptance(unsigned n) const;
|
|
|
|
/// \brief Whether the ACD corresponds to a min even or min odd
|
|
/// parity acceptance in SCC \a scc.
|
|
bool is_even(unsigned scc) const
|
|
{
|
|
if (scc >= scc_count_)
|
|
report_invalid_scc_number(scc, "is_even");
|
|
return trees_[scc].is_even;
|
|
}
|
|
|
|
/// \brief Whether the ACD globally corresponds to a min even or
|
|
/// min odd parity acceptance.
|
|
///
|
|
/// The choice between even or odd is determined by the parity
|
|
/// of the tallest tree of the ACD. In case two tree of opposite
|
|
/// parity share the tallest height, then even parity is favored.
|
|
bool is_even() const
|
|
{
|
|
return is_even_;
|
|
}
|
|
|
|
/// \brief Return the first branch for \a state
|
|
unsigned first_branch(unsigned state) const;
|
|
|
|
unsigned scc_max_level(unsigned scc) const
|
|
{
|
|
if (scc >= scc_count_)
|
|
report_invalid_scc_number(scc, "scc_max_level");
|
|
return trees_[scc].max_level;
|
|
}
|
|
|
|
/// \brief Whether the ACD has Rabin shape.
|
|
///
|
|
/// The ACD has Rabin shape if all accepting (round) nodes have no
|
|
/// children with a state in common. The acd should be built with
|
|
/// option CHECK_RABIN or CHECK_PARITY for this function to work.
|
|
bool has_rabin_shape() const;
|
|
|
|
/// \brief Whether the ACD has Streett shape.
|
|
///
|
|
/// The ACD has Streett shape if all rejecting (square) nodes have no
|
|
/// children with a state in common. The acd should be built with
|
|
/// option CHECK_STREETT or CHECK_PARITY for this function to work.
|
|
bool has_streett_shape() const;
|
|
|
|
/// \brief Whether the ACD has Streett shape.
|
|
///
|
|
/// The ACD has Streett shape if all nodes have no
|
|
/// children with a state in common. The acd should be built with
|
|
/// option CHECK_PARITY for this function to work.
|
|
bool has_parity_shape() const;
|
|
|
|
/// \brief Return the automaton on which the ACD is defined.
|
|
const const_twa_graph_ptr get_aut() const
|
|
{
|
|
return aut_;
|
|
}
|
|
|
|
/// \brief Render the ACD as in GraphViz format.
|
|
///
|
|
/// If \a id is given, it is used to give the graph an id, and,
|
|
/// all nodes will get ids as well.
|
|
void dot(std::ostream&, const char* id = nullptr) const;
|
|
|
|
private:
|
|
const scc_info* si_;
|
|
bool own_si_ = false;
|
|
acd_options opt_;
|
|
|
|
// This structure is used to represent one node in the ACD forest.
|
|
// The tree use a left-child / right-sibling representation
|
|
// (called here first_child, next_sibling). Each node
|
|
// additionally store a level (depth in the ACD, adjusted at the
|
|
// end of the construction so that all node on the same level have
|
|
// the same parity), the SCC (which is also it's tree number), and
|
|
// some bit vectors representing the edges and states of that
|
|
// node. Those bit vectors are as large as the original
|
|
// automaton, and they are shared among nodes from the different
|
|
// trees of the ACD forest (since each tree correspond to a
|
|
// different SCC, they cannot share state or edges).
|
|
struct acd_node
|
|
{
|
|
unsigned parent;
|
|
unsigned next_sibling = 0;
|
|
unsigned first_child = 0;
|
|
unsigned level;
|
|
unsigned scc;
|
|
bitvect& edges;
|
|
bitvect& states;
|
|
acd_node(bitvect& e, bitvect& s) noexcept
|
|
: edges(e), states(s)
|
|
{
|
|
}
|
|
};
|
|
// We store the nodes in a deque so that their addresses do not
|
|
// change.
|
|
std::deque<acd_node> nodes_;
|
|
// Likewise for bitvectors: this is the support for all edge vectors
|
|
// and state vectors used in acd_node.
|
|
std::deque<std::unique_ptr<bitvect>> bitvectors;
|
|
// Information about a tree of the ACD. Each tree correspond
|
|
// to an SCC.
|
|
struct scc_data
|
|
{
|
|
bool trivial; // whether the SCC is trivial we do
|
|
// not store any node for trivial
|
|
// SCCs.
|
|
unsigned root = 0; // root node of a non-trivial SCC.
|
|
bool is_even; // parity of the tree, used at the end
|
|
// of the construction to adjust
|
|
// levels.
|
|
unsigned max_level = 0; // Maximum level for this SCC.
|
|
unsigned num_nodes = 0; // Number of node in this tree. This
|
|
// is only used to share bitvectors
|
|
// between SCC: node with the same
|
|
// "rank" in each tree share the same
|
|
// bitvectors.
|
|
};
|
|
std::vector<scc_data> trees_;
|
|
unsigned scc_count_;
|
|
const_twa_graph_ptr aut_;
|
|
// Information about the overall ACD.
|
|
bool is_even_;
|
|
bool has_rabin_shape_ = true;
|
|
bool has_streett_shape_ = true;
|
|
|
|
// Build the ACD structure. Called by the constructors.
|
|
void build_();
|
|
|
|
// leftmost branch of \a node that contains \a state
|
|
unsigned leftmost_branch_(unsigned node, unsigned state) const;
|
|
|
|
#ifndef SWIG
|
|
[[noreturn]] static
|
|
void report_invalid_scc_number(unsigned num, const char* fn);
|
|
[[noreturn]] static void report_need_opt(const char* opt);
|
|
[[noreturn]] static void report_empty_acd(const char* fn);
|
|
#endif
|
|
};
|
|
|
|
/// \ingroup twa_acc_transform
|
|
/// \brief Paritize an automaton using ACD.
|
|
///
|
|
/// This corresponds to the application of Section 4 of
|
|
/// \cite casares.21.icalp
|
|
///
|
|
/// The resulting automaton has a parity acceptance that is either
|
|
/// "min odd" or "min even", depending on the original acceptance.
|
|
///
|
|
/// If \a colored is set, each output transition will have exactly
|
|
/// one color, and the output automaton will use at most n+1 colors
|
|
/// if the input has n colors. If \colored is unsed (the default),
|
|
/// output transitions will use at most one color, and output
|
|
/// automaton will use at most n colors.
|
|
SPOT_API
|
|
twa_graph_ptr acd_transform(const const_twa_graph_ptr& aut,
|
|
bool colored = false);
|
|
}
|