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
54namespace 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
78public:
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
88protected:
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 */
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
160private:
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 */
187public:
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
193private:
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 */
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 */
240
241 /*!
242 * \brief ComputeChangedObservedAfters
243 * see Fig. 12 of cited paper
244 */
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 */
266public:
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
279private:
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 */
291bool 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
311void ExtendTransRel(Generator& rGen, const EventSet& rSilent, const Idx& rFlag) {
312
313 if (rSilent.Empty()) return;
314 // HELPERS:
316 TransSet::Iterator tit1_end;
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
360void 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
377TopoSort::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
397bool 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
408bool 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
422bool 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")
644 }
645}
646
647void 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
664void 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
786void 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
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")
916 }
917}
918
919void AbstractBisimulationCTA::ComputePartition(std::list<StateSet>& rResult){
921 GenerateResult(rResult);
922}
923
924
925
926// wrappers
927void ComputeBisimulationCTA(const Generator& rGen, std::list<StateSet>& rResult){
928 std::vector<StateSet> trivial;
929 BisimulationCTA bisim(rGen,trivial);
930 bisim.ComputePartition(rResult);
931}
932
933void ComputeBisimulationCTA(const Generator& rGen, std::list<StateSet>& rResult, const std::vector<StateSet>& rPrePartition){
934 BisimulationCTA bisim(rGen,rPrePartition);
935 bisim.ComputePartition(rResult);
936}
937
938void 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
951void 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
963void 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
975void 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
1002void 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
1007void 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
1012void 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
1016void 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)
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
virtual void Clear(void)
Iterator End(void) const
virtual void InsertSet(const TBaseSet &rOtherSet)
Iterator Begin(void) const
Idx Size(void) const
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.33k --- 2025.09.16 --- c++ api documentaion by doxygen