cfl_conflequiv.cpp
Go to the documentation of this file.
1 /* FAU Discrete Event Systems Library (libfaudes)
2 
3  Copyright (C) 2015 Michael Meyer, Thomnas Moor.
4  Copyright (C) 2021,2023 Yiheng Tang, Thomnas Moor.
5  Exclusive copyright is granted to Klaus Schmidt
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
20 
21 #include "cfl_conflequiv.h"
22 #include "cfl_bisimcta.h"
23 #include "cfl_regular.h"
24 #include "cfl_graphfncts.h"
25 
26 
27 /** Two debug levels for functions in this source file **/
28 //#define FAUDES_DEBUG_COMPVER0
29 //#define FAUDES_DEBUG_COMPVER1
30 #ifdef FAUDES_DEBUG_COMPVER0
31 #define FD_CV0(message) FAUDES_WRITE_CONSOLE("FAUDES_CONFEQ: " << message)
32 #else
33 #define FD_CV0(message)
34 #endif
35 #ifdef FAUDES_DEBUG_COMPVER1
36 #define FD_CV1(message) FAUDES_WRITE_CONSOLE("FAUDES_CONFEQ: " << message)
37 #else
38 #define FD_CV1(message)
39 #endif
40 
41 
42 namespace faudes {
43 
44 
45 
46 // insert manually omega event for given automaton
47 // YT: it has also been considered that to use a omega-selfloop over all
48 // marked states. This is not functionally harmful. However, this will enlarge
49 // the set of incoming transitions for marked states, and thus will make many
50 // partitions unnecesaritly finer, e.g. those incoming-eq-based paritions and
51 // only silent incoming rules.
53  if (rGen.ExistsEvent("_OMEGA_")||rGen.ExistsEvent("_OMEGA_TERMINAL_")||rGen.ExistsState("_TERMINAL_")){
54  throw Exception("AppendOmegaTermination", "please don't use event names _OMEGA_, _OMEGA_TERMINAL_ and state name _TERMINAL_", 100);
55  }
56  Idx omega = rGen.InsEvent("_OMEGA_");
57  Idx terminal = rGen.InsState("_TERMINAL_"); // this will be marked later on
58  StateSet::Iterator sit = rGen.MarkedStatesBegin();
59  for(;sit!=rGen.MarkedStatesEnd();sit++){ // each marked state has a omega trans to terminal
60  rGen.SetTransition(*sit,omega,terminal);
61  }
62  rGen.SetMarkedState(terminal);
63  Idx omega_terminal = rGen.InsEvent("_OMEGA_TERMINAL_");
64  rGen.SetTransition(terminal,omega_terminal,terminal); // need a special selfloop for terminal state, so that partition will always separate it
65 }
66 
67 // Convenience typedef for incoming transsitions to a given state (or set of states).
68 // Since we know the target state X2 from the context, we only record X1 and Ev to
69 // experience better performance
70 typedef std::set<std::pair<Idx,Idx>> SetX1Ev;
71 
72 // Merge equivalent classes, i.e. perform quotient abstraction
73 // (this implementation works fine with a small amount of small equiv classes)
75  Generator& rGen,
76  TransSetX2EvX1& rRevTrans,
77  const std::list< StateSet >& rClasses)
78 {
79  // iterators
80  StateSet::Iterator sit;
81  StateSet::Iterator sit_end;
85  TransSet::Iterator tit_end;
86  // record for delayed delete
87  StateSet delstates;
88  // iterate classes
89  std::list< StateSet >::const_iterator qit=rClasses.begin();
90  for(;qit!=rClasses.end();++qit) {
91  sit=qit->Begin();
92  sit_end=qit->End();
93  Idx q1=*(sit++); // this than denotes the name of quotient, i.e.[q1]
94  for(;sit!=sit_end;++sit){
95  Idx q2=*sit;
96  // merge outgoing transitions form q2 to q1
97  tit = rGen.TransRelBegin(q2);
98  tit_end = rGen.TransRelEnd(q2);
99  for(;tit!=tit_end;++tit) {
100  rGen.SetTransition(q1,tit->Ev,tit->X2);
101  rRevTrans.Insert(q1,tit->Ev,tit->X2);
102  }
103  // merge incomming transitions form q2 to q1
104  rit = rRevTrans.BeginByX2(q2);
105  rit_end = rRevTrans.EndByX2(q2);
106  for(;rit!=rit_end;++rit) {
107  rGen.SetTransition(rit->X1,rit->Ev,q1);
108  rRevTrans.Insert(rit->X1,rit->Ev,q1);
109  }
110  if(rGen.ExistsMarkedState(q2))
111  rGen.InsMarkedState(q1);
112  if((rGen.ExistsInitState(q2)))
113  rGen.InsInitState(q1);
114  delstates.Insert(q2);
115  }
116  }
117  // do delete
118  rGen.DelStates(delstates);
119 }
120 
121 
122 // construct observation equivalent quotient
123 // in Pilbrow & Malik 2015, the so called "weak observation eq" is introduced which
124 // seems to be coarser. However, according to our discussion with Mr. Malik,
125 // the quotient cannot be constructed from the original automaton (with tau events),
126 // but from the saturated and tau-removed automaton. In various test cases, weak-ob-eq
127 // shows worse performance then ob-eq and thus abandoned
129  FD_DF("ObservationEquivalentQuotient(): prepare for t#"<<g.TransRelSize());
130 
131  // have extendend/reverse-ordered transition relations
132  TransSetX2EvX1 rtrans;
133  g.TransRel().ReSort(rtrans);
134  std::list<StateSet> eqclasses;
135  ComputeWeakBisimulationSatCTA(g,silent,eqclasses);
136 
137  // merge classes
138  FD_DF("ObservationEquivalentQuotient(): merging classes #"<< eqclasses.size());
139  MergeEquivalenceClasses(g,rtrans,eqclasses);
140 
141  FD_DF("ObservationEquivalentQuotient(): done with t#"<<g.TransRelSize());
142 }
143 
144 
145 // Compute the incoming non-tau transitions of a state in a saturated
146 // and tau-removed generator. Since the target X2 is known, we return
147 // a set of pairs {X1,Ev}. Moreover, for the sake of reachability from
148 // some intial state, a special pair <0,0> indicates that this state
149 // can be silently reached from some initial state. Since IncomingTransSet
150 // typically is called within a loop, we require to pre-compute the reverse
151 // transitionrelation
153  const Generator& rGen,
154  const TransSetX2EvX1& rRTrans,
155  const EventSet& silent,
156  const Idx& state,
157  SetX1Ev& result){
158  result.clear(); // initialize result
159  std::stack<Idx> todo; // states to process
160  std::set<Idx> visited; // states processed
161  todo.push(state);
162  visited.insert(state);
163 
164  while (!todo.empty()){
165  Idx cstate = todo.top();
166  todo.pop();
167  // a "flag" indicating that this state can be silently reached by
168  // some initial state, see remark at the beginning
169  if (rGen.InitStates().Exists(cstate))
170  result.insert({0,0});
171  TransSetX2EvX1::Iterator rtit = rRTrans.BeginByX2(cstate);
172  TransSetX2EvX1::Iterator rtit_end = rRTrans.EndByX2(cstate);
173  for(;rtit!=rtit_end;rtit++){
174  if (!silent.Exists(rtit->Ev))
175  result.insert({rtit->X1,rtit->Ev});
176  else{
177  if (visited.find(rtit->X1) == visited.end()){
178  todo.push(rtit->X1);
179  visited.insert(rtit->X1);
180  }
181  }
182  }
183  }
184 }
185 
186 // compute the non-silent active events of a state of agiven generator.
187 // this algo is similar to IncomingTransSet, but we shall notice
188 // that IncomingTransSet record the source state as well but here not.
190  const Generator &rGen,
191  const EventSet &silent,
192  const Idx &state,
193  EventSet &result){
194  result.Clear(); // initialize result
195  std::stack<Idx> todo; // states to process
196  std::set<Idx> visited;
197  todo.push(state);
198  visited.insert(state);
199  while (!todo.empty()){
200  Idx cstate = todo.top();
201  todo.pop();
202  TransSet::Iterator rtit = rGen.TransRelBegin(cstate);
203  TransSet::Iterator rtit_end = rGen.TransRelEnd(cstate);
204  for(;rtit!=rtit_end;rtit++){
205  if (!silent.Exists(rtit->Ev))
206  result.Insert(rtit->Ev);
207  else{
208  if (visited.find(cstate) == visited.end()){
209  todo.push(rtit->X2);
210  visited.insert(rtit->X2);
211  }
212  }
213  }
214  }
215 }
216 
217 // return eq classes of =_inc. Instead of returning list of state sets,
218 // this function retunrs a map where the keys are the (source state, (non-tau)event) - pairs
219 // and the value of each key is the corresponding eq class. Effectively, the keys are just
220 // for efficient internal search of existing classes and will not be utilised in any other
221 // functions which requies incoming eq (they will only need the values)
222 std::map<SetX1Ev,StateSet> IncomingEquivalentClasses(const Generator& rGen, const EventSet& silent){
223  FD_CV1("-- IncomingEquivalentClasses: candidats");
224  // have reverse transition relation
225  TransSetX2EvX1 rtrans;
226  rGen.TransRel().ReSort(rtrans);
227  //StateSet candidates;
228  //TransSet::Iterator tit = rGen.StatesBegin();
229  //TransSet::Iterator tit_end = rGen.StatesEnd();
230  // start the incoming equivalence partition
231  StateSet::Iterator sit = rGen.StatesBegin();
232  StateSet::Iterator sit_end = rGen.StatesEnd();
233  // set up a map <incoming transset->class> to fast access incoming transset
234  // of existing classes. This map is only meant to check incoming equivalence.
235  std::map<SetX1Ev,StateSet> incomingeqclasses;
236  std::map<SetX1Ev,StateSet>::iterator mit;
237  for(;sit!=sit_end;sit++){
238  SetX1Ev rec_income;
239  IncomingTransSet(rGen,rtrans,silent,*sit,rec_income);
240  mit = incomingeqclasses.find(rec_income);
241  if (mit != incomingeqclasses.end()){
242  mit->second.Insert(*sit);
243  } else {
244  StateSet newclass;
245  newclass.Insert(*sit);
246  incomingeqclasses.insert({rec_income,newclass});
247  }
248  }
249  return incomingeqclasses;
250 }
251 
252 // Active events rule; see cited literature 3.2.1
253 // Note: this is a re-write of Michael Meyer's implementation to refer to the
254 // ext. transistion relation, as indicated by the literature.
255 // YT: And now I have rewritten this again, hopefully for better readability
256 // YT: This rule was suggested to be implemented together with enabled continuation rule
257 // to avoid computing incoming eq multiple times, see C. Pilbrow and R. Malik 2015. However,
258 // since enabled continuation rule requires tau-loop-free automaton and active events rule
259 // may generate tau, i have separated them
260 void ActiveEventsRule(Generator& g, const EventSet& silent){
261 
262  std::map<SetX1Ev,StateSet> incomingeqclasses = IncomingEquivalentClasses(g,silent);
263  std::list<StateSet> eqclasses; // store result
264  TransSetX2EvX1 rtrans; // convenient declaration for MergeEqClasses
265 
266  FD_CV1("-- Refine for ActiveEventRule")
267  // iterate
268  std::map<SetX1Ev,StateSet>::iterator mit = incomingeqclasses.begin();
269  for (;mit!=incomingeqclasses.end();mit++){
270  // refine each =_inc class. in each refined class, states shall
271  // have the same set of non-tau active event
272  // in the course of refinement, merged
273  while(mit->second.Size()>1){
274  StateSet fineclass; // refined class
275  StateSet::Iterator sit = mit->second.Begin();
276  fineclass.Insert(*sit);
277  EventSet activeevs;
278  ActiveNonTauEvs(g,silent,*sit,activeevs); // xg: exclude tau
279  StateSet::Iterator sit_compare = sit;
280  sit_compare++; // next state in this =_inc class. compare active non-tau ev with *sit
281  mit->second.Erase(*sit);// delete to avoid duplets
282  while(sit_compare!=mit->second.End()){
283  EventSet activeevs_compare;
284  ActiveNonTauEvs(g,silent,*sit_compare,activeevs_compare);
285  if (activeevs_compare==activeevs){
286  fineclass.Insert(*sit_compare);
287  StateSet::Iterator todelete = sit_compare;
288  sit_compare++;
289  mit->second.Erase(todelete);
290  }
291  else sit_compare++;
292  }
293  if (fineclass.Size()>1) eqclasses.push_back(fineclass);
294  }
295  }
296  g.TransRel().ReSort(rtrans);
297  MergeEquivalenceClasses(g,rtrans,eqclasses);
298 }
299 
300 // YT: this is a generalized rule of silent continuation rule, see Pilbrow and Malik 2015.
301 // Note this function requires tau-loop free automata.
303  std::map<SetX1Ev,StateSet> incomingeqclasses = IncomingEquivalentClasses(g,silent);
304  std::list<StateSet> eqclasses; // store result
305  TransSetX2EvX1 rtrans; // convenient declaration for MergeEqClasses
306  // YT: we only consider states both with active tau. Both with always enabled evs,
307  // as suggested in Pilbrow & Malik, are neglected
308  eqclasses.clear();
309  rtrans.Clear();
310  std::map<SetX1Ev,StateSet>::iterator mit = incomingeqclasses.begin();
311  for (;mit!=incomingeqclasses.end();mit++){
312  StateSet fineclass; // refined class
313  StateSet::Iterator sit = mit->second.Begin();
314  while(sit!=mit->second.End()){
315  if (!(g.ActiveEventSet(*sit) * silent).Empty()){ // g: has outgoing tau
316  fineclass.Insert(*sit);
317  sit++;
318  }
319  else sit++;
320  }
321  if (fineclass.Size()>1) eqclasses.push_back(fineclass);
322  }
323  g.TransRel().ReSort(rtrans);
324  MergeEquivalenceClasses(g,rtrans,eqclasses);
325 }
326 
327 // simple function removing tau self loops
328 void RemoveTauSelfloops(Generator &g, const EventSet &silent){
330  TransSet::Iterator tit_end = g.TransRelEnd();
331  while(tit!=tit_end){
332  if (tit->X1 == tit->X2 && silent.Exists(tit->Ev)) g.ClrTransition(tit++);
333  else tit++;
334  }
335 }
336 
337 // as a special case of observation equivalence, states on a tau-loop are all equivalent
338 // and can be merged to a single state. This step is preferred to be done before
339 // (weak) observation equivalence, as they require transition saturation which is quite
340 // expensive.
341 void MergeSilentLoops(Generator &g, const EventSet &silent){
342 
343  TransSetX2EvX1 rtrans;
344  g.TransRel().ReSort(rtrans);
345 
346  // have a generator copy where only silent transitions are preserved
347  Generator copyg(g);
348  TransSet::Iterator tit = copyg.TransRel().Begin();
349  while (tit!=copyg.TransRelEnd()){
350  if (!silent.Exists(tit->Ev)) // if not a silent trans, delete
351  copyg.ClrTransition(tit++);
352  else tit++;
353  }
354 
355  // compute eqclass and merge on original generator
356  std::list<StateSet> eqclasses;
357  StateSet dummy;
358  ComputeScc(copyg,eqclasses,dummy);
359 
360  // delete trivial eqclasses (yt: I dont want to hack into the computescc function)
361  std::list<StateSet>::iterator sccit = eqclasses.begin();
362  while (sccit!=eqclasses.end()){
363  if ((*sccit).Size()==1) eqclasses.erase(sccit++);
364  else sccit++;
365  }
366 
367  MergeEquivalenceClasses(g,rtrans,eqclasses);
368 }
369 
370 // Certain conflicts. see cited literature 3.2.3
371 // -- remove outgoing transitions from not coaccessible states
373  StateSet notcoaccSet=g.States()-g.CoaccessibleSet();
374  StateSet::Iterator sit=notcoaccSet.Begin();
375  StateSet::Iterator sit_end=notcoaccSet.End();
376  for(;sit!=sit_end;++sit){
378  TransSetX1EvX2::Iterator tit_end=g.TransRelEnd(*sit);
379  for(;tit!=tit_end;) g.ClrTransition(tit++);
380  //FD_DF("RemoveCertainConflictA: remove outgoing from state "<<*sit);
381  }
382 }
383 
384 // Certain conflicts. see cited literature 3.2.3
385 // -- remove outgoing transitions from states that block by a silent event
386 void BlockingSilentEvent(Generator& g,const EventSet& silent){
387  FD_DF("BlockingSilentEvent(): prepare for t#"<<g.TransRelSize());
388  StateSet coacc=g.CoaccessibleSet();
389  StateSet sblock;
390  TransSet::Iterator tit;
391  TransSet::Iterator tit_end;
392  StateSet::Iterator sit;
393  StateSet::Iterator sit_end;
394  // loop all transitions to figure certain blocking states
395  tit=g.TransRelBegin();
396  tit_end=g.TransRelEnd();
397  for(;tit!=tit_end;++tit) {
398  if(silent.Exists(tit->Ev))
399  if(!coacc.Exists(tit->X2))
400  sblock.Insert(tit->X1);
401  }
402  // unmark blocking states and eliminate possible future
403  sit=sblock.Begin();
404  sit_end=sblock.End();
405  for(;sit!=sit_end;++sit) {
406  g.ClrMarkedState(*sit);
407  tit=g.TransRelBegin(*sit);
408  tit_end=g.TransRelEnd(*sit);
409  for(;tit!=tit_end;)
410  g.ClrTransition(tit++);
411  }
412  FD_DF("BlockingSilentEvent(): done with t#"<<g.TransRelSize());
413 }
414 
415 // Certain conflicts. see cited literature 3.2.3
416 // -- merge all states that block to one representative
418  StateSet notcoacc=g.States()-g.CoaccessibleSet();
419  // bail out on trovial case
420  if(notcoacc.Size()<2) return;
421  // have a new state
422  Idx qnc=g.InsState();
423  // fix init status
424  if((notcoacc * g.InitStates()).Size()>0)
425  g.SetInitState(qnc);
426  // fix transitions
428  TransSet::Iterator tit_end=g.TransRelEnd();
429  for(;tit!=tit_end;++tit){
430  if(notcoacc.Exists(tit->X2))
431  g.SetTransition(tit->X1,tit->Ev,qnc);
432  }
433  // delete original not coacc
434  g.DelStates(notcoacc);
435 }
436 
437 
438 // Only silent incomming rule; see cited literature 3.2.4
439 // Note: input generator must be silent-SCC-free
440 // Note: this is a complete re-re-write and needs testing for more than one candidates
441 void OnlySilentIncoming(Generator& g, const EventSet& silent){
442 
443  // figure states with only silent incomming transitions
444  // note: Michael Meyer proposed to only consider states with at least two incomming
445  // events -- this was dropped in the re-write
446  StateSet cand=g.States()-g.InitStates(); // note the initial state set is preserved
448  TransSet::Iterator tit_end = g.TransRelEnd();
449  for(;tit!=tit_end;++tit)
450  if(!silent.Exists(tit->Ev)) cand.Erase(tit->X2);
451 
452  // bail out on trivial
453  if(cand.Size()==0) {
454  return;
455  }
456 
457  StateSet::Iterator sit = cand.Begin();
458  StateSet::Iterator sit_end = cand.End();
459  while (sit!=sit_end){
460  TransSet::Iterator tit2 = g.TransRelBegin(*sit);
461  TransSet::Iterator tit2_end = g.TransRelEnd(*sit);
462  for (;tit2!=tit2_end;tit2++){
463  if (silent.Exists(tit2->Ev)){
464  break;
465  }
466  }
467 
468  if (tit2!=tit2_end) { // there is at least one tau outgoing
469  // redirect transitions
470  TransSetX2EvX1 rtrans;
471  g.TransRel().ReSort(rtrans);
472  TransSetX2EvX1::Iterator rtit = rtrans.BeginByX2(*sit);
473  TransSetX2EvX1::Iterator rtit_end = rtrans.EndByX2(*sit);
474  for(;rtit!=rtit_end;rtit++){
475  if (g.ExistsMarkedState(*sit)){ // mark predecessor if the state to remove is marked
476  g.SetMarkedState(rtit->X1);
477  }
478  TransSet::Iterator tit3 = g.TransRelBegin(*sit);
479  TransSet::Iterator tit3_end = g.TransRelEnd(*sit);
480  for (;tit3!=tit3_end;tit3++){
481  g.SetTransition(rtit->X1,tit3->Ev,tit3->X2);
482  }
483  }
484  StateSet::Iterator todelete = sit++;
485  g.DelState(*todelete);
486  }
487  else sit++;
488  }
489  // remark: with this implementation, incoming events (incl. tau) will not be changed
490  // through state removal. A candidate state in "cand" will always be a legit candidate
491 }
492 
493 
494 // Only silent outgoing rule; see cited literature 3.2.5
495 // Note: input generator must be silent-SCC-free
496 void OnlySilentOutgoing(Generator& g,const EventSet& silent){
497  StateSet::Iterator sit = g.StatesBegin();
498  StateSet::Iterator sit_end = g.StatesEnd();
499  while(sit!=sit_end){
500  // figure out whether this state is only silent outgoing
501  if (g.MarkedStates().Exists(*sit)) {sit++;continue;}
502  TransSet::Iterator tit2 = g.TransRelBegin(*sit);
503  TransSet::Iterator tit2_end = g.TransRelEnd(*sit);
504  if (tit2==tit2_end) {sit++;continue;} // sit points to a deadend state (nonmarked state without outgoing trans)
505  for (;tit2!=tit2_end;tit2++){
506  if (!silent.Exists(tit2->Ev)) break;
507  }
508  if (tit2 != tit2_end) {sit++;continue;} // sit has non-silent outgoing trans
509 
510  // sit has passed the test. relink outgoing transitions of predecessor
511  TransSetX2EvX1 rtrans;
512  g.TransRel().ReSort(rtrans);
513  // (repair intial state quicker by first iterate outgoing trans)
514  tit2 = g.TransRelBegin(*sit);
515  tit2_end = g.TransRelEnd(*sit);
516  for (;tit2!=tit2_end;tit2++){
517  TransSetX2EvX1::Iterator rtit = rtrans.BeginByX2(*sit); // incoming trans to *sit
518  TransSetX2EvX1::Iterator rtit_end = rtrans.EndByX2(*sit);
519  for (;rtit!=rtit_end;rtit++){
520  g.SetTransition(rtit->X1,rtit->Ev,tit2->X2);
521  }
522  if (g.ExistsInitState(*sit))
523  g.SetInitState(tit2->X2);
524  }
525  StateSet::Iterator todelete = sit;
526  sit++;
527  g.DelState(*todelete);
528  }
529 }
530 
532  EventSet result;
533  EventSet msilentevs=rGen.Alphabet()*silent;
534  if(msilentevs.Empty())
535  return result;
536  Idx tau=*(msilentevs.Begin());
537  result.Insert(*(msilentevs.Begin()));
538  msilentevs.Erase(tau); // from now on, msilentevs exclude tau
539  silent.EraseSet(msilentevs);
540  if (msilentevs.Empty()) // in this case, only one silent event is set to tau and no need to hide
541  return result;
543  TransSet::Iterator tit_end=rGen.TransRelEnd();
544  for(;tit!=tit_end;) {
545  if(!msilentevs.Exists(tit->Ev)) {++tit; continue;}
546  Transition t(tit->X1,tau,tit->X2);
547  rGen.ClrTransition(tit++);
548  if (!rGen.ExistsTransition(t))
549  rGen.SetTransition(t);
550  }
551  rGen.InjectAlphabet(rGen.Alphabet()-msilentevs);
552  return result;
553 }
554 
555 // convenient wrapper for tau-loop removal. Technically, the following algos are
556 // very efficient and is suggested to perform prior to other abstraction. Furthermore,
557 // input automata being tau-loop free is necessary for the following abstraction rules:
558 // - OnlySilentIncoming
559 // - OnlySilentOutgoing
560 // - EnabledContinuationRule
561 // NOTE: currently, ObservationEquivalenceQuotient utilises saturation based algo which does
562 // not require tau-loop-free. If
563 void RemoveTauLoops(Generator& rGen, const EventSet& silent){
564  MergeSilentLoops(rGen,silent);
565  RemoveTauSelfloops(rGen,silent);
566 }
567 
568 
569 // apply all of the above rules once
571  // hiding must be performed beforehand.
572  EventSet tau = HidePriviateEvs(rGen, silent);
573 
574  // ***************** abstraction rules *******************
575  FD_CV1("Applying only silent incoming rule")
576  RemoveTauLoops(rGen,tau);
577  OnlySilentIncoming(rGen,tau);
578  FD_CV1("Applying only silent outgoing rule")
579  // tau-loop-free is required. Nevertheless, after onlysilentincoming, no tau-loops will emerge
580  OnlySilentOutgoing(rGen,tau);
581  FD_CV1("Applying enabled continuation rule")
582  RemoveTauLoops(rGen,tau);
583  EnabledContinuationRule(rGen,tau);
584  FD_CV1("Applying active events rule")
585  ActiveEventsRule(rGen,tau);
586  FD_CV1("Applying observation eq quotient")
588  FD_CV1("Applying certain conflicts rule")
589  BlockingSilentEvent(rGen,tau);
590  MergeNonCoaccessible(rGen);
591  FD_DF("ConflictEquivalentAbstraction(): done with t#"<<rGen.TransRelSize());
592 }
593 
594 // apply all of the above repeatedly until a fixpoint is attained
596  Idx sz0=rGen.Size();
597  (void) sz0; // make compiler happy
598  while(true) {
599  Idx sz1=rGen.Size();
600  FD_CV1("ConflictEquivalentAbstraction(): loop with states #"<<rGen.TransRelSize());
601  FD_WPC(sz0, sz1, "ConflictEquivalentAbstraction: fixpoint iteration states #" << sz1);
602  ConflictEquivalentAbstractionOnce(rGen,rSilentEvents);
603  // break on slow progress
604  //if(rGen.Size()> 1000 && rGen.Size()>0.95*sz1) break;
605  if(rGen.Size()==sz1) break;
606  }
607  FD_CV1("ConflictEquivalentAbstraction(): done with states #"<<rGen.TransRelSize());
608 }
609 
610 // select variant
612  ConflictEquivalentAbstractionOnce(rGen,rSilentEvents);
613  //ConflictEquivalentAbstractionLoop(rGen,rSilentEvents);
614 }
615 
616 
617 // API wrapper
618 bool IsNonconflicting(const GeneratorVector& rGvec) {
619 
620  GeneratorVector gvec = rGvec;
621 
622  FD_CV0("Appending Omega event")
623  Idx git = 0;
624  for(;git!=gvec.Size();git++){
625  AppendOmegaTermination(gvec.At(git));
626  }
627 
628  bool firstCycle = true;
629  while (true){
630  FD_CV0("========================================")
631  FD_CV0("Remaining automata: #"<<gvec.Size())
632 
633  // trivial cases
634  if(gvec.Size()==0) return true;
635  if(gvec.Size()==1) break;
636 
637  // figure silent events
638  EventSet silent, all, shared;
639  Idx git = 0;
640  while(true){
641  all = all+gvec.At(git).Alphabet();
642  Idx git_next = git+1;
643  if (git_next == gvec.Size()) break;
644  for(;git_next!=gvec.Size();git_next++){
645  shared = shared
646  + (gvec.At(git).Alphabet())
647  * (gvec.At(git_next).Alphabet());
648  }
649  git++;
650  }
651  silent=all-shared; // all silent events in all current candidates
652 
653  // normalize for one silent event per generator, and then abstract.
654  // note from the second iteration, this is only necessary for the
655  // automaton composed from former candidates. This is realized by
656  // the break at the end
657  git = 0;
658  for(;git!=gvec.Size();git++){
659  // abstraction
660  FD_CV0("Abstracting Automaton "<<gvec.At(git).Name()<<", with state count: "<<gvec.At(git).Size())
661  ConflictEquivalentAbstraction(gvec.At(git), silent);
662  FD_CV0("State count after abstraction: "<<gvec.At(git).Size())
663  if(!firstCycle) break;
664  }
665  firstCycle = false;
666 
667  // candidate choice heuritics. Branch by different tasks
668  Idx imin = 0;
669  Idx jmin = 0;
670 
671  // candidat with fewest transitions 'minT'
672  git = 1;
673  for(;git!=gvec.Size();git++){
674  if(gvec.At(git).TransRelSize()<gvec.At(imin).TransRelSize())
675  imin = git;
676  }
677  // candidat with most common events 'maxC'
678  git = jmin;
679  Int score=-1;
680  for(; git!=gvec.Size(); git++){
681  if(git==imin) continue;
682  Int sharedsize = (gvec.At(git).Alphabet() * gvec.At(imin).Alphabet()).Size();
683  if ( sharedsize > score){
684  jmin = git;
685  score = sharedsize;
686  }
687  }
688  // compose candidate pair
689  Generator gij;
690  FD_CV0("Composing automata "<<gvec.At(imin).Name()<<" and "<<gvec.At(jmin).Name())
691  Parallel(gvec.At(imin),gvec.At(jmin),gij);
692  GeneratorVector newgvec;
693  newgvec.Append(gij); // the composed generator is always the first element
694  git = 0;
695  for(;git!=gvec.Size();git++){
696  if (git == imin || git == jmin) continue;
697  newgvec.Append(gvec.At(git)); // all other candidates are just copied to the next iteraion
698  }
699  gvec = newgvec;
700  }
701  return IsNonblocking(gvec.At(0));
702 }
703 
704 // API wrapper
705 bool IsNonblocking(const GeneratorVector& rGvec) {
706  return IsNonconflicting(rGvec);
707 }
708 
709 
710 } // namespace
711 
712 
713 
714 /*
715 earlier revison for inspection
716 
717 
718 
719 
720 // intentend user interface
721 bool IsNonblocking(const GeneratorVector& rGenVec) {
722  Idx i,j;
723 
724  // trivial cases
725  if(rGenVec.Size()==0) return true;
726  if(rGenVec.Size()==1) return IsNonblocking(rGenVec.At(0));
727 
728  // have a local copy of input generator
729  GeneratorVector gvec=rGenVec;
730 
731  // figure silent events
732  EventSet silent, all, shared;
733  all=gvec.At(0).Alphabet();
734  for(i=0;i<gvec.Size()-1;++i){
735  for(j=i+1;j<gvec.Size();++j){
736  const Generator& g1=gvec.At(i);
737  const Generator& g2=gvec.At(j);
738  shared=shared+g1.Alphabet()*g2.Alphabet();
739  }
740  all=all+gvec.At(i).Alphabet();
741  }
742  silent=all-shared;
743 
744  // normalize for one silent event per generator
745  for(i=0;i<gvec.Size();++i){
746  Generator& gi=gvec.At(i);
747  EventSet sili=gi.Alphabet()*silent;
748  if(sili.Size()<=1) continue;
749  Idx esi=*(sili.Begin());
750  sili.Erase(esi);
751  silent.EraseSet(sili);
752  all.EraseSet(sili);
753  TransSet::Iterator tit=gi.TransRelBegin();
754  TransSet::Iterator tit_end=gi.TransRelEnd();
755  for(;tit!=tit_end;) {
756  if(!sili.Exists(tit->Ev)) {++tit; continue;}
757  Transition t(tit->X1,esi,tit->X2);
758  gi.ClrTransition(tit++);
759  gi.SetTransition(t);
760  }
761  gi.InjectAlphabet(gi.Alphabet()-sili);
762  }
763 
764  // reduce all generators
765  for(i=0;i<gvec.Size();i++){
766  Generator& g=gvec.At(i);
767  FD_DF("Abstract generator " << g.Name());
768  FD_DF("Silent events #" << (g.Alphabet()*silent).Size());
769  ConflictEquivalentAbstraction(g,g.Alphabet()*silent);
770  if(g.InitStates().Size()>0)
771  if(g.MarkedStates().Size()==0){
772  FD_DF("No marked states (A)");
773  return false;
774  }
775  }
776 
777  // loop until resolved
778  while(gvec.Size()>=2) {
779 
780  // candidat pairs with fewest transitions 'minT'
781  Idx imin=0;
782  for(i=1;i<gvec.Size();i++){
783  if(gvec.At(i).TransRelSize()<gvec.At(imin).TransRelSize())
784  imin=i;
785  }
786 
787  // candidat pairs with most local events 'maxL'
788  Idx jmin=0;
789  Int score=-1;
790  for(i=0;i<gvec.Size();i++){
791  if(i==imin) continue;
792  const Generator& gi=gvec.At(imin);
793  const Generator& gj=gvec.At(i);
794  EventSet aij=gi.Alphabet()+gj.Alphabet();
795  EventSet shared;
796  for(j=0;j<gvec.Size();j++){
797  if(j==imin) continue;
798  if(j==i) continue;
799  shared=shared + gvec.At(j).Alphabet()*aij;
800  }
801  Int jscore= aij.Size()-shared.Size();
802  if(jscore>score) {score=jscore; jmin=i;}
803  }
804 
805  // candidat pairs with fewest states when composed 'minS'
806  //Idx jmin=0;
807  //Float score=-1;
808  //for(i=0;i<gvec.Size();i++){
809  // if(i==imin) continue;
810  // const Generator& gi=gvec.At(imin);
811  // const Generator& gj=gvec.At(i);
812  // Int jscore= gi.Size()*gj.Size()/((Float) gi.AlphabetSize()*gj.AlphabetSize()); // rough estimate
813  // if(jscore<score || score<0) {score=jscore; jmin=i;}
814  // }
815 
816  // compose candidate pair
817  Generator& gimin=gvec.At(imin);
818  Generator& gjmin=gvec.At(jmin);
819  FD_DF("Compose generator " << gimin.Name() << " and " << gjmin.Name());
820  Parallel(gimin,gjmin,gimin);
821  gvec.Erase(jmin);
822  FD_DF("Composition done t#"<< gimin.TransRelSize());
823 
824  // update shared events
825  EventSet silent, all, shared;
826  all=gvec.At(0).Alphabet();
827  for(i=0;i<gvec.Size()-1;++i){
828  for(j=i+1;j<gvec.Size();++j){
829  const Generator& g1=gvec.At(i);
830  const Generator& g2=gvec.At(j);
831  shared=shared+g1.Alphabet()*g2.Alphabet();
832  }
833  all=all+gvec.At(i).Alphabet();
834  }
835  silent=all-shared;
836 
837  // normalize for one silent event per generator
838  EventSet sili=gimin.Alphabet()*silent;
839  if(sili.Size()>1) {
840  Idx esi=*(sili.Begin());
841  sili.Erase(esi);
842  silent.EraseSet(sili);
843  TransSet::Iterator tit=gimin.TransRelBegin();
844  TransSet::Iterator tit_end=gimin.TransRelEnd();
845  for(;tit!=tit_end;) {
846  if(!sili.Exists(tit->Ev)) {++tit; continue;}
847  Transition t(tit->X1,esi,tit->X2);
848  gimin.ClrTransition(tit++);
849  gimin.SetTransition(t);
850  }
851  gimin.InjectAlphabet(gimin.Alphabet()-sili);
852  }
853 
854  // abstract
855  if(gvec.Size()>1){
856  FD_DF("Abstract generator " << gimin.Name());
857  FD_DF("Silent events #" << (gimin.Alphabet()*silent).Size());
858  ConflictEquivalentAbstraction(gimin,gimin.Alphabet()*silent);
859  if(gimin.InitStates().Size()>0)
860  if(gimin.MarkedStates().Size()==0){
861  FD_DF("No marked states (A)");
862  return false;
863  }
864  }
865 
866  }
867 
868  FD_DF("Testing final stage with t#" << gvec.At(0).TransRelSize() << "/s#" << gvec.At(0).Size());
869  bool res=gvec.At(0).BlockingStates().Size()==0;
870  FD_DF("IsNonblocking(): done");
871  return res;
872 }
873 
874 */
875 
876 
Bisimulation relations.
#define FD_CV1(message)
#define FD_CV0(message)
Two debug levels for functions in this source file.
#define FD_WPC(cntnow, contdone, message)
Application callback: optional write progress report to console or application, incl count
#define FD_DF(message)
Debug: optional report on user functions.
Operations on (directed) graphs.
Operations on regular languages.
Faudes exception class.
Set of indices.
Definition: cfl_indexset.h:78
Idx Insert(void)
Insert new index to set.
Set of indices with symbolic names.
Definition: cfl_nameset.h:69
bool Exists(const Idx &rIndex) const
Test existence of index.
bool Insert(const Idx &rIndex)
Add an element by index.
void EraseSet(const NameSet &rOtherSet)
Erase elements specified by rOtherSet.
virtual bool Erase(const Idx &rIndex)
Delete element by index.
Iterator class for high-level api to TBaseSet.
Definition: cfl_baseset.h:387
virtual const T & At(const Position &pos) const
Access element.
Set of Transitions.
Definition: cfl_transset.h:242
Iterator Begin(void) const
Iterator to begin of set.
Iterator BeginByX2(Idx x2) const
Iterator to first Transition specified by successor state x2.
bool Insert(const Transition &rTransition)
Add a Transition.
Iterator EndByX2(Idx x2) const
Iterator to first Transition after specified successor state x2.
TBaseSet< Transition, TransSort::X1EvX2 >::Iterator Iterator
Iterator on transition.
Definition: cfl_transset.h:269
void ReSort(TTransSet< OtherCmp > &res) const
Get copy of trantision relation sorted by other compare operator, e.g.
Triple (X1,Ev,X2) to represent current state, event and next state.
Definition: cfl_transset.h:57
virtual void Append(const Type &rElem)
Append specified entry.
Idx Size(void) const
Get size of vector.
Base class of all FAUDES generators.
StateSet::Iterator StatesBegin(void) const
Iterator to Begin() of state set.
const TransSet & TransRel(void) const
Return reference to transition relation.
bool SetTransition(Idx x1, Idx ev, Idx x2)
Add a transition to generator by indices.
const StateSet & MarkedStates(void) const
Return const ref of marked states.
const EventSet & Alphabet(void) const
Return const reference to alphabet.
Idx InsMarkedState(void)
Create new anonymous state and set as marked state.
EventSet ActiveEventSet(Idx x1) const
Return active event set at state x1.
const StateSet & InitStates(void) const
Const ref to initial states.
TransSet::Iterator TransRelBegin(void) const
Iterator to Begin() of transition relation.
void ClrTransition(Idx x1, Idx ev, Idx x2)
Remove a transition by indices.
void ClrMarkedState(Idx index)
Unset an existing state as marked state by index.
bool ExistsTransition(const std::string &rX1, const std::string &rEv, const std::string &rX2) const
Test for transition given by x1, ev, x2.
void SetInitState(Idx index)
Set an existing state as initial state by index.
bool ExistsState(Idx index) const
Test existence of state in state set.
StateSet::Iterator MarkedStatesBegin(void) const
Iterator to Begin() of mMarkedStates.
void Name(const std::string &rName)
Set the generator's name.
bool DelState(Idx index)
Delete a state from generator by index.
StateSet::Iterator StatesEnd(void) const
Iterator to End() of state set.
void DelStates(const StateSet &rDelStates)
Delete a set of states Cleans mpStates, mInitStates, mMarkedStates, mpTransrel, and mpStateSymboltabl...
TransSet::Iterator TransRelEnd(void) const
Iterator to End() of transition relation.
bool ExistsEvent(Idx index) const
Test existence of event in alphabet.
StateSet::Iterator MarkedStatesEnd(void) const
Iterator to End() of mMarkedStates.
Idx InsState(void)
Add new anonymous state to generator.
void SetMarkedState(Idx index)
Set an existing state as marked state by index.
Idx InsInitState(void)
Create new anonymous state and set as initial state.
bool InsEvent(Idx index)
Add an existing event to alphabet by index.
Idx TransRelSize(void) const
Get number of transitions.
StateSet CoaccessibleSet(void) const
Compute set of Coaccessible states.
Idx Size(void) const
Get generator size (number of states)
bool ExistsInitState(Idx index) const
Test existence of state in mInitStates.
bool ExistsMarkedState(Idx index) const
Test existence of state in mMarkedStates.
const StateSet & States(void) const
Return reference to state set.
void InjectAlphabet(const EventSet &rNewalphabet)
Set mpAlphabet without consistency check.
bool Empty(void) const
Test whether if the TBaseSet is Empty.
Definition: cfl_baseset.h:1824
bool Exists(const T &rElem) const
Test existence of element.
Definition: cfl_baseset.h:2115
virtual void Clear(void)
Clear all set.
Definition: cfl_baseset.h:1902
Iterator End(void) const
Iterator to the end of set.
Definition: cfl_baseset.h:1896
Iterator Begin(void) const
Iterator to the begin of set.
Definition: cfl_baseset.h:1891
virtual bool Erase(const T &rElem)
Erase element by reference.
Definition: cfl_baseset.h:2019
Idx Size(void) const
Get Size of TBaseSet.
Definition: cfl_baseset.h:1819
void ConflictEquivalentAbstraction(vGenerator &rGen, EventSet &rSilentEvents)
Conflict equivalent abstraction.
bool IsNonconflicting(const GeneratorVector &rGvec)
Test for conflicts.
bool ComputeScc(const Generator &rGen, const SccFilter &rFilter, std::list< StateSet > &rSccList, StateSet &rRoots)
Compute strongly connected components (SCC)
void Parallel(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
Parallel composition.
libFAUDES resides within the namespace faudes.
uint32_t Idx
Type definition for index type (allways 32bit)
void MergeSilentLoops(Generator &g, const EventSet &silent)
void BlockingSilentEvent(Generator &g, const EventSet &silent)
void ActiveEventsRule(Generator &g, const EventSet &silent)
std::set< std::pair< Idx, Idx > > SetX1Ev
EventSet HidePriviateEvs(Generator &rGen, EventSet &silent)
void IncomingTransSet(const Generator &rGen, const TransSetX2EvX1 &rRTrans, const EventSet &silent, const Idx &state, SetX1Ev &result)
void RemoveNonCoaccessibleOut(Generator &g)
void AppendOmegaTermination(Generator &rGen)
void OnlySilentIncoming(Generator &g, const EventSet &silent)
void ConflictEquivalentAbstractionLoop(vGenerator &rGen, EventSet &rSilentEvents)
void ConflictEquivalentAbstractionOnce(Generator &rGen, EventSet &silent)
void MergeEquivalenceClasses(Generator &rGen, TransSetX2EvX1 &rRevTrans, const std::list< StateSet > &rClasses)
void OnlySilentOutgoing(Generator &g, const EventSet &silent)
bool IsNonblocking(const GeneratorVector &rGvec)
void ObservationEquivalentQuotient(Generator &g, const EventSet &silent)
std::map< SetX1Ev, StateSet > IncomingEquivalentClasses(const Generator &rGen, const EventSet &silent)
void RemoveTauSelfloops(Generator &g, const EventSet &silent)
void ActiveNonTauEvs(const Generator &rGen, const EventSet &silent, const Idx &state, EventSet &result)
void EnabledContinuationRule(Generator &g, const EventSet &silent)
void ComputeWeakBisimulationSatCTA(const Generator &rGen, const EventSet &rSilent, std::list< StateSet > &rResult)
ComputeWeakBisimulationSatCTA weak bisimulation (aka observation eq) partition based on change-tracki...
void RemoveTauLoops(Generator &rGen, const EventSet &silent)
Remove all silent loops in a given automaton.
long int Int
Type definition for integer type (let target system decide, minimum 32bit)
void MergeNonCoaccessible(Generator &g)

libFAUDES 2.32b --- 2024.03.01 --- c++ api documentaion by doxygen