Awali
Another Weighted Automata library
proper.hh
Go to the documentation of this file.
1 // This file is part of Awali.
2 // Copyright 2016-2022 Sylvain Lombardy, Victor Marsault, Jacques Sakarovitch
3 //
4 // Awali is a free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef AWALI_ALGOS_PROPER_HH
18 # define AWALI_ALGOS_PROPER_HH
19 
20 # include <stdexcept>
21 # include <type_traits>
22 # include <unordered_map>
23 # include <unordered_set>
24 # include <utility>
25 # include <vector>
26 
27 #include <awali/sttc/algos/copy.hh>
28 #include <awali/sttc/algos/dot.hh>
33 #include <awali/sttc/core/kind.hh>
35 #include <awali/common/enums.hh>
36 #include <awali/utils/heap.hh>
37 #include <awali/common/ato.cc>
38 
39 #define STATS
40 
41 namespace awali { namespace sttc {
42 
43 
44  namespace internal
45  {
47  static inline
49  {
50  if (auto cp = getenv("AWALI_DEBUG")) {
51  return *cp ? awali::strict_atoi(cp) : 1;
52  }
53  else
54  return 0;
55  }
56 
61  template <typename Aut,
62  bool has_one = labelset_t_of<Aut>::has_one()>
63  class properer
64  {
65  using automaton_t = typename std::remove_cv<Aut>::type;
66  using weightset_t = weightset_t_of<automaton_t>;
67  using weight_t = typename weightset_t::value_t;
68  using label_t = label_t_of<automaton_t>;
69  using transitions_t = std::vector<transition_t>;
70 
71  public:
75  properer(automaton_t& aut, bool prune = true)
76  : debug_(debug_level())
77  , aut_(aut)
78  , ws_(*aut->weightset())
79  , empty_word_(aut->labelset()->one())
80  , todo_(aut->num_states())
81  , prune_(prune)
82  {}
83 
95  static void proper_here(automaton_t& aut, bool prune = true)
96  {
97  if (!is_proper(aut))
98  proper_here_<weightset_t::star_status()>(aut, prune);
99  }
100 
132  static bool in_situ_remover(automaton_t& aut, bool prune = true)
133  {
134  try
135  {
136  properer p(aut, prune);
137  p.in_situ_remover_();
138  return true;
139  }
140  catch (const std::runtime_error&)
141  {
142  return false;
143  }
144  }
145 
146  private:
154  using state_profile = std::tuple<size_t,size_t,size_t>;
155 
158  void build_heap_()
159  {
160  for (auto s: aut_->states())
161  // We don't care about states without incoming spontaneous
162  // transitions.
163  if (auto in_sp = aut_->in(s, empty_word_).size())
164  {
165  auto out_sp = aut_->out(s, empty_word_).size();
166  auto out = aut_->all_out(s).size();
167  auto t = todo_.emplace(s, state_profile
168  {out_sp, out, in_sp});
169  tickets_.emplace(s, t);
170  }
171  todo_.heapify();
172  }
173 
174  void update_heap_(state_t s)
175  {
176  if (3 < debug_)
177  {
178  std::cerr << "update heap (" << s << ") ";
179  }
180  auto t = tickets_.find(s);
181  if (t != tickets_.end())
182  todo_.update(t->second,
183  state_profile{aut_->out(s, empty_word_).size(),
184  aut_->all_out(s).size(),
185  aut_->in(s, empty_word_).size()});
186  }
187 
188 #ifdef STATS
189  unsigned added_ = 0;
190  unsigned removed_ = 0;
191 #endif
192 
204  void in_situ_remover_(state_t s)
205  {
206  const auto& tr = aut_->in(s, empty_word_);
207  // Iterate on a copy, as we remove these transitions in the
208  // loop.
209  transitions_t transitions{tr.begin(), tr.end()};
210  // The star of the weight of the loop on 's' (1 if no loop).
211  weight_t star = ws_.one();
212  using state_weight_t = std::pair<state_t, weight_t>;
213  std::vector<state_weight_t> closure;
214  for (auto t : transitions)
215  {
216  weight_t weight = aut_->weight_of(t);
217  state_t src = aut_->src_of(t);
218  if (src == s) //loop
219  star = ws_.star(weight);
220  else
221  closure.emplace_back(src, weight);
222  // Delete incoming epsilon transitions.
223  aut_->del_transition(t);
224  }
225 
226  /*
227  For each transition (t : s -- label|weight --> dst),
228  for each former
229  epsilon transition closure->first -- e|closure->second --> s
230  a transition
231  (closure->first -- label | closure->second*weight --> dst)
232  is added to the automaton (add, not set !!)
233 
234  If (s) is final with weight (weight),
235  for each former
236  epsilon transition closure->first -- e|closure->second --> s
237  pair-second * weight is added to the final weight
238  of closure->first
239  */
240  for (auto t: aut_->all_out(s))
241  {
242  // "Blowing": For each transition (or terminal arrow)
243  // outgoing from (s), the weight is multiplied by
244  // (star).
245  weight_t blow = ws_.mul(star, aut_->weight_of(t));
246  aut_->set_weight(t, blow);
247 
248  label_t label = aut_->label_of(t);
249  state_t dst = aut_->dst_of(t);
250  for (auto pair: closure)
251  {
252  state_t src = pair.first;
253  weight_t w = ws_.mul(pair.second, blow);
254  aut_->add_transition(src, dst, label, w);
255  }
256  }
257 #ifdef STATS
258  unsigned added = aut_->all_out(s).size() * closure.size();
259  unsigned removed = transitions.size();
260 #endif
261  if (prune_ && aut_->all_in(s).empty())
262  {
263 #ifdef STATS
264  removed += aut_->all_out(s).size();
265 #endif
266  aut_->del_state(s);
267  }
268 #ifdef STATS
269  added_ += added;
270  removed_ += removed;
271  if (1 < debug_)
272  std::cerr << " -" << removed << "+" << added
273  << " (-" << removed_ << "+" << added_ << ")";
274 #endif
275  }
276 
289  void in_situ_remover_()
290  {
291  build_heap_();
292  /* For each state (s), for each incoming epsilon-transitions
293  (t), if (t) is a loop, the star of its weight is computed,
294  otherwise, (t) is stored into the closure list. Then (t)
295  is removed. */
296 
297  // The neighbors of s: their profiles need to be updated after
298  // s was processed.
299  std::unordered_set<state_t> neighbors;
300  while (!todo_.empty())
301  {
302  auto s = todo_.pop();
303  tickets_.erase(s);
304  neighbors.clear();
305  for (auto t: aut_->in(s))
306  {
307  state_t n = aut_->src_of(t);
308  if (n != s)
309  neighbors.emplace(n);
310  }
311  for (auto t: aut_->out(s))
312  {
313  state_t n = aut_->dst_of(t);
314  if (n != s)
315  neighbors.emplace(n);
316  }
317 
318  in_situ_remover_(s);
319 
320  // Update all neighbors and then the heap.
321  for (auto n: neighbors)
322  update_heap_(n);
323  }
324  }
325 
327  template <star_status_t Status>
328  static
329  typename std::enable_if<Status == star_status_t::TOPS>::type
330  proper_here_(automaton_t& aut, bool = true)
331  {
332  if (!in_situ_remover(aut))
333  raise("invalid automaton");
334  }
335 
338  template <star_status_t Status>
339  static
340  typename std::enable_if<Status == star_status_t::ABSVAL>::type
341  proper_here_(automaton_t& aut, bool prune = true)
342  {
343  require(is_valid(aut), "invalid automaton");
344  in_situ_remover(aut, prune);
345  }
346 
348  template <star_status_t Status>
349  static
350  typename std::enable_if<Status == star_status_t::STARRABLE>::type
351  proper_here_(automaton_t& aut, bool prune = true)
352  {
353  in_situ_remover(aut, prune);
354  }
355 
360  template <star_status_t Status>
361  static
362  typename std::enable_if<Status == star_status_t::NON_STARRABLE>::type
363  proper_here_(automaton_t& aut, bool prune = true)
364  {
365  require(is_valid(aut), "invalid automaton");
366  in_situ_remover(aut, prune);
367  }
368 
369  private:
371  int debug_;
373  automaton_t& aut_;
375  const weightset_t& ws_;
377  label_t empty_word_;
378 
380  using heap_t=utils::min_heap<state_t,state_profile>;
381  heap_t todo_;
383  std::unordered_map<state_t, typename heap_t::claim_ticket_type> tickets_;
384 
386  bool prune_;
387  };
388 
389 
390  /*----------------------------------------------.
391  | Specialization when there is no 'one' label. |
392  `----------------------------------------------*/
393 
394  template <typename Aut>
395  class properer<Aut, false>
396  {
397  using automaton_t = typename std::remove_cv<Aut>::type;
398  public:
399  static
400 #ifndef __clang__
401  constexpr
402 #endif
403  void proper_here(automaton_t&, bool = true)
404  {}
405  };
406 
407  }
408 
409 
410  /*---------.
411  | proper. |
412  `---------*/
413 
416  template <typename Aut>
417  inline
418  bool in_situ_remover(Aut& aut, bool prune = true)
419  {
420  return internal::properer<Aut>::in_situ_remover(aut, prune);
421  }
422 
425  template <typename Aut>
426  inline
427  void proper_here(Aut& aut, direction_t dir = BACKWARD, bool prune = true)
428  {
429  switch(dir) {
430  case BACKWARD:
432  break;
433  case FORWARD:
434  transpose_here(aut);
436  transpose_here(aut);
437  }
438  }
439 
442  template <typename Aut>
443  auto
444  proper(const Aut& aut, direction_t dir = BACKWARD,
445  bool prune = true, bool keep_history = true)
446  -> decltype(copy(aut))
447  {
448  auto res = aut;
449  switch (dir)
450  {
452  res = copy(aut,keep_history);
453  proper_here(res, dir, prune);
454  return res;
456  res = transpose(aut,keep_history);
457  proper_here(res, direction_t::BACKWARD, prune);
458  transpose_here(res);
459  return res;
460  default:
461  raise("invalid direction parameter");
462  }
463  }
464 
465 }}//end of ns awali::stc
466 
467 #endif // !AWALI_ALGOS_PROPER_HH
static constexpr void proper_here(automaton_t &, bool=true)
Definition: proper.hh:403
This class contains the core of the proper algorithm.
Definition: proper.hh:64
properer(automaton_t &aut, bool prune=true)
Get ready to eliminate spontaneous transitions.
Definition: proper.hh:75
static void proper_here(automaton_t &aut, bool prune=true)
Remove the epsilon-transitions of the input The behaviour of this method depends on the star_status o...
Definition: proper.hh:95
static bool in_situ_remover(automaton_t &aut, bool prune=true)
The core of the (backward) epsilon-removal.
Definition: proper.hh:132
direction_t
Used in some algorithms in which one may considers transitions forward or backwards.
Definition: enums.hh:35
@ FORWARD
Definition: enums.hh:36
@ BACKWARD
Definition: enums.hh:37
weightset_description weightset(const std::string &k)
std::vector< transition_t > transitions(abstract_automaton_t const *aut, bool all)
static int debug_level()
Debug level set in the user's environment.
Definition: proper.hh:48
unary< type_t::star, Label, Weight > star
Definition: fwd.hh:129
typename internal::label_t_of_impl< internal::base_t< ValueSet > >::type label_t_of
Helper to retrieve the type of the labels of a value set.
Definition: traits.hh:71
auto proper(const Aut &aut, direction_t dir=BACKWARD, bool prune=true, bool keep_history=true) -> decltype(copy(aut))
Eliminate spontaneous transitions.
Definition: proper.hh:444
typename internal::labelset_t_of_impl< internal::base_t< ValueSet > >::type labelset_t_of
Helper to retrieve the type of the labelset of a value set.
Definition: traits.hh:76
bool is_proper(const Aut &aut) ATTRIBUTE_CONST
Test whether an automaton is proper.
Definition: is_proper.hh:94
AutOut transpose(Aut &aut, bool keep_history=true)
Definition: transpose.hh:79
AutOut copy(const AutIn &input, Pred keep_state, bool keep_history=true, bool transpose=false)
A copy of input keeping only its states that are accepted by keep_state.
Definition: copy.hh:189
bool in_situ_remover(Aut &aut, bool prune=true)
Blindly eliminate epsilon transitions without checking for the validity of the automaton.
Definition: proper.hh:418
void transpose_here(Aut &aut)
Definition: transpose.hh:53
bool is_valid(const Aut &aut)
Tests if aut is valid.
Definition: is_valid.hh:162
void proper_here(Aut &aut, direction_t dir=BACKWARD, bool prune=true)
Eliminate spontaneous transitions in place.
Definition: proper.hh:427
void require(bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:55
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: synchronizing_word.hh:266
typename internal::weightset_t_of_impl< internal::base_t< ValueSet > >::type weightset_t_of
Helper to retrieve the type of the weightset of a value set.
Definition: traits.hh:86
Main namespace of Awali.
Definition: ato.hh:22
int strict_atoi(const std::string &s)
unsigned state_t
Definition: types.hh:21
value_type pop()
Definition: heap.hh:58
void update(claim_ticket_type t, const priority_type &p)
Definition: heap.hh:79
claim_ticket_type emplace(const value_type &val, const priority_type &p)
Definition: heap.hh:46
void heapify()
Definition: heap.hh:72
size_t empty() const
Definition: heap.hh:42