Vcsn  2.0
Be Rational
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
synchronizing-word.hh
Go to the documentation of this file.
1 #ifndef VCSN_ALGOS_SYNCHRONIZING_WORD_HH
2 # define VCSN_ALGOS_SYNCHRONIZING_WORD_HH
3 
4 # include <algorithm>
5 # include <iostream>
6 # include <limits>
7 # include <map>
8 # include <queue>
9 # include <set>
10 # include <unordered_map>
11 # include <unordered_set>
12 # include <utility>
13 # include <vector>
14 
15 # include <boost/algorithm/string.hpp>
16 
17 # include <vcsn/algos/distance.hh>
18 # include <vcsn/algos/pair.hh>
19 # include <vcsn/ctx/context.hh>
20 # include <vcsn/ctx/traits.hh>
21 # include <vcsn/dyn/automaton.hh>
22 # include <vcsn/dyn/context.hh>
23 # include <vcsn/dyn/label.hh>
24 # include <vcsn/misc/map.hh>
25 # include <vcsn/misc/pair.hh>
26 # include <vcsn/misc/raise.hh>
27 
28 namespace vcsn
29 {
30 
31  /*--------------------------------------.
32  | is_synchronized_by(automaton, word). |
33  `--------------------------------------*/
34 
35  template <typename Aut>
36  bool
37  is_synchronized_by(const Aut& aut,
38  const typename labelset_t_of<Aut>::word_t& w)
39  {
40  using automaton_t = Aut;
41  using state_t = state_t_of<automaton_t>;
42 
43  std::unordered_set<state_t> todo;
44 
45  for (auto s : aut->states())
46  todo.insert(s);
47 
48  for (auto l : aut->labelset()->letters_of(w))
49  {
50  std::unordered_set<state_t> new_todo;
51  for (auto s : todo)
52  {
53  auto ntf = aut->out(s, l);
54  auto size = ntf.size();
55  require(0 < size, "automaton must be complete");
56  require(size < 2, "automaton must be deterministic");
57  new_todo.insert(aut->dst_of(*ntf.begin()));
58  }
59  todo = std::move(new_todo);
60  }
61 
62  return todo.size() == 1;
63  }
64 
65  namespace dyn
66  {
67  namespace detail
68  {
70  template <typename Aut, typename LabelSet>
71  bool
72  is_synchronized_by(const automaton& aut, const label& word)
73  {
74  const auto& a = aut->as<Aut>();
75  const auto& w = word->as<LabelSet>();
76  return vcsn::is_synchronized_by(a, w.label());
77  }
78 
80  (const automaton&, const label&) -> bool);
81  }
82  }
83 
84 
85  /*----------------.
86  | synchronizer. |
87  `----------------*/
88 
89  namespace detail
90  {
91  template <typename Aut>
93  {
94  public:
95  using automaton_t = Aut;
100 
101  private:
102  using pair_t = std::pair<state_t, state_t>;
103  using dist_transition_t = std::pair<unsigned, transition_t>;
104 
107  std::unordered_map<state_t, dist_transition_t> paths_;
108  std::unordered_set<state_t> todo_;
110 
111  public:
113  : aut_(aut)
114  {}
115 
116  private:
117  void init_pair(bool keep_initials = false)
118  {
119  pair_ = pair(aut_, keep_initials);
120  paths_ = paths_ibfs(pair_, pair_->singletons());
121 
122  if (keep_initials)
123  for (auto it = paths_.begin(); it != paths_.end(); /* nothing */)
124  {
125  if (pair_->is_singleton(it->first))
126  paths_.erase(it++);
127  else
128  ++it;
129  }
130  }
131 
132  void init_synchro(bool keep_initials = false)
133  {
134  init_pair(keep_initials);
135  require(pair_->states().size()
136  == paths_.size() + pair_->singletons().size(),
137  "automaton is not synchronizing");
138 
139  for (auto s : pair_->states())
140  if (!pair_->is_singleton(s))
141  todo_.insert(s);
142  }
143 
144  std::vector<transition_t> recompose_path(state_t from)
145  {
146  std::vector<transition_t> res;
147  state_t bt_curr = from;
148  while (!pair_->is_singleton(bt_curr))
149  {
150  transition_t t = paths_.find(bt_curr)->second.second;
151  res.push_back(t);
152  bt_curr = pair_->dst_of(t);
153  }
154  return res;
155  }
156 
157  int dist(state_t s)
158  {
159  if (pair_->is_singleton(s))
160  return 0;
161  return paths_.find(s)->second.first;
162  }
163 
165  {
166  auto ntf = pair_->out(s, l);
167  auto size = ntf.size();
168  require(0 < size, "automaton must be complete");
169  require(size < 2, "automaton must be deterministic");
170  return pair_->dst_of(*ntf.begin());
171  }
172 
173  void apply_label(const label_t& label)
174  {
175  res_ = aut_->labelset()->concat(res_, label);
176  std::unordered_set<state_t> new_todo;
177  for (auto s : todo_)
178  {
179  auto new_state = dest_state(s, label);
180  if (!pair_->is_singleton(new_state))
181  new_todo.insert(new_state);
182  }
183  todo_ = std::move(new_todo);
184  }
185 
186  // "Apply" a word to the set of active states (for each state, for each
187  // label, perform s = d(s))
188  void apply_path(const std::vector<transition_t>& path)
189  {
190  for (auto t : path)
191  apply_label(pair_->label_of(t));
192  }
193 
194  public:
195 
196  // We just perform an inverse BFS from q0 and put all the accessible
197  // states in 'paths'. If the size of paths is the same than the number
198  // of states of pa (minus q0), then for each pair of states (p, q),
199  // there is a word w such that d(p, w) = d(q, w), thus the automaton is
200  // synchronizing.
202  {
203  init_pair();
204  return paths_.size() == pair_->states().size() - 1;
205  }
206 
208  {
209  return synchro(&synchronizer::dist);
210  }
211 
213  {
214  return cycle_();
215  }
216 
218  {
219  return synchro(&synchronizer::phi_1);
220  }
221 
223  {
224  return synchro(&synchronizer::phi_2);
225  }
226 
228  {
229  return fastsynchro_();
230  }
231 
232  private:
233  using heuristic_t = auto (synchronizer::*)(state_t) -> int;
234 
236  {
237  init_synchro();
238  while (!todo_.empty())
239  {
240  int min = std::numeric_limits<int>::max();
241  state_t s_min = 0;
242  for (auto s : todo_)
243  {
244  int d = (this->*(heuristic))(s);
245  if (d < min)
246  {
247  min = d;
248  s_min = s;
249  }
250  }
251 
252  apply_path(recompose_path(s_min));
253  }
254  return res_;
255  }
256 
258  {
259  init_synchro(true);
260  bool first = true;
261  state_t previous = 0;
262  while (!todo_.empty())
263  {
264  int min = std::numeric_limits<int>::max();
265  state_t s_min = 0;
266  for (auto s : todo_)
267  {
268  pair_t o = pair_->get_origin(s);
269  if (!first && o.first != previous && o.second != previous)
270  continue;
271  int d = dist(s);
272  if (d < min)
273  {
274  min = d;
275  s_min = s;
276  }
277  }
278 
279  const auto& path = recompose_path(s_min);
280  pair_t pair_end = pair_->get_origin(
281  pair_->dst_of(path[path.size() - 1]));
282  assert(pair_end.first == pair_end.second);
283  previous = pair_end.first;
284  first = false;
285  apply_path(path);
286  }
287  return res_;
288  }
289 
291  {
292  init_synchro();
293 
294  // The drawback of this algorithm is that it does not guarantee us to
295  // converge, so we this to counter prevent potential infinite loops.
296  unsigned count = 0;
297  while (!todo_.empty())
298  {
299  // compute lmin = arg min { phi_3(l) } forall l in labelset
300  label_t lmin;
301  int min = std::numeric_limits<int>::max();
302  for (const auto& l : pair_->labelset()->genset())
303  {
304  int cur_min = phi_3(l);
305  if (cur_min < min)
306  {
307  min = cur_min;
308  lmin = l;
309  }
310  }
311 
312  unsigned sq_bound = aut_->states().size();
313  if (min < 0 && count++ < (sq_bound * sq_bound))
314  apply_label(lmin);
315  else
316  {
317  // fallback on the phi_2 heuristic, with a size restriction.
318  int count = 0;
319  size_t t = todo_.size();
320  int bound = std::min(aut_->states().size(), (t * t - t / 2));
321  int min = std::numeric_limits<int>::max();
322  state_t s_min = 0;
323  for (auto s : todo_)
324  {
325  if (count++ >= bound)
326  break;
327  int d = phi_2(s);
328  if (d < min)
329  {
330  min = d;
331  s_min = s;
332  }
333  }
334  apply_path(recompose_path(s_min));
335  }
336  }
337  return res_;
338  }
339 
341  int delta(state_t p, const std::vector<transition_t>& w)
342  {
343  state_t np = p;
344  for (auto t : w)
345  np = dest_state(np, pair_->label_of(t));
346  return dist(np) - dist(p);
347  }
348 
352  int phi_1(state_t p)
353  {
354  int res = 0;
355  auto w = recompose_path(p);
356  for (auto s: todo_)
357  if (s != p)
358  res += delta(s, w);
359  return res;
360  }
361 
364  int phi_2(state_t p)
365  {
366  return phi_1(p) + dist(p);
367  }
368 
371  int phi_3(const label_t& l)
372  {
373  int res = 0;
374  for (auto s: todo_)
375  res += dist(dest_state(s, l)) - dist(s);
376  return res;
377  }
378  };
379  }
380 
381 
382  /*-----------------------------.
383  | is_synchronizing(automaton). |
384  `-----------------------------*/
385 
386  template <typename Aut>
387  bool is_synchronizing(const Aut& aut)
388  {
390  return sync.is_synchronizing();
391  }
392 
393  namespace dyn
394  {
395  namespace detail
396  {
398  template <typename Aut>
399  bool is_synchronizing(const automaton& aut)
400  {
401  const auto& a = aut->as<Aut>();
402  return vcsn::is_synchronizing(a);
403  }
404 
405  REGISTER_DECLARE(is_synchronizing, (const automaton&) -> bool);
406  }
407  }
408 
409 
410  /*-------------------------------.
411  | synchronizing_word(automaton). |
412  `-------------------------------*/
413 
414  template <typename Aut>
415  typename labelset_t_of<Aut>::word_t
416  synchronizing_word(const Aut& aut, const std::string& algo = "greedy")
417  {
419  if (boost::iequals(algo, "greedy") || boost::iequals(algo, "eppstein"))
420  return sync.greedy();
421  else if (boost::iequals(algo, "cycle"))
422  return sync.cycle();
423  else if (boost::iequals(algo, "synchrop"))
424  return sync.synchroP();
425  else if (boost::iequals(algo, "synchropl"))
426  return sync.synchroPL();
427  else if (boost::iequals(algo, "fastsynchro"))
428  return sync.fastsynchro();
429  else
430  raise("synchronizing_word: invalid algorithm: ", str_escape(algo));
431  }
432 
433  namespace dyn
434  {
435  namespace detail
436  {
438  template <typename Aut, typename String>
439  label
440  synchronizing_word(const automaton& aut, const std::string& algo)
441  {
442  const auto& a = aut->as<Aut>();
443  auto word = vcsn::synchronizing_word(a, algo);
444  return make_label(make_wordset(*a->labelset()), word);
445  }
446 
448  (const automaton&, const std::string&) -> label);
449  }
450  }
451 
452 }
453 
454 #endif // !VCSN_ALGOS_SYNCHRONIZING_WORD_HH
labelset_t_of< Aut >::word_t synchronizing_word(const Aut &aut, const std::string &algo="greedy")
REGISTER_DECLARE(accessible,(const automaton &) -> automaton)
std::shared_ptr< detail::automaton_base > automaton
Definition: automaton.hh:71
std::unordered_set< state_t > todo_
label synchronizing_word(const automaton &aut, const std::string &algo)
Bridge.
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:34
std::unordered_map< state_t, dist_transition_t > paths_
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:214
std::pair< state_t, state_t > pair_t
int phi_2(state_t p)
Heuristic used for SynchroPL.
std::ostream & str_escape(std::ostream &os, const std::string &str)
Output a string, escaping special characters.
Definition: escape.cc:43
Paths in filesystems, i.e., file names.
Definition: path.hh:22
synchronizer(const automaton_t &aut)
state_t_of< automaton_t > state_t
bool is_synchronizing(const automaton &aut)
Bridge.
void init_synchro(bool keep_initials=false)
bool is_synchronized_by(const automaton &aut, const label &word)
Bridge.
state_t dest_state(state_t s, const label_t &l)
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:33
law_t< LabelSet > make_wordset(const LabelSet &ls)
The wordset of a labelset.
Definition: labelset.hh:72
int phi_1(state_t p)
Heuristic used for SynchroP.
typename detail::transition_t_of_impl< base_t< ValueSet >>::type transition_t_of
Definition: traits.hh:36
transition_t_of< automaton_t > transition_t
void apply_label(const label_t &label)
int delta(state_t p, const std::vector< transition_t > &w)
Compute dist(d(s, w)) - dist(s).
label_t_of< automaton_t > label_t
word_t synchro(heuristic_t heuristic)
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:46
auto(synchronizer::*)(state_t) -> int heuristic_t
int phi_3(const label_t &l)
Heuristic used for FastSynchro.
std::vector< transition_t > recompose_path(state_t from)
std::shared_ptr< detail::pair_automaton_impl< Aut >> pair_automaton
Definition: pair.hh:207
std::pair< unsigned, transition_t > dist_transition_t
bool is_synchronized_by(const Aut &aut, const typename labelset_t_of< Aut >::word_t &w)
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:35
typename labelset_t_of< automaton_t >::word_t word_t
void apply_path(const std::vector< transition_t > &path)
label make_label(const LabelSet &ls, const typename LabelSet::value_t &l)
Definition: label.hh:82
std::unordered_map< state_t_of< Aut >, std::pair< unsigned, transition_t_of< Aut > > > paths_ibfs(const Aut &aut, std::vector< state_t_of< Aut >> start)
Definition: distance.hh:74
bool is_synchronizing(const Aut &aut)
pair_automaton< Aut > pair_
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:39
void init_pair(bool keep_initials=false)