Awali
Another Weighted Automata library
moore_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_MOORE_QUOTIENT_HH
18 #define AWALI_ALGOS_MOORE_QUOTIENT_HH
19 
20 #include <vector>
21 #include <queue>
22 #include <awali/common/types.hh>
23 #include <awali/sttc/ctx/traits.hh>
25 
26 namespace awali {
27  namespace sttc {
28 
29  /* This is an implementation of Moore algorithm for the quotient of weighted automata.
30  This is a partition refining algorithm. At the beginning, there are three parts:
31  the pre-initial state(0), the post-final state(1), the other states(2)
32  At each round, every non singleton part 'i' is checked:
33  for each state 's' of 'i' the 'signature' of 's' is computed;
34  this is the parts that are reached from 's' for every letter, with the corresponding weight
35  the algorithm guarantees (#) that two states are "Moore equivalent" iff they have the same signature.
36  The part 'i' is then split w.r.t. the states that share a signature.
37  If no part have been split during a round, the algorithm ends.
38 
39  The rounds are separated in the queue through a false part '0' (the true part 0 never appear in the queue).
40 
41  (#) The signatures are computed through a "weak sort" implemented with linked hash maps:
42  it is a linear algorithm in amortized time.
43  First, for every state 's' in 'i', for every transition s-a|k->q,
44  the tuple (s,k,part[q]) is put in the list meet[a],
45  Then, for each letter 'a' in the domain of 'meet',
46  the tuple (s,k,a) is appended to the list meet2[part[q]],
47  Finally, for each part part[q] in the domain of 'meet2',
48  if there is not another tuple (a,part[q],k')
49  the tuple (a,part[q],k) is appended to signature[s],
50  else
51  the tuple is updated as (a,part[q],k+k')
52  This latter loop creates an ordering on letters: in every signature, the ordering over letter is a subsequence of the ordering of the domain of 'meet'
53  .
54  */
55 
56  template <typename Aut>
57  unsigned moore_quotient(const Aut& aut, std::vector<std::vector<state_t> >& states_in_part) {
58 
59  using automaton_t = Aut;
60 
63 
64  //Each part (unsigned) is a list of states
65  states_in_part.resize(3);
66  //And each state belongs to a part
67  std::vector<unsigned> part(aut->max_state()+1);
68  //The part is initialised as 2 for every state
69  //except for pre() which is 0 and post() which is in 1.
70  states_in_part[0].emplace_back(aut->pre());
71  part[aut->pre()]=0;
72  states_in_part[1].emplace_back(aut->post());
73  part[aut->post()]=1;
74  for(auto s : aut->states() ) {
75  states_in_part[2].emplace_back(s);
76  part[s]=2;
77  }
78  /* A part can potentially be split if it contains at least two states.
79  Hence singletons are not put in the queue.
80  */
81  std::queue<unsigned> queue_part;
82  if(states_in_part[2].size()>1)
83  queue_part.emplace(2);
84  //0 is a marker in the queue for the end of a round
85  //if there is no splitting between two occurences of 0
86  //the algorithm ends.
87  queue_part.emplace(0);
88  bool stop=true;
90  unsigned iterations=0;
91  while(!queue_part.empty()) {
92  iterations++;
93  //i is the part which is the current splitter
94  unsigned i= queue_part.front();
95  queue_part.pop();
96  if(i==0) {//end of the round
97  if(stop)
98  break;
99  else {
100  //the parts for the next round are exactly the parts in the queue
101  //we start a new round and put a marker in the queue
102  stop=true;
103  queue_part.emplace(0);
104  continue;
105  }
106  }
107  std::vector<state_t> states_without_successors;
108 
109  //The list of successors for each letter
111  for(auto s : states_in_part[i]) {
112  bool no_successors=true;
113  for(auto tr : aut->all_out(s) ) {
114  no_successors=false;
115  state_t r = aut->dst_of(tr);
116  label_t a = aut->label_of(tr);
117  weight_t w = aut->weight_of(tr);
118  std::tuple<state_t, weight_t, unsigned> tu{s,w,part[r]};
119  meet[a].push_back(std::move(tu));
120  }
121  if(no_successors)
122  states_without_successors.emplace_back(s);
123  }
124  if(meet.domain().empty())
125  continue;
126 
128  for(auto a : meet.domain())
129  for(auto p : meet[a]){
130  std::tuple<state_t, weight_t, label_t> tu{std::get<0>(p),std::get<1>(p),a};
131  meet2[std::get<2>(p)].push_back(std::move(tu));
132  }
133 
135  for(auto j : meet2.domain())
136  for(auto p : meet2[j]) {
137  if(signature.count(std::get<0>(p))) {
138  auto & t=signature[std::get<0>(p)].back();
139  if(std::get<0>(t)==std::get<2>(p) && std::get<1>(t)==j)
140  std::get<2>(t) = aut->context().weightset()->add(std::get<2>(t), std::get<1>(p));
141  else {
142  std::tuple<label_t,unsigned, weight_t> tu{std::get<2>(p),j,std::get<1>(p)};
143  signature[std::get<0>(p)].push_back(std::move(tu));
144  }
145  }
146  else {
147  std::tuple<label_t,unsigned, weight_t> tu{std::get<2>(p),j,std::get<1>(p)};
148  signature[std::get<0>(p)].push_back(std::move(tu));
149  }
150  }
151 
152  // We compute now the new parts.
153  std::vector<std::vector<state_t> > new_parts;
154  std::queue<std::vector<state_t> > list;
155  list.emplace(signature.domain());
156  while(!list.empty()) {
157  std::vector<state_t>& pp = list.front();
158  std::vector<state_t> shorter;
160  for(state_t& r:pp) {
161  std::vector<std::tuple<label_t,unsigned, weight_t>>& sig = signature[r];
162  if(sig.empty())
163  shorter.push_back(r);
164  else {
165  std::tuple<label_t,unsigned, weight_t> & tuple= sig.back();
166  tmplist[tuple].emplace_back(r);
167  sig.pop_back();
168  }
169  }
170  list.pop();
171  for(auto pair : tmplist.domain())
172  list.push(std::move(tmplist[pair]));
173  if(!shorter.empty())
174  new_parts.push_back(std::move(shorter));
175  }
176  //Creation of the new parts
177 
178  unsigned lim=new_parts.size();
179  //if lim=1 the part has not been split
180  if(lim>1)
181  stop=false;
182  else {
183  queue_part.emplace(i);
184  continue;
185  }
186  unsigned np=states_in_part.size();
187  for(unsigned k=0; k<lim;++k) {
188  unsigned p;
189  if(k==0) {
190  p=i;
191  states_in_part[i].swap(new_parts[0]);
192  }
193  else {
194  p=np++;
195  for(auto r : new_parts[k])
196  part[r]=p;
197  states_in_part.push_back(std::move(new_parts[k]));
198  }
199  if(states_in_part[p].size()>1) {
200  queue_part.emplace(p);
201  }
202  }
203  if(!states_without_successors.empty()) {
204  for(auto r : states_without_successors)
205  part[r]=np;
206  states_in_part.push_back(std::move(states_without_successors));
207  //the part of states without successors is not put in the queue
208  //since it can not be split
209  }
210  }
211  return iterations;
212  }
213  }
214 }//end of ns awali::stc
215 
216 #endif // !AWALI_ALGOS_MOORE_QUOTIENT_HH
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 moore_quotient(const Aut &aut, std::vector< std::vector< state_t > > &states_in_part)
Definition: moore_quotient.hh:57
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