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
42namespace 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
70typedef 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)
222std::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
260void 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
328void 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.
341void 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){
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
387 FD_DF("BlockingSilentEvent(): prepare for t#"<<g.TransRelSize());
388 StateSet coacc=g.CoaccessibleSet();
389 StateSet sblock;
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
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
441void 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
496void 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
563void 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);
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
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
705bool IsNonblocking(const GeneratorVector& rGvec) {
706 return IsNonconflicting(rGvec);
707}
708
709
710} // namespace
711
712
713
714/*
715earlier revison for inspection
716
717
718
719
720// intentend user interface
721bool 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
#define FD_CV1(message)
#define FD_CV0(message)
#define FD_WPC(cntnow, contdone, message)
#define FD_DF(message)
const std::string & Name(void) const
bool Exists(const Idx &rIndex) const
bool Insert(const Idx &rIndex)
void EraseSet(const NameSet &rOtherSet)
virtual bool Erase(const Idx &rIndex)
virtual const T & At(const Position &pos) const
Iterator Begin(void) const
Iterator BeginByX2(Idx x2) const
bool Insert(const Transition &rTransition)
Iterator EndByX2(Idx x2) const
void ReSort(TTransSet< OtherCmp > &res) const
virtual void Append(const Type &rElem)
StateSet::Iterator StatesBegin(void) const
const TransSet & TransRel(void) const
bool SetTransition(Idx x1, Idx ev, Idx x2)
const StateSet & MarkedStates(void) const
const EventSet & Alphabet(void) const
EventSet ActiveEventSet(Idx x1) const
const StateSet & InitStates(void) const
TransSet::Iterator TransRelBegin(void) const
void ClrTransition(Idx x1, Idx ev, Idx x2)
void ClrMarkedState(Idx index)
bool ExistsTransition(const std::string &rX1, const std::string &rEv, const std::string &rX2) const
void SetInitState(Idx index)
bool ExistsState(Idx index) const
StateSet::Iterator MarkedStatesBegin(void) const
bool DelState(Idx index)
StateSet::Iterator StatesEnd(void) const
void DelStates(const StateSet &rDelStates)
TransSet::Iterator TransRelEnd(void) const
bool ExistsEvent(Idx index) const
StateSet::Iterator MarkedStatesEnd(void) const
void SetMarkedState(Idx index)
bool InsEvent(Idx index)
Idx TransRelSize(void) const
StateSet CoaccessibleSet(void) const
Idx Size(void) const
bool ExistsInitState(Idx index) const
bool ExistsMarkedState(Idx index) const
const StateSet & States(void) const
void InjectAlphabet(const EventSet &rNewalphabet)
bool Empty(void) const
bool Exists(const T &rElem) const
virtual void Clear(void)
Iterator End(void) const
Iterator Begin(void) const
virtual bool Erase(const T &rElem)
Idx Size(void) const
void ConflictEquivalentAbstraction(vGenerator &rGen, EventSet &rSilentEvents)
bool IsNonconflicting(const GeneratorVector &rGvec)
bool ComputeScc(const Generator &rGen, const SccFilter &rFilter, std::list< StateSet > &rSccList, StateSet &rRoots)
void Parallel(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
uint32_t Idx
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 AppendOmegaTermination(Generator &rGen)
void OnlySilentIncoming(Generator &g, const EventSet &silent)
void RemoveNonCoaccessibleOut(Generator &g)
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)
void RemoveTauSelfloops(Generator &g, const EventSet &silent)
void ActiveNonTauEvs(const Generator &rGen, const EventSet &silent, const Idx &state, EventSet &result)
std::map< SetX1Ev, StateSet > IncomingEquivalentClasses(const Generator &rGen, const EventSet &silent)
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)
long int Int
void MergeNonCoaccessible(Generator &g)

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