Vcsn  2.1
Be Rational
minimize-weighted.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <map>
4 #include <unordered_map>
5 #include <unordered_set>
6 #include <vector>
7 
8 #include <vcsn/algos/accessible.hh> // is_trim
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 
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 
52  struct classset
53  {
54  using value_t = class_t;
55 
56  using kind_t = void;
57  static bool equal(class_t l, class_t r)
58  {
59  return l == r;
60  }
61 
62  static bool less(class_t l, class_t r)
63  {
64  return l < r;
65  }
66 
67  static size_t hash(class_t s)
68  {
69  return hash_value(s);
70  }
71  };
72 
77 
80 
81  using class_polynomial_t = typename class_polynomialset_t::value_t;
82 
84  using signature_t = std::map<label_t, class_polynomial_t>;
87  {
88  auto res = signature_t{};
89  for (auto t: a_->all_out(s))
90  cps_.add_here(res[a_->label_of(t)],
91  state_to_class_.at(a_->dst_of(t)),
92  a_->weight_of(t));
93  return res;
94  }
95 
97  {
98  size_t operator()(const signature_t& sig) const noexcept
99  {
100  size_t res = 0;
101  for (auto& t : sig)
102  {
103  hash_combine_hash(res, minimizer_.ls_.hash(t.first));
104  hash_combine_hash(res, minimizer_.cps_.hash(t.second));
105  }
106  return res;
107  }
108 
110  }; // class signature_hasher
111 
113  {
114  bool operator()(const signature_t& as,
115  const signature_t& bs) const noexcept
116  {
117  if (as.size() != bs.size())
118  return false;
119 
120  using std::begin; using std::end;
121  for (auto i = begin(as), i_end = end(as), j = begin(bs);
122  i != i_end;
123  ++i, ++j)
124  if (!minimizer_.ls_.equal(i->first, j->first)
125  || !minimizer_.cps_.equal(i->second, j->second))
126  return false;
127  return true;
128  }
129 
131  }; // class signature_equal_to
132 
134  using signature_multimap
137 
138  void clear()
139  {
140  class_to_set_.clear();
141  state_to_class_.clear();
142  num_classes_ = 0;
143  }
144 
151  class_t make_class(set_t&& set, class_t number = class_invalid)
152  {
153  if (number == class_invalid)
154  number = num_classes_++;
155 
156  for (auto s : set)
157  state_to_class_[s] = number;
158 
159  if (number < class_to_set_.size())
160  class_to_set_[number] = std::move(set);
161  else
162  {
163  assert(number == class_to_set_.size());
164  class_to_set_.emplace_back(std::move(set));
165  }
166 
167  return number;
168  }
169 
170  public:
171  minimizer(const Aut& a)
172  : a_(a)
173  , ls_(*a_->labelset())
174  , ws_(*a_->weightset())
175  {
176  require(is_trim(a_), me(), ": input must be trim");
177  }
178 
181  {
182  build_classes_();
183  return class_to_set_;
184  }
185 
188  {
189  // Don't even bother splitting into final and non-final
190  // states: post will be set apart anyway because of its
191  // signature.
192  std::unordered_set<class_t> classes;
193  {
194  const auto& all = a_->all_states();
195  classes.insert(make_class(set_t{std::begin(all), std::end(all)}));
196  }
197 
198  for (bool go_on = true; go_on; /* Nothing. */)
199  {
200  go_on = false;
201  for (auto i = std::begin(classes), end = std::end(classes);
202  i != end;
203  /* Nothing. */)
204  {
205  auto c = *i;
206  const set_t& c_states = class_to_set_.at(c);
207 
208  // Look for distinguishable states in c_states:
209  // cluster the signatures.
210  auto signature_to_state
211  = signature_multimap{1,
212  signature_hasher{*this},
213  signature_equal_to{*this}};
214  for (auto s: c_states)
215  signature_to_state[signature(s)].emplace_back(s);
216 
217  if (2 <= signature_to_state.size())
218  {
219  // Split class c.
220  go_on = true;
221  i = classes.erase(i);
222  // To keep class numbers contiguous, reuse 'c' as
223  // first class number, and then use new one (via
224  // "c = class_invalid" below).
225  for (auto& p: signature_to_state)
226  {
227  bool singleton = p.second.size() == 1;
228  class_t c2 = make_class(std::move(p.second), c);
229  if (!singleton)
230  classes.insert(c2);
231  c = class_invalid;
232  }
233  }
234  else
235  ++i;
236  } // for on classes
237  }
238  }
239  };
240 
241  } // weighted::
242 
243  template <typename Aut>
244  inline
245  auto
246  minimize_weighted(const Aut& a)
247  -> quotient_t<Aut>
248  {
250  return quotient(a, minimize.classes());
251  }
252 } // namespace vcsn
static bool equal(class_t l, class_t r)
weight_t_of< automaton_t > weight_t
std::unordered_map< signature_t, set_t, signature_hasher, signature_equal_to > signature_multimap
Cluster states per signature.
typename detail::label_t_of_impl< base_t< ValueSet >>::type label_t_of
Definition: traits.hh:46
weightset_mixin< detail::r_impl > r
Definition: fwd.hh:54
partition_automaton_t< Aut > quotient_t
The return type when calling quotient on Aut.
Definition: quotient.hh:119
std::unordered_map< state_t, class_t > state_to_class_t
polynomialset< context< classset, weightset_t >> class_polynomialset_t
The output of a given letter from a given state, keeping into account classes and weights...
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
static constexpr const char * me()
automaton_t a_
Input automaton, supplied at construction time.
class_polynomialset_t cps_
Class polynomialset.
bool operator()(const signature_t &as, const signature_t &bs) const noexcept
typename class_polynomialset_t::value_t class_polynomial_t
auto hash_value(const T &v) -> decltype(std::hash< T >
Following the naming convention of Boost.
Definition: functional.hh:63
void build_classes_()
Build the initial classes, and split until fix point.
class_to_set_t & classes()
The minimized automaton.
auto minimize_weighted(const Aut &a) -> quotient_t< Aut >
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
std::map< label_t, class_polynomial_t > signature_t
A signature: for each label, the outgoing class polynomial.
label_t_of< automaton_t > label_t
signature_t signature(state_t s) const
The signature of state s.
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
state_t_of< automaton_t > state_t
void hash_combine_hash(std::size_t &seed, size_t h)
Definition: functional.hh:25
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
size_t operator()(const signature_t &sig) const noexcept
bool is_trim(const Aut &a)
Whether all its states are useful.
Definition: accessible.hh:161
weightset_t_of< automaton_t > weightset_t
class_t make_class(set_t &&set, class_t number=class_invalid)
Make a new class with the given set of states.
Indentation relative functions.
labelset_t_of< automaton_t > labelset_t
static bool less(class_t l, class_t r)
static constexpr class_t class_invalid
An invalid class.
typename detail::weight_t_of_impl< base_t< ValueSet >>::type weight_t_of
Definition: traits.hh:50