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

libFAUDES 2.24g --- 2014.09.15 --- c++ api documentaion by doxygen