cfl_project.cpp
Go to the documentation of this file.
1 /** @file cfl_project.cpp projection and subset construction */
2 
3 /* FAU Discrete Event Systems Library (libfaudes)
4 
5  Copyright (C) 2006 Bernd Opitz
6  Copyright (C) 2006-2014 Thomas Moor
7  Exclusive copyright is granted to Klaus Schmidt
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Lesser General Public
11  License as published by the Free Software Foundation; either
12  version 2.1 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Lesser General Public License for more details.
18 
19  You should have received a copy of the GNU Lesser General Public
20  License along with this library; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22 
23 
24 #include "cfl_project.h"
25 #include "cfl_regular.h"
26 #include "cfl_graphfncts.h"
27 #include "cfl_localgen.h"
28 #include "cfl_statemin.h"
29 #include "cfl_determin.h"
30 
31 /* turn on debugging for this file */
32 //#undef FD_DF
33 //#define FD_DF(a) FD_WARN(a);
34 
35 namespace faudes {
36 
37 
38 // Project, version 2008/12, Bernd Opitz (?) --- usef for libFAUDES up to 2009
39 // This is believed to be the original implementation by Bend Opitz, and thus might have
40 // been in use since 2006, however, the below code snipped was grabbed in 2008. The call
41 // of "LocalAccessibleReach" is elegant, however, needs to look up every state twice.
42 // In large automata, this is a relevant contribution to the computational cost.
43 // Our re-code of this algorithm saves half of the lookups (see _ref).
44 
45 // Reference, 2008/12 (original Opitz?)
46 void ProjectNonDet_opitz(Generator& rGen, const EventSet& rProjectAlphabet) {
47 
48  // HELPERS:
49  StateSet reach; // StateSet for reachable states
50  std::stack<Idx> todo; // todo stack
51  StateSet done; // done set
52  Idx currentstate; // the currently processed state
53  StateSet::Iterator lit;
55  TransSet::Iterator tit_end;
56 
57  // ALGORITHM:
58  // initialize algorithm by pushing init states on todo stack
59  for (lit = rGen.InitStatesBegin(); lit != rGen.InitStatesEnd(); ++lit) {
60  FD_DF("ProjectNonDet: todo add: " << rGen.SStr(*lit));
61  todo.push(*lit);
62  }
63 
64  // process todo stack
65  while (! todo.empty()) {
66  currentstate = todo.top();
67  todo.pop();
68  done.Insert(currentstate);
69  FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
70 
71  // comp accessible reach
72  reach.Clear();
73  LocalAccessibleReach(rGen, rProjectAlphabet, currentstate, reach);
74  FD_DF("ProjectNonDet: local reach: " << reach.ToString());
75 
76  // remove all transitions that leave current state
77  // with an invisible event
78  tit = rGen.TransRelBegin(currentstate);
79  tit_end = rGen.TransRelEnd(currentstate);
80  while(tit != tit_end) {
81  FD_DF("ProjectNonDet: current transition: " << rGen.SStr(tit->X1)
82  << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(tit->X2));
83  if (! rProjectAlphabet.Exists(tit->Ev)) {
84  FD_DF("ProjectNonDet: deleting current transition");
85  rGen.ClrTransition(tit++);
86  } else {
87  ++tit;
88  }
89  }
90 
91  // relink outgoing transitions
92  FD_DF("ProjectNonDet: relinking outgoing transitions...");
93  for (lit = reach.Begin(); lit != reach.End(); ++lit) {
94  tit = rGen.TransRelBegin(*lit);
95  tit_end = rGen.TransRelEnd(*lit);
96  for (; tit != tit_end; ++tit) {
97  if (rProjectAlphabet.Exists(tit->Ev)) {
98  FD_DF("ProjectNonDet: relinking transition: " << rGen.TStr(*tit) << " to " << rGen.SStr(currentstate));
99  rGen.SetTransition(currentstate, tit->Ev, tit->X2);
100  if (! done.Exists(tit->X2)) {
101  FD_DF("ProjectNonDet: todo push: " << rGen.SStr(tit->X2));
102  todo.push(tit->X2);
103  }
104  }
105  }
106  // marked status test
107  if (rGen.ExistsMarkedState(*lit)) {
108  FD_DF("ProjectNonDet: setting marked state " << rGen.SStr(currentstate));
109  rGen.SetMarkedState(currentstate);
110  }
111  }
112  }
113 
114  // inject projection alphabet
115  rGen.InjectAlphabet(rProjectAlphabet);
116 
117  // set name
118  rGen.Name(CollapsString("Pro(" + rGen.Name() + ")"));
119 }
120 
121 
122 // Projection version 2014/03, Moor -- revision of original 2008 algorithm
123 // This is a re-code of the original libFAUDES project implementation, as proposed
124 // by Bernd Opitz. The Redesign takes into account the logarithmic cost of state lockups for
125 // large input data and avoids to call the subroutine LocalAccessibleReach. The performance gain
126 // is about factor 2. We use this implementationas a reference.
127 
128 void ProjectNonDet_ref(Generator& rGen, const EventSet& rProjectAlphabet) {
129 
130  // HELPERS:
131  std::stack<Idx> todod, todor; // states todo
132  StateSet doned, doner; // states done (aka have been put to todo)
133  Idx currentstate; // the currently processed state
134  StateSet::Iterator sit;
135  StateSet::Iterator sit_end;
136  TransSet::Iterator tit;
137  TransSet::Iterator tit_end;
138 
139  // NAME
140  std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
141 
142  // ALGORITHM:
143 
144  // initialize todo stack by adding init states to todo
145  for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
146  todod.push(*sit);
147  doned.Insert(*sit);
148  }
149 
150  // process main todo stack
151  while(!todod.empty()) {
152 
153  // loop callback
154  FD_WPC(doned.Size() - todod.size(), rGen.Size(), "ProjectNonDet() [STD]: done/size: "
155  << doned.Size() - todod.size() << " / " << rGen.Size());
156 
157  // get top of the stack
158  currentstate = todod.top();
159  todod.pop();
160  FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
161 
162  // local reach iteration
163  todor.push(currentstate);
164  doner.Clear();
165  doner.Insert(currentstate);
166  bool marked=rGen.ExistsMarkedState(currentstate);
167  while(!todor.empty()) {
168  Idx reachstate = todor.top();
169  todor.pop();
170  FD_DF("ProjectNonDet: reach: " << rGen.SStr(reachstate));
171  //track marking
172  marked|=rGen.ExistsMarkedState(reachstate);
173  // iterate successors
174  tit = rGen.TransRelBegin(reachstate);
175  tit_end = rGen.TransRelEnd();
176  for(; tit != tit_end; ++tit) {
177  if(tit->X1!=reachstate) break;
178  // for high-level events: insert new transition, add to main todo
179  if(rProjectAlphabet.Exists(tit->Ev)) {
180  rGen.SetTransition(currentstate, tit->Ev, tit->X2);
181  if(doned.Insert(tit->X2)) {
182  FD_DF("ProjectNonDet: todod insert: " << rGen.SStr(tit->X2));
183  todod.push(tit->X2);
184  }
185  }
186  // for low-level events: add new states to local reach todo
187  else {
188  if(doner.Insert(tit->X2)) {
189  todor.push(tit->X2);
190  }
191  }
192  }
193  } // loop: local reach
194 
195  // set marking
196  if(marked) rGen.SetMarkedState(currentstate);
197 
198  // remove all silent transitions that leave current state
199  tit = rGen.TransRelBegin(currentstate);
200  tit_end = rGen.TransRelEnd();
201  while(tit != tit_end) {
202  if(tit->X1!=currentstate) break;
203  FD_DF("ProjectNonDet: current transition: " << rGen.SStr(tit->X1)
204  << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(tit->X2));
205  if(!rProjectAlphabet.Exists(tit->Ev)) {
206  FD_DF("ProjectNonDet: deleting current transition");
207  rGen.ClrTransition(tit++);
208  } else {
209  ++tit;
210  }
211  }
212 
213  }// outer todo loop
214 
215  // inject projection alphabet, keep Attributes
216  rGen.RestrictAlphabet(rProjectAlphabet);
217  // make accessile
218  rGen.RestrictStates(doned);
219  // set name
220  rGen.Name(name);
221 }
222 
223 
224 /**
225  * Graph data structure for transitionrelation -- EXPERIMENTAL
226  *
227  * We have encountered situations where the set based approach implies a
228  * performace penalty for large generators. This light-weight graph class
229  * is provided to investigate the situation and to compare perfromance.
230  *
231  */
232 
233 /**
234  * Forward declaration to mimique recursive typedef by
235  * templates
236  *
237  */
238 template< class VLabel, class ELabel >
239 struct TGraph;
240 template< class VLabel, class ELabel >
241 struct TNode;
242 template< class VLabel, class ELabel >
243 struct graph_iterator_t;
244 template< class VLabel, class ELabel >
245 struct node_iterator_t;
246 template< class VLabel, class ELabel >
247 struct node_entry_t;
248 
249 
250 /**
251  * A graph is modelled as a map from vertex-labels to nodes.
252  * For our use, the vertex-labels are state indicees and each
253  * node represents the transitions from the respective state.
254  *
255  * Effectively, we implement the following structure
256  * graph:
257  * map< state-index , node >
258  * node:
259  * set< node-entrz >
260  * node-entry:
261  * pair < event-index , graph-iterator > >
262  *
263  * For convenience methods addressing the intended ussage, see
264  * also the below specialisation.
265  */
266 template< class VLabel, class ELabel >
267 struct TGraph : std::map< VLabel , TNode< VLabel , ELabel > > {
268  // convenience typedef
270  // global min, element
271  static inline Iterator InfX1(void) {
272  static TGraph gInfX1; return gInfX1.begin(); }
273 };
274 
275 
276 /**
277  * A node represents the edges related to one individual vertex.
278  * For our use, the edges are transitions from the state associated
279  * with the respective vertex.
280  */
281 template< class VLabel, class ELabel >
282 struct TNode : std::set< node_entry_t< VLabel , ELabel > > {
283  // user data
286  // constructors
287  TNode( void )
288  : std::set< node_entry_t< VLabel , ELabel > >() , RefCnt(0) , UsrFlg(0) {}
289  TNode( const typename std::set< node_entry_t< VLabel , ELabel > > n )
290  : std::set< node_entry_t< VLabel , ELabel > >(n) ,RefCnt(0) , UsrFlg(0) {}
291  // convenience tyepdef
293 };
294 
295 
296 /**
297  * A node-entry represents one edge. For our use, this corresponds
298  * to a transition and the edge-label is the event index.
299  */
300 template< class VLabel, class ELabel >
301 struct node_entry_t {
302  // data members
303  ELabel Ev;
305  // constructors
306  node_entry_t( void ) {};
308  : Ev(ent.Ev) , X2It(ent.X2It) {};
310  : Ev(ev), X2It(x2it) {}
311  // sorting
312  inline bool operator<(const node_entry_t< VLabel , ELabel >& ent) const {
313  if(this->Ev < ent.Ev) return true;
314  if(this->Ev > ent.Ev) return false;
315  if(this->X2It->first == ent.X2It->first) return false;
316  if(this->X2It == TGraph< VLabel , ELabel >::InfX1()) return true;
317  if(ent.X2It == TGraph< VLabel , ELabel >::InfX1()) return false;
318  return this->X2It->first < ent.X2It->first;
319  }
320 };
321 
322 
323 /**
324  * An iterators over the map of all nodes is interpreted
325  * as a state incl. all related transition. This impelmentation
326  * provides convenience methods to access the state index and to iterate
327  * over exiting transitions.
328  */
329 template< class VLabel, class ELabel >
330 struct graph_iterator_t : TGraph< VLabel , ELabel >::iterator {
331  // conveniend const cast
333  { return (const_cast<graph_iterator_t< VLabel , ELabel >* >(this)); }
334  // constructors
336  : TGraph< VLabel , ELabel >::iterator() {}
338  : TGraph< VLabel , ELabel >::iterator(git) {}
339  // read access
340  inline VLabel X1(void) const { return (*this)->first; }
341  inline typename TNode< VLabel , ELabel >::Iterator Begin(void) const
342  { return (*FakeConst())->second.begin(); }
343  inline typename TNode< VLabel , ELabel >::Iterator End(void) const
344  { return (*this)->second.end(); }
345  inline typename TNode< VLabel , ELabel >::Iterator Begin(ELabel Ev) const
346  { return (*FakeConst())->second.lower_bound(
347  std::make_pair(Ev , TGraph< VLabel , ELabel >::InfX1()) ); }
348  inline typename TNode< VLabel , ELabel >::Iterator End(ELabel Ev) const
349  { return (*FakeConst())->second.lower_bound(
350  std::make_pair(Ev+1 , TGraph< VLabel , ELabel >::InfX1()) ); }
351  // write access
352  inline bool Insert(const ELabel ev, const graph_iterator_t< VLabel , ELabel > x2it) {
353  if(! (*this)->second.insert(node_entry_t< VLabel , ELabel >(ev,x2it)).second) return false;
354  if(x2it!=(*this)) ++(x2it->second.RefCnt);
355  return true;
356  }
357  inline void Erase(const node_iterator_t< VLabel , ELabel > nit) {
358  if(nit->X2It != (*this)) --(nit->X2It->second.RefCnt);
359  (*this)->second.erase(nit);
360  }
361  inline void IncRefCnt(void) {
362  ++((*this)->second.RefCnt);
363  }
364  // user data
365  inline Int& UsrFlg(void) { return (*this)->second.UsrFlg; }
366  // inspect for debugging
367  std::string Str(void) const {
368  std::stringstream rep;
370  typename TNode< VLabel , ELabel >::Iterator nit_end=End();
371  rep << "[" << X1() << "] ";
372  for(; nit!=nit_end; ++nit)
373  rep << "(" << nit.Ev() << ")->" << nit.X2() << " ";
374  return rep.str();
375  }
376 
377 };
378 
379 
380 /**
381  * An iterator over the set of edges related to one vertex is interpreted
382  * as a transition.
383  */
384 template< class VLabel, class ELabel >
385 struct node_iterator_t : TNode< VLabel , ELabel >::iterator {
387  : TNode< VLabel , ELabel >::iterator() {}
389  : TNode< VLabel , ELabel >::iterator(nit) {}
390  inline ELabel Ev(void) const { return (*this)->Ev; }
391  inline graph_iterator_t< VLabel , ELabel > X2It(void) const { return (*this)->X2It; }
392  inline VLabel X2(void) const { return (*this)->X2It->first; }
393 };
394 
395 
396 /**
397  * Specialisation of the graph template to provide convenience methods
398  * addressing the intended ussage.
399  */
400 template< >
401 struct TGraph<Idx,Idx> : std::map< Idx , TNode< Idx , Idx > >{
402  // convenience typedef
404  // conveniend const cast
405  inline TGraph<Idx,Idx>* FakeConst(void) const
406  { return (const_cast<TGraph< Idx, Idx>*>(this)); }
407  // read access
408  inline Iterator Begin(void) const { return FakeConst()->begin(); }
409  inline Iterator End(void) const { return FakeConst()->end(); }
410  inline Iterator Begin(Idx x1) const { return FakeConst()->lower_bound(x1); }
411  inline Iterator End(Idx x1) const { return FakeConst()->lower_bound(x1+1); }
412  inline Iterator Find(Idx x1) const { return FakeConst()->find(x1); }
413  // write access
414  inline Iterator Insert(Idx x1) {
415  return (this->insert(std::make_pair(x1,mapped_type()))).first; }
416  inline bool Erase(Idx x1) {
417  if(Find(x1)==End()) return false;
418  Iterator git=Begin();
419  Iterator git_end=End();
420  for(;git!=git_end;++git) {
422  TNode<Idx,Idx>::Iterator nit_end=git.End();
423  while(nit!=nit_end) {
424  if(nit.X2()==x1) git.Erase(nit++);
425  else ++nit;
426  }
427  }
428  return ((*this).erase(x1)) !=0; // allways true
429  }
430  inline void Erase(Iterator git, TNode<Idx,Idx>::Iterator nit) {
431  Iterator x2it = nit.X2It();
432  git.Erase(nit);
433  if(x2it->second.RefCnt == 0 ) ((*this).erase(x2it));
434  }
435  // conversion from generator interface (destructive)
436  void Import(vGenerator& rGen) {
437  TransSet::Iterator tit = rGen.TransRelBegin();
438  TransSet::Iterator tit_end = rGen.TransRelEnd();
439  for(; tit != tit_end; rGen.ClrTransition(tit++)) {
440  Iterator git=find(tit->X1);
441  if(git==end()) git = insert(std::make_pair(tit->X1,mapped_type())).first;
442  Iterator x2it=find(tit->X2);
443  if(x2it==end()) x2it = insert(std::make_pair(tit->X2,mapped_type())).first;
444  git.Insert(tit->Ev,x2it);
445  }
446  StateSet::Iterator sit = rGen.InitStatesBegin();
447  StateSet::Iterator sit_end = rGen.InitStatesEnd();
448  for(; sit != sit_end; sit++)
449  Find(*sit).IncRefCnt();
450  };
451  // conversion to generator (destructive)
452  void Export(vGenerator& rGen) {
453  rGen.ClearTransRel();
454  Iterator git=begin();
455  Iterator git_end=end();
456  for(; git != git_end; git++) {
457  TNode<Idx,Idx>::Iterator nit =git.Begin();
458  TNode<Idx,Idx>::Iterator nit_end=git.End();
459  for(; nit != nit_end; ++nit) {
460  rGen.InjectState(git.X1());
461  rGen.InjectState(nit.X2());
462  rGen.InjectTransition(Transition(git.X1(),nit.Ev(),nit.X2()));
463  }
464  git->second.clear();
465  }
466  this->clear();
467  }
468  // inspect or debugging
469  std::string Str(void) const {
470  std::stringstream rep;
471  Iterator git=Begin();
472  Iterator git_end=End();
473  for(; git!=git_end; ++git)
474  rep << git.Str() << std::endl;
475  return rep.str();
476  }
477  // global min, element
478  static inline Iterator InfX1(void) {
479  static TGraph gInfX1;
480  return gInfX1.begin(); }
481 };
482 
483 
484 // convenience typedefs
487 
488 
489 // Projection, version 2014/03, Moor -- graph based implementation of original 2008 algorithm
490 // The performance gain is about +50%, demonstrating that the graph data structure is a more
491 // adequate representation for intensive forward reachability analysis.
492 
493 void ProjectNonDet_graph(Generator& rGen, const EventSet& rProjectAlphabet) {
494 
495  std::stack<Graph::Iterator> todod, todor; // states todo
496  std::stack<Graph::Iterator> todov; // states todo
497  StateSet doned, doner; // states done (aka have been put to todo)
498  Graph::Iterator currentstate; // the currently processed state
499  Graph::Iterator reachstate; // inner reachability loop
500  StateSet::Iterator sit;
501  Graph trg;
502  Graph::Iterator git, git_end;
503  Node::Iterator nit, nit_end;
504 
505  // NAME
506  std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
507 
508  // convert to graph
509  FD_DF("ProjectNonDet: convert to graph");
510  trg.Import(rGen);
511  FD_DF("ProjectNonDet: convert to graph: done");
512 
513  // initialize todo stack by adding init states to todo
514  for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
515  todod.push(trg.Find(*sit));
516  doned.Insert(*sit);
517  }
518 
519  // process main todo stack
520  while(!todod.empty()) {
521 
522  // loop callback
523  FD_WPC(doned.Size() - todod.size(),trg.size(), "ProjectNonDet() [G1]: done/size: "
524  << doned.Size() - todod.size() << "/" << trg.size());
525 
526  // get top of the stack
527  currentstate = todod.top();
528  todod.pop();
529  FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate.X1()));
530 
531  // local reach iteration
532  todor.push(currentstate);
533  doner.Clear();
534  doner.Insert(currentstate.X1());
535  bool marked=rGen.ExistsMarkedState(currentstate->first);
536  while(!todor.empty()) {
537  reachstate = todor.top();
538  todor.pop();
539  FD_DF("ProjectNonDet: reach: " << rGen.SStr(reachstate.X1()));
540  //track marking
541  marked|=rGen.ExistsMarkedState(reachstate.X1());
542  // iterate successors
543  nit=reachstate.Begin();
544  nit_end=reachstate.End();
545  for(; nit != nit_end; ++nit) {
546  // for high-level events: add to main todo and insert new transition,
547  if(rProjectAlphabet.Exists(nit.Ev())) {
548  if(doned.Insert(nit.X2())) {
549  FD_DF("ProjectNonDet: todod insert: " << rGen.SStr(nit.X2()));
550  todod.push(nit.X2It());
551  }
552  // transition is only new if we are not on the currentstate
553  if(reachstate!=currentstate) {
554  FD_DF("ProjectNonDet: trans insert: " << rGen.SStr(currentstate.X1()) << " -> " << rGen.SStr(nit.X2()));
555  currentstate.Insert(nit.Ev(),nit.X2It());
556  }
557  }
558  // for low-level events: add new states to reach todo
559  else {
560  if(doner.Insert(nit.X2()))
561  todor.push(nit.X2It());
562  if(nit.X2It()==currentstate)
563  todov.push(reachstate);
564  }
565  }
566  } // loop: local reach
567 
568  // set marking
569  if(marked) rGen.SetMarkedState(currentstate->first);
570 
571  // remove all silent transitions that leave current state
572  nit=currentstate.Begin();
573  nit_end=currentstate.End();
574  while(nit != nit_end) {
575  if(!rProjectAlphabet.Exists(nit.Ev()))
576  trg.Erase(currentstate,nit++);
577  //currentstate.Erase(nit++);
578  else
579  ++nit;
580  }
581 
582  }// outer todo loop
583 
584  // convert back
585  FD_DF("ProjectNonDet: convert from graph");
586  trg.Export(rGen);
587  FD_DF("ProjectNonDet: convert from graph: done");
588 
589  // inject projection alphabet, keep Attributes
590  rGen.RestrictAlphabet(rProjectAlphabet);
591  // make accessile
592  rGen.RestrictStates(doned);
593  // set name
594  rGen.Name(name);
595 }
596 
597 
598 
599 // Projection, version 2014/03, Moor -- simple algorithm
600 // For large automata, the performance penalty is prohibitive. However, this
601 // simple variant is useful for validation of correctness for other implementations.
602 
603 void ProjectNonDet_simple(Generator& rGen, const EventSet& rProjectAlphabet) {
604 
605  // HELPERS:
606  std::stack<Idx> todod; // states todo
607  StateSet doned; // states done (aka have been put to todo)
608  Idx currentstate; // the currently processed state
609  StateSet::Iterator sit, sit_end;
610  TransSet::Iterator tit, tit_end;
611  StateSet reach;
612 
613  // NAME
614  std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
615 
616  // ALGORITHM:
617 
618  // initialize todo stack by adding init states to todo
619  for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
620  todod.push(*sit);
621  doned.Insert(*sit);
622  }
623 
624  // process main todo stack
625  while(!todod.empty()) {
626 
627  // loop callback
628  FD_WPC(doned.Size(),rGen.Size(), "ProjectNonDet() [SIMPL]: current/size: "
629  << doned.Size() << " / " << rGen.Size());
630 
631  // get top of the stack
632  currentstate = todod.top();
633  todod.pop();
634  FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
635 
636  // inspect successors to obtain one-step silent reach set
637  reach.Clear();
638  tit = rGen.TransRelBegin(currentstate);
639  tit_end = rGen.TransRelEnd(currentstate);
640  for(; tit != tit_end; ++tit) {
641  if(!doned.Exists(tit->X2)) { todod.push(tit->X2); doned.Insert(tit->X2); }
642  if(rProjectAlphabet.Exists(tit->Ev)) continue;
643  if(tit->X2==currentstate) continue;
644  reach.Insert(tit->X2);
645  }
646 
647  // copy local successor transitions/marking to this state
648  bool revisit=false;
649  bool marked=rGen.ExistsMarkedState(currentstate);
650  sit = reach.Begin();
651  sit_end = reach.End();
652  for(; sit != sit_end; ++sit) {
653  tit = rGen.TransRelBegin(*sit);
654  tit_end = rGen.TransRelEnd(*sit);
655  for(; tit != tit_end; ++tit) {
656  revisit|=rGen.SetTransition(currentstate,tit->Ev,tit->X2);
657  marked|=rGen.ExistsMarkedState(tit->X1);
658  }
659  }
660  if(marked) rGen.InsMarkedState(currentstate);
661 
662  // queue again if we introdced a new transion
663  if(revisit) todod.push(currentstate);
664 
665  } // todo stack
666 
667  FD_DF("ProjectNonDet: finalize");
668 
669  // restrict to projection alphabet, keep Attributes
670  rGen.RestrictAlphabet(rProjectAlphabet);
671  // make accessile
672  rGen.Accessible();
673  // set name
674  rGen.Name(name);
675 }
676 
677 
678 // Project, version 2009/05, Tobias Barthel -- used for libFAUDES 2.14 to 2.23 (2009 -- 2014)
679 // Tobias Barthel found a test case (non-deterministic? diagnoser?) in which the original implementation
680 // by Bernd Opitz was believed to fail. Unfortunatly, this test case is now lost. As of 2014/03, we use
681 // the above re-coded version of the original algorithm as our reference. We believe it to be correct
682 // ... it passed all our test cases with identical results to the below implementation.
683 void ProjectNonDet_barthel(Generator& rGen, const EventSet& rProjectAlphabet) {
684 
685  // HELPERS:
686  StateSet reach, reachext; // StateSet for reachable states
687  StateSet todo; // states todo
688  StateSet done; // states done
689  Idx currentstate; // the currently processed state
690  StateSet::Iterator lit;
691  StateSet::Iterator lit2;
692  TransSet::Iterator tit;
693  TransSet::Iterator tit_end;
694 
695  // NAME
696  std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
697 
698  // ALGORITHM:
699 
700  // set all locally reachable states to init states
701  // tmoor 201403: is this needed to prevent the initial state to disappear?
702  reachext.Clear();
703  for(lit = rGen.InitStatesBegin(); lit != rGen.InitStatesEnd(); ++lit) {
704  reach.Clear();
705  LocalAccessibleReach(rGen, rProjectAlphabet, *lit, reach);
706  reachext.InsertSet(reach);
707  }
708  rGen.InsInitStates(reachext);
709  FD_DF("ProjectNonDet: initial states: " << rGen.InitStates().ToString());
710  // initialize algorithm by adding init states to todo
711  todo.InsertSet(rGen.InitStates());
712 
713  // process todo stack
714  while(!todo.Empty()) {
715  currentstate = *todo.Begin();
716  todo.Erase(*todo.Begin());
717  done.Insert(currentstate); // mark as done
718  FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
719 
720  // comp accessible reach
721  reach.Clear();
722  LocalAccessibleReach(rGen, rProjectAlphabet, currentstate, reach);
723  FD_DF("ProjectNonDet: local reach: " << reach.ToString());
724 
725  // relink outgoing transitions
726  FD_DF("ProjectNonDet: relinking outgoing transitions...");
727  for(lit = reach.Begin(); lit != reach.End(); ++lit) {
728  tit = rGen.TransRelBegin(*lit);
729  tit_end = rGen.TransRelEnd(*lit);
730  for(; tit != tit_end; ++tit) {
731  if(rProjectAlphabet.Exists(tit->Ev)) {
732  FD_DF("ProjectNonDet: relinking transition: " << rGen.TStr(*tit) << " to " << rGen.SStr(currentstate));
733  rGen.SetTransition(currentstate, tit->Ev, tit->X2);
734  if (!done.Exists(tit->X2)) {
735  FD_DF("ProjectNonDet: todo insert: " << rGen.SStr(tit->X2));
736  todo.Insert(tit->X2);
737  }
738  // add transistions to all states in extended local reach
739  // tmoor 201308: I dont see why we need this ...
740  reachext.Clear();
741  LocalAccessibleReach(rGen, rProjectAlphabet, tit->X2, reachext);
742  FD_DF("ProjectNonDet: local reach from state " << tit->X2 << ": " << reachext.ToString());
743  for (lit2 = reachext.Begin(); lit2 != reachext.End(); ++lit2) {
744  if (!rGen.ExistsTransition(tit->X2, tit->Ev, *lit2) && (tit->X2 != *lit2)) {
745  rGen.SetTransition(tit->X1, tit->Ev, *lit2);
746  FD_DF("ProjectNonDet: setting transition: " << rGen.SStr(tit->X1) << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(*lit2));
747  //if (!done.Exists(*lit2) && !todo.Exists(*lit2)) {
748  if (!done.Exists(*lit2)) {
749  FD_DF("ProjectNonDet: todo insert: " << rGen.SStr(tit->X2));
750  todo.Insert(*lit2);
751  }
752  }
753  }
754  }
755  }
756  // marked status test
757  if(rGen.ExistsMarkedState(*lit)) {
758  FD_DF("ProjectNonDet: setting marked state " << rGen.SStr(currentstate));
759  rGen.SetMarkedState(currentstate);
760  }
761  }
762 
763  // remove all silent transitions that leave current state
764  tit = rGen.TransRelBegin(currentstate);
765  tit_end = rGen.TransRelEnd(currentstate);
766  while(tit != tit_end) {
767  FD_DF("ProjectNonDet: current transition: " << rGen.SStr(tit->X1)
768  << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(tit->X2));
769  if(!rProjectAlphabet.Exists(tit->Ev)) {
770  FD_DF("ProjectNonDet: deleting current transition");
771  rGen.ClrTransition(tit++);
772  } else {
773  ++tit;
774  }
775  }
776 
777  }// todo loop
778 
779  // restrict to projection alphabet, keep Attributes (could also use Inject here)
780  rGen.RestrictAlphabet(rProjectAlphabet);
781 
782  // make accessile
783  rGen.Accessible();
784 
785  // set name
786  rGen.Name(name);
787 
788 }
789 
790 
791 // Project, version 2014/03, Moor -- an alternative for some large generators.
792 // This argorithm is derived from the reference version. Subsequent to each forward
793 // reachablity analysis, the states at which only non-silent events are enabled
794 // are identified as must-exit-states. From those, a backward search is conducted, to
795 // introduce more must-exit-states. Idealy, this resolvs the entire foreward reach region.
796 // This is beneficial when compared to the reference algorithm, in which only the
797 // first state of the region is resolved. However, the backward reach will stuck
798 // if the forard reach region contains strongly connected components. See also the
799 // ProjectNonDet_scc variant, which addresses this issue.
800 
801 void ProjectNonDet_fbr(Generator& rGen, const EventSet& rProjectAlphabet) {
802 
803  // HELPERS:
804 
805  std::stack<Idx> todod, todor, todof, todox; // states todo
806  StateSet doned, doner, donef, donex; // states done (aka have been put to todo)
807  Idx currentstate; // the currently processed state
808  Idx reachstate; // the currently processed state in reach iteration
809  Idx exitstate; // the currently processed state in exit search iteration
810  StateSet candx;
811  StateSet::Iterator sit;
812  StateSet::Iterator sit_end;
813  TransSet::Iterator tit;
814  TransSet::Iterator tit_end;
815  TransSetX2EvX1 revrel;
817  TransSetX2EvX1::Iterator rit_end;
818 
819  // NAME
820  std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
821 
822  // removing silent selfloops (to avoid trivial sccs and special cases in their treatment)
823  FD_WPD(0,1, "ProjectNonDet() [FB-REACH]: remove silent selfloops");
824  tit=rGen.TransRelBegin();
825  tit_end=rGen.TransRelEnd();
826  while(tit!=tit_end) {
827  if(tit->X1 == tit->X2)
828  if(!rProjectAlphabet.Exists(tit->Ev))
829  { rGen.ClrTransition(tit++); continue;}
830  ++tit;
831  }
832 
833  // initialize todo stack by init states
834  for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
835  todod.push(*sit);
836  doned.Insert(*sit);
837  }
838 
839  // process main todo stack
840  while(!todod.empty()) {
841 
842  // loop callback
843  FD_WPD(donex.Size(), rGen.Size(), "ProjectNonDet() [FB-REACH]: done/size: "
844  << donex.Size() << " / " << rGen.Size());
845 
846  // get top of the stack
847  currentstate = todod.top();
848  todod.pop();
849  FD_DF("ProjectNonDet: ---- current state: " << rGen.SStr(currentstate));
850 
851  // bail out on trivial
852  if(donex.Exists(currentstate)) continue;
853 
854  // local forward reach to find exit-only states to initialize todox
855  FD_DF("ProjectNonDet: running f-reach on " << rGen.SStr(currentstate));
856  revrel.Clear(); // local reverse map
857  todor.push(currentstate);
858  doner.Clear();
859  doner.Insert(currentstate);
860  Idx lastfound=currentstate;
861  while(!todor.empty()) {
862  reachstate = todor.top();
863  todor.pop();
864  bool local=false;
865  // iterate successors
866  tit = rGen.TransRelBegin(reachstate);
867  tit_end = rGen.TransRelEnd();
868  for(; tit != tit_end; ++tit) {
869  if(tit->X1 != reachstate) break;
870  // add high-level events to main todo
871  if(rProjectAlphabet.Exists(tit->Ev)) {
872  if(doned.Insert(tit->X2))
873  todod.push(tit->X2);
874  }
875  // add low-level events to reach todo
876  else {
877  lastfound=tit->X2;
878  revrel.Insert(*tit);
879  local=true;
880  if(doner.Insert(tit->X2))
881  todor.push(tit->X2);
882  }
883  }
884  // record exit states
885  if(!local) {
886  donex.Insert(reachstate); // global
887  if(reachstate!=currentstate) todox.push(reachstate);
888  }
889  }
890 
891  // bail out on trivial
892  if(donex.Exists(currentstate)) continue;
893 
894  FD_DF("ProjectNonDet: f-reach-proj found #" << doner.Size() << " reachable states");
895  FD_DF("ProjectNonDet: f-reach-proj found #" << (donex*doner).Size() << " exit states");
896 
897  // have fallback candidate for exit states
898  candx.Clear();
899  if(!donex.Exists(lastfound)) candx.Insert(lastfound);
900  FD_DF("ProjectNonDet: f-reach-proj found #" << candx.Size() << " exit candidates");
901 
902  // alternate backward and forward analysis until currentstate is resolved
903  while(true) {
904 
905  // backward reach on exit states to re-link predecessors and to find new exit-only states
906  FD_DF("ProjectNonDet: running b-reach-proj on exitstates");
907  while(!todox.empty()) {
908  exitstate = todox.top();
909  todox.pop();
910  FD_DF("ProjectNonDet: -- b-reach on exit state: " << rGen.SStr(exitstate));
911  // a) record attributes
912  bool mark= rGen.ExistsMarkedState(exitstate);
913  // b) figure exit transitions
914  TransSet exits;
915  tit=rGen.TransRelBegin(exitstate);
916  tit_end=rGen.TransRelEnd();
917  for(;tit!=tit_end; ++tit) {
918  if(tit->X1!=exitstate) break;
919  exits.Inject(Transition(0,tit->Ev,tit->X2));
920  }
921  // c) iterate and re-link pre-decessors
922  rit = revrel.BeginByX2(exitstate);
923  rit_end = revrel.End();
924  while(rit != rit_end) {
925  if(rit->X2!=exitstate) break;
926  // skip non-local event (automatic by local/silent reverse relation)
927  //if(rProjectAlphabet.Exists(rit->Ev)) {++rit; continue;}
928  // skip states outside current forward reach (automatic by local/silent reverse relation)
929  //if(!doner.Exists(rit->X1)) {++rit; continue;}
930  // skip states that are known exit states
931  if(donex.Exists(rit->X1)) {++rit; continue;}
932  FD_DF("ProjectNonDet: -- b-reach predecessor: " << rGen.SStr(rit->X1));
933  // propagate exit links
934  tit=exits.Begin();
935  tit_end=exits.End();
936  for(;tit!=tit_end; ++tit)
937  rGen.SetTransition(rit->X1,tit->Ev,tit->X2);
938  // propagate attributes
939  if(mark) rGen.InsMarkedState(rit->X1);
940  // unlink original local transition
941  FD_DF("unlink " << rit->Str());
942  rGen.ClrTransition(*rit);
943  // test if we found a new exit state (the if is allways true because we went back by a silent event)
944  if(!donex.Exists(rit->X1)) {
945  bool local=false;
946  tit = rGen.TransRelBegin(rit->X1); // still in revrel
947  tit_end = rGen.TransRelEnd(rit->X1);
948  while(tit != tit_end) {
949  if(rProjectAlphabet.Exists(tit->Ev)) { ++tit; continue;}
950  if(tit->X2==exitstate) {revrel.Erase(*tit); rGen.ClrTransition(tit++); continue;} //optional
951  ++tit; local=true;
952  }
953  // record new exit-only states
954  if(!local) {
955  FD_DF("ProjectNonDet: b-reach new exit state: " << rGen.SStr(currentstate));
956  todox.push(rit->X1);
957  donex.Insert(rit->X1);
958  candx.Erase(rit->X1);
959  }
960  // record candidates to overcome ssc
961  else {
962  candx.Insert(rit->X1);
963  }
964  }
965  // unlink original in reverse transition
966  revrel.Erase(rit++);
967  } // examine one exits state
968 
969  } // todox
970 
971 
972  // break f-b-alternation
973  if(doner<=donex) break; // when dealt with entire local reach
974  //if(donex.Exists(currentstate)) break; // when dealt with current state
975 
976  // do one forward reach
977  FD_DF("ProjectNonDet: b-reach-proj stuck with #" << donex.Size() << " exit states");
978  FD_DF("ProjectNonDet: choosing first of #" << candx.Size() << " exit candidates");
979  exitstate= *candx.Begin();
980 
981  // this is the std forward algorthm applied to the candidate exitstate
982  // (except that there is no need to feed the outer stack todod)
983  FD_DF("ProjectNonDet: running f-reach-proj on: " << rGen.SStr(exitstate));
984  todof.push(exitstate);
985  donef.Clear();
986  donef.Insert(exitstate);
987  bool marked=rGen.ExistsMarkedState(exitstate);
988  while(!todof.empty()) {
989  reachstate = todof.top();
990  todof.pop();
991  //track marking
992  marked|=rGen.ExistsMarkedState(reachstate);
993  // iterate successors
994  tit = rGen.TransRelBegin(reachstate);
995  tit_end = rGen.TransRelEnd();
996  for(; tit != tit_end; ++tit) {
997  if(tit->X1!=reachstate) break;
998  // for high-level events: insert new transition
999  if(rProjectAlphabet.Exists(tit->Ev))
1000  rGen.SetTransition(exitstate, tit->Ev, tit->X2);
1001  // for low-level events: add new states to f-reach todo
1002  else {
1003  if(donef.Insert(tit->X2))
1004  todof.push(tit->X2);
1005  }
1006  }
1007  } // reach
1008  FD_DF("ProjectNonDet: f-reach-proj found #" << donef.Size() << " states");
1009 
1010  // set marking
1011  if(marked) rGen.SetMarkedState(exitstate);
1012 
1013  // std forward algorithm continued - remove all silent transitions that leave exitstate
1014  tit = rGen.TransRelBegin(exitstate);
1015  tit_end = rGen.TransRelEnd();
1016  while(tit != tit_end) {
1017  if(tit->X1!=exitstate) break;
1018  if(!rProjectAlphabet.Exists(tit->Ev))
1019  rGen.ClrTransition(tit++);
1020  else
1021  ++tit;
1022  }
1023 
1024  // record new exit state
1025 #ifdef FAUDES_CHECKED
1026  if(donex.Exists(exitstate))
1027  FD_WARN("ProjectNonDet: ERROR: new exit state " << exitstate);
1028 #endif
1029  todox.push(exitstate);
1030  donex.Insert(exitstate);
1031  candx.Erase(exitstate);
1032 
1033  } // backward-forward alternation
1034 
1035  }// outer todod
1036 
1037  // inject projection alphabet, keep Attributes
1038  rGen.RestrictAlphabet(rProjectAlphabet);
1039  // make accessile
1040  rGen.RestrictStates(doned);
1041  // set name
1042  rGen.Name(name);
1043 
1044 }
1045 
1046 
1047 // Project, version 2014/03, Moor -- an alternative for some large generators.
1048 // This argorithm first substitutes each "silent strictly-connected compnent" by a
1049 // single state. The rational is that the usual forward reachalility analysis
1050 // would be applied multiple times for each such component, leading to something
1051 // close to quadratic order. Once the silent components have been eliminated,
1052 // a local backward reachability analysis can be applied, ending up with almost
1053 // linear order. Of course, the effective gain of efficiency depends on the
1054 // automata at hand --
1055 //
1056 void ProjectNonDet_scc(Generator& rGen, const EventSet& rProjectAlphabet) {
1057 
1058  // HELPERS:
1059 
1060  std::stack<Idx> todod, todor, todof, todox; // states todo
1061  StateSet doned, doner, donef, donex; // states done (aka have been put to todo)
1062  Idx currentstate; // the currently processed state
1063  Idx reachstate; // the currently processed state in reach iteration
1064  Idx exitstate; // the currently processed state in exit search iteration
1065  StateSet candx;
1066  StateSet::Iterator sit;
1067  StateSet::Iterator sit_end;
1068  TransSet::Iterator tit;
1069  TransSet::Iterator tit_end;
1070  TransSetX2EvX1 revrel;
1072  TransSetX2EvX1::Iterator rit_end;
1073 
1074  // NAME
1075  std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
1076 
1077  // removing silent selfloops (to avoid trivial sccs and special cases in their treatment)
1078  FD_WPD(0,1, "ProjectNonDet() [SCC]: remove silent selfloops");
1079  tit=rGen.TransRelBegin();
1080  tit_end=rGen.TransRelEnd();
1081  while(tit!=tit_end) {
1082  if(tit->X1 == tit->X2)
1083  if(!rProjectAlphabet.Exists(tit->Ev))
1084  { rGen.ClrTransition(tit++); continue;}
1085  ++tit;
1086  }
1087 
1088  // indentify local sccs
1089  FD_WPD(0,1, "ProjectNonDet() [SCC]: compute silent sccs");
1090  SccFilter locreach(
1092  rProjectAlphabet);
1093  std::list<StateSet> scclist;
1094  StateSet sccroots;
1095  ComputeScc(rGen,locreach,scclist,sccroots);
1096 
1097  // have one substitute state for each individual scc
1098  FD_WPD(0,1, "ProjectNonDet() [SCC]: processing #" << scclist.size() << " sccs");
1099  std::list<StateSet>::iterator cit=scclist.begin();
1100  std::list<StateSet>::iterator cit_end=scclist.end();
1101  std::map<Idx,Idx> sccxmap;
1102  // loop individual sccs
1103  for(;cit!=cit_end;++cit) {
1104  // track attributes
1105  bool init=false;
1106  bool mark=false;
1107  // introduce substitute state
1108  Idx sccx = rGen.InsState();
1109  // iterate over all transitions that start in current scc
1110  sit=cit->Begin();
1111  sit_end=cit->End();
1112  for(;sit!=sit_end;++sit){
1113  init|= rGen.ExistsInitState(*sit);
1114  mark|= rGen.ExistsMarkedState(*sit);
1115  tit=rGen.TransRelBegin(*sit);
1116  tit_end=rGen.TransRelEnd(*sit);
1117  while(tit!=tit_end) {
1118  // case a) exit this scc with any event
1119  if(!cit->Exists(tit->X2)) {
1120  rGen.SetTransition(sccx,tit->Ev,tit->X2);
1121  }
1122  // case b) remains in this scc with non-silent event
1123  else {
1124  if(rProjectAlphabet.Exists(tit->Ev))
1125  rGen.SetTransition(sccx,tit->Ev,sccx);
1126  }
1127  // delete transition
1128  rGen.ClrTransition(tit++);
1129  }
1130  }
1131  // propagate attributes to substitute state
1132  if(init) rGen.InsInitState(sccx);
1133  if(mark) rGen.InsMarkedState(sccx);
1134  // record substitution in map
1135  sit=cit->Begin();
1136  sit_end=cit->End();
1137  for(;sit!=sit_end;++sit) sccxmap[*sit]=sccx;
1138  }
1139 
1140  // apply substitution
1141  if(sccxmap.size()>0){
1142  FD_WPD(0,1, "ProjectNonDet() [SCC]: applying state substitution for #" << sccxmap.size() << " states");
1143  std::map<Idx,Idx>::iterator xxit;
1144  tit=rGen.TransRelBegin();
1145  tit_end=rGen.TransRelEnd();
1146  while(tit!=tit_end) {
1147  xxit=sccxmap.find(tit->X2);
1148  if(xxit==sccxmap.end()) {++tit;continue;}
1149  rGen.SetTransition(tit->X1,tit->Ev,xxit->second);
1150  rGen.ClrTransition(tit++);
1151  }
1152  }
1153 
1154  // delete scc states
1155  // (they dont have any transitions attached, this is cosmetic)
1156  cit=scclist.begin();
1157  cit_end=scclist.end();
1158  for(;cit!=cit_end;++cit)
1159  rGen.DelStates(*cit);
1160 
1161  // free men
1162  scclist.clear();
1163  sccxmap.clear();
1164 
1165  // initialize todo stack by init states
1166  for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
1167  todod.push(*sit);
1168  doned.Insert(*sit);
1169  }
1170 
1171  // process main todo stack
1172  while(!todod.empty()) {
1173 
1174  // loop callback
1175  FD_WPD(donex.Size(), rGen.Size(), "ProjectNonDet() [FB-REACH]: done/size: "
1176  << donex.Size() << " / " << rGen.Size());
1177 
1178  // get top of the stack
1179  currentstate = todod.top();
1180  todod.pop();
1181  FD_DF("ProjectNonDet: ---- current state: " << rGen.SStr(currentstate));
1182 
1183  // bail out on trivial
1184  if(donex.Exists(currentstate)) continue;
1185 
1186  // local forward reach to find exit-only states to initialize todox
1187  FD_DF("ProjectNonDet: running f-reach on " << rGen.SStr(currentstate));
1188  revrel.Clear(); // local reverse map
1189  todor.push(currentstate);
1190  doner.Clear();
1191  doner.Insert(currentstate);
1192  Idx lastfound=currentstate;
1193  while(!todor.empty()) {
1194  reachstate = todor.top();
1195  todor.pop();
1196  bool local=false;
1197  // iterate successors
1198  tit = rGen.TransRelBegin(reachstate);
1199  tit_end = rGen.TransRelEnd();
1200  for(; tit != tit_end; ++tit) {
1201  if(tit->X1 != reachstate) break;
1202  // add high-level events to main todo
1203  if(rProjectAlphabet.Exists(tit->Ev)) {
1204  if(doned.Insert(tit->X2))
1205  todod.push(tit->X2);
1206  }
1207  // add low-level events to reach todo
1208  else {
1209  lastfound=tit->X2;
1210  revrel.Insert(*tit);
1211  local=true;
1212  if(doner.Insert(tit->X2))
1213  todor.push(tit->X2);
1214  }
1215  }
1216  // record exit states
1217  if(!local) {
1218  donex.Insert(reachstate); // global
1219  if(reachstate!=currentstate) todox.push(reachstate);
1220  }
1221  }
1222 
1223  // bail out on trivial
1224  if(donex.Exists(currentstate)) continue;
1225 
1226  FD_DF("ProjectNonDet: f-reach-proj found #" << doner.Size() << " reachable states");
1227  FD_DF("ProjectNonDet: f-reach-proj found #" << (donex*doner).Size() << " exit states");
1228 
1229  // have fallback candidate for exit states
1230  candx.Clear();
1231  if(!donex.Exists(lastfound)) candx.Insert(lastfound);
1232  FD_DF("ProjectNonDet: f-reach-proj found #" << candx.Size() << " exit candidates");
1233 
1234  // backward reach on exit states to re-link predecessors and to find new exit-only states
1235  FD_DF("ProjectNonDet: running b-reach-proj on exitstates");
1236  while(!todox.empty()) {
1237  exitstate = todox.top();
1238  todox.pop();
1239  FD_DF("ProjectNonDet: -- b-reach on exit state: " << rGen.SStr(exitstate));
1240  // a) record attributes
1241  bool mark= rGen.ExistsMarkedState(exitstate);
1242  // b) figure exit transitions
1243  TransSet exits;
1244  tit=rGen.TransRelBegin(exitstate);
1245  tit_end=rGen.TransRelEnd();
1246  for(;tit!=tit_end; ++tit) {
1247  if(tit->X1!=exitstate) break;
1248  exits.Inject(Transition(0,tit->Ev,tit->X2));
1249  }
1250  // c) iterate and re-link pre-decessors
1251  rit = revrel.BeginByX2(exitstate);
1252  rit_end = revrel.End();
1253  while(rit != rit_end) {
1254  if(rit->X2!=exitstate) break;
1255  // skip non-local event (automatic by local/silent reverse relation)
1256  //if(rProjectAlphabet.Exists(rit->Ev)) {++rit; continue;}
1257  // skip states outside current forward reach (automatic by local/silent reverse relation)
1258  //if(!doner.Exists(rit->X1)) {++rit; continue;}
1259  // skip states that are known exit states
1260  if(donex.Exists(rit->X1)) {++rit; continue;}
1261  FD_DF("ProjectNonDet: -- b-reach predecessor: " << rGen.SStr(rit->X1));
1262  // propagate exit links
1263  tit=exits.Begin();
1264  tit_end=exits.End();
1265  for(;tit!=tit_end; ++tit)
1266  rGen.SetTransition(rit->X1,tit->Ev,tit->X2);
1267  // propagate attributes
1268  if(mark) rGen.InsMarkedState(rit->X1);
1269  // unlink original local transition
1270  FD_DF("unlink " << rit->Str());
1271  rGen.ClrTransition(*rit);
1272  // test if we found a new exit state (the if is allways true because we went back by a silent event)
1273  if(!donex.Exists(rit->X1)) {
1274  bool local=false;
1275  tit = rGen.TransRelBegin(rit->X1); // still in revrel
1276  tit_end = rGen.TransRelEnd(rit->X1);
1277  while(tit != tit_end) {
1278  if(rProjectAlphabet.Exists(tit->Ev)) { ++tit; continue;}
1279  if(tit->X2==exitstate) {revrel.Erase(*tit); rGen.ClrTransition(tit++); continue;} //optional
1280  ++tit; local=true;
1281  }
1282  // record new exit-only states
1283  if(!local) {
1284  FD_DF("ProjectNonDet: b-reach new exit state: " << rGen.SStr(currentstate));
1285  todox.push(rit->X1);
1286  donex.Insert(rit->X1);
1287  candx.Erase(rit->X1);
1288  }
1289  // record candidates to overcome ssc
1290  else {
1291  candx.Insert(rit->X1);
1292  }
1293  }
1294  // unlink original in reverse transition
1295  revrel.Erase(rit++);
1296  } // examine one exits state
1297 
1298  } // todox
1299 
1300  // since we eliminated silent sccs, we must have doner<=donex,
1301  // and we dont need to run a forward in this routive
1302 #ifdef FAUDES_CHECKED
1303  if(!donex.Exists(currentstate))
1304  FD_WARN("ProjectNonDet: ERROR: b-reach-proj stuck with #" << donex.Size() << " exit states");
1305 #endif
1306 
1307 
1308  }// outer todod
1309 
1310  // inject projection alphabet, keep Attributes
1311  rGen.RestrictAlphabet(rProjectAlphabet);
1312  // make accessile
1313  rGen.RestrictStates(doned);
1314  // set name
1315  rGen.Name(name);
1316 
1317  FD_WPD(0,1, "ProjectNonDet() [SCC]: done");
1318 }
1319 
1320 
1321 
1322 // wrapper to choose std implementation
1323 void ProjectNonDet(Generator& rGen, const EventSet& rProjectAlphabet) {
1324  ProjectNonDet_ref(rGen, rProjectAlphabet); // (here: 2014 re-code of original -2008 algorithm)
1325  //ProjectNonDet_opitz(rGen, rProjectAlphabet); // (here: original code used -2008)
1326  //ProjectNonDet_barthel(rGen, rProjectAlphabet); // (here: implementation used 2009-2013)
1327  //ProjectNonDet_fbr(rGen, rProjectAlphabet); // (here: 2014 optimized algorithm)
1328  //ProjectNonDet_scc(rGen, rProjectAlphabet); // (here: 2014 optimized algorithm)
1329 }
1330 
1331 // wrapper to make the scc version available externally (purely cosmetic)
1332 void ProjectNonDetScc(Generator& rGen, const EventSet& rProjectAlphabet) {
1333  ProjectNonDet_scc(rGen, rProjectAlphabet); // (here: 2014 optimized algorithm)
1334 }
1335 
1336 
1337 // Project(rGen, rProjectAlphabet, rResGen&)
1338 void Project(const Generator& rGen, const EventSet& rProjectAlphabet, Generator& rResGen) {
1339  FD_DF("Project(...): #" << rGen.TransRelSize());
1340  //FAUDES_TIMER_START("");
1341  // initialize result with argument generator
1342  if(&rResGen != &rGen) rResGen.Assign(rGen);
1343  // turn off state names
1344  bool se= rResGen.StateNamesEnabled();
1345  rResGen.StateNamesEnabled(false);
1346  // project non det version
1347  ProjectNonDet(rResGen, rProjectAlphabet);
1348  // make deterministic
1349  Generator* gd = rGen.New();
1350  gd->StateNamesEnabled(false);
1351  //FAUDES_TIMER_LAP("");
1352  FD_DF("Project(...): make det #" << rResGen.TransRelSize());
1353  Deterministic(rResGen, *gd);
1354  // minimize states (tmoor 201308: this is cosmetic ...
1355  // ... and turned out expensive when iterating on an observer
1356  // stateset; hence we do it only for small generators)
1357  if(gd->Size() < 20)
1358  StateMin(*gd,rResGen);
1359  else
1360  gd->Move(rResGen);
1361  delete gd;
1362  // restore state names
1363  rResGen.StateNamesEnabled(se);
1364  // set name
1365  rResGen.Name("Project("+CollapsString(rGen.Name()+")"));
1366  //FAUDES_TIMER_LAP("");
1367  FD_DF("Project(...): done #" << rResGen.TransRelSize());
1368 
1369  /*
1370  // compare with reference implementation
1371  FD_WARN("Project(...): testing #" << rGen.TransRelSize());
1372  FAUDES_TIMER_START("");
1373  Generator gref=rGen;
1374  gref.StateNamesEnabled(false);
1375  ProjectNonDet_ref(gref, rProjectAlphabet);
1376  Generator g3;
1377  g3.StateNamesEnabled(false);
1378  FD_WARN("Project(...): make det #" << gref.Size());
1379  FAUDES_TIMER_LAP("");
1380  Deterministic(gref, g3);
1381  g3.Move(gref);
1382  FAUDES_TIMER_LAP("");
1383  // compare
1384  FD_WARN("Project(...): compare #" << gref.Size());
1385  FD_WARN("Project(...): ref<=alt " << LanguageInclusion(gref,rResGen));
1386  FD_WARN("Project(...): alt<=ref " << LanguageInclusion(rResGen,gref));
1387  if(!LanguageEquality(gref,rResGen)) throw Exception("Project(Generator,EventSet)", "TEST ERROR", 506);
1388  Generator resgen=rResGen;
1389  MarkAllStates(resgen);
1390  MarkAllStates(gref);
1391  FD_WARN("Project(...): compare closed");
1392  FD_WARN("Project(...): ref<=alt " << LanguageInclusion(gref,resgen));
1393  FD_WARN("Project(...): alt<=ref " << LanguageInclusion(resgen,gref));
1394  if(!LanguageEquality(gref,resgen)) throw Exception("Project(Generator,EventSet)", "TEST ERROR", 506);
1395  */
1396 }
1397 
1398 
1399 // wrapper
1400 void aProjectNonDet(Generator& rGen, const EventSet& rProjectAlphabet) {
1401  ProjectNonDet(rGen,rProjectAlphabet);
1402 }
1403 
1404 
1405 // Project(rGen, rProjectAlphabet, rResGen&)
1406 void aProject(const Generator& rGen, const EventSet& rProjectAlphabet, Generator& rResGen) {
1407  // prepare result to keep original alphabet
1408  Generator* pResGen = &rResGen;
1409  if(&rResGen== &rGen) {
1410  pResGen= rResGen.New();
1411  }
1412  // perform op
1413  Project(rGen,rProjectAlphabet,*pResGen);
1414  // set old attributes
1415  pResGen->EventAttributes(rGen.Alphabet());
1416  // copy result
1417  if(pResGen != &rResGen) {
1418  pResGen->Move(rResGen);
1419  delete pResGen;
1420  }
1421 }
1422 
1423 
1424 
1425 // Project(rGen, rProjectAlphabet, rEntryStatesMap&, rResGen&)
1426 void Project(const Generator& rGen, const EventSet& rProjectAlphabet,
1427  std::map<Idx,StateSet>& rEntryStatesMap, Generator& rResGen) {
1428  FD_DF("Project(...)");
1429  // temporary entry state map
1430  std::map<Idx,StateSet> tmp_entrystatemap;
1431  // temporarily assign rGen to rResGen
1432  if(&rResGen != &rGen) rResGen.Assign(rGen);
1433  // project tmp with respect to palphabet
1434  ProjectNonDet_ref(rResGen, rProjectAlphabet); // must use a version that does not add states
1435  // put deterministic result into tmp
1436  Generator* tmp = rGen.New();
1437  Deterministic(rResGen, tmp_entrystatemap, *tmp);
1438  // write entry state map for minimized generator
1439  std::vector<StateSet> subsets;
1440  std::vector<Idx> newindices;
1441  // minimize states and rewrite result to rResGen
1442  StateMin(*tmp, rResGen, subsets, newindices);
1443  // build entry state map
1444  std::vector<StateSet>::size_type i;
1445  std::map<Idx,StateSet>::iterator esmit;
1446  StateSet::Iterator sit;
1447  for (i = 0; i < subsets.size(); ++i) {
1448  StateSet tmpstates;
1449  for (sit = subsets[i].Begin(); sit != subsets[i].End(); ++sit) {
1450  esmit = tmp_entrystatemap.find(*sit);
1451 #ifdef FAUDES_DEBUG_CODE
1452  if (esmit == tmp_entrystatemap.end()) {
1453  FD_DF("project internal error");
1454  abort();
1455  }
1456 #endif
1457  // insert entry states in temporary StateSet
1458  tmpstates.InsertSet(esmit->second);
1459  }
1460 
1461  rEntryStatesMap.insert(std::make_pair(newindices[i], tmpstates));
1462  }
1463  delete tmp;
1464 }
1465 
1466 
1467 // InvProject(rGen&, rProjectAlphabet)
1468 void InvProject(Generator& rGen, const EventSet& rProjectAlphabet) {
1469  // test if the alphabet of the generator is included in the given alphabet
1470  if(! (rProjectAlphabet >= (EventSet) rGen.Alphabet() ) ){
1471  std::stringstream errstr;
1472  errstr << "Input alphabet has to contain alphabet of generator \"" << rGen.Name() << "\"";
1473  throw Exception("InvProject(Generator,EventSet)", errstr.str(), 506);
1474  }
1475  EventSet newevents = rProjectAlphabet - rGen.Alphabet();
1476  // insert events into generator
1477  rGen.InsEvents(newevents);
1478  FD_DF("InvProject: adding events \"" << newevents.ToString()
1479  << "\" at every state");
1480  StateSet::Iterator lit;
1481  EventSet::Iterator eit;
1482  for (lit = rGen.StatesBegin(); lit != rGen.StatesEnd(); ++lit) {
1483  LoopCallback(); // should only be an issue for very large generators
1484  for (eit = newevents.Begin(); eit != newevents.End(); ++eit) {
1485  rGen.SetTransition(*lit, *eit, *lit);
1486  }
1487  }
1488 }
1489 
1490 
1491 
1492 // InvProject(rGen&, rProjectAlphabet)
1493 void aInvProject(Generator& rGen, const EventSet& rProjectAlphabet) {
1494  FD_DF("aInvProject(..)");
1495  // see whether the generator can digest attribues
1496  if(!rGen.Alphabet().AttributeTest(*rProjectAlphabet.AttributeType())) {
1497  InvProject(rGen,rProjectAlphabet);
1498  return;
1499  }
1500  // record extra events
1501  EventSet newevents = rProjectAlphabet - rGen.Alphabet();
1502  // perform
1503  InvProject(rGen,rProjectAlphabet);
1504  // copy all attributes from input alphabets
1505  FD_DF("aInvProject(..): fixing attributes: source " << typeid(*rProjectAlphabet.AttributeType()).name() <<
1506  " dest " << typeid(*rGen.Alphabet().AttributeType()).name());
1507  for(EventSet::Iterator eit=newevents.Begin(); eit!=newevents.End(); ++eit)
1508  rGen.EventAttribute(*eit,rProjectAlphabet.Attribute(*eit));
1509 }
1510 
1511 
1512 // InvProject
1514  const Generator& rGen,
1515  const EventSet& rProjectAlphabet,
1516  Generator& rResGen)
1517 {
1518  rResGen.Assign(rGen);
1519  aInvProject(rResGen, rProjectAlphabet);
1520 }
1521 
1522 
1523 
1524 // CreateEntryStatesMap(rRevEntryStatesMap, rEntryStatesMap)
1525 void CreateEntryStatesMap(const std::map<StateSet,Idx>& rRevEntryStatesMap,
1526  std::map<Idx,StateSet>& rEntryStatesMap) {
1527  std::map<StateSet,Idx>::const_iterator it;
1528  for (it = rRevEntryStatesMap.begin(); it != rRevEntryStatesMap.end(); ++it) {
1529  rEntryStatesMap.insert(std::make_pair(it->second, it->first));
1530  }
1531 }
1532 
1533 
1534 
1535 } // namespace faudes

libFAUDES 2.28a --- 2016.09.13 --- c++ api documentaion by doxygen