cfl_project.cppGo 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 |