Vcsn  2.0
Be Rational
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
minimize-signature.hh
Go to the documentation of this file.
1 #ifndef VCSN_ALGOS_MINIMIZE_SIGNATURE_HH
2 # define VCSN_ALGOS_MINIMIZE_SIGNATURE_HH
3 
4 # include <unordered_map>
5 # include <unordered_set>
6 
7 # include <vcsn/algos/accessible.hh>
8 # include <vcsn/algos/quotient.hh>
9 # include <vcsn/dyn/automaton.hh>
11 # include <vcsn/misc/indent.hh>
12 # include <vcsn/misc/map.hh> // vcsn::less
13 # include <vcsn/misc/raise.hh>
14 # include <vcsn/weightset/fwd.hh> // b
15 
16 namespace vcsn
17 {
18 
19  /*---------------------------------------------------------.
20  | minimization with Moore's algorithm: signature variant. |
21  `---------------------------------------------------------*/
22  namespace detail_signature
23  {
24  template <typename Aut>
25  class minimizer
26  {
27  static_assert(std::is_same<weightset_t_of<Aut>, b>::value,
28  "minimize: signature: requires Boolean weights");
29 
30  using automaton_t = Aut;
31 
33  const automaton_t &a_;
34 
36  const labelset_t& ls_;
37 
40  using class_t = unsigned;
41  using set_t = std::vector<state_t>;
42  using state_to_class_t = std::unordered_map<state_t, class_t>;
43  using class_to_set_t = std::vector<set_t>;
44 
45  constexpr static const char* me() { return "minimize-signature"; }
46 
48  constexpr static class_t class_invalid = -1;
49  unsigned num_classes_ = 0;
50 
53 
54  public:
55  static std::ostream& print_(const set_t& ss, std::ostream& o)
56  {
57  o << '{';
58  const char* sep = "";
59  for (auto s : ss)
60  {
61  o << sep << s;
62  sep = ", ";
63  }
64  return o << "}";
65  }
66 
67  public:
68  static std::ostream& print_(const class_to_set_t& c2ss, std::ostream& o)
69  {
70  const char* sep = "";
71  for (unsigned i = 0; i < c2ss.size(); ++i)
72  {
73  o << sep << '[' << i << "] = ";
74  print_(c2ss[i], o);
75  sep = "\n";
76  }
77  return o;
78  }
79 
80  // For a given state, destination states for a specific label.
82  {
83  // For some unstored state.
85  std::vector<state_t> to_states; // Ordered.
86 
87  friend
88  std::ostream&
89  operator<<(std::ostream& o, const state_output_for_label_t& out)
90  {
91  o << "out{" << out.label << " => ";
92  const char* sep = "";
93  for (auto s: out.to_states)
94  {
95  o << sep << s;
96  sep = ", ";
97  }
98  return o << "}";
99  }
100  };
101 
102  // This is sorted by label.
103  using state_output_t = std::vector<state_output_for_label_t>;
104 
105  public:
106  static std::ostream& print_(const state_output_t& outs, std::ostream& o)
107  {
108  bool first = true;
109  o << '{';
110  for (const auto& out: outs)
111  {
112  if (!first)
113  o << ", ";
114  o << out;
115  first = false;
116  }
117  return o << '}';
118  }
119 
120  // This structure is only useful at initialization time, when
121  // sorting transitions from a given state in a canonical order.
122  using label_to_states_t
123  = std::map<label_t, std::vector<state_t>, vcsn::less<labelset_t>>;
124 
125  std::unordered_map<state_t, state_output_t> state_to_state_output_;
126 
127  friend class signature_hasher;
128  class signature_hasher : public std::hash<state_output_t*>
129  {
131  unsigned num_classes_;
132  public:
133  signature_hasher(minimizer& the_minimizer,
134  size_t num_classes)
135  : state_to_class_(the_minimizer.state_to_class_)
136  , num_classes_(num_classes)
137  {}
138 
139  size_t operator()(const state_output_t* state_output_) const noexcept
140  {
141  const state_output_t& state_output = *state_output_;
142  size_t res = 0;
144  for (auto& t : state_output)
145  {
146  const label_t& label = t.label;
147  std::hash_combine(res, label);
148  // Hash the set of classes reached with label. Of
149  // course the hash must not depend on class ordering.
150  bits.reset();
151  for (auto s : t.to_states)
152  bits.set(state_to_class_.at(s));
153  std::hash_combine(res, bits);
154  }
155 #if DEBUG
156  print_(state_output, std::cerr) << " = " << res << std::endl;
157 #endif
158  return res;
159  }
160  }; // class signature_hasher
161 
162  friend class signature_equal_to;
163  class signature_equal_to : public std::equal_to<state_output_t*>
164  {
166  const labelset_t& ls_;
168  const size_t class_bound_;
169  public:
171  // FIXME: remove these unless really needed
172  const labelset_t& ls,
173  const state_to_class_t& state_to_class,
174  size_t class_bound)
175  : minimizer_(the_minimizer)
176  , ls_(ls)
177  , state_to_class_(state_to_class)
178  , class_bound_(class_bound)
179  {}
180 
181  bool operator()(const state_output_t *as_,
182  const state_output_t *bs_) const noexcept
183  {
184  const state_output_t& as = *as_;
185  const state_output_t& bs = *bs_;
186 #if DEBUG
187  print_(as, std::cerr) << " =? ";
188  print_(bs, std::cerr) << " = ";
189 #endif
190  if (as.size() != bs.size())
191  {
192 #if DEBUG
193  std::cerr << "false 1" << std::endl;
194 #endif
195  return false;
196  }
197 
198  auto b_i = bs.cbegin();
199  dynamic_bitset a_bits(class_bound_), b_bits(class_bound_);
200  for (const auto& a : as)
201  {
202  const label_t& a_label = a.label;
203  const label_t& b_label = b_i->label;
204  const std::vector<state_t>& a_states = a.to_states;
205  const std::vector<state_t>& b_states = b_i->to_states;
206 
207  if (! ls_.equals(a_label, b_label))
208  {
209 #if DEBUG
210  std::cerr << "false 2" << std::endl;
211 #endif
212  return false;
213  }
214 
215  // a_states and b_states may have different sizes, but
216  // still be considered "equal", up to the state-to-class
217  // assignment.
218 
219  // FIXME: this can and should be optimized a lot.
220  //dynamic_bitset a_bits(class_bound_), b_bits(class_bound_);
221  a_bits.reset(); b_bits.reset();
222  for (auto s : a_states)
223  a_bits.set(state_to_class_.at(s));
224  for (auto s : b_states)
225  b_bits.set(state_to_class_.at(s));
226  if (a_bits != b_bits)
227  {
228 #if DEBUG
229  std::cerr << "false 3" << std::endl;
230 #endif
231  return false;
232  }
233 
234  ++b_i;
235  }
236 
237  {
238 #if DEBUG
239  std::cerr << "true" << std::endl;
240 #endif
241  return true;
242  }
243  }
244  }; // class signature_equal_to
245 
246  friend class signature_multimap;
248  : public std::unordered_map<state_output_t*, set_t,
249  signature_hasher, signature_equal_to>
250  {
253  using super_t
254  = std::unordered_map<state_output_t*, set_t,
256  public:
258  // FIXME: remove these unless really needed.
259  const labelset_t& ls,
260  state_to_class_t& state_to_class,
261  const size_t class_bound)
262  : super_t(1,
263  signature_hasher(the_minimizer, class_bound),
264  signature_equal_to(the_minimizer,
265  ls, state_to_class, class_bound))
266  , minimizer_(the_minimizer)
267  , state_to_class_(state_to_class)
268  {}
269 
270  friend std::ostream& operator<<(std::ostream& o,
271  const signature_multimap& mm)
272  {
273  o << '{' << incendl;
274  for (const auto& o_s : mm)
275  {
276  print_(*o_s.first, o);
277  o << " : {";
278  const char* sep = "";
279  for (auto s: o_s.second)
280  {
281  o << sep << s << '%' << mm.state_to_class_.at(s);
282  sep = ", ";
283  }
284  o << '}' << iendl;
285  }
286  o << '}' << decendl;
287  return o;
288  }
289  }; // class signature_multimap
290 
291  void clear()
292  {
293  class_to_set_.clear();
294  state_to_class_.clear();
295  num_classes_ = 0;
296  }
297 
300  {
301  if (number == class_invalid)
302  number = num_classes_++;
303 
304  for (auto s : set)
305  state_to_class_[s] = number;
306 
307  if (number < class_to_set_.size())
308  class_to_set_[number] = std::move(set);
309  else
310  {
311  assert(number == class_to_set_.size());
312  class_to_set_.emplace_back(std::move(set));
313  }
314 
315  return number;
316  }
317 
318  public:
319  minimizer(const Aut& a)
320  : a_(a)
321  , ls_(*a_->labelset())
322  {
323  require(is_trim(a_), me(), ": input must be trim");
324 
325  // Fill state_to_state_output.
326  for (auto s : a_->all_states())
327  {
328  // Get the out-states from s, by label:
329  label_to_states_t label_to_states;
330  for (auto t : a_->all_out(s))
331  label_to_states[a_->label_of(t)].emplace_back(a_->dst_of(t));
332 
333  // Associate this information to s, as a vector sorted by label:
334  state_output_t& state_output = state_to_state_output_[s];
335  for (auto& l_ss : label_to_states)
336  {
337  std::sort(l_ss.second.begin(), l_ss.second.end());
338  state_output.emplace_back(state_output_for_label_t{l_ss.first,
339  std::move(l_ss.second)});
340  }
341  }
342  }
343 
346  {
347  // Don't even bother to split between final and non-final
348  // states, this initialization is useless.
349  std::unordered_set<class_t> classes;
350  {
351  const auto& all = a_->all_states();
352  classes.insert(make_class(set_t{std::begin(all), std::end(all)}));
353  }
354 
355  bool go_on;
356  do
357  {
358  go_on = false;
359 #if DEBUG
360  print_(class_to_set_, std::cerr) << std::endl;
361 #endif
362  for (auto i = std::begin(classes), end = std::end(classes);
363  i != end;
364  /* nothing. */)
365  {
366  auto c = *i;
367  const set_t& c_states = class_to_set_.at(c);
368 
369  if (c_states.size() < 2)
370  {
371  i = classes.erase(i);
372  continue;
373  }
374 
375  // Try to find distinguishable states in c_states:
377  signature_to_state(*this,
379  num_classes_);
380  for (auto s : c_states)
381  {
382 #if DEBUG
383  std::cerr << "class %" << c << " state: " << s << ' ';
384  print_(state_to_state_output_[s], std::cerr) << std::endl;
385 #endif
386  signature_to_state[&state_to_state_output_[s]].emplace_back(s);
387  }
388 #if DEBUG
389  std::cerr << "signature_to_state: " << signature_to_state
390  << std::endl;
391 #endif
392  if (2 <= signature_to_state.size())
393  {
394  go_on = true;
395  i = classes.erase(i);
396  for (auto p: signature_to_state)
397  {
398  class_t c2 = make_class(std::move(p.second), c);
399  classes.insert(c2);
400  c = class_invalid;
401  }
402  }
403  else
404  ++i;
405  } // for on classes
406  }
407  while (go_on);
408  }
409 
412  {
413  build_classes_();
414  return quotient(a_, class_to_set_);
415  }
416  };
417 
418  } // detail_signature::
419 
420  template <typename Aut>
421  inline
422  auto
423  minimize_signature(const Aut& a)
425  {
427  return minimize();
428  }
429 
430 } // namespace vcsn
431 
432 #endif // !VCSN_ALGOS_MINIMIZE_SIGNATURE_HH
std::enable_if< std::is_same< weightset_t_of< Aut >, b >::value &&labelset_t_of< Aut >::is_free(), partition_automaton< Aut > >::type minimize(const Aut &a, const std::string &algo)
Definition: minimize.hh:31
label_t_of< automaton_t > label_t
std::unordered_map< state_t, class_t > state_to_class_t
state_t_of< automaton_t > state_t
std::ostream & iendl(std::ostream &o)
Print an end of line, then set the indentation.
Definition: indent.cc:49
static constexpr const char * me()
signature_equal_to(minimizer &the_minimizer, const labelset_t &ls, const state_to_class_t &state_to_class, size_t class_bound)
std::unordered_map< state_output_t *, set_t, signature_hasher, signature_equal_to > super_t
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:34
bool is_trim(const Aut &a)
Definition: accessible.hh:151
Indentation relative functions.
friend std::ostream & operator<<(std::ostream &o, const state_output_for_label_t &out)
class_t make_class(set_t &&set, class_t number=class_invalid)
Make a new class with the given set of states.
auto quotient(const Aut &a, typename detail::quotienter< Aut >::class_to_set_t &classes) -> partition_automaton< Aut >
Definition: quotient.hh:143
std::ostream & incendl(std::ostream &o)
Increment the indentation, print an end of line, and set the indentation.
Definition: indent.cc:54
static std::ostream & print_(const class_to_set_t &c2ss, std::ostream &o)
size_t operator()(const state_output_t *state_output_) const noexcept
auto minimize_signature(const Aut &a) -> partition_automaton< Aut >
partition_automaton< automaton_t > operator()()
The minimized automaton.
static std::ostream & print_(const set_t &ss, std::ostream &o)
auto sort(const Aut &a) -> permutation_automaton< Aut >
Definition: sort.hh:185
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:33
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
Definition: traits.hh:38
std::ostream & decendl(std::ostream &o)
Decrement the indentation, print an end of line, and set the indentation.
Definition: indent.cc:59
signature_hasher(minimizer &the_minimizer, size_t num_classes)
std::vector< state_output_for_label_t > state_output_t
bool operator()(const state_output_t *as_, const state_output_t *bs_) const noexcept
friend std::ostream & operator<<(std::ostream &o, const signature_multimap &mm)
signature_multimap(minimizer &the_minimizer, const labelset_t &ls, state_to_class_t &state_to_class, const size_t class_bound)
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:46
static constexpr class_t class_invalid
An invalid class.
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:36
std::map< label_t, std::vector< state_t >, vcsn::less< labelset_t >> label_to_states_t
static std::ostream & print_(const state_output_t &outs, std::ostream &o)
const automaton_t & a_
Input automaton, supplied at construction time.
boost::dynamic_bitset<> dynamic_bitset
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:35
std::shared_ptr< detail::partition_automaton_impl< Aut >> partition_automaton
A partition automaton as a shared pointer.
void build_classes_()
Build the initial classes, and split until fix point.
labelset_t_of< automaton_t > labelset_t
std::unordered_map< state_t, state_output_t > state_to_state_output_
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:39