omg_buechictrl.cpp
Go to the documentation of this file.
1/** @file omg_buechictrl.cpp Supremal controllable sublanguage w.r.t. Buechi acceptance */
2
3/* FAU Discrete Event Systems Library (libfaudes)
4
5 Copyright (C) 2010, 2025 Thomas Moor
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
22//#define FAUDES_DEBUG_FUNCTION
23
24#include "omg_include.h"
25#include "syn_include.h"
26
27
28namespace faudes {
29
30
31/*
32***************************************************************************************
33***************************************************************************************
34 Implementation IsBuechiControllabe
35***************************************************************************************
36***************************************************************************************
37*/
38
39// IsBuechiControllable()
41 const Generator& rGenPlant,
42 const EventSet& rCAlph,
43 const Generator& rGenCand)
44{
45 FD_DF("IsBuechiControllable(\"" << rGenPlant.Name() << "\", \"" << rGenCand.Name() << "\")");
46
47 // alphabets must match
48 if ( rGenPlant.Alphabet() != rGenCand.Alphabet()) {
49 std::stringstream errstr;
50 errstr << "Alphabets of generators do not match.";
51 throw Exception("IsBuechiControllable(..)", errstr.str(), 100);
52 }
53
54#ifdef FAUDES_CHECKED
55 // generators are meant to be nonblocking
56 if( !IsBuechiTrim(rGenCand) ) {
57 std::stringstream errstr;
58 errstr << "Argument \"" << rGenCand.Name() << "\" is not omega-trim.";
59 throw Exception("IsBuechiControllable(..)", errstr.str(), 201);
60 }
61 if( !IsBuechiTrim(rGenPlant) ) {
62 std::stringstream errstr;
63 errstr << "Argument \"" << rGenPlant.Name() << "\" is not omega-trim.";
64 throw Exception("IsBuechiControllable(..)", errstr.str(), 201);
65 }
66#endif
67
68 // the trivial case: empty cand is fine
69 // (we must treat this case because empty generators are not regarded deterministic)
70 if(rGenCand.Empty()) {
71 FD_DF("IsBuechiControllable(..): empty candidate, pass");
72 return true;
73 }
74
75 // the trivial case: empty plant is fails
76 // (we must treat this case because empty generators are not regarded deterministic)
77 if(rGenPlant.Empty()) {
78 FD_DF("IsBuechiControllable(..): empty plant, fail");
79 return false;
80 }
81
82#ifdef FAUDES_CHECKED
83 // generators are meant to be deterministic
84 if ( !IsDeterministic(rGenCand) || !IsDeterministic(rGenPlant)) {
85 std::stringstream errstr;
86 errstr << "Arguments are expected to be deterministic.";
87 throw Exception("IsBuechiControllable", errstr.str(), 202);
88 }
89#endif
90
91 // test controllability
92 StateSet dummy;
93 if(!IsControllableUnchecked(rGenPlant,rCAlph,rGenCand,dummy)) return false;
94
95 // test relative closedness
96 if(!IsBuechiRelativelyClosedUnchecked(rGenPlant,rGenCand)) return false;
97
98 // pass
99 FD_DF("IsBuechiControllable(...): passed");
100 return true;
101}
102
103
104// IsBuechiControllable() wrapper
106 const System& rPlantGen,
107 const Generator& rCandGen)
108{
109 return IsBuechiControllable(rPlantGen,rPlantGen.ControllableEvents(),rCandGen);
110}
111
112/*
113***************************************************************************************
114***************************************************************************************
115 Computation of the controllable prefix / enforcing omega-controlled liveness
116***************************************************************************************
117***************************************************************************************
118*/
119
120
121
122/*
123Initial libFAUDES implementation of omega-controlled liveness, tmoor 2011, last used
124for libFAUDES 2.22p. The function retsricts the candidate to states for which a
125prefix-closed controller exists such that in closed-loop configuration the acceptance condition
126is implied by the plant acceptance condition. This corresponds to the controllable prefix,
127originally proposed by J. Thistle et al. The actual implementation, however, is based on a sufficient
128but not necessary test. It therefore identifies a subset of the controllable prefix, which may
129turn out empty even when a prefix-closed controller exists. The below code is obsolete and will
130be removed eventually.
131*/
132
133/*
134
135// ControlledBuechiLiveness(...)
136bool ControlledBuechiLiveness(
137 Generator& rSupCandGen,
138 const EventSet& rCAlph,
139 StateSet& rMarkedPlantStates,
140 StateSet& rMarkedSpecStates)
141{
142 FD_DF("ControlledBuechiLiveness(...)");
143
144 // set of critical states
145 StateSet critical;
146
147 // return true if parallelcomp contains no initial states
148 if(rSupCandGen.InitStatesEmpty()) {
149 return true;
150 }
151
152#ifdef FAUDES_DEBUG_FUNCTION
153 FD_DF("ControlledBuechiLiveness(): marked states: ");
154 StateSet ssd= rMarkedPlantStates + rMarkedSpecStates;
155 ssd.Write();
156#endif
157
158 // find un-marked sccs
159 StateSet astates = rMarkedPlantStates + rMarkedSpecStates;
160 SccFilter umfilter(SccFilter::FmIgnoreTrivial | SccFilter::FmStatesAvoid, astates);
161 std::list<StateSet> umsccs;
162 StateSet umroots;
163 ComputeScc(rSupCandGen,umfilter,umsccs,umroots);
164
165 // report
166 std::list<StateSet>::iterator ssit=umsccs.begin();
167 for(;ssit!=umsccs.end(); ++ssit) {
168 FD_DF("ControlledBuechiLiveness(): unmarked scc: " << ssit->ToString());
169 }
170
171 // good-states iteration
172 StateSet goodstates = rMarkedSpecStates;
173
174 // drive states to good states
175 while(true) {
176 // LoopCallback();
177 // allow for user interrupt, incl progress report
178 FD_WPC(1,2,"ControlledBuechiLiveness(): iterating states");
179 // test individual states
180 FD_DF("ControlledBuechiLiveness(): iterate over states (#" << rSupCandGen.Size() << ")");
181 bool found=false;
182
183 StateSet::Iterator sit = rSupCandGen.StatesBegin();
184 StateSet::Iterator sit_end = rSupCandGen.StatesEnd();
185 for(; sit!=sit_end; ++sit) {
186 bool fail = false; // cbaier 20121011
187 bool positive=false; // cbaier 20121011
188 // goodstate anyway
189 if(goodstates.Exists(*sit)) continue;
190 // test transitions
191 TransSet::Iterator tit = rSupCandGen.TransRelBegin(*sit);
192 TransSet::Iterator tit_end = rSupCandGen.TransRelEnd(*sit);
193 // no transitions at all
194 if(tit==tit_end) continue;
195 // loop over successors
196 for(; tit!=tit_end; ++tit) {
197 if(goodstates.Exists(tit->X2))
198 {positive=true; // cbaier 20121011: added
199 continue;}
200 if(rCAlph.Exists(tit->Ev)) continue;
201
202 // good states survive the loop // cbaier 20121011: added
203 if(tit!=tit_end) {
204 fail=true;
205 break;
206 }
207 }
208
209 // good states survive
210 if(!fail && positive) { // cbaier 20121011: changed
211 FD_DF("ControlledBuechiLiveness(): good state " << rSupCandGen.SStr(*sit));
212 goodstates.Insert(*sit);
213 found=true;
214 }
215 }
216
217 // test individual unmarked sccs
218 FD_DF("ControlledBuechiLiveness(): iterate over unmarked sccs (#" << umsccs.size() <<")");
219 std::list<StateSet>::iterator ssit=umsccs.begin();
220 while(ssit!=umsccs.end()) {
221 bool fail=false;
222 bool positive=false;
223 sit=ssit->Begin();
224 sit_end=ssit->End();
225 for(; sit!=sit_end; ++sit) {
226 // goodstate anyway
227 if(goodstates.Exists(*sit)) continue;
228 // test transitions
229 TransSet::Iterator tit = rSupCandGen.TransRelBegin(*sit);
230 TransSet::Iterator tit_end = rSupCandGen.TransRelEnd(*sit);
231 // no transitions at all
232 if(tit==tit_end) continue;
233 // loop over successors
234 for(; tit!=tit_end; ++tit) {
235 if(goodstates.Exists(tit->X2)) { positive=true; continue;}
236 if(rCAlph.Exists(tit->Ev)) continue; // tmoor 20110202: fixed typo
237 if(ssit->Exists(tit->X2)) continue;
238 break;
239 }
240 // good states survive the loop
241 if(tit!=tit_end) {
242 fail=true;
243 break;
244 }
245 }
246
247 // prepare next scc iterator
248 std::list<StateSet>::iterator ssitnext=ssit;
249 ++ssitnext;
250 bool lend= (ssitnext==umsccs.end());
251 // all states passed, then they are all good
252 if(!fail && positive) {
253 FD_DF("ControlledBuechiLiveness(): good scc " << ssit->ToString());
254 goodstates.InsertSet(*ssit);
255 umsccs.erase(ssit);
256 found=true;
257 }
258 // break on end of list
259 if(lend) break;
260 // progress
261 ssit=ssitnext;
262 }
263
264 // exit
265 if(!found) break;
266 };
267
268 // delete critical states
269 FD_DF("ControlledBuechiLiveness(): good states: " << goodstates.ToString())
270 StateSet::Iterator sit = rSupCandGen.StatesBegin();
271 StateSet::Iterator sit_end = rSupCandGen.StatesEnd();
272 for(; sit!=sit_end; ++sit) {
273 if(!goodstates.Exists(*sit))
274 rSupCandGen.DelState(*sit);
275 }
276
277 // return true
278 FD_DF("ControlledBuechiLiveness(): done");
279 return true;
280}
281
282*/
283
284
285/*
286This implementation is a direct transscript of the mu-calculus formulas stated in "Control of
287w-Automata, Church's Problem, and the Emptiness Problem for Tree w-Automata", by J. Thistle and
288W.M. Wonham, 1992, adapted for the special case of deterministic Buchi-automata. Referring to the
289referenced literature, the corresponding iteration is "essentially optimal" in terms of performance.
290In the below implementation, we unwind each individual mu/nu quantifiation literally and resist
291to apply strategic substitutions in order to obtain a reference implementation. This is suspected
292to introduce (linear) penalty for avoidable boolean operations on sets of states. In a recent study,
293Christian Wamser proposed a fine-tuned alternative implementation which will be integrated in
294libFAUDES in due course.
295*/
296
298 Generator& rSupCandGen,
299 const EventSet& rCAlph,
300 const StateSet& rPlantMarking)
301{
302
303 FD_DF("ControlledBuechiLiveness()");
304
305 // declare iterate sets
306 StateSet resolved, initialK, targetLstar;
307 StateSet initialL, targetL;
308 StateSet domainL, target1;
309 StateSet target, domain, theta;
310
311 // convenience
312 const StateSet& full = rSupCandGen.States();
313 Idx fsz=full.Size();
314
315 // evaluate mu(resolved).nu(initialK)[ p(initialK * markedK + resolved )) ];
316 // here, p(T) denotes the set of states that can be driven to enter T under liveness assumption inf-markL;
317 // use std. mu-nu-iteration
318 resolved.Clear();
319 while(true) {
320 Idx rsz = resolved.Size();
321 initialK = full;
322 while(true) {
323 Idx iKsz = initialK.Size();
324
325 // prepare target for P(): targetLstar = initialK * markedK + resolved
326 targetLstar = (initialK * rSupCandGen.MarkedStates()) + resolved;
327 FD_DF("ControlledBuechiLiveness(): [STD] iterate resolved/targetLstar #" << rsz << "/" << targetLstar.Size());
328
329 // evaluate p(targetLstar) = mu(initialL).[ thetaTilde(targetLstar+initialL) ]
330 // here, thetaTilde(T) denotes the set of states that can be controlled such that T is persistently
331 // reachable and markL is not passed before T is entered;
332 // start with initialL:=0 and iterate initialL += thetaTilde(targetLstar+initialL);
333 initialL.Clear();
334 while(true) {
335 Idx iLsz = initialL.Size();
336
337 // prepare targetL=targetLstar+initialL
338 targetL=targetLstar+initialL;
339 FD_DF("ControlledBuechiLiveness(): [STD] ---- iterate targetL #" << targetL.Size());
340
341 // evaluate thetaTilde(targetL)=nu(domainL).mu(target1)[ theta(targetL+(target1-markL), domainL-markL) ];
342 // here, theta(T,D) is the set of states that, by one transition, can enter T and can be controlled not to exit D+T;
343 // start with domainL:=full and iterate domainL -= mu(target1)[theta(targetL+(target1-markL), domainL-markL)];
344 domainL=full;
345 targetL = targetLstar + initialL;
346 while(true) {
347 Idx dLsz = domainL.Size();
348 FD_DF("ControlledBuechiLiveness(): [STD] --- iterate domainL #" << domainL.Size());
349
350 // user interrupt/ progress
351 FD_WPC(1,2,"ControlledBuechiLiveness(): [STD] iterating reverse dynamics");
352
353 // prepare domain = domainL - marL
354 domain=domainL-rPlantMarking;
355
356 // evaluate mu(target1)[ theta(targetL+(target1-markL), domain) ];
357 // start with target1:=0 and iterate target+=theta(targetL+(target1-markL), domainL-markL) ];
358 target1.Clear();
359 while(true) {
360 Idx t1sz = target1.Size();
361
362 // prepare target= targetL+(target1-markL)
363 target = targetL + (target1-rPlantMarking);
364
365 // evaluate theta(target,domain)
366 FD_DF("ControlledBuechiLiveness(): [STD] -- evaluate theta for target/domain # "
367 << target.Size() << "/" << domain.Size());
368 theta.Clear();
369 StateSet::Iterator sit = full.Begin();
370 StateSet::Iterator sit_end = full.End();
371 for(;sit!=sit_end;++sit) {
372 bool pass = false;
373 bool fail = false;
374 TransSet::Iterator tit = rSupCandGen.TransRelBegin(*sit);
375 TransSet::Iterator tit_end = rSupCandGen.TransRelEnd(*sit);
376 for(;tit!=tit_end; ++tit) {
377 if(target.Exists(tit->X2)) {pass = true; continue;}
378 if(domain.Exists(tit->X2)) {continue;}
379 if(!rCAlph.Exists(tit->Ev)){ fail = true; break;}
380 }
381 if(pass && !fail) {
382 theta.Insert(*sit);
383 FD_DF("ControlledBuechiLiveness(): [STD] theta found state " << rSupCandGen.SStr(*sit));
384 }
385 } // end: theta
386
387 // mu-loop on target1
388 target1.InsertSet(theta);
389 if(target1.Size()==t1sz) break;
390 if(target1.Size()==fsz) break;
391 } // end: mu
392
393 FD_DF("ControlledBuechiLiveness(): [STD] -- mu-target1 # " << target1.Size());
394
395 // nu-loop on domainL
396 domainL.RestrictSet(target1);
397 if(domainL.Size()==dLsz) break;
398 if(domainL.Size()==0) break;
399 } // end: nu
400
401 FD_DF("ControlledBuechiLiveness(): [STD] --- nu-domainL-mu-target1 # " << domainL.Size());
402
403 // mu-loop on initialL
404 initialL.InsertSet(domainL);
405 if(initialL.Size()==iLsz) break;
406 if(initialL.Size()==fsz) break;
407 } // end: mu
408
409 // nu-loop on initialK
410 initialK.RestrictSet(initialL);
411 if(initialK.Size()==iKsz) break;
412 if(initialK.Size()==0) break;
413 } // end: nu
414
415 // mu-loop on resolved
416 resolved.InsertSet(initialK);
417 if(resolved.Size()==rsz) break;
418 if(resolved.Size()==fsz) break;
419 } // end: mu
420
421 // restrict candidate to resolved states
422 rSupCandGen.DelStates(full - resolved);
423
424 // done
425 return true;
426}
427
428
429/*
430Variant of the controllable prefix to extract a particular feedback map. Again, this is
431very close to the relevant literature and meant as a reference. It is not particulary performant.
432*/
433
435 Generator& rSupCandGen,
436 const EventSet& rCAlph,
437 const StateSet& rPlantMarking,
438 std::map< Idx , EventSet>& rFeedbackMap)
439{
440
441 FD_DF("ControlledBuechiLiveness()");
442
443 // declare iterate sets
444 StateSet resolved, initialK, targetLstar;
445 StateSet initialL, targetL;
446 StateSet domainL, target1;
447 StateSet target, domain, theta;
448
449 // record controls per state
450 EventSet disable;
451 std::map< Idx , EventSet> controls1;
452 std::map< Idx , EventSet> controls1L;
453 std::map< Idx , EventSet> controls1X;
454
455 // convenience
456 const StateSet& full = rSupCandGen.States();
457 Idx fsz=full.Size();
458
459 // evaluate mu(resolved).nu(initialK)[ p(initialK * markedK + resolved )) ];
460 resolved.Clear();
461 while(true) {
462 Idx xsz = resolved.Size();
463 initialK = full;
464 while(true) {
465 Idx rsz = initialK.Size();
466 targetLstar = (initialK * rSupCandGen.MarkedStates()) + resolved;
467 FD_DF("ControlledBuechiLiveness(): [FBM] iterate resolved/targetLstar #" << xsz << "/" << targetLstar.Size());
468
469 // reset controls
470 controls1L.clear();
471
472 // evaluate p(targetLstar) = mu(initialL).[ thetaTilde(targetLstar+initialL) ]
473 initialL.Clear();
474 while(true) {
475 Idx t1Lsz = initialL.Size();
476 targetL = targetLstar + initialL;
477 FD_DF("ControlledBuechiLiveness(): [FBM] ---- iterate targetL #" << targetL.Size());
478
479 // evaluate thetaTilde(targetL)=nu(domainL).mu(target1)[ theta(targetL+(target1-markL), domainL-markL) ];
480 domainL=full;
481 while(true) {
482 Idx dsz = domainL.Size();
483 domain=domainL-rPlantMarking;
484 FD_DF("ControlledBuechiLiveness(): [FBM] --- iterate domain #" << domain.Size());
485
486 // reset controls
487 controls1.clear();
488
489 // user interrupt/ progress
490 FD_WPC(1,2,"ControlledBuechiLiveness(): [FBM] iterating reverse dynamics");
491
492 // evaluate mu(target1)[ theta(targetL+(target1-markL), domain) ];
493 target1.Clear();
494 while(true) {
495 Idx t1sz = target1.Size();
496 target = targetL + (target1-rPlantMarking);
497
498 // evaluate theta(target,domain)
499 FD_DF("ControlledBuechiLiveness(): [FBM] -- evaluate theta for target/domain # "
500 << target.Size() << "/" << domain.Size());
501 theta.Clear();
502 StateSet::Iterator sit = full.Begin();
503 StateSet::Iterator sit_end = full.End();
504 for(;sit!=sit_end;++sit) {
505 bool pass = false;
506 bool fail = false;
507 disable.Clear();
508 TransSet::Iterator tit = rSupCandGen.TransRelBegin(*sit);
509 TransSet::Iterator tit_end = rSupCandGen.TransRelEnd(*sit);
510 for(;tit!=tit_end; ++tit) {
511 if(target.Exists(tit->X2)) { pass = true; continue; }
512 if(domain.Exists(tit->X2)) { continue; }
513 if(!rCAlph.Exists(tit->Ev)){ fail = true; break; }
514 disable.Insert(tit->Ev);
515 }
516 if(pass && !fail) {
517 theta.Insert(*sit);
518 if(controls1.find(*sit)==controls1.end()) controls1[*sit]=disable;
519 FD_DF("ControlledBuechiLiveness(): [FBM] theta found state " << rSupCandGen.SStr(*sit));
520#ifdef FAUDES_DEBUG_FUNCTION
521 if(!disable.Empty()) disable.Write();
522#endif
523 }
524 } // end: theta
525
526 // mu-loop on target1
527 target1.InsertSet(theta);
528 if(target1.Size()==t1sz) break;
529 if(target1.Size()==fsz) break;
530 } // end: mu
531
532 FD_DF("ControlledBuechiLiveness(): [FBM] -- mu-target1 # " << target1.Size());
533
534 // nu-loop on domainL
535 domainL.RestrictSet(target1);
536 if(domainL.Size()==dsz) break;
537 if(domainL.Size()==0) break;
538 } // end: nu
539
540 FD_DF("ControlledBuechiLiveness(): [FBM] --- nu-domainL-mu-target1 # " << domainL.Size());
541
542 // merge controls
543 std::map< Idx , EventSet>::iterator cit=controls1.begin();
544 std::map< Idx , EventSet>::iterator cit_end=controls1.end();
545 for(;cit!=cit_end;++cit) {
546 if(controls1L.find(cit->first)!=controls1L.end()) continue;
547 controls1L[cit->first]=cit->second;
548 }
549
550 // mu-loop on initialL
551 initialL.InsertSet(domainL);
552 if(initialL.Size()==t1Lsz) break;
553 if(initialL.Size()==fsz) break;
554 } // end: mu
555
556 // nu-loop on initialK
557 initialK.RestrictSet(initialL);
558 if(initialK.Size()==rsz) break;
559 if(initialK.Size()==0) break;
560 } // end: nu
561
562 // merge controls
563 std::map< Idx , EventSet>::iterator cit=controls1L.begin();
564 std::map< Idx , EventSet>::iterator cit_end=controls1L.end();
565 for(;cit!=cit_end;++cit) {
566 if(controls1X.find(cit->first)!=controls1X.end()) continue;
567 controls1X[cit->first]=cit->second;
568 }
569
570 // mu-loop on resolved
571 resolved.InsertSet(initialK);
572 if(resolved.Size()==xsz) break;
573 if(resolved.Size()==fsz) break;
574 } // end: mu
575
576 // restrict candidate to valid restrict
577 rSupCandGen.DelStates(full - resolved);
578
579 // re-write controls as feedback map
580 EventSet ucalph = rSupCandGen.Alphabet() - rCAlph;
581 StateSet::Iterator sit = resolved.Begin();
582 StateSet::Iterator sit_end = resolved.End();
583 for(;sit!=sit_end;++sit) {
584 FD_DF("ControlledBuechiLiveness(): [FBM] controls " << rSupCandGen.SStr(*sit) << " " << controls1X[*sit].ToString());
585 rFeedbackMap[*sit]= (rSupCandGen.ActiveEventSet(*sit) + ucalph) - controls1X[*sit];
586 }
587
588 // done
589 return true;
590}
591
592
593
594
595// helper class: mergable control pattern
597public:
598 // merge with other
599 void merge(const MCtrlPattern& other) {
601 enable_one.insert(other.enable_one.begin(),other.enable_one.end());
602 }
603 // test conflict
604 bool conflict() {
605 std::set< EventSet >::iterator eit =enable_one.begin();
606 std::set< EventSet >::iterator eit_end =enable_one.end();
607 for(; eit!=eit_end ; ++eit)
608 if( (*eit - disable_all).Empty() ) return true;
609 return false;
610 }
611 // member variables
613 std::set< EventSet > enable_one;
614};
615
616
617
618/*
619Variant of a controllable prefix under partial observation. This is **experimantal**.
620Based on the situation of full observation, this variant records control patterns
621used to establish controlled liveness and only accepts a new state if the corresponding
622pattern complies all recorded patterns that correspond to the same observation.
623The result is a (possibly strict) subset of the controllable prefix for the purpose.
624Theoretical background will be provided in due course.
625*/
626
628 Generator& rSupCandGen,
629 const EventSet& rCAlph,
630 const StateSet& rPlantMarking,
631 std::map< Idx , Idx>& rControllerStatesMap,
632 std::map< Idx , EventSet>& rFeedbackMap)
633{
634
635 FD_DF("ControlledBuechiLiveness(): [POBS]: cand #" << rSupCandGen.Size());
636
637 // debugging: interpret empty controllermap as identity
638 StateSet::Iterator xit = rSupCandGen.StatesBegin();
639 StateSet::Iterator xit_end = rSupCandGen.StatesEnd();
640 for(;xit!=xit_end;++xit) {
641 std::map< Idx , Idx >::const_iterator cxit=rControllerStatesMap.find(*xit);
642 if(cxit==rControllerStatesMap.end()) rControllerStatesMap[*xit]=*xit;
643 }
644
645 // informative
646 std::map< Idx , Idx>::const_iterator oit = rControllerStatesMap.begin();
647 std::set< Idx > ostates;
648 for(; oit != rControllerStatesMap.end(); ++oit)
649 ostates.insert(oit->second);
650 FD_DF("ControlledBuechiLiveness(): [POBS]: " << "obs #" << ostates.size());
651
652 // declare iterate sets
653 StateSet resolved, initialK, targetLstar;
654 StateSet initialL, targetL;
655 StateSet domainL, target1;
656 StateSet target, domain, theta;
657
658 // record controls per state
659 EventSet disable;
660 EventSet enable;
661 std::map< Idx , MCtrlPattern> controlsT;
662 std::map< Idx , MCtrlPattern> controls1;
663 std::map< Idx , MCtrlPattern> controls1L;
664 std::map< Idx , MCtrlPattern> controls1X;
665
666 // convenience
667 const StateSet& full = rSupCandGen.States();
668 Idx fsz=full.Size();
669
670 // evaluate mu(resolved).nu(initialK)[ p(initialK * markedK + resolved )) ];
671 resolved.Clear();
672 while(true) {
673 Idx xsz = resolved.Size();
674 initialK = full;
675 while(true) {
676 Idx rsz = initialK.Size();
677 targetLstar = (initialK * rSupCandGen.MarkedStates()) + resolved;
678 FD_DF("ControlledBuechiLiveness(): [POBS] iterate resolved/targetLstar #" << xsz << "/" << targetLstar.Size());
679
680 // reset controls
681 controls1L.clear();
682
683 // evaluate p(targetLstar) = mu(initialL).[ thetaTilde(targetLstar+initialL) ]
684 initialL.Clear();
685 while(true) {
686 Idx t1Lsz = initialL.Size();
687 targetL = targetLstar + initialL;
688 FD_DF("ControlledBuechiLiveness(): [POBS] ---- iterate targetL #" << targetL.Size());
689
690 // evaluate thetaTilde(targetL)=nu(domainL).mu(target1)[ theta(targetL+(target1-markL), domainL-markL) ];
691 domainL=full;
692 while(true) {
693 Idx dsz = domainL.Size();
694 domain=domainL-rPlantMarking;
695 FD_DF("ControlledBuechiLiveness(): [POBS] --- iterate domain #" << domain.Size());
696
697 // reset controls
698 controls1.clear();
699
700 // user interrupt/ progress
701 FD_WPC(1,2,"ControlledBuechiLiveness(): [POBS] iterating reverse dynamics");
702
703 // evaluate mu(target1)[ theta(targetL+(target1-markL), domain) ];
704 target1.Clear();
705 while(true) {
706 Idx t1sz = target1.Size();
707 target = targetL + (target1-rPlantMarking);
708
709 // evaluate theta(target,domain) in three passes
710 FD_DF("ControlledBuechiLiveness(): [POBS] -- evaluate theta for target/domain # "
711 << target.Size() << "/" << domain.Size());
712 theta.Clear();
713 controlsT.clear();
714
715 // pass 1: find new candidate states and acumulate required controls
716 StateSet::Iterator sit = full.Begin();
717 StateSet::Iterator sit_end = full.End();
718 for(;sit!=sit_end;++sit) {
719 Idx cx=rControllerStatesMap[*sit];
720 bool pass = false;
721 bool fail = false;
722 disable.Clear();
723 enable.Clear();
724 TransSet::Iterator tit = rSupCandGen.TransRelBegin(*sit);
725 TransSet::Iterator tit_end = rSupCandGen.TransRelEnd(*sit);
726 for(;tit!=tit_end; ++tit) {
727 if(disable.Exists(tit->Ev)) continue;
728 if(target.Exists(tit->X2)) {enable.Insert(tit->Ev); pass = true; continue;}
729 if(domain.Exists(tit->X2)) {continue;}
730 if(!rCAlph.Exists(tit->Ev)){ fail = true; break;}
731 disable.Insert(tit->Ev);
732 }
733 if(pass && !fail) {
734 // initialize control with existing patterns
735 if(controlsT.find(cx)==controlsT.end()) {
736 if(controls1.find(cx)!=controls1.end())
737 controlsT[cx].merge(controls1[cx]);
738 if(controls1L.find(cx)!=controls1L.end())
739 controlsT[cx].merge(controls1L[cx]);
740 if(controls1X.find(cx)!=controls1X.end())
741 controlsT[cx].merge(controls1X[cx]);
742 }
743 // apply additional pattern
744 controlsT[cx].disable_all.InsertSet(disable);
745 controlsT[cx].enable_one.insert(enable);
746 }
747 } // end: theta pass 1
748
749 // pass 2: merge new controls to controls1, reject conflicting
750 std::map< Idx , MCtrlPattern >::iterator cpit=controlsT.begin();
751 std::map< Idx , MCtrlPattern >::iterator cpit_end=controlsT.end();
752 while(cpit!=cpit_end) {
753 if(cpit->second.conflict()) {controlsT.erase(cpit++); continue;}
754 controls1[cpit->first].merge(cpit->second);
755 ++cpit;
756 }
757
758 // pass 3: evaluate theta by accumulated controls control
759 sit = rSupCandGen.StatesBegin();
760 sit_end = rSupCandGen.StatesEnd();
761 for(;sit!=sit_end;++sit) {
762 bool pass = false;
763 bool fail = false;
764 Idx cx=rControllerStatesMap[*sit];
765 if(controls1.find(cx)==controls1.end()) continue;
766 disable=controls1[cx].disable_all;
767 TransSet::Iterator tit = rSupCandGen.TransRelBegin(*sit);
768 TransSet::Iterator tit_end = rSupCandGen.TransRelEnd(*sit);
769 for(;tit!=tit_end; ++tit) {
770 if(disable.Exists(tit->Ev)) continue;
771 if(target.Exists(tit->X2)) {pass = true; continue;}
772 if(domain.Exists(tit->X2)) {continue;}
773 fail = true;
774 }
775 if(pass && !fail) {
776 theta.Insert(*sit);
777 //FD_DF("ControlledBuechiLiveness(): [POBS] theta candidate verified " << rSupCandGen.SStr(*sit));
778 }
779 } // end: theta pass 3
780
781 // mu-loop on target1
782 target1.InsertSet(theta);
783 if(target1.Size()==t1sz) break;
784 if(target1.Size()==fsz) break;
785 } // end: mu
786
787 FD_DF("ControlledBuechiLiveness(): [POBS] -- mu-target1 # " << target1.Size());
788
789 // nu-loop on domainL
790 domainL.RestrictSet(target1);
791 if(domainL.Size()==dsz) break;
792 if(domainL.Size()==0) break;
793 } // end: nu
794
795 FD_DF("ControlledBuechiLiveness(): [POBS] --- nu-domainL-mu-target1 # " << domainL.Size());
796
797 // merge controls
798 std::map< Idx , MCtrlPattern>::iterator cit = controls1.begin();
799 std::map< Idx , MCtrlPattern>::iterator cit_end = controls1.end();
800 for(;cit!=cit_end;++cit)
801 controls1L[cit->first].merge(cit->second);
802
803 // mu-loop on initialL
804 initialL.InsertSet(domainL);
805 if(initialL.Size()==t1Lsz) break;
806 if(initialL.Size()==fsz) break;
807 } // end: mu
808
809 // nu-loop on initialK
810 initialK.RestrictSet(initialL);
811 if(initialK.Size()==rsz) break;
812 if(initialK.Size()==0) break;
813 } // end: nu
814
815 // merge controls
816 std::map< Idx , MCtrlPattern>::iterator cit = controls1L.begin();
817 std::map< Idx , MCtrlPattern>::iterator cit_end = controls1L.end();
818 for(;cit!=cit_end;++cit)
819 controls1X[cit->first].merge(cit->second);
820
821 // mu-loop on resolved
822 resolved.InsertSet(initialK);
823 if(resolved.Size()==xsz) break;
824 if(resolved.Size()==fsz) break;
825 } // end: mu
826
827 // restrict candidate to valid restrict
828 rSupCandGen.DelStates(full - resolved);
829
830 // debugging
831 if(rSupCandGen.IsCoaccessible())
832 FD_DF("ControlledBuechiLiveness(): [POBS] ---- coaccessible ok");
833 if(rSupCandGen.IsComplete())
834 FD_DF("ControlledBuechiLiveness(): [POBS] ---- complete ok");
835 if(!rSupCandGen.InitStates().Empty())
836 FD_DF("ControlledBuechiLiveness(): [POBS] ---- init state ok");
837
838 // re-write controls as feedback map (w.r.t. candidate states)
839 StateSet::Iterator sit = resolved.Begin();
840 StateSet::Iterator sit_end = resolved.End();
841 for(;sit!=sit_end;++sit) {
842 Idx cx=rControllerStatesMap[*sit];
843 //FD_DF("ControlledBuechiLiveness(): [POBS] controls at cx=" << cx << " for plant state " << rSupCandGen.SStr(*sit) << " " << controls1X[cx].disable_all.ToString());
844 rFeedbackMap[*sit]= rSupCandGen.Alphabet() - controls1X[cx].disable_all;
845 }
846
847 // done
848 return true;
849}
850
851
852/*
853***************************************************************************************
854***************************************************************************************
855 Implementation of SupBuechiCon and friends
856***************************************************************************************
857***************************************************************************************
858*/
859
860// helper class: omega-product state
861class OPSState {
862public:
863 // minimal interface
865 OPSState(const Idx& rq1, const Idx& rq2, const bool& rf) :
866 q1(rq1), q2(rq2), m1required(rf) {};
867 std::string Str(void) { return ToStringInteger(q1)+"|"+
869 // order
870 bool operator < (const OPSState& other) const {
871 if (q1 < other.q1) return(true);
872 if (q1 > other.q1) return(false);
873 if (q2 < other.q2) return(true);
874 if (q2 > other.q2) return(false);
875 if (m1required && !other.m1required) return(true);
876 return(false);
877 }
878 // member variables
882};
883
884
885// SupBuechiConProduct(rPlantGen, rCAlph, rSpecGen, rReverseCompositionMap, rResGen)
887 const Generator& rPlantGen,
888 const EventSet& rCAlph,
889 const Generator& rSpecGen,
890 std::map< OPSState , Idx>& rProductCompositionMap,
891 Generator& rResGen)
892{
893 FD_DF("SupBuechiConProduct(" << &rPlantGen << "," << &rSpecGen << ")");
894
895 // prepare result
896 rResGen.Clear();
897 rResGen.InjectAlphabet(rPlantGen.Alphabet());
898
899 // trivial case
900 if (rPlantGen.InitStatesEmpty()) {
901 FD_DF("SupBuechiConProduct: plant got no initial states. "
902 << "parallel composition contains empty language.");
903 return;
904 }
905 if (rSpecGen.InitStatesEmpty()) {
906 FD_DF("SupBuechiConProduct: spec got no initial states. "
907 << "parallel composition contains empty language.");
908 return;
909 }
910
911 // todo stack
912 std::stack< OPSState > todo;
913 // current/next state as pair with flag
914 OPSState currentp, nextp;
915 // current/next state as target index
916 Idx currentt, nextt;
917 TransSet::Iterator ptit, ptit_end, stit, stit_end;
918 std::map< OPSState, Idx>::iterator rcit;
919 // critical states
920 StateSet critical;
921 // disabled events
922 EventSet disable;
923
924
925 // push initial state on todo stack
926 currentp = OPSState(*rPlantGen.InitStatesBegin(), *rSpecGen.InitStatesBegin(), true);
927 todo.push(currentp);
928 currentt=rResGen.InsInitState();
929 rProductCompositionMap[currentp] = currentt;
930
931 // process/feed todo stack
932 FD_DF("SupBuechiConProduct: processing reachable states:");
933 while(!todo.empty()) {
934 // allow for user interrupt
935 // LoopCallback();
936 // allow for user interrupt, incl progress report
937 FD_WPC(rProductCompositionMap.size(),rProductCompositionMap.size()+todo.size(),"SupBuechiConProduct(): processing");
938 FD_DF("SupBuechiConProduct(): processing" << rProductCompositionMap.size() << " " << todo.size());
939 // get next reachable state from todo stack
940 currentp = todo.top();
941 currentt = rProductCompositionMap[currentp];
942 todo.pop();
943 // skip critical (tmoor 201308)
944 if(critical.Exists(currentt)) continue;
945 // report
946 FD_DF("SupBuechiConProduct: processing (" << currentp.Str() << " -> " << currentt <<")");
947
948 // iterate over transitions, pass1: figure whether current state becomes critical (tmoor 201308)
949 ptit = rPlantGen.TransRelBegin(currentp.q1);
950 ptit_end = rPlantGen.TransRelEnd(currentp.q1);
951 stit = rSpecGen.TransRelBegin(currentp.q2);
952 stit_end = rSpecGen.TransRelEnd(currentp.q2);
953 disable.Clear();
954 // process all transitions and increment iterators strategically
955 while((ptit != ptit_end) && (stit != stit_end)) {
956 FD_DF("SupBuechiConProduct: current plant-transition: " << rPlantGen.TStr(*ptit) );
957 FD_DF("SupBuechiConProduct: current spec-transition: " << rSpecGen.TStr(*stit));
958 // case A: execute common event
959 if(ptit->Ev == stit->Ev) {
960 nextp = OPSState(ptit->X2, stit->X2,currentp.m1required);
961 if(currentp.m1required) {
962 if(rPlantGen.ExistsMarkedState(currentp.q1)) nextp.m1required=false;
963 } else {
964 if(rSpecGen.ExistsMarkedState(currentp.q2)) nextp.m1required=true;
965 }
966 // figure whether successor state is known to be critical
967 rcit = rProductCompositionMap.find(nextp);
968 if(rcit != rProductCompositionMap.end()) {
969 if(critical.Exists(rcit->second)) {
970 FD_DF("SupBuechiConParallel: common event " << rSpecGen.EStr(stit->Ev) << " to a critical states");
971 // if event is uncontrollable, current state becomes critical
972 if(!rCAlph.Exists(ptit->Ev)) {
973 FD_DF("SupBuechiConProduct: critical insert" << currentt);
974 critical.Insert(currentt); // tmoor 201308
975 // exit all loops
976 ptit = ptit_end;
977 stit = stit_end;
978 break;
979 }
980 // else, event is disabled
981 disable.Insert(stit->Ev);
982 }
983 }
984 // increment transition
985 ++ptit;
986 ++stit;
987 }
988 // case B: process plant event that is disabled by the specification
989 else if (ptit->Ev < stit->Ev) {
990 FD_DF("SupConProduct: " << rPlantGen.EStr(ptit->Ev) << " is enabled in the plant but disabled in the specification");
991 // if the event is uncontrollable, this makes the current state critical
992 if (!rCAlph.Exists(ptit->Ev)) {
993 FD_DF("SupBuechiConProduct: disabled event " << rPlantGen.EStr(ptit->Ev) << " is uncontrollable");
994 FD_DF("SupBuechiConProduct: critical insert" << currentt);
995 critical.Insert(currentt);
996 // exit all loops
997 ptit = ptit_end;
998 stit = stit_end;
999 break;
1000 }
1001 FD_DF("SupBuechiConProduct: incrementing plant transrel");
1002 ++ptit;
1003 }
1004 // increment titg and titg, case C: process h event that is not enabled for g
1005 else {
1006 FD_DF("SupBuechiConProduct: incrementing spec transrel");
1007 ++stit;
1008 }
1009 } // end while incrementing
1010 // increment iterators case D: process leftover events of plant
1011 while (ptit != ptit_end) {
1012 FD_DF("SupBuechiConProduct: current g-transition: " << rPlantGen.TStr(*ptit));
1013 FD_DF("SupBuechiConProduct: current h-transition: end");
1014 // if uncontrollable event leaves candidate
1015 if (!rCAlph.Exists(ptit->Ev)) {
1016 FD_DF("SupBuechiConProduct: asynchron executed uncontrollable "
1017 << "event " << rPlantGen.EStr(ptit->Ev) << " leaves specification:");
1018 FD_DF("SupConProduct: critical insert" << rPlantGen.SStr(currentt));
1019 critical.Insert(currentt);
1020 // exit this loop
1021 break;
1022 }
1023 FD_DF("SupBuechiConProduct: incrementing g transrel");
1024 ++ptit;
1025 } // end iterating pass1
1026
1027 // skip critical (tmoor 201308)
1028 if(critical.Exists(currentt)) continue;
1029
1030 // iterate over transitions, pass2: execute shared events (tmoor 201308)
1031 FD_DF("SupBuechiConProduct(): processing pass2");
1032 ptit = rPlantGen.TransRelBegin(currentp.q1);
1033 ptit_end = rPlantGen.TransRelEnd(currentp.q1);
1034 stit = rSpecGen.TransRelBegin(currentp.q2);
1035 stit_end = rSpecGen.TransRelEnd(currentp.q2);
1036 // process all transitions and increment iterators strategically
1037 while((ptit != ptit_end) && (stit != stit_end)) {
1038 FD_DF("SupBuechiConProduct: current plant-transition: " << rPlantGen.TStr(*ptit) );
1039 FD_DF("SupBuechiConProduct: current spec-transition: " << rSpecGen.TStr(*stit));
1040 // case A: execute common event
1041 if(ptit->Ev == stit->Ev) {
1042 if(!disable.Exists(stit->Ev)) {
1043 FD_DF("SupBuechiConProduct: executing common event " << rPlantGen.EStr(ptit->Ev));
1044 nextp = OPSState(ptit->X2, stit->X2,currentp.m1required);
1045 if(currentp.m1required) {
1046 if(rPlantGen.ExistsMarkedState(currentp.q1)) nextp.m1required=false;
1047 } else {
1048 if(rSpecGen.ExistsMarkedState(currentp.q2)) nextp.m1required=true;
1049 }
1050 // figure target index
1051 rcit = rProductCompositionMap.find(nextp);
1052 // if state is new: add to todo list and result
1053 if(rcit == rProductCompositionMap.end()) {
1054 todo.push(nextp);
1055 nextt = rResGen.InsState();
1056 rProductCompositionMap[nextp] = nextt;
1057 if(!nextp.m1required)
1058 if(rSpecGen.ExistsMarkedState(nextp.q2))
1059 rResGen.SetMarkedState(nextt);
1060 FD_DF("SupBuechiConProduct: todo push: (" << nextp.Str() << ") -> " << nextt);
1061 } else {
1062 nextt = rcit->second;
1063 }
1064 // set the transition and increment iterators
1065 FD_DF("SupBuechierParallel: add transition to new generator: " << rResGen.TStr(Transition(currentt, ptit->Ev, nextt)));
1066 rResGen.SetTransition(currentt, ptit->Ev, nextt);
1067 }
1068 // increment transition
1069 ++ptit;
1070 ++stit;
1071 } // end: if processing common event
1072 // case B: process plant event that is disabled by the specification
1073 else if (ptit->Ev < stit->Ev) {
1074 ++ptit;
1075 }
1076 // increment titg and titg, case C: process h event that is not enabled for g
1077 else {
1078 ++stit;
1079 }
1080 } // end while incrementing for pass2
1081
1082 } // while todo
1083
1084 FD_DF("SupBuechiConProduct: deleting critical states: " << critical.ToString());
1085 rResGen.DelStates(critical);
1086
1087 // restrict composition map
1088 std::map< OPSState , Idx>::iterator cit = rProductCompositionMap.begin();
1089 while(cit != rProductCompositionMap.end())
1090 if(!rResGen.ExistsState(cit->second)) rProductCompositionMap.erase(cit++);
1091 else cit++;
1092
1093}
1094
1095
1096
1097
1098// SupBuechiConUnchecked(rPlantGen, rCAlph, rSpecGen,rResGen)
1100 const Generator& rPlantGen,
1101 const EventSet& rCAlph,
1102 const Generator& rSpecGen,
1103 StateSet& rPlantMarking,
1104 Generator& rResGen)
1105{
1106 FD_DF("SupBuechiConUnchecked(\"" << rPlantGen.Name() << "\", \"" << rSpecGen.Name() << "\")");
1107
1108 // PREPARE RESULT:
1109 Generator* pResGen = &rResGen;
1110 if(&rResGen== &rPlantGen || &rResGen== &rSpecGen) {
1111 pResGen= rResGen.New();
1112 }
1113 pResGen->Clear();
1114 pResGen->InjectAlphabet(rPlantGen.Alphabet());
1115
1116 // controllable events
1117 FD_DF("SupBuechiCon: controllable events: " << rCAlph.ToString());
1118
1119 // perform product
1120 std::map< OPSState , Idx> cmap;
1121 SupBuechiConProduct(rPlantGen, rCAlph, rSpecGen, cmap, *pResGen);
1122
1123 // figure plant marking
1124 rPlantMarking.Clear();
1125 std::map< OPSState, Idx>::iterator pcit;
1126 for(pcit=cmap.begin(); pcit!=cmap.end(); ++pcit)
1127 if(rPlantGen.ExistsMarkedState(pcit->first.q1))
1128 rPlantMarking.Insert(pcit->second);
1129
1130 // fix statenames (debugging)
1131#ifdef FAUDES_DEBUG_FUNCTION
1132 std::map< OPSState, Idx>::iterator dcit;
1133 for(dcit=cmap.begin(); dcit!=cmap.end(); ++dcit) {
1134 Idx x1=dcit->first.q1;
1135 Idx x2=dcit->first.q2;
1136 bool m1requ=dcit->first.m1required;
1137 Idx x12=dcit->second;
1138 if(!pResGen->ExistsState(x12)) continue;
1139 std::string name1= rPlantGen.StateName(x1);
1140 if(name1=="") name1=ToStringInteger(x1);
1141 std::string name2= rSpecGen.StateName(x2);
1142 if(name2=="") name1=ToStringInteger(x2);
1143 std::string name12;
1144 if(m1requ) name12= name1 + "|" + name2 + "|r1m";
1145 else name12= name1 + "|" + name2 + "|r2m";
1146 name12=pResGen->UniqueStateName(name12);
1147 pResGen->StateName(x12,name12);
1148 }
1149#endif
1150
1151#ifdef FAUDES_DEBUG_FUNCTION
1152 pResGen->DWrite();
1153 pResGen->Write("tmp_syn_xxx_"+pResGen->Name()+".gen");
1154 pResGen->WriteStateSet(rPlantMarking);
1155#endif
1156
1157 // iterate for required properties
1158 while(true) {
1159 // catch trivial
1160 if(pResGen->Empty()) break;
1161 // fast inner loop: prefix controllability and omega-trim
1162 while(true) {
1163 Idx count1 = pResGen->Size();
1164 FD_DF("SupBuechiCon: iterate: do prefix con on #" << pResGen->Size());
1165 SupConClosedUnchecked(rPlantGen, rCAlph, *pResGen);
1166 FD_DF("SupBuechiCon: iterate: do coaccessible on #" << pResGen->Size());
1167 pResGen->Coaccessible();
1168 FD_DF("SupBuechiCon: iterate: do accessible on #" << pResGen->Size());
1169 pResGen->Accessible();
1170 FD_DF("SupBuechiCon: iterate: do complete on #" << pResGen->Size());
1171 pResGen->Complete();
1172 if(pResGen->Size() == count1) break;
1173 if(pResGen->Size() == 0) break;
1174 }
1175 // slow outer loop: controlled liveness aka restrict to controllable prefix
1176 Idx count2 = pResGen->Size();
1177 FD_DF("SupBuechiCon: iterate: do controlled liveness on #" << pResGen->Size());
1178 ControlledBuechiLiveness(*pResGen,rCAlph,rPlantMarking);
1179 if(pResGen->Size() == count2) break;
1180 }
1181
1182 // fix statenames ...
1183 std::map< OPSState, Idx>::iterator rcit;
1184 if(rPlantGen.StateNamesEnabled() && rSpecGen.StateNamesEnabled() && pResGen->StateNamesEnabled())
1185 for(rcit=cmap.begin(); rcit!=cmap.end(); rcit++) {
1186 Idx x1=rcit->first.q1;
1187 Idx x2=rcit->first.q2;
1188 bool m1requ=rcit->first.m1required;
1189 Idx x12=rcit->second;
1190 if(!pResGen->ExistsState(x12)) continue;
1191 std::string name1= rPlantGen.StateName(x1);
1192 if(name1=="") name1=ToStringInteger(x1);
1193 std::string name2= rSpecGen.StateName(x2);
1194 if(name2=="") name1=ToStringInteger(x2);
1195 std::string name12;
1196 if(m1requ) name12= name1 + "|" + name2 + "|r1m";
1197 else name12= name1 + "|" + name2 + "|r2m";
1198 name12=pResGen->UniqueStateName(name12);
1199 pResGen->StateName(x12,name12);
1200 }
1201
1202 // .. or clear them (?)
1203 if(!(rPlantGen.StateNamesEnabled() && rSpecGen.StateNamesEnabled() && pResGen->StateNamesEnabled()))
1204 pResGen->StateNamesEnabled(false);
1205
1206 // copy result
1207 if(pResGen != &rResGen) {
1208 pResGen->Move(rResGen);
1209 delete pResGen;
1210 }
1211
1212 FD_DF("SupBuechiCon: return #" << rResGen.Size());
1213}
1214
1215
1216// SupConBuechiNormUnchecked(rPlantGen, rCAlph, rOAlph, rSpecGen, rFeedbackMap, rResGen)
1218 const Generator& rPlantGen,
1219 const EventSet& rCAlph,
1220 const EventSet& rOAlph,
1221 const Generator& rSpecGen,
1222 StateSet& rPlantMarking,
1223 std::map< Idx , Idx >& rObserverStateMap,
1224 std::map< Idx , EventSet>& rFeedbackMap,
1225 Generator& rResGen)
1226{
1227 FD_DF("SupBuechiConNorm(\"" << rPlantGen.Name() << "\", \"" << rSpecGen.Name() << "\")");
1228
1229 // prepare result
1230 Generator* pResGen = &rResGen;
1231 if(&rResGen== &rPlantGen || &rResGen== &rSpecGen) {
1232 pResGen= rResGen.New();
1233 }
1234 pResGen->Clear();
1235 pResGen->InjectAlphabet(rPlantGen.Alphabet());
1236
1237 // report events
1238 FD_DF("SupBuechiConNorm: controllable events: " << rCAlph.ToString());
1239 FD_DF("SupBuechiConNorm: un-observabel events: " << (rPlantGen.Alphabet()-rOAlph).ToString());
1240
1241 // perform product
1242 std::map< OPSState , Idx> cmap;
1243 SupBuechiConProduct(rPlantGen, rCAlph, rSpecGen, cmap, *pResGen);
1244 BuechiTrim(*pResGen);
1245
1246 // no statenames impülemented
1247 pResGen->StateNamesEnabled(false);
1248
1249 // figure plant marking
1250 rPlantMarking.Clear();
1251 std::map< OPSState, Idx>::iterator pcit;
1252 for(pcit=cmap.begin(); pcit!=cmap.end(); ++pcit)
1253 if(rPlantGen.ExistsMarkedState(pcit->first.q1))
1254 rPlantMarking.Insert(pcit->second);
1255
1256 // extend by observer states
1257 Generator obsG= *pResGen;
1258 MarkAllStates(obsG);
1259 Project(obsG,rOAlph,obsG);
1260 InvProject(obsG,pResGen->Alphabet());
1261 std::map< std::pair<Idx,Idx>, Idx> rObserverCompositionMap;
1262 Product(*pResGen, obsG, rObserverCompositionMap, *pResGen);
1263 // track plantmarking
1264 StateSet fixmarking;
1265 std::map< std::pair<Idx,Idx>, Idx>::iterator cit;
1266 for(cit=rObserverCompositionMap.begin(); cit!=rObserverCompositionMap.end();++cit)
1267 if(rPlantMarking.Exists(cit->first.first)) fixmarking.Insert(cit->second);
1268 rPlantMarking=fixmarking;
1269 // construct reverse map "pResGen.State --> obsG.State"
1270 std::map< std::pair<Idx,Idx>, Idx>::iterator sit;
1271 for(sit=rObserverCompositionMap.begin(); sit!=rObserverCompositionMap.end();++sit)
1272 rObserverStateMap[sit->second]=sit->first.second;
1273
1274#ifdef FAUDES_DEBUG_FUNCTION
1275 pResGen->DWrite();
1276 pResGen->Write("tmp_syn_xxx_"+pResGen->Name()+".gen");
1277 pResGen->WriteStateSet(rPlantMarking);
1278#endif
1279
1280 FD_DF("SupBuechiConNorm(): cand #" << pResGen->Size() << " obs #" << obsG.Size());
1281 // make resulting generator trim until it's fully controllable
1282 while(true) {
1283 // catch trivial
1284 if(pResGen->Empty()) break;
1285 // fast inner loop: prefix controllability and omega-trim
1286 while(true) {
1287 Idx count1 = pResGen->Size();
1288 FD_DF("SupBuechiConNorm: iterate: do prefix-contr on #" << pResGen->Size());
1289 SupConClosedUnchecked(rPlantGen, rCAlph, *pResGen);
1290 FD_DF("SupBuechiConNorm: iterate: do accessible on #" << pResGen->Size());
1291 pResGen->Accessible();
1292 FD_DF("SupBuechiConNorm: iterate: do coaccessible on #" << pResGen->Size());
1293 pResGen->Coaccessible();
1294 FD_DF("SupBuechiConNorm: iterate: do prefix-norm on #" << pResGen->Size());
1295 SupConNormClosedUnchecked(rPlantGen, rOAlph, rOAlph, obsG, *pResGen);
1296 FD_DF("SupBuechiConNorm: iterate: do coaccessible on #" << pResGen->Size());
1297 pResGen->Coaccessible();
1298 FD_DF("SupBuechiConNorm: iterate: do accessible on #" << pResGen->Size());
1299 pResGen->Accessible();
1300 FD_DF("SupBuechiConNorm: iterate: do complete on #" << pResGen->Size());
1301 pResGen->Complete();
1302 if(pResGen->Size() == count1) break;
1303 if(pResGen->Size() == 0) break;
1304 }
1305 // slow outer loop: controlled liveness
1306 Idx count2 = pResGen->Size();
1307 FD_DF("SupBuechiConNorm: iterate: do controlled liveness on #" << pResGen->Size());
1308
1309 // cosmetic: restrict observer map and count effective states
1310 std::map< Idx , Idx>::iterator oit = rObserverStateMap.begin();
1311 while(oit != rObserverStateMap.end())
1312 if(!pResGen->ExistsState(oit->first)) rObserverStateMap.erase(oit++);
1313 else oit++;
1314 std::set< Idx > ostates;
1315 for(oit = rObserverStateMap.begin(); oit != rObserverStateMap.end(); ++oit)
1316 ostates.insert(oit->second);
1317
1318 //omega controlled liveness aka controllabe prefix
1319 FD_DF("SupBuechiConNorm(): cand #" << pResGen->Size() << " obs #" << ostates.size());
1320 std::map< Idx , EventSet> feedback;
1321 ControlledBuechiLiveness(*pResGen,rCAlph,rPlantMarking,rObserverStateMap,feedback);
1322 //ControlledBuechiLiveness(*pResGen,rCAlph,rPlantMarking);
1323 if(pResGen->Size() == count2) break;
1324 }
1325
1326 // copy result
1327 if(pResGen != &rResGen) {
1328 pResGen->Move(rResGen);
1329 delete pResGen;
1330 }
1331}
1332
1333
1334/*
1335****************************************
1336* SUPCON: WRAPPER / USER FUNCTIONS *
1337****************************************
1338*/
1339
1340
1341// SupBuechiCon(rPlantGen, rCAlph, rSpecGen, rResGen)
1343 const Generator& rPlantGen,
1344 const EventSet& rCAlph,
1345 const Generator& rSpecGen,
1346 Generator& rResGen)
1347{
1348 // consitenct check
1349 ControlProblemConsistencyCheck(rPlantGen, rCAlph, rSpecGen);
1350 // execute
1351 StateSet plantmarking;
1352 SupBuechiConUnchecked(rPlantGen, rCAlph, rSpecGen, plantmarking, rResGen);
1353 // record name
1354 rResGen.Name(CollapsString("SupBuechiCon(("+rPlantGen.Name()+"),("+rSpecGen.Name()+"))"));
1355}
1356
1357
1358// SupBuechiCon for Systems:
1359// uses and maintains controllablity from plant
1361 const System& rPlantGen,
1362 const Generator& rSpecGen,
1363 Generator& rResGen) {
1364 // prepare result
1365 Generator* pResGen = &rResGen;
1366 if(&rResGen== &rPlantGen || &rResGen== &rSpecGen) {
1367 pResGen= rResGen.New();
1368 }
1369 // execute
1370 SupBuechiCon(rPlantGen, rPlantGen.ControllableEvents(),rSpecGen,*pResGen);
1371 // copy all attributes of input alphabet
1372 pResGen->EventAttributes(rPlantGen.Alphabet());
1373 // copy result
1374 if(pResGen != &rResGen) {
1375 pResGen->Move(rResGen);
1376 delete pResGen;
1377 }
1378}
1379
1380// BuechiCon(rPlantGen, rCAlph, rSpecGen, rResGen)
1382 const Generator& rPlantGen,
1383 const EventSet& rCAlph,
1384 const Generator& rSpecGen,
1385 Generator& rResGen)
1386{
1387
1388 // consitency check
1389 ControlProblemConsistencyCheck(rPlantGen, rCAlph, rSpecGen);
1390 // compute supremal closed-loop behaviour
1391 StateSet plantmarking;
1392 SupBuechiConUnchecked(rPlantGen, rCAlph, rSpecGen, plantmarking, rResGen);
1393 // compute restrictive feedback
1394 std::map< Idx , EventSet> feedback;
1395 ControlledBuechiLiveness(rResGen, rCAlph, plantmarking, feedback);
1396 // apply restrictive feedback
1397 StateSet::Iterator sit = rResGen.StatesBegin();
1398 StateSet::Iterator sit_end = rResGen.StatesEnd();
1399 for(;sit!=sit_end;++sit) {
1400 const EventSet& pattern = feedback[*sit];
1401 TransSet::Iterator tit = rResGen.TransRelBegin(*sit);
1402 TransSet::Iterator tit_end = rResGen.TransRelEnd(*sit);
1403 while(tit!=tit_end) {
1404 if(pattern.Exists(tit->Ev)) { tit++; continue;};
1405 rResGen.ClrTransition(tit++);
1406 }
1407 }
1408 // cosmetic trim
1409 rResGen.Trim();
1410
1411#ifdef FAUDES_CODE
1412 // during development: test controllability and livenes
1413 if(!IsControllable(rPlantGen,rCAlph,rResGen)) {
1414 throw Exception("BuechiCon(..)", "ERROR: controllability test failed", 500);
1415 }
1416 if(!IsBuechiRelativelyClosed(rPlantGen,rResGen)) {
1417 throw Exception("BuechiCon(..)", "ERROR: rel. top. closedness test failed", 500);
1418 }
1419#endif
1420
1421 // record name
1422 rResGen.Name(CollapsString("BuechiCon(("+rPlantGen.Name()+"),("+rSpecGen.Name()+"))"));
1423}
1424
1425// BuechiCon for Systems:
1426// uses and maintains controllablity from plant
1428 const System& rPlantGen,
1429 const Generator& rSpecGen,
1430 Generator& rResGen)
1431{
1432 // prepare result
1433 Generator* pResGen = &rResGen;
1434 if(&rResGen== &rPlantGen || &rResGen== &rSpecGen) {
1435 pResGen= rResGen.New();
1436 }
1437 // execute
1438 BuechiCon(rPlantGen, rPlantGen.ControllableEvents(),rSpecGen,*pResGen);
1439 // copy all attributes of input alphabet
1440 pResGen->EventAttributes(rPlantGen.Alphabet());
1441 // copy result
1442 if(pResGen != &rResGen) {
1443 pResGen->Move(rResGen);
1444 delete pResGen;
1445 }
1446}
1447
1448
1449// SupBuechiConNorm(rPlantGen, rCAlph, rOAlph, rSpecGen, rResGen)
1451 const Generator& rPlantGen,
1452 const EventSet& rCAlph,
1453 const EventSet& rOAlph,
1454 const Generator& rSpecGen,
1455 Generator& rResGen)
1456{
1457 // consitenct check
1458 ControlProblemConsistencyCheck(rPlantGen, rCAlph, rOAlph, rSpecGen);
1459 // execute
1460 std::map< Idx , Idx> constates;
1461 std::map< Idx , EventSet> feedback;
1462 StateSet plantmarking;
1463 SupBuechiConNormUnchecked(rPlantGen, rCAlph, rOAlph, rSpecGen, plantmarking, constates, feedback, rResGen);
1464 // record name
1465 rResGen.Name(CollapsString("SupBuechiConNorm(("+rPlantGen.Name()+"),("+rSpecGen.Name()+"))"));
1466}
1467
1468
1469// SupBuechiConNorm for Systems:
1470// uses and maintains controllablity from plant
1472 const System& rPlantGen,
1473 const Generator& rSpecGen,
1474 Generator& rResGen) {
1475 // prepare result
1476 Generator* pResGen = &rResGen;
1477 if(&rResGen== &rPlantGen || &rResGen== &rSpecGen) {
1478 pResGen= rResGen.New();
1479 }
1480 // execute
1481 SupBuechiConNorm(rPlantGen, rPlantGen.ControllableEvents(),rPlantGen.ObservableEvents(),rSpecGen,*pResGen);
1482 // copy all attributes of input alphabet
1483 pResGen->EventAttributes(rPlantGen.Alphabet());
1484 // copy result
1485 if(pResGen != &rResGen) {
1486 pResGen->Move(rResGen);
1487 delete pResGen;
1488 }
1489}
1490
1491// BuechiConNrm(rPlantGen, rCAlph, rOAlph, rSpecGen, rResGen)
1493 const Generator& rPlantGen,
1494 const EventSet& rCAlph,
1495 const EventSet& rOAlph,
1496 const Generator& rSpecGen,
1497 Generator& rResGen)
1498{
1499
1500 // consitency check
1501 ControlProblemConsistencyCheck(rPlantGen, rCAlph, rOAlph, rSpecGen);
1502 // compute supremal closed-loop behaviour
1503 StateSet plantmarking;
1504 std::map< Idx , Idx> cxmap;
1505 std::map< Idx , EventSet> feedback;
1506 SupBuechiConNormUnchecked(rPlantGen, rCAlph, rOAlph, rSpecGen, plantmarking, cxmap, feedback, rResGen);
1507 // compute restrictive feedback
1508 ControlledBuechiLiveness(rResGen, rCAlph, plantmarking, cxmap, feedback);
1509 // apply restrictive feedback
1510 StateSet::Iterator sit = rResGen.StatesBegin();
1511 StateSet::Iterator sit_end = rResGen.StatesEnd();
1512 for(;sit!=sit_end;++sit) {
1513 // Idx cx = cxmap[*sit]; etc ...
1514 const EventSet& pattern = feedback[*sit];
1515 TransSet::Iterator tit = rResGen.TransRelBegin(*sit);
1516 TransSet::Iterator tit_end = rResGen.TransRelEnd(*sit);
1517 while(tit!=tit_end) {
1518 if(pattern.Exists(tit->Ev)) { tit++; continue;};
1519 rResGen.ClrTransition(tit++);
1520 }
1521 }
1522 // trim (?)
1523 BuechiTrim(rResGen);
1524//#ifdef FAUDES_CODE
1525 // during development: test controllability, normality and livenes
1526 if(!IsControllable(rPlantGen,rCAlph,rResGen)) {
1527 throw Exception("BuechiConNorm(..)", "ERROR: controllability test failed", 500);
1528 }
1529 if(!IsBuechiRelativelyClosed(rPlantGen,rResGen)) {
1530 throw Exception("BuechiConNorm(..)", "ERROR: rel. top. closedness test failed", 500);
1531 }
1532 Generator prK=rResGen;
1533 MarkAllStates(prK);
1534 Generator prL=rPlantGen;
1535 prL.Trim();
1536 MarkAllStates(prL);
1537 if(!IsNormal(prL,rOAlph,prK)){
1538 throw Exception("BuechiConNorm(..)", "ERROR: prefix normality test failed", 500);
1539 }
1540//#endif
1541
1542 // record name
1543 rResGen.Name(CollapsString("BuechiConNorm(("+rPlantGen.Name()+"),("+rSpecGen.Name()+"))"));
1544}
1545
1546// BuechiConNorm for Systems
1547// uses and maintains controllablity from plant
1549 const System& rPlantGen,
1550 const Generator& rSpecGen,
1551 Generator& rResGen) {
1552 // prepare result
1553 Generator* pResGen = &rResGen;
1554 if(&rResGen== &rPlantGen || &rResGen== &rSpecGen) {
1555 pResGen= rResGen.New();
1556 }
1557 // execute
1558 BuechiConNorm(rPlantGen, rPlantGen.ControllableEvents(),rPlantGen.ObservableEvents(), rSpecGen,*pResGen);
1559 // copy all attributes of input alphabet
1560 pResGen->EventAttributes(rPlantGen.Alphabet());
1561 // copy result
1562 if(pResGen != &rResGen) {
1563 pResGen->Move(rResGen);
1564 delete pResGen;
1565 }
1566}
1567
1568
1569
1570} // name space
#define FD_WPC(cntnow, contdone, message)
#define FD_DF(message)
const std::string & Name(void) const
std::set< EventSet > enable_one
void merge(const MCtrlPattern &other)
bool Exists(const Idx &rIndex) const
virtual void InsertSet(const NameSet &rOtherSet)
bool Insert(const Idx &rIndex)
std::string Str(void)
bool operator<(const OPSState &other) const
OPSState(const Idx &rq1, const Idx &rq2, const bool &rf)
const TaEventSet< EventAttr > & Alphabet(void) const
EventSet ControllableEvents(void) const
EventSet ObservableEvents(void) const
void DWrite(const Type *pContext=0) const
std::string ToString(const std::string &rLabel="", const Type *pContext=0) const
void Write(const Type *pContext=0) const
StateSet::Iterator StatesBegin(void) const
StateSet::Iterator InitStatesBegin(void) const
bool SetTransition(Idx x1, Idx ev, Idx x2)
const StateSet & MarkedStates(void) const
const EventSet & Alphabet(void) const
virtual void Move(vGenerator &rGen)
bool InitStatesEmpty(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 WriteStateSet(const StateSet &rStateSet) const
bool IsComplete(void) const
virtual vGenerator * New(void) const
bool ExistsState(Idx index) const
bool IsCoaccessible(void) const
std::string TStr(const Transition &rTrans) const
std::string StateName(Idx index) const
StateSet::Iterator StatesEnd(void) const
void DelStates(const StateSet &rDelStates)
TransSet::Iterator TransRelEnd(void) const
std::string EStr(Idx index) const
void SetMarkedState(Idx index)
bool Empty(void) const
virtual void EventAttributes(const EventSet &rEventSet)
bool StateNamesEnabled(void) const
Idx Size(void) const
std::string SStr(Idx index) const
virtual void Clear(void)
bool ExistsMarkedState(Idx index) const
std::string UniqueStateName(const std::string &rName) 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
virtual void RestrictSet(const TBaseSet &rOtherSet)
virtual void InsertSet(const TBaseSet &rOtherSet)
Iterator Begin(void) const
Idx Size(void) const
void Product(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
void MarkAllStates(vGenerator &rGen)
void Project(const Generator &rGen, const EventSet &rProjectAlphabet, Generator &rResGen)
void InvProject(Generator &rGen, const EventSet &rProjectAlphabet)
bool IsDeterministic(const vGenerator &rGen)
void BuechiConNorm(const Generator &rPlantGen, const EventSet &rCAlph, const EventSet &rOAlph, const Generator &rSpecGen, Generator &rResGen)
void SupBuechiCon(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, Generator &rResGen)
bool IsBuechiControllable(const Generator &rGenPlant, const EventSet &rCAlph, const Generator &rGenCand)
bool BuechiTrim(vGenerator &rGen)
void BuechiCon(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, Generator &rResGen)
void SupBuechiConNorm(const Generator &rPlantGen, const EventSet &rCAlph, const EventSet &rOAlph, const Generator &rSpecGen, Generator &rResGen)
bool IsControllable(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSupCandGen)
bool IsNormal(const Generator &rL, const EventSet &rOAlph, const Generator &rK)
uint32_t Idx
bool IsControllableUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSupCandGen, StateSet &rCriticalStates)
void SupBuechiConUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, StateSet &rPlantMarking, Generator &rResGen)
void SupConNormClosedUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, const EventSet &rOAlph, Generator &rObserverGen, Generator &rSupCandGen)
bool ControlledBuechiLiveness(Generator &rSupCandGen, const EventSet &rCAlph, const StateSet &rPlantMarking)
void ControlProblemConsistencyCheck(const GeneratorVector &rPlantGenVec, const EventSet &rConAlph, const GeneratorVector &rSpecGenVec)
void SupConClosedUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, Generator &rSupCandGen)
bool IsBuechiTrim(const vGenerator &rGen)
bool IsBuechiRelativelyClosed(const Generator &rGenPlant, const Generator &rGenCand)
std::string ToStringInteger(Int number)
Definition cfl_utils.cpp:43
void SupBuechiConProduct(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, std::map< OPSState, Idx > &rProductCompositionMap, Generator &rResGen)
void SupBuechiConNormUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, const EventSet &rOAlph, const Generator &rSpecGen, StateSet &rPlantMarking, std::map< Idx, Idx > &rObserverStateMap, std::map< Idx, EventSet > &rFeedbackMap, Generator &rResGen)
std::string CollapsString(const std::string &rString, unsigned int len)
Definition cfl_utils.cpp:91
bool IsBuechiRelativelyClosedUnchecked(const Generator &rGenPlant, const Generator &rGenCand)

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