cfl_project.cpp

Go to the documentation of this file.
00001 /** @file cfl_project.cpp projection and subset construction */
00002 
00003 /* FAU Discrete Event Systems Library (libfaudes)
00004 
00005    Copyright (C) 2006  Bernd Opitz
00006    Copyright (C) 2006-2014  Thomas Moor
00007    Exclusive copyright is granted to Klaus Schmidt
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Lesser General Public
00011    License as published by the Free Software Foundation; either
00012    version 2.1 of the License, or (at your option) any later version.
00013 
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Lesser General Public License for more details.
00018 
00019    You should have received a copy of the GNU Lesser General Public
00020    License along with this library; if not, write to the Free Software
00021    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00022 
00023 
00024 #include "cfl_project.h"
00025 #include "cfl_regular.h"
00026 #include "cfl_graphfncts.h"
00027 #include "cfl_localgen.h"
00028 #include "cfl_statemin.h"
00029 #include "cfl_determin.h"
00030 
00031 namespace faudes {
00032 
00033 
00034 // Project, version 2008/12, Bernd Opitz (?) --- usef for libFAUDES up to 2009
00035 // This is believed to be the original implementation by Bend Opitz, and thus might have 
00036 // been in use since 2006, however, the code snipped was grabed in 2008. The call 
00037 // of "LocalAccessibleReach" is elegant, however, needs to look up every state twice. 
00038 // In large automata, this is a relevant contribution to the computational cost. 
00039 // Our re-code of this algorithm saves half of the lookups (see _ref).
00040 
00041 // Reference, 2008/12 (original Opitz?)
00042 void ProjectNonDet_opitz(Generator& rGen, const EventSet& rProjectAlphabet) {
00043 
00044   // HELPERS:
00045   StateSet reach; // StateSet for reachable states
00046   std::stack<Idx> todo; // todo stack
00047   StateSet done; // done set
00048   Idx currentstate; // the currently processed state
00049   StateSet::Iterator lit; 
00050   TransSet::Iterator tit;
00051   TransSet::Iterator tit_end;
00052 
00053   // ALGORITHM:
00054   // initialize algorithm by pushing init states on todo stack
00055   for (lit = rGen.InitStatesBegin(); lit != rGen.InitStatesEnd(); ++lit) {
00056     FD_DF("ProjectNonDet: todo add: " << rGen.SStr(*lit));
00057     todo.push(*lit);
00058   }
00059 
00060   // process todo stack
00061   while (! todo.empty()) {
00062     currentstate = todo.top();
00063     todo.pop();
00064     done.Insert(currentstate); 
00065     FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
00066 
00067     // comp accessible reach
00068     reach.Clear();
00069     LocalAccessibleReach(rGen, rProjectAlphabet, currentstate, reach);
00070     FD_DF("ProjectNonDet: local reach: " << reach.ToString());
00071 
00072     // remove all transitions that leave current state 
00073     // with an invisible event 
00074     tit = rGen.TransRelBegin(currentstate);
00075     tit_end = rGen.TransRelEnd(currentstate);
00076     while(tit != tit_end) {
00077       FD_DF("ProjectNonDet: current transition: " << rGen.SStr(tit->X1)
00078           << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(tit->X2));
00079       if (! rProjectAlphabet.Exists(tit->Ev)) {
00080         FD_DF("ProjectNonDet: deleting current transition");
00081         TransSet::Iterator tit_tmp = tit;
00082         ++tit;
00083         rGen.ClrTransition(tit_tmp);
00084       } else {
00085         ++tit;
00086       }
00087     }
00088     
00089     // relink outgoing transitions
00090     FD_DF("ProjectNonDet: relinking outgoing transitions...");
00091     for (lit = reach.Begin(); lit != reach.End(); ++lit) {
00092       tit = rGen.TransRelBegin(*lit);
00093       tit_end = rGen.TransRelEnd(*lit);
00094       for (; tit != tit_end; ++tit) {
00095         if (rProjectAlphabet.Exists(tit->Ev)) {
00096           FD_DF("ProjectNonDet: relinking transition: " << rGen.TStr(*tit) << " to " << rGen.SStr(currentstate));
00097           rGen.SetTransition(currentstate, tit->Ev, tit->X2);
00098           if (! done.Exists(tit->X2)) {
00099             FD_DF("ProjectNonDet: todo push: " << rGen.SStr(tit->X2));
00100             todo.push(tit->X2);
00101           }
00102         }
00103       }
00104       // marked status test
00105       if (rGen.ExistsMarkedState(*lit)) {
00106         FD_DF("ProjectNonDet: setting marked state " << rGen.SStr(currentstate));
00107         rGen.SetMarkedState(currentstate);
00108       }
00109     }
00110   }
00111 
00112   // inject projection alphabet
00113   rGen.InjectAlphabet(rProjectAlphabet);
00114   
00115   // set name
00116   rGen.Name(CollapsString("Pro(" + rGen.Name() + ")"));
00117 }
00118 
00119 
00120 // Projection version 2014/03, Moor -- revision of original 2008 algorithm 
00121 // This is a re-code of the original libFAUDES project implementation, as proposed
00122 // by Bernd Opitz. The Redesign takes into account the logarithmic cost of state lockups for
00123 // large input data and avoids to call the subroutine LocalAccessibleReach. The performance gain
00124 // is about factor 2. We use this implementationas a reference.
00125 
00126 void ProjectNonDet_ref(Generator& rGen, const EventSet& rProjectAlphabet) {
00127 
00128   // HELPERS:
00129   std::stack<Idx> todod, todor; // states todo 
00130   StateSet doned, doner;        // states done (aka have been put to todo) 
00131   Idx currentstate;             // the currently processed state
00132   StateSet::Iterator sit;
00133   StateSet::Iterator sit_end;
00134   TransSet::Iterator tit;
00135   TransSet::Iterator tit_end;
00136 
00137   // NAME
00138   std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
00139 
00140   // ALGORITHM:
00141 
00142   // initialize todo stack by adding init states to todo 
00143   for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
00144     todod.push(*sit);
00145     doned.Insert(*sit); 
00146   }
00147     
00148   // process main todo stack
00149   while(!todod.empty()) {
00150 
00151     // loop callback
00152     FD_WPC(doned.Size() - todod.size(), rGen.Size(), "ProjectNonDet() [STD]: done/size: " 
00153      <<  doned.Size() - todod.size() << " / " << rGen.Size());
00154 
00155     // get top of the stack
00156     currentstate = todod.top();
00157     todod.pop();
00158     FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
00159 
00160     // local reach iteration
00161     todor.push(currentstate);
00162     doner.Clear();
00163     doner.Insert(currentstate); 
00164     bool marked=rGen.ExistsMarkedState(currentstate);
00165     while(!todor.empty()) {
00166       Idx reachstate = todor.top();
00167       todor.pop();
00168       FD_DF("ProjectNonDet: reach: " << rGen.SStr(reachstate));
00169       //track marking
00170       marked|=rGen.ExistsMarkedState(reachstate);
00171       // iterate successors
00172       tit = rGen.TransRelBegin(reachstate);
00173       tit_end = rGen.TransRelEnd();  
00174       for(; tit != tit_end; ++tit) {
00175         if(tit->X1!=reachstate) break;
00176     // for high-level events: insert new transition, add to main todo
00177   if(rProjectAlphabet.Exists(tit->Ev)) {
00178     rGen.SetTransition(currentstate, tit->Ev, tit->X2);
00179     if(doned.Insert(tit->X2)) {
00180       FD_DF("ProjectNonDet: todod insert: " << rGen.SStr(tit->X2));
00181       todod.push(tit->X2);
00182     }
00183   }
00184         // for low-level events: add new states to local reach todo
00185     else {
00186           if(doner.Insert(tit->X2)) {
00187       todor.push(tit->X2);
00188     }
00189   }
00190       }
00191     } // loop: local reach
00192 
00193     // set marking
00194     if(marked) rGen.SetMarkedState(currentstate);
00195 
00196     // remove all silent transitions that leave current state 
00197     tit = rGen.TransRelBegin(currentstate);
00198     tit_end = rGen.TransRelEnd();
00199     while(tit != tit_end) {
00200       if(tit->X1!=currentstate) break;
00201       FD_DF("ProjectNonDet: current transition: " << rGen.SStr(tit->X1)
00202       << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(tit->X2));
00203       if(!rProjectAlphabet.Exists(tit->Ev)) {
00204   FD_DF("ProjectNonDet: deleting current transition");
00205   rGen.ClrTransition(tit++);
00206       } else {
00207   ++tit;
00208       }
00209     }
00210 
00211   }// outer todo loop
00212 
00213   // inject projection alphabet, keep Attributes 
00214   rGen.RestrictAlphabet(rProjectAlphabet);
00215   // make accessile 
00216   rGen.RestrictStates(doned); 
00217   // set name
00218   rGen.Name(name);
00219 }
00220 
00221 
00222 /**
00223  * Graph data structure for transitionrelation -- EXPERIMENTAL
00224  *
00225  * We have encountered situations where the set based approach implies a 
00226  * performace penalty for large generators. This light-weight graph class 
00227  * is provided to investigate the situation and to compare perfromance.
00228  *
00229  */
00230 
00231 /**
00232  * Forward declaration to mimique recursive typedef by
00233  * templates
00234  *
00235  */
00236 template< class VLabel, class ELabel >
00237 struct TGraph;
00238 template< class VLabel, class ELabel >
00239 struct TNode;
00240 template< class VLabel, class ELabel >
00241 struct graph_iterator_t;
00242 template< class VLabel, class ELabel >
00243 struct node_iterator_t;
00244 template< class VLabel, class ELabel >
00245 struct node_entry_t;
00246 
00247 
00248 /**
00249  * A graph is modelled as a map from vertex-labels to nodes.
00250  * For our use, the vertex-labels are state indicees and each
00251  * node represents the transitions from the respective state.
00252  * 
00253  * Effectively, we implement the following structure
00254  * graph: 
00255  *    map< state-index , node >
00256  * node:
00257  *    set< node-entrz >
00258  * node-entry:
00259  *    pair < event-index , graph-iterator > >
00260  *
00261  * For convenience methods addressing the intended ussage, see
00262  * also the below specialisation.
00263  */
00264 template< class VLabel, class ELabel >
00265 struct TGraph : std::map< VLabel , TNode< VLabel , ELabel > > {
00266   // convenience typedef
00267   typedef graph_iterator_t< VLabel , ELabel> Iterator;
00268   // global min, element
00269   static inline Iterator InfX1(void) { 
00270     static TGraph gInfX1; return gInfX1.begin(); }
00271 };
00272 
00273 
00274 /**
00275  * A node represents the edges related to one individual vertex.
00276  * For our use, the edges are transitions from the state associated
00277  * with the respective vertex.
00278  */
00279 template< class VLabel, class ELabel >
00280 struct TNode : std::set< node_entry_t< VLabel , ELabel > > {
00281   // user data
00282   Idx RefCnt;
00283   Int UsrFlg;
00284   // constructors
00285   TNode( void )
00286     : std::set< node_entry_t< VLabel , ELabel > >() , RefCnt(0) , UsrFlg(0) {}
00287   TNode( const typename std::set< node_entry_t< VLabel , ELabel > > n )
00288     : std::set< node_entry_t< VLabel , ELabel > >(n) ,RefCnt(0) , UsrFlg(0) {}
00289   // convenience tyepdef
00290   typedef node_iterator_t< VLabel , ELabel> Iterator;
00291 }; 
00292 
00293 
00294 /**
00295  * A node-entry represents one edge. For our use, this corresponds
00296  * to a transition and the edge-label is the event index.
00297  */
00298 template< class VLabel, class ELabel >
00299 struct node_entry_t {
00300   // data members
00301   ELabel Ev;
00302   graph_iterator_t< VLabel , ELabel > X2It;
00303   // constructors
00304   node_entry_t( void ) {};
00305   node_entry_t( const node_entry_t< VLabel , ELabel >& ent) 
00306    : Ev(ent.Ev) , X2It(ent.X2It) {};
00307   node_entry_t( ELabel ev , graph_iterator_t< VLabel , ELabel > x2it)
00308     : Ev(ev), X2It(x2it) {}
00309   // sorting
00310   inline bool operator<(const node_entry_t< VLabel , ELabel >& ent) const {
00311     if(this->Ev < ent.Ev) return true;
00312     if(this->Ev > ent.Ev) return false;
00313     if(this->X2It->first == ent.X2It->first) return false;
00314     if(this->X2It == TGraph< VLabel , ELabel >::InfX1()) return true;
00315     if(ent.X2It == TGraph< VLabel , ELabel >::InfX1()) return false;
00316     return this->X2It->first < ent.X2It->first;
00317   }
00318 };
00319 
00320 
00321 /**
00322  * An iterators over the map of all nodes is interpreted
00323  * as a state incl. all related transition. This impelmentation
00324  * provides convenience methods to access the state index and to iterate 
00325  * over exiting transitions.
00326  */
00327 template< class VLabel, class ELabel >
00328 struct graph_iterator_t : TGraph< VLabel , ELabel >::iterator {
00329   // conveniend const cast
00330   inline graph_iterator_t< VLabel , ELabel >* FakeConst(void) const 
00331     { return (const_cast<graph_iterator_t< VLabel , ELabel >* >(this)); }
00332   // constructors
00333   graph_iterator_t( void )
00334     : TGraph< VLabel , ELabel  >::iterator() {}
00335   graph_iterator_t( typename TGraph< VLabel , ELabel >::iterator git)
00336     : TGraph< VLabel , ELabel  >::iterator(git) {}
00337   // read access
00338   inline VLabel X1(void) const { return (*this)->first; }
00339   inline typename TNode< VLabel , ELabel >::Iterator Begin(void) const
00340     { return (*FakeConst())->second.begin(); }
00341   inline typename TNode< VLabel , ELabel >::Iterator End(void) const
00342     { return (*this)->second.end(); }
00343   inline typename TNode< VLabel , ELabel >::Iterator Begin(ELabel Ev) const
00344     { return (*FakeConst())->second.lower_bound(
00345   std::make_pair(Ev , TGraph< VLabel , ELabel >::InfX1()) ); }
00346   inline typename TNode< VLabel , ELabel >::Iterator End(ELabel Ev) const
00347     { return (*FakeConst())->second.lower_bound(
00348   std::make_pair(Ev+1 , TGraph< VLabel , ELabel >::InfX1()) ); }
00349   // write access
00350   inline bool Insert(const ELabel ev, const graph_iterator_t< VLabel , ELabel > x2it) {
00351     if(! (*this)->second.insert(node_entry_t< VLabel , ELabel >(ev,x2it)).second) return false; 
00352     if(x2it!=(*this)) ++(x2it->second.RefCnt);
00353     return true;
00354   }  
00355   inline void Erase(const node_iterator_t< VLabel , ELabel > nit) {
00356     if(nit->X2It != (*this)) --(nit->X2It->second.RefCnt);
00357     (*this)->second.erase(nit);
00358   }  
00359   inline void IncRefCnt(void) {
00360     ++((*this)->second.RefCnt);
00361   }  
00362   // user data
00363   inline Int& UsrFlg(void) { return (*this)->second.UsrFlg; }
00364   // inspect for debugging
00365   std::string Str(void) const {
00366     std::stringstream rep;
00367     typename TNode< VLabel , ELabel >::Iterator nit=Begin();
00368     typename TNode< VLabel , ELabel >::Iterator nit_end=End();
00369     rep << "[" << X1() << "] ";
00370     for(; nit!=nit_end; ++nit) 
00371       rep << "(" << nit.Ev() << ")->" << nit.X2() << " ";
00372     return rep.str();
00373   }
00374 
00375 };
00376 
00377 
00378 /**
00379  * An iterator over the set of edges related to one vertex is interpreted
00380  * as a transition.
00381  */
00382 template< class VLabel, class ELabel >
00383 struct node_iterator_t : TNode< VLabel , ELabel >::iterator {
00384   node_iterator_t( void )
00385     : TNode< VLabel , ELabel >::iterator() {}
00386   node_iterator_t( const typename TNode< VLabel , ELabel >::iterator & nit)
00387     : TNode< VLabel , ELabel >::iterator(nit) {}
00388   inline ELabel Ev(void) const { return (*this)->Ev; }
00389   inline graph_iterator_t< VLabel , ELabel > X2It(void) const { return (*this)->X2It; }
00390   inline VLabel X2(void) const { return (*this)->X2It->first; }
00391 };
00392 
00393 
00394 /**
00395  * Specialisation of the graph template to provide convenience methods 
00396  * addressing the intended ussage.
00397  */
00398 template< >
00399 struct TGraph<Idx,Idx> : std::map< Idx , TNode< Idx , Idx > >{ 
00400   // convenience typedef
00401   typedef graph_iterator_t< Idx , Idx> Iterator;
00402   // conveniend const cast
00403   inline TGraph<Idx,Idx>*  FakeConst(void) const 
00404     { return (const_cast<TGraph< Idx, Idx>*>(this)); }
00405   // read access
00406   inline Iterator Begin(void) const { return FakeConst()->begin(); }
00407   inline Iterator End(void)   const { return FakeConst()->end(); }
00408   inline Iterator Begin(Idx x1) const { return FakeConst()->lower_bound(x1); }
00409   inline Iterator End(Idx x1)   const { return FakeConst()->lower_bound(x1+1); }
00410   inline Iterator Find(Idx x1) const { return FakeConst()->find(x1); }
00411   // write access
00412   inline Iterator Insert(Idx x1) { 
00413     return (this->insert(std::make_pair(x1,mapped_type()))).first; }
00414   inline bool Erase(Idx x1) {
00415     if(Find(x1)==End()) return false;
00416     Iterator git=Begin();
00417     Iterator git_end=End();
00418     for(;git!=git_end;++git) {
00419       TNode<Idx,Idx>::Iterator nit=git.Begin();
00420       TNode<Idx,Idx>::Iterator nit_end=git.End();
00421       while(nit!=nit_end) {
00422         if(nit.X2()==x1) git.Erase(nit++);
00423         else ++nit;
00424       }
00425     }
00426     return ((*this).erase(x1)) !=0; // allways true
00427   }
00428   inline void Erase(Iterator git, TNode<Idx,Idx>::Iterator nit) {
00429     Iterator x2it = nit.X2It();
00430     git.Erase(nit);
00431     if(x2it->second.RefCnt == 0 ) ((*this).erase(x2it)); 
00432   }
00433   // conversion from generator interface (destructive)
00434   void Import(vGenerator& rGen) {
00435     TransSet::Iterator tit = rGen.TransRelBegin();
00436     TransSet::Iterator tit_end = rGen.TransRelEnd();  
00437     for(; tit != tit_end; rGen.ClrTransition(tit++)) {
00438       Iterator git=find(tit->X1);
00439       if(git==end()) git = insert(std::make_pair(tit->X1,mapped_type())).first; 
00440       Iterator x2it=find(tit->X2);
00441       if(x2it==end()) x2it = insert(std::make_pair(tit->X2,mapped_type())).first; 
00442       git.Insert(tit->Ev,x2it);
00443     }
00444     StateSet::Iterator sit = rGen.InitStatesBegin();
00445     StateSet::Iterator sit_end = rGen.InitStatesEnd();
00446     for(; sit != sit_end; sit++) 
00447       Find(*sit).IncRefCnt();
00448   };
00449   // conversion to generator (destructive)
00450   void Export(vGenerator& rGen) {
00451     rGen.ClearTransRel();
00452     Iterator git=begin();
00453     Iterator git_end=end();
00454     for(; git != git_end; git++) {
00455       TNode<Idx,Idx>::Iterator nit =git.Begin();
00456       TNode<Idx,Idx>::Iterator nit_end=git.End();
00457       for(; nit != nit_end; ++nit) {
00458         rGen.InjectState(git.X1());                
00459         rGen.InjectState(nit.X2());                
00460         rGen.InjectTransition(Transition(git.X1(),nit.Ev(),nit.X2()));
00461       }
00462       git->second.clear();
00463     }
00464     this->clear();
00465   }
00466   // inspect or debugging
00467   std::string Str(void) const {
00468     std::stringstream rep;
00469     Iterator git=Begin();
00470     Iterator git_end=End();
00471     for(; git!=git_end; ++git) 
00472       rep << git.Str() << std::endl;
00473     return rep.str();
00474   }
00475   // global min, element
00476   static inline Iterator InfX1(void) { 
00477     static TGraph gInfX1;
00478     return gInfX1.begin(); }
00479 };
00480 
00481 
00482 // convenience typedefs
00483 typedef TNode<Idx,Idx> Node; 
00484 typedef TGraph<Idx,Idx> Graph;
00485 
00486 
00487 // Projection, version 2014/03, Moor -- graph based implementation of original 2008 algorithm
00488 // The performance gain is about +50%, demonstrating that the graph data structure is more
00489 // a adequate representation for intensive forward reachability analysis.
00490 
00491 void ProjectNonDet_graph(Generator& rGen, const EventSet& rProjectAlphabet) {
00492 
00493   std::stack<Graph::Iterator> todod, todor; // states todo 
00494   std::stack<Graph::Iterator> todov; // states todo 
00495   StateSet doned, doner;        // states done (aka have been put to todo) 
00496   Graph::Iterator currentstate;  // the currently processed state
00497   Graph::Iterator reachstate;   // inner reachability loop
00498   StateSet::Iterator sit;
00499   Graph trg;
00500   Graph::Iterator git, git_end;
00501   Node::Iterator nit, nit_end;
00502 
00503   // NAME
00504   std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
00505 
00506   // convert to graph
00507   FD_WARN("ProjectNonDet: convert to graph");
00508   trg.Import(rGen);
00509   FD_WARN("ProjectNonDet: convert to graph: done");
00510 
00511   // initialize todo stack by adding init states to todo 
00512   for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
00513     todod.push(trg.Find(*sit));
00514     doned.Insert(*sit); 
00515   }
00516     
00517   // process main todo stack
00518   while(!todod.empty()) {
00519 
00520     // loop callback
00521     FD_WPC(doned.Size() - todod.size(),trg.size(), "ProjectNonDet() [G1]: done/size: " 
00522      <<  doned.Size() - todod.size() << "/" << trg.size());
00523 
00524     // get top of the stack
00525     currentstate = todod.top();
00526     todod.pop();
00527     FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate.X1()));
00528 
00529     // local reach iteration
00530     todor.push(currentstate);
00531     doner.Clear();
00532     doner.Insert(currentstate.X1()); 
00533     bool marked=rGen.ExistsMarkedState(currentstate->first);
00534     while(!todor.empty()) {
00535       reachstate = todor.top();
00536       todor.pop();
00537       FD_DF("ProjectNonDet: reach: " << rGen.SStr(reachstate.X1()));
00538       //track marking
00539       marked|=rGen.ExistsMarkedState(reachstate.X1());
00540       // iterate successors
00541       nit=reachstate.Begin();
00542       nit_end=reachstate.End();
00543       for(; nit != nit_end; ++nit) {
00544     // for high-level events: add to main todo and insert new transition, 
00545   if(rProjectAlphabet.Exists(nit.Ev())) {
00546     if(doned.Insert(nit.X2())) {
00547       FD_DF("ProjectNonDet: todod insert: " << rGen.SStr(nit.X2()));
00548       todod.push(nit.X2It());
00549     }
00550           // transition is only new if we are not on the currentstate
00551           if(reachstate!=currentstate) {
00552         FD_DF("ProjectNonDet: trans insert: " << rGen.SStr(currentstate.X1()) << " -> " << rGen.SStr(nit.X2()));
00553             currentstate.Insert(nit.Ev(),nit.X2It());
00554     }
00555   }
00556         // for low-level events: add new states to reach todo
00557     else {
00558           if(doner.Insert(nit.X2())) 
00559       todor.push(nit.X2It());
00560           if(nit.X2It()==currentstate) 
00561             todov.push(reachstate);
00562   }
00563       }
00564     } // loop: local reach
00565 
00566     // set marking
00567     if(marked) rGen.SetMarkedState(currentstate->first);
00568 
00569     // remove all silent transitions that leave current state 
00570     nit=currentstate.Begin();
00571     nit_end=currentstate.End();
00572     while(nit != nit_end) {
00573       if(!rProjectAlphabet.Exists(nit.Ev())) 
00574   trg.Erase(currentstate,nit++);
00575   //currentstate.Erase(nit++);
00576       else 
00577   ++nit;
00578     }
00579 
00580   }// outer todo loop
00581 
00582   // convert back
00583   FD_WARN("ProjectNonDet: convert from graph");
00584   trg.Export(rGen);
00585   FD_WARN("ProjectNonDet: convert from graph: done");
00586 
00587   // inject projection alphabet, keep Attributes 
00588   rGen.RestrictAlphabet(rProjectAlphabet);
00589   // make accessile 
00590   rGen.RestrictStates(doned);
00591   // set name
00592   rGen.Name(name);
00593 }
00594 
00595 
00596 
00597 // Projection, version 2014/03, Moor -- simple algorithm
00598 // For large automata, the performance penalty is prohibitive. However, this
00599 // simple variant is useful for validation of correctness for other implementations.
00600 
00601 void ProjectNonDet_simple(Generator& rGen, const EventSet& rProjectAlphabet) {
00602 
00603   // HELPERS:
00604   std::stack<Idx> todod; // states todo 
00605   StateSet doned;         // states done (aka have been put to todo) 
00606   Idx currentstate; // the currently processed state
00607   StateSet::Iterator sit, sit_end;
00608   TransSet::Iterator tit, tit_end;
00609   StateSet reach;
00610 
00611   // NAME
00612   std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
00613 
00614   // ALGORITHM:
00615 
00616   // initialize todo stack by adding init states to todo 
00617   for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
00618     todod.push(*sit);
00619     doned.Insert(*sit); 
00620   }
00621     
00622   // process main todo stack
00623   while(!todod.empty()) {
00624 
00625     // loop callback
00626     FD_WPC(doned.Size(),rGen.Size(), "ProjectNonDet() [SIMPL]: current/size: " 
00627       <<  doned.Size() << " / " << rGen.Size());
00628 
00629     // get top of the stack
00630     currentstate = todod.top();
00631     todod.pop();
00632     FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
00633 
00634     // inspect successors to obtain one-step silent reach set
00635     reach.Clear();
00636     tit = rGen.TransRelBegin(currentstate);
00637     tit_end = rGen.TransRelEnd(currentstate);
00638     for(; tit != tit_end; ++tit) {
00639       if(!doned.Exists(tit->X2)) { todod.push(tit->X2); doned.Insert(tit->X2); }
00640       if(rProjectAlphabet.Exists(tit->Ev)) continue;
00641       if(tit->X2==currentstate) continue;
00642       reach.Insert(tit->X2);
00643     }
00644   
00645     // copy local successor transitions/marking to this state
00646     bool revisit=false;
00647     bool marked=rGen.ExistsMarkedState(currentstate);
00648     sit = reach.Begin();
00649     sit_end = reach.End();
00650     for(; sit != sit_end; ++sit) {
00651       tit = rGen.TransRelBegin(*sit);
00652       tit_end = rGen.TransRelEnd(*sit);
00653       for(; tit != tit_end; ++tit) {
00654         revisit|=rGen.SetTransition(currentstate,tit->Ev,tit->X2);
00655         marked|=rGen.ExistsMarkedState(tit->X1);
00656       }
00657     }
00658     if(marked) rGen.InsMarkedState(currentstate);
00659 
00660     // queue again if we introdced a new transion
00661     if(revisit) todod.push(currentstate);
00662 
00663   } // todo stack
00664 
00665   FD_DF("ProjectNonDet: finalize");
00666 
00667   // restrict to projection alphabet, keep Attributes
00668   rGen.RestrictAlphabet(rProjectAlphabet);
00669   // make accessile 
00670   rGen.Accessible();
00671   // set name
00672   rGen.Name(name);
00673 }
00674 
00675 
00676 // Project, version 2009/05, Tobias Barthel -- used for libFAUDES 2.14 to 2.23 (2009 -- 2014)
00677 // Tobias Barthel found a test case (non-deterministic? diagnoser?) in which the original implementation 
00678 // by Bernd Opitz was believed to fail. Unfortunatly, this test case is now lost. As of 2014/03, we use 
00679 // the above re-coded version of the original algorithm as our reference. We believe it to be  correct 
00680 // ... it passed all our test cases with identical results to the below implementation.
00681 void ProjectNonDet_barthel(Generator& rGen, const EventSet& rProjectAlphabet) {
00682 
00683   // HELPERS:
00684   StateSet reach, reachext; // StateSet for reachable states
00685   StateSet todo; // states todo
00686   StateSet done; // states done 
00687   Idx currentstate; // the currently processed state
00688   StateSet::Iterator lit;
00689   StateSet::Iterator lit2;
00690   TransSet::Iterator tit;
00691   TransSet::Iterator tit_end;
00692 
00693   // NAME
00694   std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
00695 
00696   // ALGORITHM:
00697 
00698   // set all locally reachable states to init states
00699   // tmoor 201403: is this needed to prevent the initial state to disappear?
00700   reachext.Clear();
00701   for(lit = rGen.InitStatesBegin(); lit != rGen.InitStatesEnd(); ++lit) {
00702     reach.Clear();
00703     LocalAccessibleReach(rGen, rProjectAlphabet, *lit, reach);
00704     reachext.InsertSet(reach);
00705   }   
00706   rGen.InsInitStates(reachext);
00707   FD_DF("ProjectNonDet: initial states: " << rGen.InitStates().ToString());
00708   // initialize algorithm by adding init states to todo 
00709   todo.InsertSet(rGen.InitStates());
00710     
00711   // process todo stack
00712   while(!todo.Empty()) {
00713     currentstate = *todo.Begin();
00714     todo.Erase(*todo.Begin());
00715     done.Insert(currentstate); // mark as done
00716     FD_DF("ProjectNonDet: current state: " << rGen.SStr(currentstate));
00717 
00718     // comp accessible reach
00719     reach.Clear();
00720     LocalAccessibleReach(rGen, rProjectAlphabet, currentstate, reach);
00721     FD_DF("ProjectNonDet: local reach: " << reach.ToString());
00722     
00723     // relink outgoing transitions
00724     FD_DF("ProjectNonDet: relinking outgoing transitions...");
00725     for(lit = reach.Begin(); lit != reach.End(); ++lit) {
00726       tit = rGen.TransRelBegin(*lit);
00727       tit_end = rGen.TransRelEnd(*lit);
00728       for(; tit != tit_end; ++tit) {
00729   if(rProjectAlphabet.Exists(tit->Ev)) {
00730     FD_DF("ProjectNonDet: relinking transition: " << rGen.TStr(*tit) << " to " << rGen.SStr(currentstate));
00731     rGen.SetTransition(currentstate, tit->Ev, tit->X2);
00732     if (!done.Exists(tit->X2)) {
00733       FD_DF("ProjectNonDet: todo insert: " << rGen.SStr(tit->X2));
00734       todo.Insert(tit->X2);
00735     }
00736     // add transistions to all states in extended local reach
00737           // tmoor 201308: I dont see why we need this ...
00738     reachext.Clear();
00739     LocalAccessibleReach(rGen, rProjectAlphabet, tit->X2, reachext);
00740     FD_DF("ProjectNonDet: local reach from state " << tit->X2 << ": " << reachext.ToString());
00741     for (lit2 = reachext.Begin(); lit2 != reachext.End(); ++lit2) {
00742       if (!rGen.ExistsTransition(tit->X2, tit->Ev, *lit2) && (tit->X2 != *lit2)) {
00743         rGen.SetTransition(tit->X1, tit->Ev, *lit2);
00744         FD_DF("ProjectNonDet: setting transition: " << rGen.SStr(tit->X1) << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(*lit2));
00745         //if (!done.Exists(*lit2) && !todo.Exists(*lit2)) {
00746         if (!done.Exists(*lit2)) {
00747     FD_DF("ProjectNonDet: todo insert: " << rGen.SStr(tit->X2));
00748     todo.Insert(*lit2);
00749         }
00750       }
00751     }
00752   }
00753       }
00754       // marked status test
00755       if(rGen.ExistsMarkedState(*lit)) {
00756   FD_DF("ProjectNonDet: setting marked state " << rGen.SStr(currentstate));
00757   rGen.SetMarkedState(currentstate);
00758       }
00759     }
00760    
00761     // remove all silent transitions that leave current state 
00762     tit = rGen.TransRelBegin(currentstate);
00763     tit_end = rGen.TransRelEnd(currentstate);
00764     while(tit != tit_end) {
00765       FD_DF("ProjectNonDet: current transition: " << rGen.SStr(tit->X1)
00766       << "-" << rGen.EStr(tit->Ev) << "-" << rGen.SStr(tit->X2));
00767       if(!rProjectAlphabet.Exists(tit->Ev)) {
00768   FD_DF("ProjectNonDet: deleting current transition");
00769   rGen.ClrTransition(tit++);
00770       } else {
00771   ++tit;
00772       }
00773     }
00774 
00775   }// todo loop
00776 
00777   // restrict to projection alphabet, keep Attributes (could also use Inject here)
00778   rGen.RestrictAlphabet(rProjectAlphabet);
00779 
00780   // make accessile
00781   rGen.Accessible();
00782   
00783   // set name
00784   rGen.Name(name);
00785 
00786 }
00787 
00788 
00789 // Project, version 2014/03, Moor -- an alternative for some large generators.
00790 // This argorithm is derived from the reference version. Subsequent to each forward
00791 // reachablity analysis, the states at which only non-silent events are enabled 
00792 // are identified as must-exit-states. From those, a backward search is conducted, to
00793 // introduce more must-exit-states. Idealy, this resolvs the entire foreard reach region.
00794 // This is beneficial when compared to the reference algorithm, in which only the
00795 // first state of the region is resolved. However, the backward reach will stuck
00796 // if the forard reach region contains strongly connected components. See also the 
00797 // ProjectNonDet_scc variant, which addresses this issue.
00798 
00799 void ProjectNonDet_fbr(Generator& rGen, const EventSet& rProjectAlphabet) {
00800 
00801   // HELPERS:
00802 
00803   std::stack<Idx> todod, todor, todof, todox;  // states todo 
00804   StateSet doned, doner, donef, donex;         // states done (aka have been put to todo) 
00805   Idx currentstate;                     // the currently processed state
00806   Idx reachstate;                       // the currently processed state in reach iteration
00807   Idx exitstate;                        // the currently processed state in exit search iteration
00808   StateSet candx;
00809   StateSet::Iterator sit;
00810   StateSet::Iterator sit_end;
00811   TransSet::Iterator tit;
00812   TransSet::Iterator tit_end;
00813   TransSetX2EvX1 revrel;
00814   TransSetX2EvX1::Iterator rit;
00815   TransSetX2EvX1::Iterator rit_end;
00816 
00817   // NAME
00818   std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
00819 
00820   // removing silent selfloops (to avoid trivial sccs and special cases in their treatment)
00821   FD_WPD(0,1, "ProjectNonDet() [FB-REACH]: remove silent selfloops");
00822   tit=rGen.TransRelBegin(); 
00823   tit_end=rGen.TransRelEnd(); 
00824   while(tit!=tit_end) {
00825     if(tit->X1 == tit->X2)
00826        if(!rProjectAlphabet.Exists(tit->Ev))
00827    { rGen.ClrTransition(tit++); continue;}
00828     ++tit;
00829   }
00830 
00831   // initialize todo stack by init states
00832   for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
00833     todod.push(*sit);
00834     doned.Insert(*sit); 
00835   }
00836 
00837   // process main todo stack
00838   while(!todod.empty()) {
00839 
00840     // loop callback
00841     FD_WPD(donex.Size(), rGen.Size(), "ProjectNonDet() [FB-REACH]: done/size: " 
00842      <<  donex.Size()  << " / " << rGen.Size());
00843 
00844     // get top of the stack
00845     currentstate = todod.top();
00846     todod.pop();
00847     FD_DF("ProjectNonDet: ---- current state: " << rGen.SStr(currentstate));
00848 
00849     // bail out on trivial
00850     if(donex.Exists(currentstate)) continue;
00851 
00852     // local forward reach to find exit-only states to initialize todox
00853     FD_DF("ProjectNonDet: running f-reach on " << rGen.SStr(currentstate));
00854     revrel.Clear();  // local reverse map
00855     todor.push(currentstate);
00856     doner.Clear();
00857     doner.Insert(currentstate); 
00858     Idx lastfound=currentstate;
00859     while(!todor.empty()) {
00860       reachstate = todor.top();
00861       todor.pop();
00862       bool local=false;
00863       // iterate successors
00864       tit = rGen.TransRelBegin(reachstate);
00865       tit_end = rGen.TransRelEnd();  
00866       for(; tit != tit_end; ++tit) {
00867         if(tit->X1 != reachstate) break;
00868     // add high-level events to main todo
00869   if(rProjectAlphabet.Exists(tit->Ev)) {
00870     if(doned.Insert(tit->X2)) 
00871       todod.push(tit->X2);
00872   }
00873         // add low-level events to reach todo
00874         else {
00875           lastfound=tit->X2;
00876           revrel.Insert(*tit);
00877           local=true;
00878           if(doner.Insert(tit->X2)) 
00879       todor.push(tit->X2);
00880   }
00881       }
00882       // record exit states
00883       if(!local) {
00884         donex.Insert(reachstate); // global
00885         if(reachstate!=currentstate) todox.push(reachstate);
00886       }
00887     }
00888 
00889     // bail out on trivial
00890     if(donex.Exists(currentstate)) continue;
00891 
00892     FD_DF("ProjectNonDet: f-reach-proj found #" << doner.Size() << " reachable states");
00893     FD_DF("ProjectNonDet: f-reach-proj found #" << (donex*doner).Size() << " exit states");
00894 
00895     // have fallback candidate for exit states
00896     candx.Clear();
00897     if(!donex.Exists(lastfound)) candx.Insert(lastfound);
00898     FD_DF("ProjectNonDet: f-reach-proj found #" << candx.Size() << " exit candidates");
00899 
00900     // alternate backward and forward analysis until currentstate is resolved
00901     while(true) {
00902 
00903       // backward reach on exit states to re-link predecessors and to find new exit-only states
00904       FD_DF("ProjectNonDet: running b-reach-proj on exitstates");
00905       while(!todox.empty()) {
00906         exitstate = todox.top();
00907         todox.pop();
00908         FD_DF("ProjectNonDet: -- b-reach on exit state: " << rGen.SStr(exitstate));
00909         // a) record attributes
00910         bool mark= rGen.ExistsMarkedState(exitstate);
00911         // b) figure exit transitions
00912         TransSet exits;
00913         tit=rGen.TransRelBegin(exitstate); 
00914         tit_end=rGen.TransRelEnd(); 
00915         for(;tit!=tit_end; ++tit) {
00916           if(tit->X1!=exitstate) break;
00917           exits.Inject(Transition(0,tit->Ev,tit->X2));
00918   }
00919         // c) iterate and re-link pre-decessors
00920         rit = revrel.BeginByX2(exitstate);
00921         rit_end = revrel.End();
00922         while(rit != rit_end) {
00923           if(rit->X2!=exitstate) break;
00924           // skip non-local event (automatic by local/silent reverse relation)
00925       //if(rProjectAlphabet.Exists(rit->Ev)) {++rit; continue;}
00926           // skip states outside current forward reach (automatic by local/silent reverse relation)
00927           //if(!doner.Exists(rit->X1)) {++rit; continue;}
00928           // skip states that are known exit states
00929           if(donex.Exists(rit->X1)) {++rit; continue;}
00930           FD_DF("ProjectNonDet: -- b-reach predecessor: " << rGen.SStr(rit->X1));
00931           // propagate exit links
00932           tit=exits.Begin(); 
00933           tit_end=exits.End(); 
00934           for(;tit!=tit_end; ++tit) 
00935             rGen.SetTransition(rit->X1,tit->Ev,tit->X2);
00936           // propagate attributes
00937           if(mark) rGen.InsMarkedState(rit->X1);
00938           // unlink original local transition
00939           FD_DF("unlink " << rit->Str());
00940           rGen.ClrTransition(*rit);
00941           // test if we found a new exit state (the if is allways true because we went back by a silent event)
00942           if(!donex.Exists(rit->X1)) {
00943             bool local=false;
00944             tit = rGen.TransRelBegin(rit->X1); // still in revrel
00945             tit_end = rGen.TransRelEnd(rit->X1);  
00946             while(tit != tit_end) {
00947               if(rProjectAlphabet.Exists(tit->Ev)) { ++tit; continue;}
00948           if(tit->X2==exitstate) {revrel.Erase(*tit); rGen.ClrTransition(tit++); continue;} //optional
00949               ++tit; local=true; 
00950             }
00951             // record new exit-only states
00952             if(!local) {
00953               FD_DF("ProjectNonDet: b-reach new exit state: " << rGen.SStr(currentstate));
00954               todox.push(rit->X1);
00955               donex.Insert(rit->X1);
00956               candx.Erase(rit->X1);
00957           } 
00958             // record candidates to overcome ssc  
00959             else {
00960               candx.Insert(rit->X1);
00961       }
00962       }
00963           // unlink original in reverse transition
00964           revrel.Erase(rit++);
00965         } // examine one exits state
00966 
00967       } // todox
00968 
00969 
00970       // break f-b-alternation
00971       if(doner<=donex) break;  // when dealt with entire local reach
00972       //if(donex.Exists(currentstate)) break; // when dealt with current state
00973 
00974       // do one forward reach 
00975       FD_DF("ProjectNonDet: b-reach-proj stuck with #" << donex.Size() << " exit states");
00976       FD_DF("ProjectNonDet: choosing first of #" << candx.Size() << " exit candidates");
00977       exitstate= *candx.Begin();
00978      
00979       // this is the std forward algorthm applied to the candidate exitstate
00980       // (except that there is no need to feed the outer stack todod)
00981       FD_DF("ProjectNonDet: running f-reach-proj on: " << rGen.SStr(exitstate));
00982       todof.push(exitstate);
00983       donef.Clear();
00984       donef.Insert(exitstate); 
00985       bool marked=rGen.ExistsMarkedState(exitstate);
00986       while(!todof.empty()) {
00987         reachstate = todof.top();
00988         todof.pop();
00989         //track marking
00990         marked|=rGen.ExistsMarkedState(reachstate);
00991         // iterate successors
00992         tit = rGen.TransRelBegin(reachstate);
00993         tit_end = rGen.TransRelEnd();  
00994         for(; tit != tit_end; ++tit) {
00995           if(tit->X1!=reachstate) break;
00996         // for high-level events: insert new transition
00997     if(rProjectAlphabet.Exists(tit->Ev)) 
00998       rGen.SetTransition(exitstate, tit->Ev, tit->X2);
00999           // for low-level events: add new states to f-reach todo
01000         else {
01001             if(donef.Insert(tit->X2)) 
01002         todof.push(tit->X2);
01003     }
01004   }
01005       } // reach
01006       FD_DF("ProjectNonDet: f-reach-proj found #" << donef.Size() << " states");
01007    
01008       // set marking
01009       if(marked) rGen.SetMarkedState(exitstate);
01010 
01011       // std forward algorithm continued - remove all silent transitions that leave exitstate
01012       tit = rGen.TransRelBegin(exitstate);
01013       tit_end = rGen.TransRelEnd();
01014       while(tit != tit_end) {
01015         if(tit->X1!=exitstate) break;
01016         if(!rProjectAlphabet.Exists(tit->Ev)) 
01017     rGen.ClrTransition(tit++);
01018         else 
01019   ++tit;
01020       }
01021 
01022       // record new exit state
01023 #ifdef FAUDES_CHECKED
01024       if(donex.Exists(exitstate)) 
01025   FD_WARN("ProjectNonDet: ERROR: new exit state " << exitstate);
01026 #endif
01027       todox.push(exitstate);
01028       donex.Insert(exitstate);
01029       candx.Erase(exitstate);
01030 
01031     }  // backward-forward alternation
01032 
01033   }// outer todod 
01034 
01035   // inject projection alphabet, keep Attributes 
01036   rGen.RestrictAlphabet(rProjectAlphabet);
01037   // make accessile 
01038   rGen.RestrictStates(doned);
01039   // set name
01040   rGen.Name(name);
01041 
01042 }
01043 
01044 
01045 // Project, version 2014/03, Moor -- an alternative for some large generators.
01046 // This argorithm first substitutes each "silent strictly-connected compnent" by a 
01047 // single state. The rational is that the usual forward reachalility analysis 
01048 // would be applied multiple times for each such component, leading to something
01049 // close to quadratic order. Once the silent components have been eliminated,
01050 // a local backward reachability analysis can be applied, ending up with almost
01051 // linear order. Of course, the effective gain of efficiency depends on the
01052 // automata at hand --  
01053 //
01054 void ProjectNonDet_scc(Generator& rGen, const EventSet& rProjectAlphabet) {
01055 
01056   // HELPERS:
01057 
01058   std::stack<Idx> todod, todor, todof, todox;  // states todo 
01059   StateSet doned, doner, donef, donex;         // states done (aka have been put to todo) 
01060   Idx currentstate;                     // the currently processed state
01061   Idx reachstate;                       // the currently processed state in reach iteration
01062   Idx exitstate;                        // the currently processed state in exit search iteration
01063   StateSet candx;
01064   StateSet::Iterator sit;
01065   StateSet::Iterator sit_end;
01066   TransSet::Iterator tit;
01067   TransSet::Iterator tit_end;
01068   TransSetX2EvX1 revrel;
01069   TransSetX2EvX1::Iterator rit;
01070   TransSetX2EvX1::Iterator rit_end;
01071 
01072   // NAME
01073   std::string name=CollapsString("ProjectNonDet(" + rGen.Name() + ")");
01074 
01075   // removing silent selfloops (to avoid trivial sccs and special cases in their treatment)
01076   FD_WPD(0,1, "ProjectNonDet() [SCC]: remove silent selfloops");
01077   tit=rGen.TransRelBegin(); 
01078   tit_end=rGen.TransRelEnd(); 
01079   while(tit!=tit_end) {
01080     if(tit->X1 == tit->X2)
01081        if(!rProjectAlphabet.Exists(tit->Ev))
01082    { rGen.ClrTransition(tit++); continue;}
01083     ++tit;
01084   }
01085 
01086   // indentify local sccs
01087   FD_WPD(0,1, "ProjectNonDet() [SCC]: compute silent sccs");
01088   SccFilter locreach(
01089      SccFilter::FmEventsAvoid|SccFilter::FmIgnoreUnaccessible|SccFilter::FmIgnoreTrivial,
01090      rProjectAlphabet);
01091   std::list<StateSet> scclist;
01092   StateSet sccroots;
01093   ComputeScc(rGen,locreach,scclist,sccroots);
01094 
01095   // have one substitute state for each individual scc
01096   FD_WPD(0,1, "ProjectNonDet() [SCC]: processing #" << scclist.size() << " sccs");
01097   std::list<StateSet>::iterator cit=scclist.begin();
01098   std::list<StateSet>::iterator cit_end=scclist.end();
01099   std::map<Idx,Idx> sccxmap;
01100   // loop individual sccs
01101   for(;cit!=cit_end;++cit) {
01102     // track attributes
01103     bool init=false;
01104     bool mark=false;
01105     // introduce substitute state
01106     Idx sccx = rGen.InsState();
01107     // iterate over all transitions that start in current scc
01108     sit=cit->Begin();    
01109     sit_end=cit->End();    
01110     for(;sit!=sit_end;++sit){
01111       init|= rGen.ExistsInitState(*sit);
01112       mark|= rGen.ExistsMarkedState(*sit);
01113       tit=rGen.TransRelBegin(*sit); 
01114       tit_end=rGen.TransRelEnd(*sit); 
01115       while(tit!=tit_end) {
01116         // case a) exit this scc with any event
01117         if(!cit->Exists(tit->X2)) {
01118           rGen.SetTransition(sccx,tit->Ev,tit->X2);
01119   }
01120         // case b) remains in this scc with non-silent event
01121         else {
01122           if(rProjectAlphabet.Exists(tit->Ev)) 
01123             rGen.SetTransition(sccx,tit->Ev,sccx);
01124   }
01125         // delete transition
01126         rGen.ClrTransition(tit++);
01127       }
01128     }
01129     // propagate attributes to substitute state
01130     if(init) rGen.InsInitState(sccx);
01131     if(mark) rGen.InsMarkedState(sccx);
01132     // record substitution in map
01133     sit=cit->Begin();    
01134     sit_end=cit->End();    
01135     for(;sit!=sit_end;++sit) sccxmap[*sit]=sccx;
01136   }
01137 
01138   // apply substitution
01139   if(sccxmap.size()>0){
01140     FD_WPD(0,1, "ProjectNonDet() [SCC]: applying state substitution for #" << sccxmap.size() << " states");
01141     std::map<Idx,Idx>::iterator xxit;
01142     tit=rGen.TransRelBegin(); 
01143     tit_end=rGen.TransRelEnd(); 
01144     while(tit!=tit_end) {
01145       xxit=sccxmap.find(tit->X2);
01146       if(xxit==sccxmap.end()) {++tit;continue;}
01147       rGen.SetTransition(tit->X1,tit->Ev,xxit->second);
01148       rGen.ClrTransition(tit++);
01149     }
01150   }
01151 
01152   // delete scc states 
01153   // (they dont have any transitions attached, this is cosmetic)
01154   cit=scclist.begin();
01155   cit_end=scclist.end();
01156   for(;cit!=cit_end;++cit) 
01157     rGen.DelStates(*cit);
01158 
01159   // free men
01160   scclist.clear();
01161   sccxmap.clear();
01162  
01163   // initialize todo stack by init states
01164   for(sit=rGen.InitStatesBegin(); sit!=rGen.InitStatesEnd(); ++sit) {
01165     todod.push(*sit);
01166     doned.Insert(*sit); 
01167   }
01168 
01169   // process main todo stack
01170   while(!todod.empty()) {
01171 
01172     // loop callback
01173     FD_WPD(donex.Size(), rGen.Size(), "ProjectNonDet() [FB-REACH]: done/size: " 
01174      <<  donex.Size()  << " / " << rGen.Size());
01175 
01176     // get top of the stack
01177     currentstate = todod.top();
01178     todod.pop();
01179     FD_DF("ProjectNonDet: ---- current state: " << rGen.SStr(currentstate));
01180 
01181     // bail out on trivial
01182     if(donex.Exists(currentstate)) continue;
01183 
01184     // local forward reach to find exit-only states to initialize todox
01185     FD_DF("ProjectNonDet: running f-reach on " << rGen.SStr(currentstate));
01186     revrel.Clear();  // local reverse map
01187     todor.push(currentstate);
01188     doner.Clear();
01189     doner.Insert(currentstate); 
01190     Idx lastfound=currentstate;
01191     while(!todor.empty()) {
01192       reachstate = todor.top();
01193       todor.pop();
01194       bool local=false;
01195       // iterate successors
01196       tit = rGen.TransRelBegin(reachstate);
01197       tit_end = rGen.TransRelEnd();  
01198       for(; tit != tit_end; ++tit) {
01199         if(tit->X1 != reachstate) break;
01200     // add high-level events to main todo
01201   if(rProjectAlphabet.Exists(tit->Ev)) {
01202     if(doned.Insert(tit->X2)) 
01203       todod.push(tit->X2);
01204   }
01205         // add low-level events to reach todo
01206         else {
01207           lastfound=tit->X2;
01208           revrel.Insert(*tit);
01209           local=true;
01210           if(doner.Insert(tit->X2)) 
01211       todor.push(tit->X2);
01212   }
01213       }
01214       // record exit states
01215       if(!local) {
01216         donex.Insert(reachstate); // global
01217         if(reachstate!=currentstate) todox.push(reachstate);
01218       }
01219     }
01220 
01221     // bail out on trivial
01222     if(donex.Exists(currentstate)) continue;
01223 
01224     FD_DF("ProjectNonDet: f-reach-proj found #" << doner.Size() << " reachable states");
01225     FD_DF("ProjectNonDet: f-reach-proj found #" << (donex*doner).Size() << " exit states");
01226 
01227     // have fallback candidate for exit states
01228     candx.Clear();
01229     if(!donex.Exists(lastfound)) candx.Insert(lastfound);
01230     FD_DF("ProjectNonDet: f-reach-proj found #" << candx.Size() << " exit candidates");
01231 
01232     // backward reach on exit states to re-link predecessors and to find new exit-only states
01233     FD_DF("ProjectNonDet: running b-reach-proj on exitstates");
01234     while(!todox.empty()) {
01235       exitstate = todox.top();
01236       todox.pop();
01237       FD_DF("ProjectNonDet: -- b-reach on exit state: " << rGen.SStr(exitstate));
01238       // a) record attributes
01239       bool mark= rGen.ExistsMarkedState(exitstate);
01240       // b) figure exit transitions
01241       TransSet exits;
01242       tit=rGen.TransRelBegin(exitstate); 
01243       tit_end=rGen.TransRelEnd(); 
01244       for(;tit!=tit_end; ++tit) {
01245         if(tit->X1!=exitstate) break;
01246         exits.Inject(Transition(0,tit->Ev,tit->X2));
01247       }
01248       // c) iterate and re-link pre-decessors
01249       rit = revrel.BeginByX2(exitstate);
01250       rit_end = revrel.End();
01251       while(rit != rit_end) {
01252         if(rit->X2!=exitstate) break;
01253         // skip non-local event (automatic by local/silent reverse relation)
01254         //if(rProjectAlphabet.Exists(rit->Ev)) {++rit; continue;}
01255         // skip states outside current forward reach (automatic by local/silent reverse relation)
01256         //if(!doner.Exists(rit->X1)) {++rit; continue;}
01257         // skip states that are known exit states
01258         if(donex.Exists(rit->X1)) {++rit; continue;}
01259         FD_DF("ProjectNonDet: -- b-reach predecessor: " << rGen.SStr(rit->X1));
01260         // propagate exit links
01261         tit=exits.Begin(); 
01262         tit_end=exits.End(); 
01263         for(;tit!=tit_end; ++tit) 
01264           rGen.SetTransition(rit->X1,tit->Ev,tit->X2);
01265         // propagate attributes
01266         if(mark) rGen.InsMarkedState(rit->X1);
01267         // unlink original local transition
01268         FD_DF("unlink " << rit->Str());
01269         rGen.ClrTransition(*rit);
01270         // test if we found a new exit state (the if is allways true because we went back by a silent event)
01271         if(!donex.Exists(rit->X1)) {
01272           bool local=false;
01273           tit = rGen.TransRelBegin(rit->X1); // still in revrel
01274           tit_end = rGen.TransRelEnd(rit->X1);  
01275           while(tit != tit_end) {
01276             if(rProjectAlphabet.Exists(tit->Ev)) { ++tit; continue;}
01277         if(tit->X2==exitstate) {revrel.Erase(*tit); rGen.ClrTransition(tit++); continue;} //optional
01278             ++tit; local=true; 
01279           }
01280           // record new exit-only states
01281           if(!local) {
01282             FD_DF("ProjectNonDet: b-reach new exit state: " << rGen.SStr(currentstate));
01283             todox.push(rit->X1);
01284             donex.Insert(rit->X1);
01285             candx.Erase(rit->X1);
01286         } 
01287           // record candidates to overcome ssc  
01288           else {
01289             candx.Insert(rit->X1);
01290           }
01291         }
01292         // unlink original in reverse transition
01293         revrel.Erase(rit++);
01294       } // examine one exits state
01295 
01296     } // todox
01297 
01298     // since we eliminated silent sccs, we must have doner<=donex,
01299     // and we dont need to run a forward in this routive
01300 #ifdef FAUDES_CHECKED
01301     if(!donex.Exists(currentstate)) 
01302       FD_WARN("ProjectNonDet: ERROR: b-reach-proj stuck with #" << donex.Size() << " exit states");
01303 #endif
01304  
01305 
01306   }// outer todod 
01307 
01308   // inject projection alphabet, keep Attributes 
01309   rGen.RestrictAlphabet(rProjectAlphabet);
01310   // make accessile 
01311   rGen.RestrictStates(doned);
01312   // set name
01313   rGen.Name(name);
01314 
01315   FD_WPD(0,1, "ProjectNonDet() [SCC]: done");
01316 }
01317 
01318 
01319       
01320 // wrapper to choose std implementation 
01321 void ProjectNonDet(Generator& rGen, const EventSet& rProjectAlphabet) {
01322   ProjectNonDet_ref(rGen, rProjectAlphabet);     // (here: 2014 re-code of original -2008 algorithm)
01323   //ProjectNonDet_opitz(rGen, rProjectAlphabet);   // (here: original code used -2008)
01324   //ProjectNonDet_barthel(rGen, rProjectAlphabet); // (here: implementation used 2009-2013)
01325   //ProjectNonDet_fbr(rGen, rProjectAlphabet);     // (here: 2014 optimized algorithm)
01326   //ProjectNonDet_scc(rGen, rProjectAlphabet);     // (here: 2014 optimized algorithm)
01327 }
01328 
01329 // wrapper to make the scc version available externally (purely cosmetic)
01330 void ProjectNonDetScc(Generator& rGen, const EventSet& rProjectAlphabet) {
01331   ProjectNonDet_scc(rGen, rProjectAlphabet); // (here: 2014 optimized algorithm)
01332 }
01333 
01334 
01335 // Project(rGen, rProjectAlphabet, rResGen&)
01336 void Project(const Generator& rGen, const EventSet& rProjectAlphabet, Generator& rResGen) {
01337   FD_DF("Project(...): #" << rGen.TransRelSize());
01338   //FAUDES_TIMER_START("");
01339   // initialize result with argument generator
01340   if(&rResGen != &rGen) rResGen.Assign(rGen);
01341   // turn off state names
01342   bool se= rResGen.StateNamesEnabled();
01343   rResGen.StateNamesEnabled(false);
01344   // project non det version
01345   ProjectNonDet(rResGen, rProjectAlphabet);
01346   // make deterministic
01347   Generator* gd = rGen.New(); 
01348   gd->StateNamesEnabled(false);
01349   //FAUDES_TIMER_LAP("");
01350   FD_DF("Project(...): make det #" << rResGen.TransRelSize());
01351   Deterministic(rResGen, *gd);
01352   // minimize states  (tmoor 201308: this is cosmetic ... 
01353   // ... and turned out expensive when iterating on an observer 
01354   // stateset; hence we do it only for small generators)
01355   if(gd->Size() < 20)
01356     StateMin(*gd,rResGen);
01357   else
01358     gd->Move(rResGen);
01359   delete gd;
01360   // restore state names
01361   rResGen.StateNamesEnabled(se);
01362   // set name
01363   rResGen.Name("Project("+CollapsString(rGen.Name()+")")); 
01364   //FAUDES_TIMER_LAP("");
01365   FD_DF("Project(...): done #" << rResGen.TransRelSize());
01366   
01367   /*
01368   // compare with reference implementation
01369   FD_WARN("Project(...): testing #" << rGen.TransRelSize());
01370   FAUDES_TIMER_START("");
01371   Generator gref=rGen;
01372   gref.StateNamesEnabled(false);
01373   ProjectNonDet_ref(gref, rProjectAlphabet);
01374   Generator g3;
01375   g3.StateNamesEnabled(false);
01376   FD_WARN("Project(...): make det #" << gref.Size());
01377   FAUDES_TIMER_LAP("");
01378   Deterministic(gref, g3);
01379   g3.Move(gref);
01380   FAUDES_TIMER_LAP("");
01381   // compare
01382   FD_WARN("Project(...): compare #" << gref.Size());
01383   FD_WARN("Project(...): ref<=alt " << LanguageInclusion(gref,rResGen));
01384   FD_WARN("Project(...): alt<=ref " << LanguageInclusion(rResGen,gref));
01385   if(!LanguageEquality(gref,rResGen))    throw Exception("Project(Generator,EventSet)", "TEST ERROR", 506);
01386   Generator resgen=rResGen;
01387   MarkAllStates(resgen);
01388   MarkAllStates(gref);
01389   FD_WARN("Project(...): compare closed");
01390   FD_WARN("Project(...): ref<=alt " << LanguageInclusion(gref,resgen));
01391   FD_WARN("Project(...): alt<=ref " << LanguageInclusion(resgen,gref));
01392   if(!LanguageEquality(gref,resgen))    throw Exception("Project(Generator,EventSet)", "TEST ERROR", 506);  
01393   */
01394 }
01395 
01396    
01397 // wrapper
01398 void aProjectNonDet(Generator& rGen, const EventSet& rProjectAlphabet) {
01399   ProjectNonDet(rGen,rProjectAlphabet);
01400 }
01401 
01402 
01403 // Project(rGen, rProjectAlphabet, rResGen&)
01404 void aProject(const Generator& rGen, const EventSet& rProjectAlphabet, Generator& rResGen) {
01405   // prepare result to keep original alphabet
01406   Generator* pResGen = &rResGen;
01407   if(&rResGen== &rGen) {
01408     pResGen= rResGen.New();
01409   }
01410   // perform op
01411   Project(rGen,rProjectAlphabet,*pResGen);
01412   // set old attributes
01413   pResGen->EventAttributes(rGen.Alphabet());
01414   // copy result
01415   if(pResGen != &rResGen) {
01416     pResGen->Move(rResGen);
01417     delete pResGen;
01418   }
01419 }
01420 
01421 
01422 
01423 // Project(rGen, rProjectAlphabet, rEntryStatesMap&, rResGen&)
01424 void Project(const Generator& rGen, const EventSet& rProjectAlphabet,
01425        std::map<Idx,StateSet>& rEntryStatesMap, Generator& rResGen) {
01426   FD_DF("Project(...)");
01427   // temporary entry state map
01428   std::map<Idx,StateSet> tmp_entrystatemap;
01429   // temporarily assign rGen to rResGen
01430   if(&rResGen != &rGen) rResGen.Assign(rGen); 
01431   // project tmp with respect to palphabet
01432   ProjectNonDet_ref(rResGen, rProjectAlphabet);  // must use a version that does not add states
01433   // put deterministic result into tmp
01434   Generator* tmp = rGen.New();
01435   Deterministic(rResGen, tmp_entrystatemap, *tmp);
01436   // write entry state map for minimized generator
01437   std::vector<StateSet> subsets;
01438   std::vector<Idx> newindices;
01439   // minimize states and rewrite result to rResGen
01440   StateMin(*tmp, rResGen, subsets, newindices);
01441   // build entry state map
01442   std::vector<StateSet>::size_type i;
01443   std::map<Idx,StateSet>::iterator esmit;
01444   StateSet::Iterator sit;
01445   for (i = 0; i < subsets.size(); ++i) {  
01446     StateSet tmpstates;
01447     for (sit = subsets[i].Begin(); sit != subsets[i].End(); ++sit) {
01448       esmit = tmp_entrystatemap.find(*sit);
01449 #ifdef FAUDES_DEBUG_CODE
01450       if (esmit == tmp_entrystatemap.end()) {
01451         FD_DF("project internal error");
01452   abort();
01453       }
01454 #endif
01455       // insert entry states in temporary StateSet
01456       tmpstates.InsertSet(esmit->second);
01457     }
01458 
01459     rEntryStatesMap.insert(std::make_pair(newindices[i], tmpstates));
01460   }
01461   delete tmp;
01462 }
01463 
01464 
01465 // InvProject(rGen&, rProjectAlphabet)
01466 void InvProject(Generator& rGen, const EventSet& rProjectAlphabet) {
01467   // test if the alphabet of the generator is included in the given alphabet
01468   if(! (rProjectAlphabet >= (EventSet) rGen.Alphabet() ) ){
01469     std::stringstream errstr;
01470     errstr << "Input alphabet has to contain alphabet of generator \"" << rGen.Name() << "\"";
01471     throw Exception("InvProject(Generator,EventSet)", errstr.str(), 506);
01472   }
01473   EventSet newevents = rProjectAlphabet - rGen.Alphabet();
01474   // insert events into generator
01475   rGen.InsEvents(newevents);
01476   FD_DF("InvProject: adding events \"" << newevents.ToString() 
01477   << "\" at every state");
01478   StateSet::Iterator lit;
01479   EventSet::Iterator eit;
01480   for (lit = rGen.StatesBegin(); lit != rGen.StatesEnd(); ++lit) {
01481     LoopCallback(); // should only be an issue for very large generators 
01482     for (eit = newevents.Begin(); eit != newevents.End(); ++eit) {
01483       rGen.SetTransition(*lit, *eit, *lit);
01484     }
01485   }
01486 }
01487 
01488 
01489 
01490 // InvProject(rGen&, rProjectAlphabet)
01491 void aInvProject(Generator& rGen, const EventSet& rProjectAlphabet) {
01492   FD_DF("aInvProject(..)");
01493   // see whether the generator can digest attribues
01494   if(!rGen.Alphabet().AttributeTry(rProjectAlphabet.Attribute())) {
01495     InvProject(rGen,rProjectAlphabet);
01496     return;
01497   }
01498   // record extra events
01499   EventSet newevents = rProjectAlphabet - rGen.Alphabet();
01500   // perform
01501   InvProject(rGen,rProjectAlphabet);
01502   // copy all attributes from input alphabets
01503   FD_DF("aInvProject(..): fixing attributes: source " << typeid(rProjectAlphabet.Attribute()).name() <<
01504     " dest " << typeid(rGen.Alphabet().Attribute()).name());
01505   for(EventSet::Iterator eit=newevents.Begin(); eit!=newevents.End(); ++eit)  
01506     rGen.EventAttribute(*eit,rProjectAlphabet.Attribute(*eit));
01507 }
01508 
01509 
01510 // InvProject
01511 void aInvProject(
01512   const Generator& rGen,
01513   const EventSet& rProjectAlphabet,
01514   Generator& rResGen)
01515 {
01516   rResGen.Assign(rGen);
01517   aInvProject(rResGen, rProjectAlphabet);
01518 }
01519 
01520 
01521 
01522 // CreateEntryStatesMap(rRevEntryStatesMap, rEntryStatesMap)
01523 void CreateEntryStatesMap(const std::map<StateSet,Idx>& rRevEntryStatesMap,
01524         std::map<Idx,StateSet>& rEntryStatesMap) {
01525   std::map<StateSet,Idx>::const_iterator it;
01526   for (it = rRevEntryStatesMap.begin(); it != rRevEntryStatesMap.end(); ++it) {
01527     rEntryStatesMap.insert(std::make_pair(it->second, it->first));
01528   }
01529 }
01530 
01531 
01532 
01533 } // namespace faudes

libFAUDES 2.23h --- 2014.04.03 --- c++ api documentaion by doxygen