cfl_bisimcta.cpp
Go to the documentation of this file.
1 /** @file cfl_bisimcta.cpp Bisimulation relations (CTA)
2 
3  Functions to compute bisimulation relations on dynamic systems (represented
4  by non-deterministic finite automata).
5 
6  More specifically, we we implement algorithms to obtain ordinary/delayed/weak
7  bisimulations partitions based on change-tracking. In large, these implementations
8  are expected to perform better than the classical variants found in cfl_bisimulation.h".
9 
10  This code was originally developed by Yiheng Tang in the context of compositional
11  verification in 2020/21.
12 
13 **/
14 
15 /* FAU Discrete Event Systems Library (libfaudes)
16 
17  Copyright (C) 2020/21, Yiheng Tang
18  Copyright (C) 2021, Thomas Moor
19  Exclusive copyright is granted to Klaus Schmidt
20 
21  This library is free software; you can redistribute it and/or
22  modify it under the terms of the GNU Lesser General Public
23  License as published by the Free Software Foundation; either
24  version 2.1 of the License, or (at your option) any later version.
25 
26  This library is distributed in the hope that it will be useful,
27  but WITHOUT ANY WARRANTY; without even the implied warranty of
28  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29  Lesser General Public License for more details.
30 
31  You should have received a copy of the GNU Lesser General Public
32  License along with this library; if not, write to the Free Software
33  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
34 
35 #include "cfl_bisimcta.h"
36 
37 
38 // *******************************
39 // *******************************
40 // Preliminaries
41 // *******************************
42 // *******************************
43 
44 #define BISIM_VERB1(msg) \
45  { if(faudes::ConsoleOut::G()->Verb() >=1 ) { \
46  std::ostringstream cfl_line; cfl_line << msg << std::endl; faudes::ConsoleOut::G()->Write(cfl_line.str(),0,0,0);} }
47 #define BISIM_VERB2(msg) \
48  { if(faudes::ConsoleOut::G()->Verb() >=2 ) { \
49  std::ostringstream cfl_line; cfl_line << msg << std::endl; faudes::ConsoleOut::G()->Write(cfl_line.str(),0,0,0);} }
50 #define BISIM_VERB0(msg) \
51  { if(faudes::ConsoleOut::G()->Verb() >=0 ) { \
52  std::ostringstream cfl_line; cfl_line << msg << std::endl; faudes::ConsoleOut::G()->Write(cfl_line.str(),0,0,0);} }
53 
54 namespace faudes {
55 
56 
57 // ****************************************************************************************************************************
58 // ****************************************************************************************************************************
59 // Private part of header
60 // ****************************************************************************************************************************
61 // ****************************************************************************************************************************
62 
63 
64 /*!
65  * \brief The Bisimulation class
66  * The following class implements a basic normal bisimulation partition algorithms and derives a class
67  * for partitioning w.r.t. delayed bisimulation and weak bisimulation . All these algorithms are introduced
68  * in "Computing Maximal Weak and Other Bisimulations" from Alexandre Boulgakov et. al. (2016).
69  * The utilised normal bisimulation algorithm originates from the "change tracking algorithm"
70  * from Stefan Blom and Simona Orzan (see ref. of cited paper). Besides, the current paper uses topological
71  * sorted states to avoid computing saturated transitions explicitly when computing delayed and weak bisimulation.
72  *
73  * IMPORTANT NOTE: both algorithms for delayed and weak bisimulation requires tau-loop free automaton. This shall
74  * be done extern beforehand by using e.g. FactorTauLoops(Generator &rGen, const Idx &rSilent).
75  */
76 
78 public:
79  BisimulationCTA(const Generator &rGen, const std::vector<StateSet> &rPrePartition) : mGen(&rGen), mPrePartition(rPrePartition){}
80  virtual ~BisimulationCTA() = default;
81 
82  /*!
83  * \brief BisimulationPartition
84  * wrapper
85  */
86  virtual void ComputePartition(std::list<StateSet>& rResult);
87 
88 protected:
89 
90  // encoded data structure
91  struct State {
92  Idx id; // source state idx
93  std::vector< std::vector<Idx>> suc; // successors by event
94  std::vector< Idx > pre; // predecessors (neglect event, only for figuring affected)
95  std::vector< std::set<Idx> > cafter; // cafter by event (the std::set<Idx> is the set of c-values)
96  std::vector< Idx > evs; // active event set, only for pre-partition. (indicates "delayed" active ev in case of daleyd- or weak-bisim)
97  Idx c = 0; // this shall be replaced by 1 for each state (except #0 state)
98 
99  // only for derived classes, recored predececssors with silent event.
100  // in such cases, "pre" records only non-tau predecessors
101  std::vector< Idx > taupre;
102  };
103  std::vector<State> mStates; // vector of all states [starting with 1 -- use min-index for debugging]
104  std::vector<Idx> mEvents; // vector of all events [starting with 1]
105 
106  /*!
107  * \brief EncodeData
108  * encode source generator into vector-form data structures to accelerate the iteration
109  *
110  * NOTE: this function is virtual since derived classes, in the context of
111  * abstraction, have different steps
112  */
113  virtual void EncodeData(void);
114 
115  /*!
116  * \brief FirstStepApproximation (see Fig. 2 (b) in cited paper)
117  * Prepartition states according to their active events.
118  * If a prepartition already exists, this function will refine the prepartition
119  *
120  * NOTE: this function is also utilised by delayed- and weak-bisimulation since in both
121  * cases, State.evs neglect tau (as tau is always active)
122  *
123  */
124  void FirstStepApproximation(void);
125 
126  /*!
127  * \brief RefineChanged
128  * refine equivalence classes contained affected nodes in the last iteration
129  * (see Fig. 5 in cited paper)
130  */
131  void RefineChanged(void);
132 
133  /*!
134  * \brief GenerateResult
135  * generate partition result w.r.t. original state indices (without trivial classes)
136  * \param rResult partition w.r.t. original state indices without trivial classes
137  */
138  void GenerateResult(std::list<StateSet>& rResult);
139 
140  /*! keep the original generator */
141  const Generator* mGen;
142 
143  /*! state prepartition with original state idx (the same as state label as in cited paper). Empty for trivial prepartition. */
144  const std::vector<StateSet> mPrePartition;
145 
146  /*! persisted data structures, see cited paper. */
147  std::vector<bool> mAffected;
148  std::vector<bool> mChanged;
149  Idx mCmax; // maximal value of c in the current partition
150 
151  /*! constant parameters for encoding*/
152  Idx mStateSize; // state count, for vector resizing and iteration
153  Idx mAlphSize; // event count, for vector resizing and iteration
154 
155  /*! the current (and also final) partition. Partition is represented
156  * by states sorted by c_value. Access mStates for c_value to get the exact partition.
157  */
158  std::vector<Idx> mPartition;
159 
160 private:
161 
162  /*!
163  * \brief ComputeBisimulation
164  * perform the overall iteration
165  */
166  void ComputeBisimulation(void);
167 
168  /*!
169  * \brief ComputeChangedAfters
170  * Compute changed afters of each affected state. (see Fig 5. in cited paper)
171  * Affected states are those having some successors that have changed class (namely c_value)
172  * in the last iteration
173  * NOTE: this function is made private in that derived classes compute cafters
174  * w.r.t. silent event and these are accomplished by ComputeChangedDelayedAfters
175  * and ComputeChangedObservedAfters, respectively
176  */
177  void ComputeChangedAfters(void);
178 
179 
180 };
181 
182 /*!
183  * \brief The DelayedBisimulation class
184  * Derived from Bisimulation class. We implement the "two-pass change-tracking" algorithm from the cited paper.
185  */
187 public:
188  AbstractBisimulationCTA (const Generator& rGen, const Idx& rSilent, const Idx& rTaskFlag, const std::vector<StateSet>& rPrePartition)
189  : BisimulationCTA(rGen,rPrePartition), mTau(rSilent), mTaskFlag(rTaskFlag){}
190 
191  virtual void ComputePartition(std::list<StateSet>& rResult);
192 
193 private:
194  /*!
195  * \brief tau
196  * persist index of original silent event
197  */
198  const Idx mTau;
199 
200  /*!
201  * \brief mTaskFlag
202  * flag for task:
203  * mTaskFlag == 1: delayed bisimulation
204  * mTaskFlag == 2: weak bisimulation
205  */
206  const Idx mTaskFlag;
207 
208  /*!
209  * \brief EncodeData
210  * basic preparation for bisimulation
211  * NOTE: this function is virtual since derived classes, in the context of
212  * abstraction, have different steps
213  */
214  virtual void EncodeData(void);
215 
216  /*!
217  * \brief MarkTauAffected
218  * perform a recursive Depth-First-Search to mark all tau* predecessors as affected
219  * based on given state index rState.
220  * NOTE: rState is also a tau* predecessor of itself, thus also affected
221  * NOTE: as tau-loop-free is guaranteed, no need to use visited list to avoid duplet
222  * NOTE: in most cases, argument rAffected directly takes mAffected. The only
223  * exception is that in ComputeChangedObservedAfters, we need to buffer an intermediate
224  * affected-vector.
225  */
226  void MarkTauStarAffected(std::vector<bool>& rAffected, const Idx& rState);
227 
228  /*!
229  * \brief ComputeDelayedBisimulation
230  * perform the overall iteration based on different task flag value, see mTaskFlag
231  */
233 
234 
235  /*!
236  * \brief ComputeChangedDelayedAfters
237  * see Fig. 10 of cited paper
238  */
239  void ComputeChangedDelayedAfters(void);
240 
241  /*!
242  * \brief ComputeChangedObservedAfters
243  * see Fig. 12 of cited paper
244  */
245  void ComputeChangedObservedAfters(void);
246 
247 };
248 
249 
250 
251 // *******************************
252 // *******************************
253 // topological sort
254 // *******************************
255 // *******************************
256 
257 
258 /*!
259  * \brief The TopoSort class
260  * perform a topological sort on states of a given automaton. if s1 is before s2 in the sort
261  * then there is no path from s2 to s1. The algorithm can be found in
262  * https://en.wikipedia.org/wiki/Topological_sorting
263  * under depth-first search, which is considered as first invented by R. Tarjan in 1976.
264  */
265 class TopoSort{
266 public:
267  struct State{
269  bool permanent = 0;
270  bool temporary = 0;
271  std::vector<Idx> succs;
272  };
273  TopoSort (const Generator& rGen, const EventSet& rEvs);
274 
275  bool Sort(std::list<Idx>& result);
276 
277  bool Visit(State& rNode);
278 
279 private:
280  const Generator* mGen;
282  std::vector<State> mStates;
283  std::list<Idx> mResult;
284 };
285 
286 /*!
287  * \brief topoSort
288  * wrapper for topological sortation
289  * rEvs is the set of events which will be considered while sorting
290  */
291 bool topoSort (const Generator& rGen, const EventSet& rEvs, std::list<Idx>& result);
292 
293 
294 
295 
296 // ****************************************************************************************************************************
297 // ****************************************************************************************************************************
298 // Implementation part
299 // ****************************************************************************************************************************
300 // ****************************************************************************************************************************
301 
302 
303 // *******************************
304 // *******************************
305 // Transition saturation
306 // *******************************
307 // *******************************
308 
309 
310 // YT: note flag: 1 := delayed bisim; 2 := weak bisim
311 void ExtendTransRel(Generator& rGen, const EventSet& rSilent, const Idx& rFlag) {
312 
313  if (rSilent.Empty()) return;
314  // HELPERS:
315  TransSet::Iterator tit1;
316  TransSet::Iterator tit1_end;
317  TransSet::Iterator tit2;
318  TransSet::Iterator tit2_end;
319  TransSet newtrans;
320 
321  // initialize result with original transitionrelation
322  TransSet xTrans=rGen.TransRel();
323  newtrans = rGen.TransRel(); // initialize iteration
324  // loop for fixpoint
325  while(!newtrans.Empty()) {
326  // store new transitions for next iteration
327  TransSet nextnewtrans;
328  // loop over all transitions in newtrans
329  tit1=newtrans.Begin();
330  tit1_end=newtrans.End();
331  for(;tit1!=tit1_end; ++tit1) {
332  // if it is silent add transition to non silent successor directly
333  if(rSilent.Exists(tit1->Ev)) {
334  tit2=xTrans.Begin(tit1->X2);
335  tit2_end=xTrans.End(tit1->X2);
336  for(;tit2!=tit2_end; ++tit2) {
337 // if(!rSilentAlphabet.Exists(tit2->Ev)) // tmoor 18-09-2019
338  if (!xTrans.Exists(tit1->X1,tit2->Ev,tit2->X2))
339  nextnewtrans.Insert(tit1->X1,tit2->Ev,tit2->X2);
340  }
341  }
342  else if (rFlag == 2){ // in case for observed transition (flag == 2), add non-silent transition to silent successor
343  tit2=xTrans.Begin(tit1->X2);
344  tit2_end=xTrans.End(tit1->X2);
345  for(;tit2!=tit2_end; ++tit2) {
346  if(rSilent.Exists(tit2->Ev)) // silent successor
347  if (!xTrans.Exists(tit1->X1,tit1->Ev,tit2->X2))
348  nextnewtrans.Insert(tit1->X1,tit1->Ev,tit2->X2);
349  }
350  }
351  }
352  // insert new transitions
353  xTrans.InsertSet(nextnewtrans);
354  // update trans for next iteration.
355  newtrans = nextnewtrans;
356  }
357  rGen.InjectTransRel(xTrans);
358 }
359 
360 void InstallSelfloops(Generator &rGen, const EventSet& rEvs){
361  if (rEvs.Empty()) return;
362  StateSet::Iterator sit = rGen.StatesBegin();
363  for(;sit!=rGen.StatesEnd();sit++){
364  EventSet::Iterator eit = rEvs.Begin();
365  for(;eit!=rEvs.End();eit++)
366  rGen.SetTransition(*sit,*eit,*sit);
367  }
368 }
369 
370 
371 // *******************************
372 // *******************************
373 // topological sort
374 // *******************************
375 // *******************************
376 
377 TopoSort::TopoSort (const Generator& rGen, const EventSet &rEvs) {
378  mGen = &rGen;
379  nxidx=1;
380 
381  // encode transition relation [effectively buffer log-n search]
382  Idx max=0;
383  std::map<Idx,Idx> smap;
384  mStates.resize(mGen->States().Size()+1);
385  StateSet::Iterator sit=mGen->StatesBegin();
386  for(; sit != mGen->StatesEnd(); ++sit) {
387  smap[*sit]=++max;
388  mStates[max].id=*sit;
389  }
391  for(; tit != mGen->TransRelEnd(); ++tit) {
392  if (rEvs.Exists(tit->Ev))
393  mStates[smap[tit->X1]].succs.push_back(smap[tit->X2]);
394  }
395 }
396 
397 bool TopoSort::Sort (std::list<Idx> &result){
398  std::vector<State>::iterator sit = mStates.begin();
399  sit++;
400  for(;sit!=mStates.end();sit++){
401  if ((*sit).permanent) continue;
402  if (!Visit(*sit)) return false;
403  }
404  result = mResult;
405  return true;
406 }
407 
408 bool TopoSort::Visit(State &rState){
409  if (rState.permanent) return true;
410  if (rState.temporary) return false;
411  rState.temporary = 1;
412  std::vector<Idx>::iterator sit = rState.succs.begin();
413  for(;sit!=rState.succs.end();sit++){
414  if(!Visit(mStates[*sit])) return false;
415  }
416  rState.temporary = 0;
417  rState.permanent = 1;
418  mResult.push_front(rState.id);
419  return true;
420 }
421 
422 bool topoSort(const Generator& rGen, const EventSet &rEvs, std::list<Idx>& result){
423  TopoSort topo(rGen, rEvs);
424  return topo.Sort(result);
425 }
426 
427 
428 
429 // *****************************************************
430 // *****************************************************
431 // (strong) bisimulation
432 // *****************************************************
433 // *****************************************************
434 
436  mStateSize = mGen->States().Size()+1;
437  mAlphSize = mGen->Alphabet().Size()+1;
438 
439  // encode transition relation [effectively buffer log-n search]
440  std::map<Idx,Idx> smap;
441  std::map<Idx,Idx> emap;
442  Idx max=0;
443  mEvents.resize(mAlphSize);
444  EventSet::Iterator eit= mGen->AlphabetBegin();
445  for(; eit != mGen->AlphabetEnd(); ++eit) {
446  emap[*eit]=++max;
447  mEvents[max]=*eit;
448  }
449  if (mPrePartition.empty())
450  mCmax = 1;
451  else
452  mCmax = mPrePartition.size();
453  max=0;
454  mStates.resize(mStateSize);
455  mPartition.resize(mStateSize-1);
456  StateSet::Iterator sit=mGen->StatesBegin();
457  for(; sit != mGen->StatesEnd(); ++sit) {
458  mPartition[max] = max+1; // trivial partition
459  smap[*sit]=++max;
460  mStates[max].id=*sit;
461  mStates[max].suc.resize(mAlphSize);
462  mStates[max].cafter.resize(mAlphSize);
463  // initialize cafter
464  const EventSet& activeevs = mGen->ActiveEventSet(*sit);
465  EventSet::Iterator eit = activeevs.Begin();
466  for(;eit!=activeevs.End();eit++)
467  mStates[max].evs.push_back(emap[*eit]);
468  // initialize c value. c = 1 if no prepartition; c = (index of prepartion) + 1 if prepartition defined
469  if (mPrePartition.empty()) mStates[max].c = mCmax;
470  else{
471  Idx prepit = 0;
472  for(;prepit<mPrePartition.size();prepit++){
473  if (mPrePartition[prepit].Exists(*sit)) break;
474  }
475  if (prepit == mPrePartition.size())
476  throw Exception("EncodeData:: ", "invalide prepartition. State "+ ToStringInteger(*sit) + "is not allocated.", 100);
477  mStates[max].c = prepit+1;
478  }
479  }
481  for(; tit != mGen->TransRelEnd(); ++tit) {
482  mStates[smap[tit->X1]].suc[emap[tit->Ev]].push_back(smap[tit->X2]);
483  // since predecessors are recorded neglecting events, we (shall) check for
484  // duplication before inserting. This is actually optional.
485  std::vector<Idx>::const_iterator preit = mStates[smap[tit->X2]].pre.begin();
486  for (;preit!=mStates[smap[tit->X2]].pre.end();preit++){
487  if (*preit==smap[tit->X1]) break;
488  }
489  if (preit==mStates[smap[tit->X2]].pre.end())
490  mStates[smap[tit->X2]].pre.push_back(smap[tit->X1]);
491  }
492 
493  // initialize affected and changed vector
494  mAffected.resize(mStateSize);
495  mAffected[0] = 0;
496  mChanged.resize(mStateSize);
497  mChanged[0] = 0;
498  Idx iit = 1;
499  for(;iit<mStateSize;iit++){
500  mAffected[iit] = 0;
501  mChanged[iit] = 1;
502  }
503 }
504 
506  // set up (modified) Phi as in the cited paper. pairs (state, (activeEvs,c-value)) by lex-sort
507  std::sort(mPartition.begin(),mPartition.end(), [this](const Idx& state1, const Idx& state2){
508  if (this->mStates[state1].evs < this->mStates[state2].evs) return 1;
509  if (this->mStates[state1].evs > this->mStates[state2].evs) return 0;
510  if (this->mStates[state1].c < this->mStates[state2].c) return 1;
511  return 0;
512  });
513  // assign new c_values
514  std::vector<Idx> evs(1);
515  evs[0] = 0; //initialize invalid active event set
516  Idx c = 0; // initialize invalide c value
517  std::vector<Idx>::const_iterator partit = mPartition.begin();
518  for(;partit!=mPartition.end();partit++){
519  if(mStates[*partit].evs != evs || mStates[*partit].c != c){
520  evs = mStates[*partit].evs;
521  c = mStates[*partit].c;
522  mCmax++;
523  }
524  mStates[*partit].c = mCmax;
525  }
526 }
527 
529  // recompute mAffected
530  std::fill(mAffected.begin(),mAffected.end(),0);
531  Idx stateit = 1;
532  for(;stateit<mStateSize;stateit++){
533  if (!mChanged[stateit]) continue;
534  std::vector<Idx>::iterator predit = mStates[stateit].pre.begin();
535  for(;predit!=mStates[stateit].pre.end();predit++) mAffected[*predit] = 1;
536  }
537  // compute cafters
538  stateit = 1;
539  for (;stateit<mStateSize;stateit++){
540  if (!mAffected[stateit]) continue;
541  // clear cafter of this node
542  std::set<Idx> emptyset;
543  std::fill(mStates[stateit].cafter.begin(),mStates[stateit].cafter.end(),emptyset);
544  // compute new cafter
545  Idx sucevit = 1;
546  for( ; sucevit<mAlphSize; sucevit++){
547  std::vector<Idx>::const_iterator sucstateit = mStates[stateit].suc[sucevit].begin();
548  for(;sucstateit!=mStates[stateit].suc[sucevit].end();sucstateit++)
549  mStates[stateit].cafter[sucevit].insert(mStates[*sucstateit].c);
550  }
551  }
552 }
553 
555 
556 
557  std::fill(mChanged.begin(),mChanged.end(),0);
558  std::vector<Idx>::iterator partit = mPartition.begin();
559  while(partit!=mPartition.end()){
560  // treat affected node is merged into the following step
561 
562  // get class containing affected node (line 25 in cited paper)
563  // iterate on mPartition, i.e. mPartition must already sorted by c
564  // we simply iterate forward to get the current class. if no states in class is affected, skip
565  Idx c = mStates[*partit].c;
566  std::list<Idx> eqclass;
567  //std::vector<Idx>::iterator partit_rec = partit;
568  bool affectedflag = false;
569  while(mStates[*partit].c == c){
570  eqclass.push_back(*partit);
571  if (mAffected[*partit]) affectedflag = true;
572  partit++;
573  if (partit==mPartition.end()) break;
574  }
575  if (eqclass.size()==1 || !affectedflag) continue; // skip trivial class or non-affected class
576 
577  // reorder nodes in eqclasses by moving those in affected to the front (line 26 in cited paper)
578  std::list<Idx>::iterator bound = std::partition(eqclass.begin(), eqclass.end(),
579  [this](const Idx& state){return this->mAffected[state];});
580 
581  // sort affected nodes by cafter (line 27. Note in cited paper line 27, it says sort "NOT" affected nodes.
582  // this shall be a typo since all unaffected nodes in this class shall have the same cafter, as their
583  // cafters are not changed from the last iteration)
584  std::list<Idx> eqclass_aff; // unhook elements before bound
585  eqclass_aff.splice(eqclass_aff.begin(),eqclass,eqclass.begin(),bound);
586  eqclass_aff.sort([this](const Idx& state1, const Idx& state2){
587  return this->mStates[state1].cafter < this->mStates[state2].cafter;});
588  eqclass.splice(eqclass.begin(),eqclass_aff);// hook the sorted eqclass_aff again to the front of eqclass
589 
590  // delete the largest set of states with the same cafter (line 28). c_value of these states are preserved
591  std::list<Idx>::iterator eqclassit = eqclass.begin();
592  std::vector<std::set<Idx>> maxCafter; // cafter corresponding to most states in the current class
593  std::vector<std::set<Idx>> currentCafter; // initialize an invalid cafter for comparison
594  Idx maxSize = 0;
595  Idx currentSize = 0;
596  for(;eqclassit!=eqclass.end();eqclassit++){
597  if (mStates[*eqclassit].cafter!=currentCafter){
598  if (currentSize > maxSize){ // update max if the latest record is greater
599  maxSize = currentSize;
600  maxCafter = currentCafter;
601  }
602  currentSize = 1;
603  currentCafter = mStates[*eqclassit].cafter;
604  }
605  else{
606  currentSize++;
607  }
608  }
609  if (maxCafter.empty()) continue; // namely all states has the same cafter. no need to refine.
610 
611  // refine this class. The greatest refined class is removed and all other refined classes
612  // will have new c_value (line 29-37)
613  eqclass.remove_if([this, maxCafter](const Idx& state){
614  return this->mStates[state].cafter==maxCafter;});
615  currentCafter.clear();
616  eqclassit = eqclass.begin();
617  for(;eqclassit!=eqclass.end();eqclassit++){
618  if(mStates[*eqclassit].cafter!=currentCafter){
619  currentCafter = mStates[*eqclassit].cafter;
620  mCmax++;
621  }
622  mStates[*eqclassit].c = mCmax;
623  mChanged[*eqclassit] = 1;
624  }
625  }
626 
627  // sort by c
628  std::sort(mPartition.begin(),mPartition.end(), [this](const Idx& state1, const Idx& state2){
629  return this->mStates[state1].c<this->mStates[state2].c;
630  });
631 }
632 
633 // the wrapper
635  BISIM_VERB2("Initializing data")
636  EncodeData();
637  BISIM_VERB2("Doing FirstStepApproximation")
639  while (std::find(mChanged.begin(),mChanged.end(),1)!=mChanged.end()){
640  BISIM_VERB2("Doing ComputeChangedAfters")
642  BISIM_VERB2("Doing RefineChanged")
643  RefineChanged();
644  }
645 }
646 
647 void BisimulationCTA::GenerateResult(std::list<StateSet>& rResult){
648  rResult.clear();
649  Idx c = 0;
650  std::vector<Idx>::const_iterator partit = mPartition.begin();
651  StateSet eqclass;
652  for(;partit!=mPartition.end();partit++){
653  if (mStates[*partit].c != c){
654  if (eqclass.Size()>1) rResult.push_back(eqclass);
655  eqclass.Clear();
656  eqclass.Insert(mStates[*partit].id);
657  c = mStates[*partit].c;
658  }
659  else eqclass.Insert(mStates[*partit].id);
660  }
661  if (eqclass.Size()>1) rResult.push_back(eqclass); // insert the last partitio
662 }
663 
664 void BisimulationCTA::ComputePartition(std::list<StateSet>& rResult){
666  GenerateResult(rResult);
667 }
668 
669 // *****************************************************
670 // *****************************************************
671 // delayed and weak bisimulation
672 // *****************************************************
673 // *****************************************************
674 
676  mStateSize = mGen->States().Size()+1;
677  mAlphSize = mGen->Alphabet().Size(); // Note the difference with BisimulationCTA::EncodeData(). Index 0 is for silent event
678 
679  // encode transition relation [effectively buffer log-n search]
680  std::map<Idx,Idx> smap;
681  std::map<Idx,Idx> emap;
682 
683  Idx max=0;
684  mEvents.resize(mAlphSize);
685  EventSet::Iterator eit= mGen->AlphabetBegin();
686  for(; eit != mGen->AlphabetEnd(); ++eit) {
687  if (*eit != mTau){
688  emap[*eit]=++max;
689  mEvents[max]=*eit;
690  }
691  else{
692  emap[*eit]=0;
693  mEvents[0]=*eit;
694  }
695  }
696 
697  // perform topological sort, throw if with tau-loops
698  std::list<Idx> topo;
699  EventSet tau;
700  tau.Insert(mTau);
701  if (!topoSort(*mGen,tau,topo))
702  throw Exception("DelayedBisimulationCTA::EncodeData(): ", "input automaton shall not have tau loop. Please factor"
703  "tau-loops before partition", 500);
704 
705  if (mPrePartition.empty())
706  mCmax = 1;
707  else
708  mCmax = mPrePartition.size();
709  max=0;
710  mStates.resize(mStateSize);
711  mPartition.resize(mStateSize-1);
712  // iterate over topo sorted states BACKWARDS and install into mStates
713  // mStates shall have the upstream order. this is a pure design feature
714  // as all iterations will start from the downstream-most state in the topo sort.
715  // By installing them backwards, we always start the iteration from the first state
716  std::list<Idx>::const_reverse_iterator sit=topo.rbegin();
717  for(; sit != topo.rend(); ++sit) {
718  mPartition[max] = max+1; // trivial partition
719  smap[*sit]=++max;
720  mStates[max].id=*sit;
721  mStates[max].suc.resize(mAlphSize);
722  mStates[max].cafter.resize(mAlphSize);
723  // install DELAYED active events
724  EventSet activeevs = mGen->ActiveEventSet(*sit); // direct active events
725  TransSet::Iterator tit = mGen->TransRelBegin(*sit); // active events of one-step tau successors
726  for(;tit!=mGen->TransRelEnd(*sit);tit++){
727  if (tit->Ev!=mTau) continue;
728  // since active events are encoded in vectors, iterate over active event vector of successor
729  std::vector<Idx>::const_iterator eit = mStates[smap[tit->X2]].evs.begin(); // the state smap[tit->X2] must have been encoded
730  for(;eit!=mStates[smap[tit->X2]].evs.end();eit++){
731  activeevs.Insert(mEvents[*eit]); // set operation to avoid duplication
732  }
733  }
734  EventSet::Iterator eit = activeevs.Begin();
735  for(;eit!=activeevs.End();eit++){
736  if (*eit == mTau) continue; // tau is always active, no need to take into consideration
737  mStates[max].evs.push_back(emap[*eit]);
738  }
739  // initialize c value. c = 1 if no prepartition; c = (index of prepartion) + 1 if prepartition defined
740  if (mPrePartition.empty()) mStates[max].c = mCmax;
741  else{
742  Idx prepit = 0;
743  for(;prepit<mPrePartition.size();prepit++){
744  if (mPrePartition[prepit].Exists(*sit)) break;
745  }
746  if (prepit == mPrePartition.size())
747  throw Exception("EncodeData:: ", "invalide prepartition. State "+ ToStringInteger(*sit) + "is not allocated.", 100);
748  mStates[max].c = prepit+1;
749  }
750  }
752  for(; tit != mGen->TransRelEnd(); ++tit) {
753  mStates[smap[tit->X1]].suc[emap[tit->Ev]].push_back(smap[tit->X2]);
754  // distinguish tau-predecessor and non-tau-predecessor
755  // since predecessors are recorded neglecting events, we (shall) check for
756  // duplication before inserting. This is actually optional.
757  if (emap[tit->Ev]!=0){
758 
759  std::vector<Idx>::const_iterator preit = mStates[smap[tit->X2]].pre.begin();
760  for (;preit!=mStates[smap[tit->X2]].pre.end();preit++){
761  if (*preit==smap[tit->X1])
762  break;
763  }
764  if (preit==mStates[smap[tit->X2]].pre.end())
765  mStates[smap[tit->X2]].pre.push_back(smap[tit->X1]);
766  }
767  else{
768  std::vector<Idx>::const_iterator preit = mStates[smap[tit->X2]].taupre.begin();
769  for (;preit!=mStates[smap[tit->X2]].taupre.end();preit++){
770  if (*preit==smap[tit->X1])
771  break;
772  }
773  if (preit==mStates[smap[tit->X2]].taupre.end())
774  mStates[smap[tit->X2]].taupre.push_back(smap[tit->X1]);
775  }
776  }
777 
778  // initialize affected and changed vector
779  mAffected.resize(mStateSize);
780  std::fill(mAffected.begin(),mAffected.end(),0);
781  mChanged.resize(mStateSize);
782  std::fill(mChanged.begin(),mChanged.end(),1);
783  mChanged[0] = 0;
784 }
785 
786 void AbstractBisimulationCTA::MarkTauStarAffected(std::vector<bool> &rAffected, const Idx& rState){
787  rAffected[rState] = 1;
788  std::vector<Idx>::const_iterator taupredit = mStates[rState].taupre.begin();
789  for(;taupredit!=mStates[rState].taupre.end();taupredit++){
790  if(!rAffected[*taupredit]){
791  MarkTauStarAffected(rAffected, *taupredit);
792  }
793  }
794 }
795 
797  std::fill(mAffected.begin(),mAffected.end(),0);
798  Idx stateit = 1;
799 
800  // figure out all affected
801  for(;stateit<mStateSize;stateit++){
802  if (!mChanged[stateit]) continue;
803  // all non-tau predecessors and their tau* predecessors
804  std::vector<Idx>::iterator predit = mStates[stateit].pre.begin();
805  for(;predit!=mStates[stateit].pre.end();predit++){
807  }
808  // this state and its tau* predecessors
809  MarkTauStarAffected(mAffected, stateit);
810  }
811  stateit = 1;
812  for(;stateit<mStateSize;stateit++){
813  if(!mAffected[stateit]) continue;
814  std::set<Idx> emptyset;
815  std::fill(mStates[stateit].cafter.begin(),mStates[stateit].cafter.end(),emptyset);
816  // compute new cafter
817  // implicit selfloop (line 18)
818  mStates[stateit].cafter[0].insert(mStates[stateit].c);
819  // visible afters (line 19-21)
820  Idx sucevit = 0; // handle tau successor as well
821  for( ; sucevit<mAlphSize; sucevit++){
822  std::vector<Idx>::const_iterator sucstateit = mStates[stateit].suc[sucevit].begin();
823  for(;sucstateit!=mStates[stateit].suc[sucevit].end();sucstateit++){
824  mStates[stateit].cafter[sucevit].insert(mStates[*sucstateit].c);
825  }
826  }
827  // delayed afters (line 22-26)
828  std::vector<Idx>::const_iterator suctauit = mStates[stateit].suc[0].begin(); // iterate over tau successors
829  for(;suctauit!=mStates[stateit].suc[0].end();suctauit++){
830  // append all cafters of suctauit to stateit
831  Idx sucsucevit = 0;
832  for( ; sucsucevit<mAlphSize; sucsucevit++){
833  mStates[stateit].cafter[sucsucevit].insert(
834  mStates[*suctauit].cafter[sucsucevit].begin(),
835  mStates[*suctauit].cafter[sucsucevit].end());
836  }
837  }
838  }
839 }
840 
842  std::fill(mAffected.begin(),mAffected.end(),0);
843  Idx stateit = 1;
844 
845  // figure out all affected
846  for(;stateit<mStateSize;stateit++){
847  if (!mChanged[stateit]) continue;
848  // buffer a vector of all tau* predecessors, including this state itself
849  std::vector<bool> taustarpred(mStateSize);
850  MarkTauStarAffected(taustarpred,stateit);
851  // for each taustarpred, mark itself and all its non-tau preds and their tau* preds as affected
852  Idx affectedit = 1;
853  for (;affectedit<mStateSize;affectedit++){
854  if (!taustarpred[affectedit]) continue;
855  std::vector<Idx>::iterator predit = mStates[affectedit].pre.begin();
856  for(;predit!=mStates[affectedit].pre.end();predit++){
858  }
859  mAffected[affectedit] = 1; // no need to mark all taupred of affectedit as they are all in taustarpred
860  }
861  }
862 
863  stateit = 1;
864  for(;stateit<mStateSize;stateit++){
865  if(!mAffected[stateit]) continue;
866  std::set<Idx> emptyset;
867  std::fill(mStates[stateit].cafter.begin(),mStates[stateit].cafter.end(),emptyset);
868  // implicit selfloop (line 16)
869  mStates[stateit].cafter[0].insert(mStates[stateit].c);
870  // merge (tau-)cafter of tau successors
871  std::vector<Idx>::const_iterator tausucit = mStates[stateit].suc[0].begin();
872  for (;tausucit!=mStates[stateit].suc[0].end();tausucit++){
873  mStates[stateit].cafter[0].insert(mStates[*tausucit].cafter[0].begin(),mStates[*tausucit].cafter[0].end());
874  }
875  }
876 
877  stateit = 1;
878  for(;stateit<mStateSize;stateit++){
879  if(!mAffected[stateit]) continue;
880  // merge (tau-)cafter of non-tau successor (line 23-25)
881  Idx sucevit = 1; // skip tau (0)
882  for (;sucevit<mAlphSize;sucevit++){
883  std::vector<Idx>::const_iterator sucstateit = mStates[stateit].suc[sucevit].begin();
884  for(;sucstateit!=mStates[stateit].suc[sucevit].end();sucstateit++){
885  mStates[stateit].cafter[sucevit].insert(mStates[*sucstateit].cafter[0].begin(),mStates[*sucstateit].cafter[0].end());
886  }
887  }
888  // merge (nontau-) cafter of tau sucessros (line 26-30)
889  Idx sucsucevit = 1; // non-tau suc-successors
890  for(;sucsucevit<mAlphSize;sucsucevit++){
891  std::vector<Idx>::const_iterator sucstateit = mStates[stateit].suc[0].begin(); // iterate over tau-succesor states
892  for(;sucstateit!=mStates[stateit].suc[0].end();sucstateit++){
893  mStates[stateit].cafter[sucsucevit].insert(mStates[*sucstateit].cafter[sucsucevit].begin(),mStates[*sucstateit].cafter[sucsucevit].end());
894  }
895  }
896  }
897 }
898 
899 // the internal wrapper
901  BISIM_VERB2("Initializing data")
902  EncodeData();
903  BISIM_VERB2("Doing FirstStepApproximation")
905  while (std::find(mChanged.begin(),mChanged.end(),1)!=mChanged.end()){
906  if (mTaskFlag==1){
907  BISIM_VERB2("Doing ComputeChangedDelayedAfters")
909  }
910  else if (mTaskFlag==2){
911  BISIM_VERB2("Doing ComputeChangedObservedAfters")
913  }
914  BISIM_VERB2("Doing RefineChanged")
915  RefineChanged();
916  }
917 }
918 
919 void AbstractBisimulationCTA::ComputePartition(std::list<StateSet>& rResult){
921  GenerateResult(rResult);
922 }
923 
924 
925 
926 // wrappers
927 void ComputeBisimulationCTA(const Generator& rGen, std::list<StateSet>& rResult){
928  std::vector<StateSet> trivial;
929  BisimulationCTA bisim(rGen,trivial);
930  bisim.ComputePartition(rResult);
931 }
932 
933 void ComputeBisimulationCTA(const Generator& rGen, std::list<StateSet>& rResult, const std::vector<StateSet>& rPrePartition){
934  BisimulationCTA bisim(rGen,rPrePartition);
935  bisim.ComputePartition(rResult);
936 }
937 
938 void ComputeDelayedBisimulationCTA(const Generator& rGen, const EventSet &rSilent, std::list<StateSet>& rResult){
939  if (rSilent.Empty()){
940  ComputeBisimulationCTA(rGen,rResult);
941  }
942  else if (rSilent.Size()==1){
943  std::vector<StateSet> trivial;
944  AbstractBisimulationCTA dbisim(rGen,*(rSilent.Begin()),1,trivial);
945  dbisim.ComputePartition(rResult);
946  }
947  else throw Exception("ComputeDelayedBisimulationCTA:","silent alphabet can contain at most one event", 100);
948 
949 }
950 
951 void ComputeBisimulationCTA(const Generator& rGen, const EventSet &rSilent, std::list<StateSet>& rResult, const std::vector<StateSet>& rPrePartition){
952  if (rSilent.Empty()){
953  ComputeBisimulationCTA(rGen,rResult);
954  }
955  else if (rSilent.Size()==1){
956  AbstractBisimulationCTA dbisim(rGen,*(rSilent.Begin()),1,rPrePartition);
957  dbisim.ComputePartition(rResult);
958  }
959  else throw Exception("ComputeDelayedBisimulationCTA:","silent alphabet can contain at most one event", 100);
960 
961 }
962 
963 void ComputeWeakBisimulationCTA(const Generator& rGen, const EventSet &rSilent, std::list<StateSet>& rResult){
964  if (rSilent.Empty()){
965  ComputeBisimulationCTA(rGen,rResult);
966  }
967  else if (rSilent.Size()==1){
968  std::vector<StateSet> trivial;
969  AbstractBisimulationCTA wbisim(rGen,*(rSilent.Begin()),2,trivial);
970  wbisim.ComputePartition(rResult);
971  }
972  else throw Exception("ComputeWeakBisimulation::","silent alphabet can contain at most one event", 100);
973 }
974 
975 void ComputeWeakBisimulation(const Generator& rGen, const EventSet &rSilent, std::list<StateSet>& rResult, const std::vector<StateSet>& rPrePartition){
976  if (rSilent.Empty()){
977  ComputeBisimulationCTA(rGen,rResult);
978  }
979  else if (rSilent.Size()==1){
980  AbstractBisimulationCTA wbisim(rGen,*(rSilent.Begin()),2,rPrePartition);
981  wbisim.ComputePartition(rResult);
982  }
983  else throw Exception("ComputeWeakBisimulationCTA::","silent alphabet can contain at most one event", 100);
984 }
985 
987  const Generator& rGen, const EventSet& rSilent, std::list<StateSet>& rResult, const Idx& rFlag, const std::vector<StateSet>& rPrePartition){
988  if (rSilent.Empty()){
989  ComputeBisimulationCTA(rGen,rResult);
990  }
991  else if (rSilent.Size()==1){
992  Generator xg(rGen);
993  ExtendTransRel(xg,rSilent,rFlag);
994  InstallSelfloops(xg,rSilent);
995  BisimulationCTA bisim(xg, rPrePartition);
996  bisim.ComputePartition(rResult);
997  }
998  else throw Exception("ComputeAbstractBisimulationSatCTA::","silent alphabet can contain at most one event", 100);
999 }
1000 
1001 
1002 void ComputeDelayedBisimulationSatCTA(const Generator& rGen, const EventSet& rSilent, std::list<StateSet>& rResult){
1003  std::vector<StateSet> trivial;
1004  ComputeAbstractBisimulationSatCTA(rGen,rSilent,rResult,1,trivial);
1005 }
1006 
1007 void ComputeWeakBisimulationSatCTA(const Generator& rGen, const EventSet& rSilent, std::list<StateSet>& rResult){
1008  std::vector<StateSet> trivial;
1009  ComputeAbstractBisimulationSatCTA(rGen,rSilent,rResult,2,trivial);
1010 }
1011 
1012 void ComputeDelayedBisimulationSatCTA(const Generator& rGen, const EventSet& rSilent, std::list<StateSet>& rResult, const std::vector<StateSet>& rPrePartition){
1013  ComputeAbstractBisimulationSatCTA(rGen,rSilent,rResult,1,rPrePartition);
1014 }
1015 
1016 void ComputeWeakBisimulationSatCTA(const Generator& rGen, const EventSet& rSilent, std::list<StateSet>& rResult, const std::vector<StateSet>& rPrePartition){
1017  ComputeAbstractBisimulationSatCTA(rGen,rSilent,rResult,2,rPrePartition);
1018 }
1019 
1020 } //namespace faudes
#define BISIM_VERB2(msg)
The DelayedBisimulation class Derived from Bisimulation class. We implement the "two-pass change-trac...
void ComputeChangedDelayedAfters(void)
ComputeChangedDelayedAfters see Fig. 10 of cited paper.
virtual void ComputePartition(std::list< StateSet > &rResult)
BisimulationPartition wrapper.
void ComputeChangedObservedAfters(void)
ComputeChangedObservedAfters see Fig. 12 of cited paper.
const Idx mTaskFlag
mTaskFlag flag for task: mTaskFlag == 1: delayed bisimulation mTaskFlag == 2: weak bisimulation
void ComputeAbstractBisimulation()
ComputeDelayedBisimulation perform the overall iteration based on different task flag value,...
void MarkTauStarAffected(std::vector< bool > &rAffected, const Idx &rState)
MarkTauAffected perform a recursive Depth-First-Search to mark all tau* predecessors as affected base...
const Idx mTau
tau persist index of original silent event
AbstractBisimulationCTA(const Generator &rGen, const Idx &rSilent, const Idx &rTaskFlag, const std::vector< StateSet > &rPrePartition)
virtual void EncodeData(void)
EncodeData basic preparation for bisimulation NOTE: this function is virtual since derived classes,...
The Bisimulation class The following class implements a basic normal bisimulation partition algorithm...
virtual ~BisimulationCTA()=default
std::vector< Idx > mEvents
const std::vector< StateSet > mPrePartition
void RefineChanged(void)
RefineChanged refine equivalence classes contained affected nodes in the last iteration (see Fig....
virtual void ComputePartition(std::list< StateSet > &rResult)
BisimulationPartition wrapper.
virtual void EncodeData(void)
EncodeData encode source generator into vector-form data structures to accelerate the iteration.
void FirstStepApproximation(void)
FirstStepApproximation (see Fig. 2 (b) in cited paper) Prepartition states according to their active ...
void GenerateResult(std::list< StateSet > &rResult)
GenerateResult generate partition result w.r.t. original state indices (without trivial classes)
void ComputeBisimulation(void)
ComputeBisimulation perform the overall iteration.
const Generator * mGen
std::vector< bool > mAffected
BisimulationCTA(const Generator &rGen, const std::vector< StateSet > &rPrePartition)
std::vector< bool > mChanged
std::vector< State > mStates
std::vector< Idx > mPartition
void ComputeChangedAfters(void)
ComputeChangedAfters Compute changed afters of each affected state. (see Fig 5. in cited paper) Affec...
bool Exists(const Idx &rIndex) const
bool Insert(const Idx &rIndex)
Iterator Begin(void) const
Iterator End(void) const
bool Exists(const Transition &t) const
bool Insert(const Transition &rTransition)
TBaseSet< Transition, TransSort::X1EvX2 >::Iterator Iterator
Definition: cfl_transset.h:273
The TopoSort class perform a topological sort on states of a given automaton. if s1 is before s2 in t...
std::vector< State > mStates
bool Visit(State &rNode)
const Generator * mGen
TopoSort(const Generator &rGen, const EventSet &rEvs)
std::list< Idx > mResult
bool Sort(std::list< Idx > &result)
StateSet::Iterator StatesBegin(void) const
const TransSet & TransRel(void) const
bool SetTransition(Idx x1, Idx ev, Idx x2)
const EventSet & Alphabet(void) const
EventSet ActiveEventSet(Idx x1) const
TransSet::Iterator TransRelBegin(void) const
EventSet::Iterator AlphabetBegin(void) const
StateSet::Iterator StatesEnd(void) const
TransSet::Iterator TransRelEnd(void) const
void InjectTransRel(const TransSet &rNewtransrel)
EventSet::Iterator AlphabetEnd(void) const
const StateSet & States(void) const
bool Empty(void) const
Definition: cfl_baseset.h:1904
virtual void Clear(void)
Definition: cfl_baseset.h:2104
Iterator End(void) const
Definition: cfl_baseset.h:2098
virtual void InsertSet(const TBaseSet &rOtherSet)
Definition: cfl_baseset.h:2194
Iterator Begin(void) const
Definition: cfl_baseset.h:2093
Idx Size(void) const
Definition: cfl_baseset.h:1899
uint32_t Idx
void ExtendTransRel(Generator &rGen, const EventSet &rSilent, const Idx &rFlag)
ExtendTransRel perform transition saturation w.r.t. silent evs. Two different saturation modes are se...
void InstallSelfloops(Generator &rGen, const EventSet &rEvs)
InstallSelfloops install selfloops on all states of given event set. intended to install silent event...
void ComputeAbstractBisimulationSatCTA(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult, const Idx &rFlag, const std::vector< StateSet > &rPrePartition)
ComputeAbstractBisimulationSatCTA saturation and change-tracking based partition algorithm for either...
void ComputeWeakBisimulation(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult, const std::vector< StateSet > &rPrePartition)
bool topoSort(const Generator &rGen, const EventSet &rEvs, std::list< Idx > &result)
topoSort wrapper for topological sortation rEvs is the set of events which will be considered while s...
void ComputeBisimulationCTA(const Generator &rGen, std::list< StateSet > &rResult)
ComputeBisimulationCTA basic bisimulation partition algorithm based on change-tracking algorithm.
void ComputeWeakBisimulationCTA(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult)
ComputeWeakBisimulationCTA weak bisimulation (aka observation eq) partition based on change-tracking ...
std::string ToStringInteger(Int number)
Definition: cfl_utils.cpp:43
void ComputeDelayedBisimulationCTA(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult)
void ComputeWeakBisimulationSatCTA(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult)
ComputeWeakBisimulationSatCTA weak bisimulation (aka observation eq) partition based on change-tracki...
void ComputeDelayedBisimulationSatCTA(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult)
ComputeDelayedBisimulationSatCTA delayed bisimulation partition based on change-tracking algorithm an...
std::vector< std::set< Idx > > cafter
std::vector< std::vector< Idx > > suc
std::vector< Idx > succs

libFAUDES 2.33l --- 2025.09.16 --- c++ api documentaion by doxygen