1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2011, 2013-2018, 2020-2021 Laboratoire de
3 // Recherche et Developpement de l'Epita (LRDE).
4 // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
5 // département Systèmes Répartis Coopératifs (SRC), Université Pierre
6 // et Marie Curie.
7 //
8 // This file is part of Spot, a model checking library.
9 //
10 // Spot is free software; you can redistribute it and/or modify it
11 // 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 // (at your option) any later version.
14 //
15 // Spot is distributed in the hope that it will be useful, but WITHOUT
16 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 // or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
18 // License for more details.
19 //
20 // 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 <map>
26 #include <list>
27 #include <iosfwd>
28 #include <bddx.h>
29 #include <spot/misc/optionmap.hh>
30 #include <spot/twa/twagraph.hh>
31 #include <spot/twaalgos/emptiness_stats.hh>
32 
33 namespace spot
34 {
35   struct twa_run;
36   typedef std::shared_ptr<twa_run> twa_run_ptr;
37   typedef std::shared_ptr<const twa_run> const_twa_run_ptr;
38 
39   /// \addtogroup emptiness_check Emptiness-checks
40   /// \ingroup twa_algorithms
41   ///
42   /// You can create an emptiness check either by instantiating it
43   /// explicitly (calling one of the functions of \ref
44   /// emptiness_check_algorithms "this list"), or indirectly via
45   /// spot::make_emptiness_check_instantiator().  The latter function
46   /// allows user-options to influence the choice of the
47   /// emptiness-check algorithm used, and the intermediate
48   /// instantiator object can be used to query to properties of the
49   /// emptiness check selected.
50   ///
51   /// All emptiness-check algorithms follow the same interface.
52   /// Basically once you have constructed an instance of
53   /// spot::emptiness_check, you should call
54   /// spot::emptiness_check::check() to check the automaton.
55   ///
56   /// If spot::emptiness_check::check() returns 0, then the automaton
57   /// was found empty.  Otherwise the automaton accepts some run.
58   /// (Beware that some algorithms---those using bit-state
59   /// hashing---may found the automaton to be empty even if it is not
60   /// actually empty.)
61   ///
62   /// When spot::emptiness_check::check() does not return 0, it
63   /// returns an instance of spot::emptiness_check_result.  You can
64   /// try to call spot::emptiness_check_result::accepting_run() to
65   /// obtain an accepting run.  For some emptiness-check algorithms,
66   /// spot::emptiness_check_result::accepting_run() will require some
67   /// extra computation.  Most emptiness-check algorithms are able to
68   /// return such an accepting run, however this is not mandatory and
69   /// spot::emptiness_check_result::accepting_run() can return 0 (this
70   /// does not means by anyway that no accepting run exist).
71   ///
72   /// The acceptance run returned by
73   /// spot::emptiness_check_result::accepting_run(), if any, is of
74   /// type spot::twa_run.  \ref twa_run "This page" gathers existing
75   /// operations on these objects.
76   ///
77   /// @{
78 
79   /// \brief The result of an emptiness check.
80   ///
81   /// Instances of these class should not last longer than the
82   /// instances of emptiness_check that produced them as they
83   /// may reference data internal to the check.
84   class SPOT_API emptiness_check_result
85   {
86   public:
emptiness_check_result(const const_twa_ptr & a,option_map o=option_map ())87     emptiness_check_result(const const_twa_ptr& a,
88                            option_map o = option_map())
89       : a_(a), o_(o)
90     {
91     }
92 
93     virtual
~emptiness_check_result()94     ~emptiness_check_result()
95     {
96     }
97 
98     /// \brief Return a run accepted by the automata passed to
99     /// the emptiness check.
100     ///
101     /// This method might actually compute the acceptance run.  (Not
102     /// all emptiness check algorithms actually produce a
103     /// counter-example as a side-effect of checking emptiness, some
104     /// need some post-processing.)
105     ///
106     /// This can also return 0 if the emptiness check algorithm
107     /// cannot produce a counter example (that does not mean there
108     /// is no counter-example; the mere existence of an instance of
109     /// this class asserts the existence of a counter-example).
110     virtual twa_run_ptr accepting_run();
111 
112     /// The automaton on which an accepting_run() was found.
113     const const_twa_ptr&
automaton() const114     automaton() const
115     {
116       return a_;
117     }
118 
119     /// Return the options parametrizing how the accepting run is computed.
120     const option_map&
options() const121     options() const
122     {
123       return o_;
124     }
125 
126     /// Modify the algorithm options.
127     const char* parse_options(char* options);
128 
129     /// Return statistics, if available.
130     virtual const unsigned_statistics* statistics() const;
131 
132   protected:
133     /// Notify option updates.
134     virtual void options_updated(const option_map& old);
135 
136     const_twa_ptr a_;                ///< The automaton.
137     option_map o_;                ///< The options.
138   };
139 
140   typedef std::shared_ptr<emptiness_check_result> emptiness_check_result_ptr;
141 
142   /// Common interface to emptiness check algorithms.
143   class SPOT_API emptiness_check:
144     public std::enable_shared_from_this<emptiness_check>
145   {
146   public:
emptiness_check(const const_twa_ptr & a,option_map o=option_map ())147     emptiness_check(const const_twa_ptr& a, option_map o = option_map())
148       : a_(a), o_(o)
149     {
150     }
151     virtual ~emptiness_check();
152 
153     /// The automaton that this emptiness-check inspects.
154     const const_twa_ptr&
automaton() const155     automaton() const
156     {
157       return a_;
158     }
159 
160     /// Return the options parametrizing how the emptiness check is realized.
161     const option_map&
options() const162     options() const
163     {
164       return o_;
165     }
166 
167     /// Modify the algorithm options.
168     const char* parse_options(char* options);
169 
170     /// Return false iff accepting_run() can return 0 for non-empty automata.
171     virtual bool safe() const;
172 
173     /// \brief Check whether the automaton contain an accepting run.
174     ///
175     /// Return 0 if the automaton accepts no run.  Return an instance
176     /// of emptiness_check_result otherwise.  This instance might
177     /// allow to obtain one sample acceptance run.  The result has to
178     /// be destroyed before the emptiness_check instance that
179     /// generated it.
180     ///
181     /// Some emptiness_check algorithms may allow check() to be called
182     /// several time, but generally you should not assume that.
183     ///
184     /// Some emptiness_check algorithms, especially those using bit state
185     /// hashing may return 0 even if the automaton is not empty.
186     /// \see safe()
187     virtual emptiness_check_result_ptr check() = 0;
188 
189     /// Return statistics, if available.
190     virtual const unsigned_statistics* statistics() const;
191 
192     /// Return emptiness check statistics, if available.
193     virtual const ec_statistics* emptiness_check_statistics() const;
194 
195     /// Print statistics, if any.
196     virtual std::ostream& print_stats(std::ostream& os) const;
197 
198     /// Notify option updates.
199     virtual void options_updated(const option_map& old);
200 
201   protected:
202     const_twa_ptr a_;                ///< The automaton.
203     option_map o_;                ///< The options
204   };
205 
206   typedef std::shared_ptr<emptiness_check> emptiness_check_ptr;
207 
208   class emptiness_check_instantiator;
209   typedef std::shared_ptr<emptiness_check_instantiator>
210     emptiness_check_instantiator_ptr;
211 
212   /// Dynamically create emptiness checks.  Given their name and options.
213   class SPOT_API emptiness_check_instantiator
214   {
215   public:
216     /// Actually instantiate the emptiness check, for \a a.
217     emptiness_check_ptr instantiate(const const_twa_ptr& a) const;
218 
219     /// Accessor to the options.
220     /// @{
221     const option_map&
options() const222     options() const
223     {
224       return o_;
225     }
226 
227     option_map&
options()228     options()
229     {
230       return o_;
231     }
232     /// @}
233 
234     /// \brief Minimum number of acceptance sets supported by
235     /// the emptiness check.
236     unsigned int min_sets() const;
237 
238     /// \brief Maximum number of acceptance conditions supported by
239     /// the emptiness check.
240     ///
241     /// \return \c -1U if no upper bound exists.
242     unsigned int max_sets() const;
243   protected:
244     emptiness_check_instantiator(option_map o, void* i);
245 
246     option_map o_;
247     void *info_;
248   };
249 
250   /// \brief Create an emptiness-check instantiator, given the name
251   /// of an emptiness check.
252   ///
253   /// This is a convenient entry point to instantiate an emptiness
254   /// check with user-supplied options.
255   ///
256   /// \param name should have the form \c "name" or \c "name(options)".
257   ///
258   /// \return Return an emptiness-check instantiator.  On error, the
259   /// function returns \c nullptr.  If the name of the algorithm was
260   /// unknown, \c *err will be set to \c name.  If some fragment of
261   /// the options could not be parsed, \c *err will point to that
262   /// fragment.
263   ///
264   /// The following names supported and correspond to different emptiness
265   /// check algorithms:
266   /// - `Cou99` uses `spot::couvreur99()`, that works with Fin-less
267   ///       acceptance conditions, with any number of acceptance sets.
268   ///       The following options can be used:
269   ///     - `shy` Compute all successors of each state, then explore already
270   ///       visited states first. This usually helps to merge SCCs, and
271   ///       thus exit sooner. However because all successors have to be
272   ///       computed and stored, it often consume more memory.
273   ///     - `group` Setting this option is meaningful only when shy is
274   ///       used. If set (the default), the successors of all the states
275   ///       that belong to the same SCC will be considered when choosing
276   ///       a successor. Otherwise, only the successor of the topmost
277   ///       state on the DFS stack are considered.
278   ///     - `poprem` Specifies how the algorithm should handle the
279   ///       destruction of non-accepting maximal strongly connected
280   ///       components. If poprem is set, the algorithm will keep a list
281   ///       of all states of a SCC that are fully processed and should be
282   ///       removed once the MSCC is popped. If poprem is unset (the
283   ///       default), the MSCC will be traversed again (i.e. generating
284   ///       the successors of the root recursively) for deletion. This
285   ///       is a choice between memory and speed.
286   ///
287   ///   Examples:
288   ///   \code
289   ///   Cou99
290   ///   Cou99(shy !group)
291   ///   Cou99(shy group)
292   ///   Cou99(poprem)
293   ///   Cou99(poprem shy !group)
294   ///   Cou99(poprem shy group)
295   ///   \endcode
296   ///
297   /// - `GC04` uses `spot::explicit_gv04_check()` and works on automata
298   ///   with Fin-less acceptance conditions using at most one acceptance
299   ///   set.  No options are supported.
300   ///
301   ///   Example:
302   ///   \code
303   ///   GC04
304   ///   \endcode
305   ///
306   /// - `CVWY90` uses `spot::magic_search()` and work on automata with
307   ///   Fin-less acceptance conditions using at most one acceptance
308   ///   set.  Set option `bsh` to the size of a hash-table if you want
309   ///   to activate bit-state hashing.
310   ///
311   ///   Examples:
312   ///   \code
313   ///   CVWY90
314   ///   CVWY90(bsh=4M)
315   ///   \endcode
316   ///
317   /// - `SE05` uses `spot::se05()` and works on work on automata with
318   ///   Fin-less acceptance conditions using at most one acceptance
319   ///   set.  Set option `bsh` to the size of a hash-table if you want
320   ///   to activate bit-state hashing.
321   ///
322   ///   Examples:
323   ///   \code
324   ///   SE05
325   ///   SE05(bsh=4M)
326   ///   \endcode
327   ///
328   /// - `Tau03` uses `spot::explicit_tau03_search()` and work on automata with
329   ///   Fin-less acceptance conditions using at least one acceptance
330   ///   set.  No options are supported.
331   ///
332   ///   Example:
333   ///   \code
334   ///   Tau03
335   ///   \endcode
336   ///
337   /// - `Tau03_opt` uses `spot::explicit_tau03_opt_search()` and work on
338   /// automata with any Fin-less acceptance.  The following options are
339   /// supported:
340   ///   - `weights` This option is set by default and activates the usage
341   ///     of weights in the top-level DFS as well as in nested DFSs.
342   ///   - `redweights` This option is set by default, and activates the
343   ///     usage of weights in nested DFSs. It is meaningful only if
344   ///     weights is set.
345   ///   - `condstack` This option is unset by default, and activates
346   ///     the use of the "conditional stack" optimization described by
347   ///     Heikki Tauriainen.
348   ///   - ordering This option is unset by default, and activates the
349   ///     use of the "ordering" heuristic described by Heikki Tauriainen
350   ///     in Research Report A96 from the Helsinki University of Technology.
351   ///
352   ///   Example:
353   ///   \code
354   ///   Tau03_opt
355   ///   Tau03_opt(!weights)
356   ///   Tau03_opt(!redweights)
357   ///   Tau03_opt(condstack)
358   ///   Tau03_opt(condstack !weights)
359   ///   Tau03_opt(condstack !redweights)
360   ///   \endcode
361   SPOT_API emptiness_check_instantiator_ptr
362   make_emptiness_check_instantiator(const char* name, const char** err);
363 
364   /// @}
365 
366 
367   /// \addtogroup emptiness_check_algorithms Emptiness-check algorithms
368   /// \ingroup emptiness_check
369 
370   /// \addtogroup twa_run TωA runs and supporting functions
371   /// \ingroup emptiness_check
372   /// @{
373 
374   /// An accepted run, for a twa.
375   struct SPOT_API twa_run final
376   {
377     struct step {
378       const state* s;
379       bdd label;
380       acc_cond::mark_t acc;
381 
stepspot::twa_run::step382       step(const state* s, bdd label, acc_cond::mark_t acc) noexcept
383         : s(s), label(label), acc(acc)
384       {
385       }
386       step() = default;
387     };
388 
389     typedef std::list<step> steps;
390 
391     steps prefix;
392     steps cycle;
393     const_twa_ptr aut;
394 
395     ~twa_run();
twa_runspot::twa_run396     twa_run(const const_twa_ptr& aut) noexcept
397       : aut(aut)
398     {
399     }
400     twa_run(const twa_run& run);
401     twa_run& operator=(const twa_run& run);
402 
403     /// \brief Raise an exception of the cycle is empty.
404     ///
405     /// It is OK for a twa_run to have an empty cycle while the run is
406     /// being filled by some procedure.  But after that, we expect
407     /// cycles to be non-empty.  Calling this function will raise an
408     /// std::runtime_error if the cycle is empty, giving \a where
409     /// (usually the name of the calling function) as context.
410     void ensure_non_empty_cycle(const char* where) const;
411 
412     /// \brief Reduce an accepting run.
413     ///
414     /// Return a run which is still accepting for <code>aut</code>,
415     /// but is no longer than this one.
416     ///
417     /// This is done by trying to find a fragment of the accepting
418     /// single that is accepting, and trying to close a cycle around
419     /// this fragment with fewer edges than in the original cycle.
420     /// (This step works best in Fin-less automata.)  And then trying
421     /// to find a shorter prefix leading to any state of the cycle.
422     ///
423     /// An <code>std::runtime_error</code> is thrown if the run
424     /// to reduce is not accepting.
425     twa_run_ptr reduce() const;
426 
427     /// \brief Project an accepting run
428     ///
429     /// This only works if the automaton associated to this run has
430     /// been created with otf_product() or product(), and \a other is
431     /// one of the two operands of the product.
432     ///
433     /// Use the \a right Boolean to specify whether \a other was a
434     /// left or right operand.
435     twa_run_ptr project(const const_twa_ptr& other, bool right = false);
436 
437     /// \brief Replay a run.
438     ///
439     /// This is similar to <code>os << run;</code>, except that the
440     /// run is actually replayed on the automaton while it is printed.
441     /// The output will stop if the run cannot be completed.
442     ///
443     /// \param os the stream on which the replay should be traced
444     /// \param debug if set the output will be more verbose and extra
445     ///              debugging informations will be output on failure
446     /// \return true iff the run could be completed
447     bool replay(std::ostream& os, bool debug = false) const;
448 
449     /// \brief Highlight the accepting run on the automaton.
450     ///
451     /// Note that this works only if the automaton is a twa_graph_ptr.
452     void highlight(unsigned color);
453 
454     /// \brief Return a twa_graph_ptr corresponding to \a run
455     ///
456     /// Identical states are merged.
457     ///
458     /// If \a preserve_names is set, the created states are named
459     /// using the format_state() result from the original state.
460     twa_graph_ptr as_twa(bool preserve_names = false) const;
461 
462     /// \brief Display a twa_run.
463     ///
464     /// Output the prefix and cycle parts of the twa_run \a run on \a os.
465     ///
466     /// The automaton object (stored by \a run) is used only to format
467     /// the states, and to know how to print the BDDs describing the
468     /// conditions and acceptance conditions of the run; it is
469     /// <b>not</b> used to replay the run.  In other words this
470     /// function will work even if the twa_run you are trying to print
471     /// appears to connect states that are not connected.
472     ///
473     /// This is unlike replay_twa_run(), which will ensure the run
474     /// actually exists in the automaton (and will also display any
475     /// transition annotation).
476     SPOT_API
477     friend std::ostream& operator<<(std::ostream& os, const twa_run& run);
478   };
479   /// @}
480 
481   /// \addtogroup emptiness_check_stats Emptiness-check statistics
482   /// \ingroup emptiness_check
483 }
484