Awali
Another Weighted Automata library
hopcroft_quotient.hh
Go to the documentation of this file.
1 // This file is part of Awali.
2 // Copyright 2016-2022 Sylvain Lombardy, Victor Marsault, Jacques Sakarovitch
3 //
4 // Awali is a free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef AWALI_ALGOS_HOPCROFT_QUOTIENT_HH
18 #define AWALI_ALGOS_HOPCROFT_QUOTIENT_HH
19 
20 #include <unordered_map>
21 #include<limits>
22 #include<vector>
23 #include<list>
24 #include<queue>
25 
26 #include <awali/common/types.hh>
27 #include <awali/sttc/misc/pair.hh>
29 # include <limits>
30 
31 namespace awali {
32  namespace sttc {
33 
34  /*
35  * minimization with Hopcroft for incomplete deterministic automata
36  * (cf. Beal & Crochemore, Minimizing incomplete automata, 2008)
37  */
38  /* Description of the algorithm
39 
40  This algorithm compute the coarsest congruence ~ of the set of states Q such that,
41  for each pair of states p and q, if p~q then
42  1. p if final <=> q is final
43  2. for every letter a, the set of succ(p,a) = succ(q,a) mod ~
44 
45  This is a refinement algorithm.
46  There is first a partition between final and non final states.
47  The principle is that every part p is used as a "splitter":
48  for every predecessor r of any s of p,
49  the "signature" of r is computed:
50  that is the list of letters for which the successor of r exists and is in p.
51  Every concerned part is the split w.r.t the signatures of its states.
52 
53  To achieve a O(|A||Q|) complexity in each iteration,
54  weak sort is used.
55  Here, the weak sort is not implemented with sparse lists, but with linked_hash_map;
56  the complexity is therefore in amortized O(A||Q|).
57 
58  1. For every letter a that appear in a transition incoming to p,
59  meet[a] is the list of predecessors of some state of p by a
60  2. For every letter in the domain of meet,
61  for every state r in meet[a]
62  a is appended to the list signature[r]
63  -> letters appear in signatures in the same ordering
64 
65  Then, the list of states which have a signature must be (weakly) sorted in such a way
66  that states with the same signature and that belong to the same part are neighbours
67  1. For each state r in the domain of signature
68  r is appended to tmplist[part[r]]
69  Then, the list of lists (tmplist[p] | p in domain of tmplist) are concatenated.
70  For convenience, this concatenation 'list' is represented as a list of lists.
71  2. For every integer k,
72  r is appended to tmplist[signature[k]]
73  Then, the list of lists (tmplist[a] | a in domain of tmplist) are concatenated.
74  This is done for k up to the longest signature; if some signature are shorter,
75  the corresponding states are set in the "special list" list.
76 
77  Finally all the concerned part are splitted.
78  The smallest subpart inherits the index of the original part, the other receive fresh indices.
79  If there is more than a subpart, every subpart but the largest is pushed into the queue of splitters.
80  This leads to a logarithmic number of iterations
81  */
82 
83  template <typename Aut>
84  unsigned hopcroft_quotient(const Aut& aut, std::vector<std::list<state_t> >& states_in_part, bool cancellative = false) {
85 
86  using automaton_t = Aut;
87 
90 
91  const auto& weightset_ = *aut->weightset();
92  std::vector<unsigned > size_of_part;
93  //Each part (unsigned) is a list of states
94  states_in_part.resize(3);
95  size_of_part.resize(3);
96  //And each state belongs to a part
97  state_t state_capacity=aut->max_state()+1;
98  std::vector<unsigned> part(state_capacity);
99  std::vector<std::list<state_t>::iterator> address_in_part(state_capacity);
100  //The part is initialised as 0 for every state
101  //except for post() which is in 1.
102  states_in_part[0].emplace_back(aut->pre());
103  part[aut->pre()]=0;
104  states_in_part[1].emplace_back(aut->post());
105  part[aut->post()]=1;
106  size_of_part[0]=size_of_part[1]=1;
107  for(auto s : aut->states() ) {
108  states_in_part[2].emplace_front(s);
109  part[s]=2;
110  address_in_part[s]=states_in_part[2].begin();
111  }
112  size_of_part[2]=aut->num_states();
113  unsigned n=3; // number of parts
114 
115  /* A part is a "splitter" if its predecessors can be considered to split the parts
116  */
117  std::queue<unsigned> splitters;
118  std::vector<bool> is_in_queue;
119  splitters.emplace(1);
120  splitters.emplace(2);
121  is_in_queue.push_back(false);
122  is_in_queue.push_back(true);
123  is_in_queue.push_back(true);
125  unsigned iterations=0;
126  while(!splitters.empty()) {
127  iterations++;
128  //i is the part which is the current splitter
129  unsigned i= splitters.front();
130  splitters.pop();
131  is_in_queue[i]=false;
132  //The list of predecessors for each letter
134  for(auto s : states_in_part[i]) {
135  for(auto tr : aut->all_in(s) ) {
136  state_t r = aut->src_of(tr);
137  if(size_of_part[part[r]]==1)
138  continue;
139  label_t a = aut->label_of(tr);
140  weight_t w = aut->weight_of(tr);
141  meet[a].emplace_back(std::pair<state_t, weight_t>{r,w});
142  }
143  }
144  if(meet.domain().empty())
145  continue;
146  /* The signature of each met 'r' state is computed
147  * signature[r] is the list of letters 'a' such that succ(r,a) is in part 'i'.
148  * The ordering of letters in signature[r] is compliant with the ordering
149  * of letters in meet.domain().
150  * Hence, if two states have successors in 'i' with the same letters, they have the same
151  * signature.
152  */
154  for(auto a : meet.domain())
155  for(auto p : meet[a]) {
156  state_t r=p.first;
157  weight_t w=p.second;
158  if(signature.count(r)==0 || signature[r].back().first!=a)
159  signature[r].emplace_back(std::pair<label_t, weight_t>{a,w});
160  else {
161  weight_t& w1 = signature[r].back().second;
162  w1 = weightset_.add(w1,w);
163  }
164  }
165  /* We build now a list in which states with the same signature
166  * that belong to the same part are gathered
167  * For convenience, this is actually a list of lists
168  */
169 
171  std::queue<std::vector<state_t> > list;
172  {
174  for(auto r : signature.domain())
175  tmplist[part[r]].emplace_back(r);
176  for(auto j : tmplist.domain())
177  list.push(std::move(tmplist[j]));
178  }
179  while(!list.empty()) {
180  std::vector<state_t>& pp = list.front();
181  std::vector<state_t> shorter;
182  unsigned current_part = part[pp.front()];
183  internal::linked_hash_map<std::pair<label_t,weight_t>,std::vector<state_t> > tmplist;
184  for(state_t& r:pp) {
185  std::vector<std::pair<label_t, weight_t>>& sig = signature[r];
186  if(sig.empty())
187  shorter.push_back(r);
188  else {
189  std::pair<label_t,weight_t>& pair= sig.back();
190  tmplist[pair].emplace_back(r);
191  sig.pop_back();
192  }
193  }
194  list.pop();
195  for(auto pair : tmplist.domain())
196  list.push(std::move(tmplist[pair]));
197  if(!shorter.empty())
198  new_parts_in_this_part[current_part].push_back(std::move(shorter));
199  }
200  //
201  //Creation of the new parts
202  for(unsigned p : new_parts_in_this_part.domain()) {
203  unsigned s=0;
204  auto &pp=new_parts_in_this_part[p];
205  for(auto& c : pp)
206  s=s+c.size();
207  bool all_states_met= size_of_part[p]==s;
208  unsigned nn=n;
209  if(!all_states_met)
210  size_of_part[p]-=s;
211  if(pp.size()>1 || !all_states_met) { // there is a split
212  bool first=true;
213  //update parts
214  for(auto& c : pp) {
215  unsigned k;
216  if(first && all_states_met) {
217  k=p;
218  size_of_part[p]=c.size();
219  }
220  else {
221  k=nn++;
222  states_in_part.emplace_back(std::list<state_t>());
223  size_of_part.push_back(c.size());
224  }
225  first=false;
226  for(state_t r : c) {
227  states_in_part[p].erase(address_in_part[r]);
228  states_in_part[k].emplace_front(r);
229  address_in_part[r]=states_in_part[k].begin();
230  part[r]=k;
231  }
232  }
233  //insert parts in queue
234  if(cancellative && !is_in_queue[p]) {
235  unsigned max=p, size_max=size_of_part[p];
236  for(unsigned k=n; k<nn; ++k)
237  if(size_of_part[k]>size_max) {
238  size_max=size_of_part[k];
239  max=k;
240  }
241  if(max!=p) {
242  splitters.emplace(p);
243  is_in_queue[p]=true;
244  }
245  for(unsigned k=n; k<nn; ++k)
246  if(max!=k) {
247  splitters.emplace(k);
248  is_in_queue.push_back(true);
249  }
250  else
251  is_in_queue.push_back(false);
252  } else {
253  if(!is_in_queue[p]) {
254  splitters.emplace(p);
255  is_in_queue[p]=true;
256  }
257  for(unsigned k=n; k<nn; ++k) {
258  splitters.emplace(k);
259  is_in_queue.push_back(true);
260  }
261  }
262  n=nn;
263  }
264  }
265  }
266  return iterations;
267  }
268 
269  }
270 }//end of ns awali::stc
271 
272 #endif // !AWALI_ALGOS_HOPCROFT_QUOTIENT_HH
The semiring of complex numbers.
Definition: c.hh:44
The semiring of Natural numbers.
Definition: n.hh:34
The semiring of natural numbers bounded by K.
Definition: nn.hh:42
The semiring of floating Numbers.
Definition: r.hh:35
any_t label_t
Type for (transition) labels; it is an alias to any_t since its precise type depends on the weightset...
Definition: typedefs.hh:48
any_t weight_t
Type for (transition) weights; it is an alias to any_t since the its precise type depends on the weig...
Definition: typedefs.hh:61
typename internal::label_t_of_impl< internal::base_t< ValueSet > >::type label_t_of
Helper to retrieve the type of the labels of a value set.
Definition: traits.hh:71
typename internal::weight_t_of_impl< internal::base_t< ValueSet > >::type weight_t_of
Helper to retrieve the type of the weights of a value set.
Definition: traits.hh:81
auto meet(const ratexpset< Ctx1 > &a, const ratexpset< Ctx2 > &b) -> ratexpset< meet_t< Ctx1, Ctx2 >>
The meet of two ratexpsets.
Definition: ratexpset.hh:434
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: synchronizing_word.hh:266
unsigned hopcroft_quotient(const Aut &aut, std::vector< std::list< state_t > > &states_in_part, bool cancellative=false)
Definition: hopcroft_quotient.hh:84
ATTRIBUTE_CONST int max(int a, int b)
Definition: arith.hh:54
Main namespace of Awali.
Definition: ato.hh:22
unsigned state_t
Definition: types.hh:21
Definition: linkedhashmap.hh:31
unsigned count(const K &k) const
Definition: linkedhashmap.hh:46
const std::vector< K > & domain() const
Definition: linkedhashmap.hh:36