Vcsn  2.8
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/tags.hh>
9 #include <vcsn/algos/accessible.hh> // is_trim
10 #include <vcsn/misc/indent.hh>
11 #include <vcsn/misc/raise.hh>
12 
13 namespace vcsn
14 {
15 
16  /*-------------------------------------------------------------------------.
17  | minimization with signatures; general version working on any weightset. |
18  `-------------------------------------------------------------------------*/
19 
20  namespace detail
21  {
22  template <Automaton Aut>
23  class minimizer<Aut, weighted_tag>
24  {
25  using automaton_t = Aut;
26 
32  using class_t = unsigned;
33  using set_t = std::vector<state_t>;
34  using state_to_class_t = std::unordered_map<state_t, class_t>;
35  using class_to_set_t = std::vector<set_t>;
36 
37  constexpr static const char* me() { return "minimize-weighted"; }
38 
40  constexpr static class_t class_invalid = -1;
42  struct classset
43  {
44  using value_t = class_t;
45  using kind_t = void;
46 
47  static constexpr bool
49  {
50  // FIXME: We should not need this. This is to please
51  // polynomialset::print.
52  return false;
53  }
54 
55  static std::ostream&
56  print(value_t v, std::ostream& out = std::cout, format = {})
57  {
58  return out << v;
59  }
60 
61  static bool equal(class_t l, class_t r)
62  {
63  return l == r;
64  }
65 
66  static bool less(class_t l, class_t r)
67  {
68  return l < r;
69  }
70 
71  static size_t hash(class_t s)
72  {
73  return hash_value(s);
74  }
75  };
76 
81 
82  using class_polynomial_t = typename class_polynomialset_t::value_t;
83 
85  using signature_t = std::map<label_t, class_polynomial_t>;
88  {
89  auto res = signature_t{};
90  for (auto t: all_out(a_, s))
91  cps_.add_here(res[a_->label_of(t)],
92  state_to_class_.at(a_->dst_of(t)),
93  a_->weight_of(t));
94  return res;
95  }
96 
97  struct signature_hasher
98  {
99  size_t operator()(const signature_t& sig) const noexcept
100  {
101  size_t res = 0;
102  for (auto& t : sig)
103  {
104  hash_combine_hash(res, minimizer_.ls_.hash(t.first));
105  hash_combine_hash(res, minimizer_.cps_.hash(t.second));
106  }
107  return res;
108  }
109 
111  }; // class signature_hasher
112 
113  struct signature_equal_to
114  {
115  bool operator()(const signature_t& as,
116  const signature_t& bs) const noexcept
117  {
118  if (as.size() != bs.size())
119  return false;
120 
121  using std::begin; using std::end;
122  for (auto i = begin(as), i_end = end(as), j = begin(bs);
123  i != i_end;
124  ++i, ++j)
125  if (!minimizer_.ls_.equal(i->first, j->first)
126  || !minimizer_.cps_.equal(i->second, j->second))
127  return false;
128  return true;
129  }
130 
132  }; // class signature_equal_to
133 
135  using signature_multimap
137  signature_hasher, signature_equal_to>;
138 
139  void clear()
140  {
141  class_to_set_.clear();
142  state_to_class_.clear();
143  num_classes_ = 0;
144  }
145 
152  class_t make_class(set_t&& set, class_t number = class_invalid)
153  {
154  if (number == class_invalid)
155  number = num_classes_++;
156 
157  for (auto s : set)
158  state_to_class_[s] = number;
159 
160  if (number < class_to_set_.size())
161  class_to_set_[number] = std::move(set);
162  else
163  {
164  assert(number == class_to_set_.size());
165  class_to_set_.emplace_back(std::move(set));
166  }
167 
168  return number;
169  }
170 
171  public:
172  minimizer(const Aut& a)
173  : a_(a)
174  {
175  require(is_trim(a_), me(), ": input must be trim");
176  }
177 
180  {
181  build_classes_();
182  return class_to_set_;
183  }
184 
187  {
188  // Don't even bother splitting into final and non-final
189  // states: post will be set apart anyway because of its
190  // signature.
191  std::unordered_set<class_t> classes;
192  {
193  const auto& all = a_->all_states();
194  classes.insert(make_class(set_t{std::begin(all), std::end(all)}));
195  }
196 
197  for (bool go_on = true; go_on; /* Nothing. */)
198  {
199  go_on = false;
200  for (auto i = std::begin(classes), end = std::end(classes);
201  i != end;
202  /* Nothing. */)
203  {
204  auto c = *i;
205  const set_t& c_states = class_to_set_.at(c);
206 
207  // Look for distinguishable states in c_states:
208  // cluster the signatures.
209  auto signature_to_state
210  = signature_multimap{1,
211  signature_hasher{*this},
212  signature_equal_to{*this}};
213  for (auto s: c_states)
214  signature_to_state[signature(s)].emplace_back(s);
215 
216  if (2 <= signature_to_state.size())
217  {
218  // Split class c.
219  go_on = true;
220  i = classes.erase(i);
221  // To keep class numbers contiguous, reuse 'c' as
222  // first class number, and then use new one (via
223  // "c = class_invalid" below).
224  for (auto& p: signature_to_state)
225  {
226  bool singleton = p.second.size() == 1;
227  class_t c2 = make_class(std::move(p.second), c);
228  if (!singleton)
229  classes.insert(c2);
230  c = class_invalid;
231  }
232  }
233  else
234  ++i;
235  } // for on classes
236  }
237  }
238 
241 
242  const labelset_t& ls_ = *a_->labelset();
243  const weightset_t& ws_ = *a_->weightset();
244 
245  unsigned num_classes_ = 0;
246 
249 
251  class_polynomialset_t cps_{{classset{}, ws_}};
252  };
253  } // weighted::
254 } // namespace vcsn
automaton_t a_
Input automaton, supplied at construction time.
weightset_mixin< detail::r_impl > r
Definition: fwd.hh:54
bool operator()(const signature_t &as, const signature_t &bs) const noexcept
typename detail::weightset_t_of_impl< base_t< ValueSet > >::type weightset_t_of
Definition: traits.hh:67
typename class_polynomialset_t::value_t class_polynomial_t
class_t make_class(set_t &&set, class_t number=class_invalid)
Make a new class with the given set of states.
std::unordered_map< signature_t, set_t, signature_hasher, signature_equal_to > signature_multimap
Cluster states per signature.
class_to_set_t & classes()
The minimized automaton.
typename detail::state_t_of_impl< base_t< ValueSet > >::type state_t_of
Definition: traits.hh:64
void build_classes_()
Build the initial classes, and split until fix point.
Request for the weighted version of an algorithm.
Definition: tags.hh:149
std::map< label_t, class_polynomial_t > signature_t
A signature: for each label, the outgoing class polynomial.
auto all_out(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions leaving state s.
Definition: automaton.hh:67
An input/output format for valuesets.
Definition: format.hh:13
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
void hash_combine_hash(std::size_t &seed, size_t h)
Definition: functional.hh:56
auto hash_value(const T &v) -> decltype(std::hash< T >
Following the naming convention of Boost.
Definition: functional.hh:45
typename detail::labelset_t_of_impl< base_t< ValueSet > >::type labelset_t_of
Definition: traits.hh:63
static std::ostream & print(value_t v, std::ostream &out=std::cout, format={})
Definition: a-star.hh:8
std::unordered_map< state_t, class_t > state_to_class_t
typename detail::label_t_of_impl< base_t< ValueSet > >::type label_t_of
Definition: traits.hh:62
Signature of a function call.
Definition: signature.hh:15
bool is_trim(const Aut &a)
Whether all its states are useful.
Definition: accessible.hh:165
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:87
Request the unordered_map implementation.
typename detail::weight_t_of_impl< base_t< ValueSet > >::type weight_t_of
Definition: traits.hh:66
Indentation relative functions.
signature_t signature(state_t s) const
The signature of state s.
return res
Definition: multiply.hh:399
bool all(Bool &&... values)
Whether all the values evaluate as true.
Definition: tuple.hh:492
std::set< std::pair< std::string, std::string > > class_t
A set of label ranges.
Definition: fwd.hh:12
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:86
size_t operator()(const signature_t &sig) const noexcept