Vcsn  2.1
Be Rational
minimize-signature.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <unordered_map>
4 #include <unordered_set>
5 
6 #include <vcsn/algos/accessible.hh> // is_trim
7 #include <vcsn/algos/quotient.hh>
9 #include <vcsn/misc/indent.hh>
10 #include <vcsn/misc/map.hh> // vcsn::less
11 #include <vcsn/misc/raise.hh>
12 #include <vcsn/weightset/fwd.hh> // b
13 
14 namespace vcsn
15 {
16 
17  /*---------------------------------------------------------.
18  | minimization with Moore's algorithm: signature variant. |
19  `---------------------------------------------------------*/
20  namespace detail_signature
21  {
22  template <typename Aut>
23  class minimizer
24  {
25  static_assert(std::is_same<weightset_t_of<Aut>, b>::value,
26  "minimize: signature: requires Boolean weights");
27 
28  using automaton_t = Aut;
29 
32 
34  const labelset_t& ls_;
35 
38  using class_t = unsigned;
39  using set_t = std::vector<state_t>;
40  using state_to_class_t = std::unordered_map<state_t, class_t>;
41  using class_to_set_t = std::vector<set_t>;
42 
43  constexpr static const char* me() { return "minimize-signature"; }
44 
46  constexpr static class_t class_invalid = -1;
47  unsigned num_classes_ = 0;
48 
51 
52  // For a given state, destination states for a specific label.
54  {
55  // For some unstored state.
57  std::vector<state_t> to_states; // Ordered.
58  };
59 
60  // This is sorted by label.
61  using state_output_t = std::vector<state_output_for_label_t>;
62 
65  std::unordered_map<state_t, state_output_t> state_to_state_output_;
66 
68  {
69  // FIXME: G++ 4.9 requires this ctor, which is wrong.
71  : minimizer_(m)
72  {}
73 
74  size_t operator()(const state_output_t& state_output) const noexcept
75  {
76  size_t res = 0;
77  dynamic_bitset bits(num_classes_);
78  for (auto& t : state_output)
79  {
80  const label_t& label = t.label;
81  hash_combine_hash(res, minimizer_.ls_.hash(label));
82  // Hash the set of classes reached with label. Of
83  // course the hash must not depend on class ordering.
84  bits.reset();
85  for (auto s : t.to_states)
86  bits.set(state_to_class_.at(s));
87  vcsn::hash_combine(res, bits);
88  }
89  return res;
90  }
91 
92  size_t operator()(state_t s) const noexcept
93  {
95  }
96 
98  const state_to_class_t& state_to_class_ = minimizer_.state_to_class_;
99  unsigned num_classes_ = minimizer_.num_classes_;
100  }; // class signature_hasher
101 
103  {
104  // FIXME: G++ 4.9 requires this ctor, which is wrong.
106  : minimizer_(m)
107  {}
108  bool operator()(const state_output_t& as,
109  const state_output_t& bs) const noexcept
110  {
111  if (as.size() != bs.size())
112  return false;
113 
114  dynamic_bitset a_bits(num_classes_), b_bits(num_classes_);
115  for (auto i = as.cbegin(), i_end = as.cend(), j = bs.cbegin();
116  i != i_end;
117  ++i, ++j)
118  {
119  if (! ls_.equal(i->label, j->label))
120  return false;
121 
122  a_bits.reset(); b_bits.reset();
123  for (auto s : i->to_states)
124  a_bits.set(state_to_class_.at(s));
125  for (auto s : j->to_states)
126  b_bits.set(state_to_class_.at(s));
127  if (a_bits != b_bits)
128  return false;
129  }
130 
131  return true;
132  }
133 
134  bool operator()(const state_t& a, const state_t& b) const noexcept
135  {
138  }
139 
141  const labelset_t& ls_ = minimizer_.ls_;
142  const state_to_class_t& state_to_class_ = minimizer_.state_to_class_;
143  const size_t num_classes_ = minimizer_.num_classes_;
144  }; // class signature_equal_to
145 
150  using signature_multimap
153 
154  void clear()
155  {
156  class_to_set_.clear();
157  state_to_class_.clear();
158  num_classes_ = 0;
159  }
160 
167  class_t make_class(set_t&& set, class_t number = class_invalid)
168  {
169  if (number == class_invalid)
170  number = num_classes_++;
171 
172  for (auto s : set)
173  state_to_class_[s] = number;
174 
175  if (number < class_to_set_.size())
176  class_to_set_[number] = std::move(set);
177  else
178  {
179  assert(number == class_to_set_.size());
180  class_to_set_.emplace_back(std::move(set));
181  }
182 
183  return number;
184  }
185 
186  public:
187  minimizer(const Aut& a)
188  : a_(a)
189  , ls_(*a_->labelset())
190  {
191  require(is_trim(a_), me(), ": input must be trim");
192 
193  // Fill state_to_state_output.
194  for (auto s : a_->all_states())
195  {
196  // Sort of a transition-map for state s.
197  using label_to_states_t
198  = std::map<label_t, std::vector<state_t>, vcsn::less<labelset_t>>;
199 
200  // Get the out-states from s, by label:
201  label_to_states_t label_to_states;
202  for (auto t : a_->all_out(s))
203  label_to_states[a_->label_of(t)].emplace_back(a_->dst_of(t));
204 
205  // Associate this information to s, as a vector sorted by label:
206  state_output_t& state_output = state_to_state_output_[s];
207  for (auto& l_ss : label_to_states)
208  {
209  boost::sort(l_ss.second);
210  state_output.emplace_back(state_output_for_label_t{l_ss.first,
211  std::move(l_ss.second)});
212  }
213  }
214  }
215 
218  {
219  build_classes_();
220  return class_to_set_;
221  }
222 
223  private:
226  {
227  // The list of classes candidates for splitting.
228  //
229  // Don't even bother to split between final and non-final
230  // states, this initialization is useless.
231  std::unordered_set<class_t> classes;
232  {
233  const auto& all = a_->all_states();
234  classes.insert(make_class(set_t{std::begin(all), std::end(all)}));
235  }
236 
237  for (bool go_on = true; go_on; /* Nothing. */)
238  {
239  go_on = false;
240  for (auto i = std::begin(classes), end = std::end(classes);
241  i != end;
242  /* nothing. */)
243  {
244  auto c = *i;
245  const set_t& c_states = class_to_set_.at(c);
246 
247  // Look for distinguishable states in c_states:
248  // cluster the signatures.
249  auto sig_to_state
250  = signature_multimap{1,
251  signature_hasher{*this},
252  signature_equal_to{*this}};
253  for (auto s : c_states)
254  sig_to_state[s].emplace_back(s);
255 
256  if (2 <= sig_to_state.size())
257  {
258  // Split class c.
259  go_on = true;
260  i = classes.erase(i);
261  // To keep class numbers contiguous, reuse 'c' as
262  // first class number, and then use new one (via
263  // "c = class_invalid" below).
264  for (auto& p: sig_to_state)
265  {
266  bool singleton = p.second.size() == 1;
267  class_t c2 = make_class(std::move(p.second), c);
268  if (!singleton)
269  classes.insert(c2);
270  c = class_invalid;
271  }
272  }
273  else
274  ++i;
275  } // for on classes
276  }
277  }
278  };
279 
280  } // detail_signature::
281 
282  template <typename Aut>
283  inline
284  auto
285  minimize_signature(const Aut& a)
286  -> quotient_t<Aut>
287  {
289  return quotient(a, minimize.classes());
290  }
291 
292 } // namespace vcsn
boost::dynamic_bitset<> dynamic_bitset
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:46
std::shared_ptr< const detail::label_base > label
Definition: fwd.hh:59
partition_automaton_t< Aut > quotient_t
The return type when calling quotient on Aut.
Definition: quotient.hh:119
bool operator()(const state_output_t &as, const state_output_t &bs) const noexcept
Functor to compare Values of ValueSets.
Definition: functional.hh:72
state_t_of< automaton_t > state_t
typename detail::state_t_of_impl< base_t< ValueSet >>::type state_t_of
Definition: traits.hh:48
auto quotient(const Aut &a, typename detail::quotienter< Aut >::class_to_set_t &cs) -> quotient_t< Aut >
Definition: quotient.hh:124
typename detail::weightset_t_of_impl< base_t< ValueSet >>::type weightset_t_of
Definition: traits.hh:51
std::unordered_map< state_t, set_t, signature_hasher, signature_equal_to > signature_multimap
Cluster states per signature.
void hash_combine(std::size_t &seed, const T &v)
Definition: functional.hh:32
class_to_set_t & classes()
The partition, as a list of classes.
size_t operator()(const state_output_t &state_output) const noexcept
vcsn::enable_if_t< std::is_same< weightset_t_of< Aut >, b >::value &&labelset_t_of< Aut >::is_free(), quotient_t< Aut > > minimize(const Aut &a, const std::string &algo="auto")
Definition: minimize.hh:30
static constexpr class_t class_invalid
An invalid class.
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
bool operator()(const state_t &a, const state_t &b) const noexcept
auto minimize_signature(const Aut &a) -> quotient_t< Aut >
void hash_combine_hash(std::size_t &seed, size_t h)
Definition: functional.hh:25
std::unordered_map< state_t, class_t > state_to_class_t
typename detail::labelset_t_of_impl< base_t< ValueSet >>::type labelset_t_of
Definition: traits.hh:47
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:75
automaton_t a_
Input automaton, supplied at construction time.
bool is_trim(const Aut &a)
Whether all its states are useful.
Definition: accessible.hh:161
label_t_of< automaton_t > label_t
static constexpr const char * me()
std::unordered_map< state_t, state_output_t > state_to_state_output_
Sort of a transition map for each state: state -> vector of (label, destination states).
Indentation relative functions.
class_t make_class(set_t &&set, class_t number=class_invalid)
Make a new class with the given set of states.
auto sort(const Aut &a) -> permutation_automaton< Aut >
Definition: sort.hh:163
std::vector< state_output_for_label_t > state_output_t
void build_classes_()
Build the initial classes, and split until fix point.
labelset_t_of< automaton_t > labelset_t