gtec.hh 9.25 KB
Newer Older
1
// -*- coding: utf-8 -*-
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
2
// Copyright (C) 2008, 2013, 2014, 2015 Laboratoire de Recherche et
3
// Développement de l'Epita (LRDE).
4
// Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
5
6
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
7
8
9
10
11
//
// 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
12
// the Free Software Foundation; either version 3 of the License, or
13
14
15
16
17
18
19
20
// (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
21
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
22

23
#pragma once
24

25
#include <stack>
26
#include "status.hh"
27
28
#include "twaalgos/emptiness.hh"
#include "twaalgos/emptiness_stats.hh"
29
30
31

namespace spot
{
32
33
34
  /// \addtogroup emptiness_check_algorithms
  /// @{

35
36
37
  /// \brief Check whether the language of an automate is empty.
  ///
  /// This is based on the following paper.
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  /** \verbatim
      @InProceedings{couvreur.99.fm,
        author    = {Jean-Michel Couvreur},
        title     = {On-the-fly Verification of Temporal Logic},
        pages     = {253--271},
        editor    = {Jeannette M. Wing and Jim Woodcock and Jim Davies},
        booktitle = {Proceedings of the World Congress on Formal Methods in
                     the Development of Computing Systems (FM'99)},
        publisher = {Springer-Verlag},
        series    = {Lecture Notes in Computer Science},
        volume    = {1708},
        year      = {1999},
        address   = {Toulouse, France},
        month     = {September},
        isbn      = {3-540-66587-0}
      }
      \endverbatim */
55
  ///
56
57
58
59
60
61
62
  /// A recursive definition of the algorithm would look as follows,
  /// but the implementation is of course not recursive.
  /// (<code>&lt;Sigma, Q, delta, q, F&gt;</code> is the automaton to
  /// check, H is an associative array mapping each state to its
  /// positive DFS order or 0 if it is dead, SCC is and ACC are two
  /// stacks.)
  ///
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  /** \verbatim
      check(<Sigma, Q, delta, q, F>, H, SCC, ACC)
        if q is not in H   // new state
            H[q] = H.size + 1
            SCC.push(<H[q], {}>)
            forall <a, s> : <q, _, a, s> in delta
                ACC.push(a)
                res = check(<Sigma, Q, delta, s, F>, H, SCC, ACC)
                if res
                    return res
            <n, _> = SCC.top()
            if n = H[q]
                SCC.pop()
                mark_reachable_states_as_dead(<Sigma, Q, delta, q, F>, H$)
            return 0
        else
            if H[q] = 0 // dead state
                ACC.pop()
                return true
            else // state in stack: merge SCC
                all = {}
                do
                    <n, a> = SCC.pop()
                    all = all union a union { ACC.pop() }
                until n <= H[q]
                SCC.push(<n, all>)
                if all != F
                    return 0
                return new emptiness_check_result(necessary data)
      \endverbatim */
93
94
95
96
97
  ///
  /// check() returns 0 iff the automaton's language is empty.  It
  /// returns an instance of emptiness_check_result.  If the automaton
  /// accept a word.  (Use emptiness_check_result::accepting_run() to
  /// extract an accepting run.)
98
  ///
99
100
  /// There are two variants of this algorithm: spot::couvreur99_check and
  /// spot::couvreur99_check_shy.  They differ in their memory usage, the
101
102
103
  /// number for successors computed before they are used and the way
  /// the depth first search is directed.
  ///
104
  /// spot::couvreur99_check performs a straightforward depth first search.
105
  /// The DFS stacks store twa_succ_iterators, so that only the
106
107
  /// iterators which really are explored are computed.
  ///
108
  /// spot::couvreur99_check_shy tries to explore successors which are
109
110
111
  /// visited states first.  this helps to merge SCCs and generally
  /// helps to produce shorter counter-examples.  However this
  /// algorithm cannot stores unprocessed successors as
112
  /// twa_succ_iterators: it must compute all successors of a state
113
114
  /// at once in order to decide which to explore first, and must keep
  /// a list of all unexplored successors in its DFS stack.
115
  ///
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  /// The couvreur99() function is a wrapper around these two flavors
  /// of the algorithm.  \a options is an option map that specifies
  /// which algorithms should be used, and how.
  ///
  /// The following options are available.
  /// \li \c "shy" : if non zero, then spot::couvreur99_check_shy is used,
  ///                otherwise (and by default) spot::couvreur99_check is used.
  ///
  /// \li \c "poprem" : specifies how the algorithm should handle the
  /// destruction of non-accepting maximal strongly connected
  /// components.  If \c poprem is non null, the algorithm will keep a
  /// list of all states of a SCC that are fully processed and should
  /// be removed once the MSCC is popped.  If \c poprem is null (the
  /// default), the MSCC will be traversed again (i.e. generating the
  /// successors of the root recursively) for deletion.  This is a
  /// choice between memory and speed.
  ///
  /// \li \c "group" : this options is used only by spot::couvreur99_check_shy.
  /// If non null (the default), the successors of all the
  /// states that belong to the same SCC will be considered when
  /// choosing a successor.  Otherwise, only the successor of the
  /// topmost state on the DFS stack are considered.
138
  SPOT_API emptiness_check_ptr
139
  couvreur99(const const_twa_ptr& a, option_map options = option_map());
140

141
#ifndef SWIG
142
143
144
  /// \brief An implementation of the Couvreur99 emptiness-check algorithm.
  ///
  /// See the documentation for spot::couvreur99.
145
  class SPOT_API couvreur99_check: public emptiness_check, public ec_statistics
146
147
  {
  public:
148
    couvreur99_check(const const_twa_ptr& a, option_map o = option_map());
149
    virtual ~couvreur99_check();
150
151

    /// Check whether the automaton's language is empty.
152
    virtual emptiness_check_result_ptr check();
153

154
155
    virtual std::ostream& print_stats(std::ostream& os) const;

156
157
158
159
160
161
162
    /// \brief Return the status of the emptiness-check.
    ///
    /// When check() succeed, the status should be passed along
    /// to spot::counter_example.
    ///
    /// This status should not be deleted, it is a pointer
    /// to a member of this class that will be deleted when
163
    /// the couvreur99 object is deleted.
164
    std::shared_ptr<const couvreur99_check_status> result() const;
165
166

  protected:
167
    std::shared_ptr<couvreur99_check_status> ecs_;
168
169
170
171
172
173
    /// \brief Remove a strongly component from the hash.
    ///
    /// This function remove all accessible state from a given
    /// state. In other words, it removes the strongly connected
    /// component that contains this state.
    void remove_component(const state* start_delete);
174
175
176

    /// Whether to store the state to be removed.
    bool poprem_;
177
178
179
    /// Number of dead SCC removed by the algorithm.
    unsigned removed_components;
    unsigned get_removed_components() const;
180
    unsigned get_vmsize() const;
181
182
  };

183
  /// \brief A version of spot::couvreur99_check that tries to visit
184
  /// known states first.
185
  ///
186
  /// See the documentation for spot::couvreur99.
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
187
  class SPOT_API couvreur99_check_shy final: public couvreur99_check
188
189
  {
  public:
190
    couvreur99_check_shy(const const_twa_ptr& a, option_map o = option_map());
191
    virtual ~couvreur99_check_shy();
192

193
    virtual emptiness_check_result_ptr check();
194
195
196

  protected:
    struct successor {
197
      acc_cond::mark_t acc;
198
      const spot::state* s;
199
      successor(acc_cond::mark_t acc, const spot::state* s): acc(acc), s(s) {}
200
201
202
    };

    // We use five main data in this algorithm:
203
204
    // * couvreur99_check::root, a stack of strongly connected components (SCC),
    // * couvreur99_check::h, a hash of all visited nodes, with their order,
205
206
    //   (it is called "Hash" in Couvreur's paper)
    // * arc, a stack of acceptance conditions between each of these SCC,
207
    std::stack<acc_cond::mark_t> arc;
208
209
210
211
212
213
214
    // * num, the number of visited nodes.  Used to set the order of each
    //   visited node,
    int num;
    // * todo, the depth-first search stack.  This holds pairs of the
    //   form (STATE, SUCCESSORS) where SUCCESSORS is a list of
    //   (ACCEPTANCE_CONDITIONS, STATE) pairs.
    typedef std::list<successor> succ_queue;
215

216
217
218
    // Position in the loop seeking known successors.
    succ_queue::iterator pos;

219
220
221
222
    struct todo_item
    {
      const state* s;
      int n;
223
224
      succ_queue q;		// Unprocessed successors of S
      todo_item(const state* s, int n, couvreur99_check_shy* shy);
225
226
227
    };

    typedef std::list<todo_item> todo_list;
228
229
    todo_list todo;

230
231
    void clear_todo();

232
233
234
    /// Dump the queue for debugging.
    void dump_queue(std::ostream& os = std::cerr);

235
    /// Whether successors should be grouped for states in the same SCC.
236
    bool group_;
237
238
239
    // If the "group2" option is set (it implies "group"), we
    // reprocess the successor states of SCC that have been merged.
    bool group2_;
240
  };
241
#endif
242

243
  /// @}
244
}