Vcsn  2.0
Be Rational
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
minimize-weighted.hh
Go to the documentation of this file.
1 #ifndef VCSN_ALGOS_MINIMIZE_WEIGHTED_HH
2 # define VCSN_ALGOS_MINIMIZE_WEIGHTED_HH
3 
4 # include <unordered_map>
5 # include <unordered_set>
6 
7 # include <vcsn/algos/accessible.hh>
8 # include <vcsn/dyn/automaton.hh>
9 # include <vcsn/misc/indent.hh>
10 # include <vcsn/misc/raise.hh>
11 
12 namespace vcsn
13 {
14 
15  /*-------------------------------------------------------------------------.
16  | minimization with signatures; general version working on any weightset. |
17  `-------------------------------------------------------------------------*/
18  namespace detail_weighted
19  {
20  template <typename Aut>
21  class minimizer
22  {
23  using automaton_t = Aut;
24 
26  const automaton_t& a_;
27 
29  const labelset_t& ls_;
30 
32  const weightset_t& ws_;
33 
37  using class_t = unsigned;
38  using set_t = std::vector<state_t>;
39  using state_to_class_t = std::unordered_map<state_t, class_t>;
40  using class_to_set_t = std::vector<set_t>;
41 
42  constexpr static const char* me() { return "minimize-weighted"; }
43 
45  constexpr static class_t class_invalid = -1;
46  unsigned num_classes_ = 0;
47 
50 
51  using weight_and_state_t = std::pair<weight_t, state_t>;
52 
54  {
55  // For some unstored state.
57  std::vector<weight_and_state_t> weights_and_destinations;
58  };
59 
60  // This is sorted by label.
61  using state_output_t = std::vector<state_output_for_label_t>;
62 
63  friend class label_less;
64  class label_less
65  {
67  public:
68  label_less(minimizer& the_minimizer)
69  : minimizer_(the_minimizer)
70  {}
71  bool operator()(const label_t& a,
72  const label_t& b) const noexcept
73  {
74  return minimizer_.ls_.less_than(a, b);
75  }
76  }; // class label_less
77 
78  // This structure is only useful at initialization time, when
79  // sorting transitions from a given state in a canonical order.
80  class label_to_weights_and_states_t: public std::map<label_t,
81  std::vector<weight_and_state_t>,
82  label_less>
83  {
84  public:
86  : std::map<label_t,
87  std::vector<weight_and_state_t>,
88  label_less>(the_minimizer)
89  {}
90  }; // class label_to_weights_and_states_t
91 
92  std::unordered_map<state_t, state_output_t> state_to_state_output_;
93 
98  using state_label_output_map_t = std::map<class_t, weight_t>;
99 
101  state_label_output_map(const std::vector<weight_and_state_t>& wss) const
102  {
104 
105  for (const auto& wd : wss)
106  {
107  class_t c = state_to_class_.at(wd.second);
108  const auto& i = res.find(c);
109  if (i == res.end())
110  res[c] = wd.first;
111  else
112  {
113  i->second = ws_.add(i->second, wd.first);
114  if (ws_.is_zero(i->second))
115  res.erase(c);
116  }
117  }
118  return res;
119  }
120 
121  friend class signature_hasher;
122  class signature_hasher : public std::hash<state_output_t*>
123  {
125  unsigned num_classes_;
126  public:
127  signature_hasher(minimizer& the_minimizer,
128  size_t num_classes)
129  : minimizer_(the_minimizer)
130  , num_classes_(num_classes)
131  {}
132 
133  size_t operator()(const state_output_t* state_output_) const noexcept
134  {
135  const state_output_t& state_output = *state_output_;
136  size_t res = 0;
137  for (auto& t : state_output)
138  {
140  minimizer_.state_label_output_map(t.weights_and_destinations);
141  // I've chosen *not* to hash the label when all
142  // transitions with a given label cancel one another.
143  if (! map.empty())
144  std::hash_combine(res, labelset_t::hash(t.label));
145  for (const auto& cw : map)
146  {
147  std::hash_combine(res, cw.first);
148  std::hash_combine(res, weightset_t::hash(cw.second));
149  }
150  }
151  return res;
152  }
153  }; // class signature_hasher
154 
155  friend class signature_equal_to;
156  class signature_equal_to : public std::equal_to<state_output_t*>
157  {
159  const size_t class_bound_;
160  public:
162  size_t class_bound)
163  : minimizer_(the_minimizer)
164  , class_bound_(class_bound)
165  {}
166 
168  const state_label_output_map_t& b) const noexcept
169  {
170  if (a.size() != b.size())
171  return false;
172  for (const auto& cw : a)
173  {
174  const auto& cwb = b.find(cw.first);
175  if (cwb == b.end())
176  return false;
177  if (! weightset_t::equals(cw.second, cwb->second))
178  return false;
179  }
180  return true;
181  }
182 
186  void first(const state_output_t& v,
187  typename state_output_t::const_iterator& i,
189  {
190  i = v.cbegin();
191  map.clear();
192 
193  if (i != v.cend())
194  map = std::move(minimizer_
195  .state_label_output_map(i->weights_and_destinations));
196  }
197 
201  void next(const state_output_t& v,
202  typename state_output_t::const_iterator& i,
204  {
205  while (true)
206  {
207  i ++;
208  map.clear();
209  if (i == v.cend())
210  return;
211  map = std::move(minimizer_
212  .state_label_output_map(i->weights_and_destinations));
213  if (! map.empty())
214  return;
215  }
216  }
217 
218  bool operator()(const state_output_t *as_,
219  const state_output_t *bs_) const noexcept
220  {
221  const state_output_t& as = *as_;
222  const state_output_t& bs = *bs_;
223 
224  // Scan as and bs in lockstep, verifying that they match for
225  // each letter. Letters occur in the same order by
226  // construction.
227  typename state_output_t::const_iterator ia, ib;
228  state_label_output_map_t mapa, mapb;
229  first(as, ia, mapa);
230  first(bs, ib, mapb);
231  // if ((ia == as.cend()) != (ib == bs.cend()))
232  // return false;
233  for (/* Nothing. */;
234  ia != as.cend() && ib != bs.cend();
235  next(as, ia, mapa), next(bs, ib, mapb))
236  if (! minimizer_.ls_.equals(ia->label, ib->label)
237  || ! match(mapa, mapb))
238  return false;
239 
240  return (ia == as.cend()) == (ib == bs.cend());
241  }
242  }; // class signature_equal_to
243 
244  friend class signature_multimap;
246  : public std::unordered_map<state_output_t*, set_t,
247  signature_hasher, signature_equal_to>
248  {
250  using super_t
251  = std::unordered_map<state_output_t*, set_t,
253  public:
255  const size_t class_bound)
256  : super_t(1,
257  signature_hasher(the_minimizer, class_bound),
258  signature_equal_to(the_minimizer, class_bound))
259  , minimizer_(the_minimizer)
260  {}
261  }; // class signature_multimap
262 
263  void clear()
264  {
265  class_to_set_.clear();
266  state_to_class_.clear();
267  num_classes_ = 0;
268  }
269 
271  class_t make_class(set_t&& set, class_t number = class_invalid)
272  {
273  if (number == class_invalid)
274  number = num_classes_++;
275 
276  for (auto s : set)
277  state_to_class_[s] = number;
278 
279  if (number < class_to_set_.size())
280  class_to_set_[number] = std::move(set);
281  else
282  {
283  assert(number == class_to_set_.size());
284  class_to_set_.emplace_back(std::move(set));
285  }
286 
287  return number;
288  }
289 
290  public:
291  minimizer(const Aut& a)
292  : a_(a)
293  , ls_(*a_->labelset())
294  , ws_(*a_->weightset())
295  {
296  require(is_trim(a_), me(), ": input must be trim");
297 
298  // Fill state_to_state_output.
299  for (auto s : a_->all_states())
300  {
301  // Get the out-states from s, by label:
302  label_to_weights_and_states_t label_to_weights_and_states(*this);
303  for (auto t : a_->all_out(s))
304  label_to_weights_and_states[a_->label_of(t)]
305  .emplace_back(std::pair<weight_t, state_t>{a_->weight_of(t),
306  a_->dst_of(t)});
307 
308  // Associate this information to s, as a vector sorted by label:
309  state_output_t& state_output = state_to_state_output_[s];
310  for (auto& l_wss : label_to_weights_and_states)
311  {
312  std::sort(l_wss.second.begin(),
313  l_wss.second.end(),
314  [](const weight_and_state_t& l,
315  const weight_and_state_t& r)
316  {
317  if (l.second < r.second)
318  return true;
319  else if (r.second < l.second)
320  return false;
321  else
322  return weightset_t::less_than(l.first, r.first);
323  });
324  state_output.emplace_back(state_output_for_label_t{l_wss.first,
325  std::move(l_wss.second)});
326  }
327  }
328  }
329 
332  {
333  // Don't even bother splitting into final and non-final
334  // states: post will be set apart anyway because of its
335  // signature.
336  std::unordered_set<class_t> classes;
337  {
338  const auto& all = a_->all_states();
339  classes.insert(make_class(set_t{std::begin(all), std::end(all)}));
340  }
341 
342  bool go_on;
343  do
344  {
345  go_on = false;
346  for (auto i = std::begin(classes), end = std::end(classes);
347  i != end;
348  /* nothing. */)
349  {
350  auto c = *i;
351  const set_t& c_states = class_to_set_.at(c);
352 
353  // If a class is too small to be split omit it from
354  // further consideration.
355  if (c_states.size() < 2)
356  {
357  i = classes.erase(i);
358  continue;
359  }
360 
361  // Try to find distinguishable states in c_states:
362  signature_multimap signature_to_state(*this, num_classes_);
363  for (auto s : c_states)
364  signature_to_state[& state_to_state_output_[s]].emplace_back(s);
365  if (2 <= signature_to_state.size())
366  {
367  go_on = true;
368  i = classes.erase(i);
369  for (auto& p: signature_to_state)
370  {
371  class_t c2 = make_class(std::move(p.second), c);
372  classes.insert(c2);
373  c = class_invalid;
374  }
375  }
376  else
377  ++i;
378  } // for on classes
379  }
380  while (go_on);
381  }
382 
385  {
386  build_classes_();
387  return quotient(a_, class_to_set_);
388  }
389  };
390 
391  } // weighted::
392 
393  template <typename Aut>
394  inline
395  auto
396  minimize_weighted(const Aut& a)
398  {
400  return minimize();
401  }
402 
403 } // namespace vcsn
404 
405 #endif // !VCSN_ALGOS_MINIMIZE_WEIGHTED_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
weightset_t_of< automaton_t > weightset_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.
std::vector< state_output_for_label_t > state_output_t
void first(const state_output_t &v, typename state_output_t::const_iterator &i, state_label_output_map_t &map) const
Update the iterator to point to the first non-empty state_output_for_label_t or v.cend(), and update the map to be the one associated to *i, or empty.
state_t_of< automaton_t > state_t
auto quotient(const Aut &a, typename detail::quotienter< Aut >::class_to_set_t &classes) -> partition_automaton< Aut >
Definition: quotient.hh:143
signature_equal_to(minimizer &the_minimizer, size_t class_bound)
label_t_of< automaton_t > label_t
static constexpr class_t class_invalid
An invalid class.
auto minimize_weighted(const Aut &a) -> partition_automaton< Aut >
signature_multimap(minimizer &the_minimizer, const size_t class_bound)
const state_label_output_map_t state_label_output_map(const std::vector< weight_and_state_t > &wss) const
weight_t_of< automaton_t > weight_t
auto sort(const Aut &a) -> permutation_automaton< Aut >
Definition: sort.hh:185
void next(const state_output_t &v, typename state_output_t::const_iterator &i, state_label_output_map_t &map) const
Update the iterator to point to the next non-empty state_output_for_label_t or v.cend(), and update the map to be the one associated to *i, or empty.
auto map(const std::tuple< Ts...> &ts, Fun f) -> decltype(map_tuple_(f, ts, make_index_sequence< sizeof...(Ts)>()))
Map a function on a tuple, return tuple of the results.
Definition: tuple.hh:101
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::unordered_map< state_t, class_t > state_to_class_t
std::unordered_map< state_t, state_output_t > state_to_state_output_
bool operator()(const state_output_t *as_, const state_output_t *bs_) const noexcept
void build_classes_()
Build the initial classes, and split until fix point.
static constexpr const char * me()
labelset_t_of< automaton_t > labelset_t
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:36
const automaton_t & a_
Input automaton, supplied at construction time.
bool match(const state_label_output_map_t &a, const state_label_output_map_t &b) const noexcept
std::map< class_t, weight_t > state_label_output_map_t
The output of a given letter from a given state, keeping into account classes and weights...
typename detail::weight_t_of_impl< base_t< ValueSet >>::type weight_t_of
Definition: traits.hh:37
bool operator()(const label_t &a, const label_t &b) const noexcept
size_t operator()(const state_output_t *state_output_) const noexcept
partition_automaton< automaton_t > operator()()
The minimized automaton.
signature_hasher(minimizer &the_minimizer, size_t num_classes)
class_t make_class(set_t &&set, class_t number=class_invalid)
Make a new class with the given set of states.
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.
RatExpSet::value_t less_than(const RatExpSet &rs, const typename RatExpSet::value_t &v)
Definition: less-than.hh:150
std::pair< weight_t, state_t > weight_and_state_t
variadic_mul_mixin< detail::r_impl > r
Definition: fwd.hh:42
std::unordered_map< state_output_t *, set_t, signature_hasher, signature_equal_to > super_t
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:39