1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2011, 2013-2019, 2021  Laboratoire de Recherche et
3 // Développement 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 /// FIXME:
24 /// * Test some heuristics on the order of visit of the successors in the blue
25 ///   dfs:
26 ///   - favorize the arcs conducting to the blue stack (the states of color
27 ///     cyan)
28 ///   - in this category, favorize the labelled arcs
29 ///   - for the remaining ones, favorize the arcs labelled by the greatest
30 ///     number of new acceptance conditions (notice that this number may evolve
31 ///     after the visit of previous successors).
32 ///
33 /// * Add a bit-state hashing version.
34 
35 //#define TRACE
36 
37 #include "config.h"
38 #include <iostream>
39 #ifdef TRACE
40 #define trace std::cerr
41 #else
42 #define trace while (0) std::cerr
43 #endif
44 
45 #include <cassert>
46 #include <vector>
47 #include <stack>
48 #include <spot/misc/hash.hh>
49 #include <spot/twa/twa.hh>
50 #include <spot/twaalgos/emptiness.hh>
51 #include <spot/twaalgos/emptiness_stats.hh>
52 #include <spot/twaalgos/tau03opt.hh>
53 #include <spot/priv/weight.hh>
54 #include <spot/twaalgos/ndfs_result.hxx>
55 
56 namespace spot
57 {
58   namespace
59   {
60     enum color {WHITE, CYAN, BLUE};
61 
62     /// \brief Emptiness checker on spot::tgba automata having a finite number
63     /// of acceptance conditions (i.e. a TGBA).
64     template <typename heap>
65     class tau03_opt_search : public emptiness_check, public ec_statistics
66     {
67     public:
68       /// \brief Initialize the search algorithm on the automaton \a a
tau03_opt_search(const const_twa_ptr & a,size_t size,option_map o)69       tau03_opt_search(const const_twa_ptr& a, size_t size, option_map o)
70         : emptiness_check(a, o),
71           current_weight(a->acc()),
72           h(size),
73           use_condition_stack(o.get("condstack")),
74           use_ordering(use_condition_stack && o.get("ordering")),
75           use_weights(o.get("weights", 1)),
76           use_red_weights(use_weights && o.get("redweights", 1))
77       {
78         if (a->acc().uses_fin_acceptance())
79           throw std::runtime_error("tau03opt requires Fin-less acceptance");
80       }
81 
~tau03_opt_search()82       virtual ~tau03_opt_search()
83       {
84         // Release all iterators on the stacks.
85         while (!st_blue.empty())
86           {
87             h.pop_notify(st_blue.front().s);
88             a_->release_iter(st_blue.front().it);
89             st_blue.pop_front();
90           }
91         while (!st_red.empty())
92           {
93             h.pop_notify(st_red.front().s);
94             a_->release_iter(st_red.front().it);
95             st_red.pop_front();
96           }
97       }
98 
99       /// \brief Perform an emptiness check.
100       ///
101       /// \return non null pointer iff the algorithm has found an
102       /// accepting path.
check()103       virtual emptiness_check_result_ptr check() override
104       {
105         if (!st_blue.empty())
106             return nullptr;
107         assert(st_red.empty());
108         const state* s0 = a_->get_init_state();
109         inc_states();
110         h.add_new_state(s0, CYAN, current_weight);
111         push(st_blue, s0, bddfalse, {});
112         auto t = std::static_pointer_cast<tau03_opt_search>
113           (this->emptiness_check::shared_from_this());
114         if (dfs_blue())
115           return std::make_shared<ndfs_result<tau03_opt_search<heap>, heap>>(t);
116         return nullptr;
117       }
118 
print_stats(std::ostream & os) const119       virtual std::ostream& print_stats(std::ostream &os) const override
120       {
121         os << states() << " distinct nodes visited" << std::endl;
122         os << transitions() << " transitions explored" << std::endl;
123         os << max_depth() << " nodes for the maximal stack depth" << std::endl;
124         return os;
125       }
126 
get_heap() const127       const heap& get_heap() const
128         {
129           return h;
130         }
131 
get_st_blue() const132       const stack_type& get_st_blue() const
133         {
134           return st_blue;
135         }
136 
get_st_red() const137       const stack_type& get_st_red() const
138         {
139           return st_red;
140         }
141 
142     private:
push(stack_type & st,const state * s,const bdd & label,acc_cond::mark_t acc)143       void push(stack_type& st, const state* s,
144                 const bdd& label, acc_cond::mark_t acc)
145       {
146         inc_depth();
147         twa_succ_iterator* i = a_->succ_iter(s);
148         i->first();
149         st.emplace_front(s, i, label, acc);
150       }
151 
pop(stack_type & st)152       void pop(stack_type& st)
153       {
154         dec_depth();
155         a_->release_iter(st.front().it);
156         st.pop_front();
157       }
158 
project_acc(acc_cond::mark_t acc) const159       acc_cond::mark_t project_acc(acc_cond::mark_t acc) const
160       {
161         if (!use_ordering)
162           return acc;
163         // FIXME: This should be improved.
164         std::vector<unsigned> res;
165         unsigned max = a_->num_sets();
166         for (unsigned n = 0; n < max && acc.has(n); ++n)
167           res.emplace_back(n);
168         return acc_cond::mark_t(res.begin(), res.end());
169       }
170 
171       /// \brief weight of the state on top of the blue stack.
172       weight current_weight;
173 
174       /// \brief Stack of the blue dfs.
175       stack_type st_blue;
176 
177       /// \brief Stack of the red dfs.
178       stack_type st_red;
179 
180       /// \brief Map where each visited state is colored
181       /// by the last dfs visiting it.
182       heap h;
183 
184       /// Whether to use the "condition stack".
185       bool use_condition_stack;
186       /// Whether to use an ordering between the acceptance conditions.
187       /// Effective only if using the condition stack.
188       bool use_ordering;
189       /// Whether to use weights to abort earlier.
190       bool use_weights;
191       /// Whether to use weights in the red dfs.
192       bool use_red_weights;
193 
dfs_blue()194       bool dfs_blue()
195       {
196         while (!st_blue.empty())
197           {
198             stack_item& f = st_blue.front();
199             trace << "DFS_BLUE treats: " << a_->format_state(f.s) << std::endl;
200             if (!f.it->done())
201               {
202                 const state *s_prime = f.it->dst();
203                 trace << "  Visit the successor: "
204                       << a_->format_state(s_prime) << std::endl;
205                 bdd label = f.it->cond();
206                 auto acc = f.it->acc();
207                 // Go down the edge (f.s, <label, acc>, s_prime)
208                 f.it->next();
209                 inc_transitions();
210                 typename heap::color_ref c_prime = h.get_color_ref(s_prime);
211                 if (c_prime.is_white())
212                   {
213                     trace << "  It is white, go down" << std::endl;
214                     if (use_weights)
215                       current_weight.add(acc);
216                     inc_states();
217                     h.add_new_state(s_prime, CYAN, current_weight);
218                     push(st_blue, s_prime, label, acc);
219                   }
220                 else
221                   {
222                     typename heap::color_ref c = h.get_color_ref(f.s);
223                     assert(!c.is_white());
224                     if (c_prime.get_color() == CYAN
225                         && a_->acc().accepting
226                         (current_weight.diff(a_->acc(), c_prime. get_weight())
227                          | c.get_acc() | acc | c_prime.get_acc()))
228                       {
229                         trace << "  It is cyan and acceptance condition "
230                               << "is reached, report cycle" << std::endl;
231                         c_prime.cumulate_acc(a_->acc().all_sets());
232                         push(st_red, s_prime, label, acc);
233                         return true;
234                       }
235                     else
236                       {
237                         trace << "  It is cyan or blue and";
238                         auto acu = acc | c.get_acc();
239                         auto acp = project_acc(acu);
240                         if ((c_prime.get_acc() & acp) != acp)
241                           {
242                             trace << "  a propagation is needed, "
243                                   << "start a red dfs" << std::endl;
244                             c_prime.cumulate_acc(acp);
245                             push(st_red, s_prime, label, acc);
246                             if (dfs_red(acu))
247                               return true;
248                           }
249                         else
250                           {
251                             trace << " no propagation is needed, pop it."
252                                   << std::endl;
253                             h.pop_notify(s_prime);
254                           }
255                       }
256                   }
257               }
258             else
259             // Backtrack the edge
260             //        (predecessor of f.s in st_blue, <f.label, f.acc>, f.s)
261               {
262                 trace << "  All the successors have been visited" << std::endl;
263                 stack_item f_dest(f);
264                 pop(st_blue);
265                 if (use_weights)
266                   current_weight.sub(f_dest.acc);
267                 typename heap::color_ref c_prime = h.get_color_ref(f_dest.s);
268                 assert(!c_prime.is_white());
269                 c_prime.set_color(BLUE);
270                 if (!st_blue.empty())
271                   {
272                     typename heap::color_ref c =
273                                           h.get_color_ref(st_blue.front().s);
274                     assert(!c.is_white());
275                     auto acu = f_dest.acc | c.get_acc();
276                     auto acp = project_acc(acu);
277                     if ((c_prime.get_acc() & acp) != acp)
278                       {
279                         trace << "  The arc from "
280                               << a_->format_state(st_blue.front().s)
281                               << " to the current state implies to "
282                               << " start a red dfs" << std::endl;
283                         c_prime.cumulate_acc(acp);
284                         push(st_red, f_dest.s, f_dest.label, f_dest.acc);
285                         if (dfs_red(acu))
286                             return true;
287                       }
288                     else
289                       {
290                         trace << "  Pop it" << std::endl;
291                         h.pop_notify(f_dest.s);
292                       }
293                   }
294                 else
295                   {
296                     trace << "  Pop it" << std::endl;
297                     h.pop_notify(f_dest.s);
298                   }
299               }
300           }
301         return false;
302       }
303 
304       bool
dfs_red(acc_cond::mark_t acu)305       dfs_red(acc_cond::mark_t acu)
306       {
307         assert(!st_red.empty());
308 
309         // These are useful only when USE_CONDITION_STACK is set.
310         typedef std::pair<acc_cond::mark_t, unsigned> cond_level;
311         std::stack<cond_level> condition_stack;
312         unsigned depth = 1;
313         condition_stack.emplace(acc_cond::mark_t({}), 0);
314 
315         while (!st_red.empty())
316           {
317             stack_item& f = st_red.front();
318             trace << "DFS_RED treats: " << a_->format_state(f.s) << std::endl;
319             if (!f.it->done())
320               {
321                 const state *s_prime = f.it->dst();
322                 trace << "  Visit the successor: "
323                       << a_->format_state(s_prime) << std::endl;
324                 bdd label = f.it->cond();
325                 auto acc = f.it->acc();
326                 // Go down the edge (f.s, <label, acc>, s_prime)
327                 f.it->next();
328                 inc_transitions();
329                 typename heap::color_ref c_prime = h.get_color_ref(s_prime);
330                 if (c_prime.is_white())
331                   {
332                     trace << "  It is white, pop it" << std::endl;
333                     s_prime->destroy();
334                     continue;
335                   }
336                 else if (c_prime.get_color() == CYAN &&
337                          a_->acc().accepting
338                          (acc | acu | c_prime.get_acc() |
339                           (use_red_weights ?
340                            current_weight.diff(a_->acc(),
341                                                c_prime.
342                                                get_weight())
343                            : acc_cond::mark_t({}))))
344                   {
345                     trace << "  It is cyan and acceptance condition "
346                           << "is reached, report cycle" << std::endl;
347                     c_prime.cumulate_acc(a_->acc().all_sets());
348                     push(st_red, s_prime, label, acc);
349                     return true;
350                   }
351                 acc_cond::mark_t acp;
352                 if (use_ordering)
353                   acp = project_acc(acu | acc | c_prime.get_acc());
354                 else if (use_condition_stack)
355                   acp = acu | acc;
356                 else
357                   acp = acu;
358                 if ((c_prime.get_acc() & acp) != acp)
359                   {
360                     trace << "  It is cyan or blue and propagation "
361                           << "is needed, go down"
362                           << std::endl;
363                     c_prime.cumulate_acc(acp);
364                     push(st_red, s_prime, label, acc);
365                     if (use_condition_stack)
366                       {
367                         auto old = acu;
368                         acu |= acc;
369                         condition_stack.emplace(acu - old, depth);
370                       }
371                     ++depth;
372                   }
373                 else
374                   {
375                     trace << "  It is cyan or blue and no propagation "
376                           << "is needed , pop it" << std::endl;
377                     h.pop_notify(s_prime);
378                   }
379               }
380             else // Backtrack
381               {
382                 trace << "  All the successors have been visited, pop it"
383                       << std::endl;
384                 h.pop_notify(f.s);
385                 pop(st_red);
386                 --depth;
387                 if (condition_stack.top().second == depth)
388                   {
389                     acu -= condition_stack.top().first;
390                     condition_stack.pop();
391                   }
392               }
393           }
394         assert(depth == 0);
395         assert(condition_stack.empty());
396         return false;
397       }
398 
399     };
400 
401     class explicit_tau03_opt_search_heap final
402     {
403       typedef state_map<std::pair<weight, acc_cond::mark_t>> hcyan_type;
404       typedef state_map<std::pair<color, acc_cond::mark_t>> hash_type;
405     public:
406       class color_ref final
407       {
408       public:
color_ref(hash_type * h,hcyan_type * hc,const state * s,const weight * w,acc_cond::mark_t * a)409         color_ref(hash_type* h, hcyan_type* hc, const state* s,
410                   const weight* w, acc_cond::mark_t* a)
411           : is_cyan(true), w(w), ph(h), phc(hc), ps(s), pc(nullptr), acc(a)
412           {
413           }
color_ref(color * c,acc_cond::mark_t * a)414         color_ref(color* c, acc_cond::mark_t* a)
415           : is_cyan(false), pc(c), acc(a)
416           {
417           }
get_color() const418         color get_color() const
419           {
420             if (is_cyan)
421               return CYAN;
422             return *pc;
423           }
get_weight() const424         const weight& get_weight() const
425           {
426             assert(is_cyan);
427             return *w;
428           }
set_color(color c)429         void set_color(color c)
430           {
431             assert(!is_white());
432             if (is_cyan)
433               {
434                 assert(c != CYAN);
435                 std::pair<hash_type::iterator, bool> p;
436                 p = ph->emplace(ps, std::make_pair(c, *acc));
437                 assert(p.second);
438                 acc = &(p.first->second.second);
439                 int i = phc->erase(ps);
440                 assert(i == 1);
441                 (void)i;
442               }
443             else
444               {
445                 SPOT_ASSUME(pc);
446                 *pc=c;
447               }
448           }
get_acc() const449         acc_cond::mark_t get_acc() const
450           {
451             assert(!is_white());
452             SPOT_ASSUME(acc);
453             return *acc;
454           }
cumulate_acc(acc_cond::mark_t a)455         void cumulate_acc(acc_cond::mark_t a)
456           {
457             assert(!is_white());
458             SPOT_ASSUME(acc);
459             *acc |= a;
460           }
is_white() const461         bool is_white() const
462           {
463             return !is_cyan && !pc;
464           }
465       private:
466         bool is_cyan;
467         const weight* w; // point to the weight of a state in hcyan
468         hash_type* ph; //point to the main hash table
469         hcyan_type* phc; // point to the hash table hcyan
470         const state* ps; // point to the state in hcyan
471         color *pc; // point to the color of a state stored in main hash table
472         acc_cond::mark_t* acc; // point to the acc set of a state stored
473                                 // in main hash table  or hcyan
474       };
475 
explicit_tau03_opt_search_heap(size_t)476       explicit_tau03_opt_search_heap(size_t)
477         {
478         }
479 
~explicit_tau03_opt_search_heap()480       ~explicit_tau03_opt_search_heap()
481         {
482           hcyan_type::const_iterator sc = hc.begin();
483           while (sc != hc.end())
484             {
485               const state* ptr = sc->first;
486               ++sc;
487               ptr->destroy();
488             }
489           hash_type::const_iterator s = h.begin();
490           while (s != h.end())
491             {
492               const state* ptr = s->first;
493               ++s;
494               ptr->destroy();
495             }
496         }
497 
get_color_ref(const state * & s)498       color_ref get_color_ref(const state*& s)
499         {
500           hcyan_type::iterator ic = hc.find(s);
501           if (ic == hc.end())
502             {
503               hash_type::iterator it = h.find(s);
504               if (it == h.end())
505                 // white state
506                 return color_ref(nullptr, nullptr);
507               if (s != it->first)
508                 {
509                   s->destroy();
510                   s = it->first;
511                 }
512               // blue or red state
513               return color_ref(&it->second.first, &it->second.second);
514             }
515           if (s != ic->first)
516             {
517               s->destroy();
518               s = ic->first;
519             }
520           // cyan state
521           return color_ref(&h, &hc, ic->first,
522                            &ic->second.first, &ic->second.second);
523         }
524 
add_new_state(const state * s,color c,const weight & w)525       void add_new_state(const state* s, color c, const weight& w)
526         {
527           assert(hc.find(s) == hc.end() && h.find(s) == h.end());
528           assert(c == CYAN);
529           (void)c;
530           hc.emplace(std::piecewise_construct,
531                      std::forward_as_tuple(s),
532                      std::forward_as_tuple(w, acc_cond::mark_t({})));
533         }
534 
pop_notify(const state *) const535       void pop_notify(const state*) const
536         {
537         }
538 
has_been_visited(const state * s) const539       bool has_been_visited(const state* s) const
540         {
541           hcyan_type::const_iterator ic = hc.find(s);
542           if (ic == hc.end())
543             {
544               hash_type::const_iterator it = h.find(s);
545               return (it != h.end());
546             }
547           return true;
548         }
549 
550       enum { Has_Size = 1 };
size() const551       int size() const
552         {
553           return h.size() + hc.size();
554         }
555 
556     private:
557 
558       // associate to each blue and red state its color and its acceptance set
559       hash_type h;
560       // associate to each cyan state its weight and its acceptance set
561       hcyan_type hc;
562     };
563 
564   } // anonymous
565 
566   emptiness_check_ptr
explicit_tau03_opt_search(const const_twa_ptr & a,option_map o)567   explicit_tau03_opt_search(const const_twa_ptr& a, option_map o)
568   {
569     return SPOT_make_shared_enabled__
570       (tau03_opt_search<explicit_tau03_opt_search_heap>, a, 0, o);
571   }
572 
573 }
574