omg_rabinfnct.cpp
Go to the documentation of this file.
1/** @file omg_rabinfnct.cpp
2
3Operations regarding omega languages accepted by Rabin automata
4
5*/
6
7/* FAU Discrete Event Systems Library (libFAUDES)
8
9Copyright (C) 2025 Thomas Moor
10
11This library is free software; you can redistribute it and/or
12modify it under the terms of the GNU Lesser General Public
13License as published by the Free Software Foundation; either
14version 2.1 of the License, or (at your option) any later version.
15
16This library is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public
22License along with this library; if not, write to the Free Software
23Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
24
25
26
27#include "omg_rabinfnct.h"
28
29
30namespace faudes {
31
32// RabinLifeStates
33// compute states that are not lifelocks/deadlocks wrt one Rabin pair
35 const TransSet& rTransRel,
36 const TransSetX2EvX1& rRevTransRel,
37 const RabinPair& rRPair,
38 StateSet& rLife)
39{
40 // convenience accessors
41 const StateSet& iset=rRPair.ISet();
42 const StateSet& rset=rRPair.RSet();
43 // initialise optimistic candidate of life states
44 rLife=iset;
45 // iterate for overall fixpoint
46 bool fix=false;
47 while(!fix) {
48 // record size to detect change
49 std::size_t invsz=rLife.Size();
50 // nu-iteration to restrict rLife to an existential forward invariant
51 bool nufix=false;
52 StateSet nudel;
53 while(!nufix) {
54 nudel.Clear();
55 StateSet::Iterator sit=rLife.Begin();
56 StateSet::Iterator sit_end=rLife.End();
57 while(sit!=sit_end) {
58 if((rTransRel.SuccessorStates(*sit) * rLife).Empty())
59 nudel.Insert(*sit);
60 ++sit;
61 }
62 rLife.EraseSet(nudel);
63 nufix=nudel.Empty();
64 }
65 // mu-iteration to obtain existential backward reach from rset
66 StateSet breach=rLife*rset;
67 StateSet todo=breach;
68 bool mufix=false;
69 StateSet muins;
70 while(!mufix) {
71 muins.Clear();
72 StateSet::Iterator sit=todo.Begin();
73 StateSet::Iterator sit_end=todo.End();
74 while(sit!=sit_end) {
75 muins.InsertSet(rRevTransRel.PredecessorStates(*sit));
76 ++sit;
77 }
78 muins.RestrictSet(rLife);
79 muins.EraseSet(breach);
80 breach.InsertSet(muins);
81 todo=muins;
82 mufix=muins.Empty();
83 }
84 // restrict rLife to breach
85 rLife=rLife*breach;
86 // sense change
87 if(invsz == rLife.Size()) fix=true;
88 }
89 // one more mu-iteration to obtain existential backward reach from inv
90 bool mufix=false;
91 StateSet todo=rLife;
92 StateSet muins;
93 while(!mufix) {
94 muins.Clear();
95 StateSet::Iterator sit=todo.Begin();
96 StateSet::Iterator sit_end=todo.End();
97 while(sit!=sit_end) {
98 muins.InsertSet(rRevTransRel.PredecessorStates(*sit));
99 ++sit;
100 }
101 muins.EraseSet(rLife);
102 rLife.InsertSet(muins);
103 todo=muins;
104 mufix=muins.Empty();
105 }
106 // done
107}
108
109
110// RabinLifeStates API wrapper
112 const vGenerator& rRAut,
113 const RabinPair& rRPair,
114 StateSet& rLife)
115{
116 // convenience accessor
117 const TransSet& transrel=rRAut.TransRel();
118 TransSetX2EvX1 revtransrel(transrel);
119 // run algorithm
120 RabinLifeStates(transrel,revtransrel,rRPair,rLife);
121}
122
123
124// RabinLifeStates API wrapperv
125// report staes that are no deadloack/lifelock wrt Rabiin appectance
126void RabinLifeStates(const RabinAutomaton& rRAut, StateSet& rLife) {
127 // convenience accessor
128 const RabinAcceptance& raccept=rRAut.RabinAcceptance();
129 const TransSet& transrel=rRAut.TransRel();
130 TransSetX2EvX1 revtransrel(transrel);
131 // pessimistic candidate for the trim set
132 rLife.Clear();
133 // iterate over Rabin pairs
134 StateSet inv;
135 RabinAcceptance::CIterator rit=raccept.Begin();
136 for(;rit!=raccept.End();++rit) {
137 RabinLifeStates(transrel,revtransrel,*rit,inv);
138 rLife.InsertSet(inv);
139 }
140}
141
142
143// IsRabinLife
144// (returns true iff no accessible state blocks)
145bool IsRabinLife(const RabinAutomaton& rRAut) {
146 StateSet rLife;
147 RabinLifeStates(rRAut,rLife);
148 return rRAut.AccessibleSet() <= rLife;
149}
150
151// RabinTrimSet
152// (returns the sets that conbtribute to the Rabin accepted omega-language)
153void RabinTrimSet(const RabinAutomaton& rRAut, StateSet& rTrim) {
154 // get life states
155 RabinLifeStates(rRAut,rTrim);
156 // only reachable states contribute
157 rTrim.RestrictSet(rRAut.AccessibleSet());
158}
159
160// RabinTrim
161// (return True if result contains at least one initial state)
163 // trim automaton
164 StateSet trim;
165 RabinTrimSet(rRAut,trim);
166 rRAut.RestrictStates(trim);
167 // figure result
168 bool res= !rRAut.InitStates().Empty();
169 return res;
170}
171
172// rti wrapper
173bool RabinTrim(const RabinAutomaton& rRAut, RabinAutomaton& rRes) {
174 rRes=rRAut;
175 return RabinTrim(rRes);
176}
177
178// IsRabinTrim
179// (returns true iff accessible and life)
180bool IsRabinTrim(const RabinAutomaton& rRAut) {
181 StateSet rLife;
182 if(!rRAut.IsAccessible()) return false;
183 RabinLifeStates(rRAut,rLife);
184 return (rRAut.States()<=rLife);
185}
186
187
188// RabinSimplify
190 // convenience accessor
191 RabinAcceptance& raccept=rRAut.RabinAcceptance();
192 const TransSet& transrel=rRAut.TransRel();
193 const TransSetX2EvX1 revtransrel(transrel);
194 // trim each Rabin pair to its life states
195 StateSet alife;
196 StateSet plife;
197 RabinAcceptance::Iterator rit=raccept.Begin();
198 for(;rit!=raccept.End();++rit) {
199 RabinLifeStates(transrel,revtransrel,*rit,plife);
200 rit->RestrictStates(plife);
201 alife.InsertSet(plife);
202 }
203 // remove obviously redundant (could do better here)
204 raccept.EraseDoublets();
205}
206
207// rti wrapper
208void RabinSimplify(const RabinAutomaton& rRAut, RabinAutomaton& rRes) {
209 rRes=rRAut;
210 RabinSimplify(rRes);
211}
212
213// Rabin-Buechi product (lifting individual acceptence conditions, generated languages not affected
214// if arguments are full))
215void RabinBuechiAutomaton(const RabinAutomaton& rRAut, const Generator& rBAut, RabinAutomaton& rRes) {
216 // prepare result
217 RabinAutomaton* pRes = &rRes;
218 if(&rRes== &rRAut || &rRes== &rBAut) {
219 pRes= rRes.New();
220 }
221 // utils
222 StateSet::Iterator sit;
223 StateSet::Iterator sit_end;
224 std::map< std::pair<Idx,Idx>, Idx>::iterator cit;
225 std::map< std::pair<Idx,Idx>, Idx> cmap;
226 // std product (this will make res->Alphabet the union of the arguments)
227 Product(rRAut,rBAut,cmap,*pRes);
228 // set conversion rRAut-state to set of new states
229 std::map< Idx , StateSet > rmap;
230 cit=cmap.begin();;
231 for(;cit!=cmap.end();++cit)
232 rmap[cit->first.first].Insert(cit->second);
233 // copy and fix Rabin acceptance
234 pRes->RabinAcceptance().Clear();
235 RabinAcceptance::CIterator rit=rRAut.RabinAcceptance().Begin();;
236 RabinAcceptance::CIterator rit_end=rRAut.RabinAcceptance().End();;
237 for(;rit!=rit_end;++rit) {
238 RabinPair rpair;
239 sit=rit->RSet().Begin();
240 sit_end=rit->RSet().End();
241 for(;sit!=sit_end;++sit)
242 rpair.RSet().InsertSet(rmap[*sit]);
243 sit=rit->ISet().Begin();
244 sit_end=rit->ISet().End();
245 for(;sit!=sit_end;++sit)
246 rpair.ISet().InsertSet(rmap[*sit]);
247 pRes->RabinAcceptance().Insert(rpair);
248 }
249 // set conversion rBAut-state to set of new states
250 std::map< Idx , StateSet > bmap;
251 cit=cmap.begin();;
252 for(;cit!=cmap.end();++cit)
253 bmap[cit->first.second].Insert(cit->second);
254 // set buechi marking
255 sit=rBAut.MarkedStatesBegin();
256 sit_end=rBAut.MarkedStatesEnd();
257 for(;sit!=sit_end;++sit)
258 pRes->InsMarkedStates(bmap[*sit]);
259 // be attribute aware
260 bool careattr=rRAut.Alphabet().EqualAttributes(rBAut.Alphabet());
261 if(careattr) {
262 pRes->EventAttributes(rRAut.Alphabet());
263 }
264 // copy result
265 if(pRes != &rRes) {
266 pRes->Move(rRes);
267 delete pRes;
268 }
269}
270
271
272// helper class for omega compositions
273class RPState {
274public:
275 // minimal interface
277 RPState(const Idx& rq1, const Idx& rq2, const bool& rf) :
278 q1(rq1), q2(rq2), m1required(rf), mresolved(false) {};
279 std::string Str(void) { return ToStringInteger(q1)+"|"+
281 // order
282 bool operator < (const RPState& other) const {
283 if (q1 < other.q1) return(true);
284 if (q1 > other.q1) return(false);
285 if (q2 < other.q2) return(true);
286 if (q2 > other.q2) return(false);
287 if (!m1required && other.m1required) return(true);
288 if (m1required && !other.m1required) return(false);
289 if (!mresolved && other.mresolved) return(true);
290 return(false);
291 }
292 // member variables
297};
298
299
300// product of rabin and buechi automata, langugae intersection (only one rabin pair)
301void RabinBuechiProduct(const RabinAutomaton& rRAut, const Generator& rBAut, RabinAutomaton& rRes) {
302 // we can only handle one Rabin pair
303 if(rRAut.RabinAcceptance().Size()!=1){
304 std::stringstream errstr;
305 errstr << "the current implementation requires exactly one Rabin pair";
306 throw Exception("RabinBuechiProduct", errstr.str(), 80);
307 }
308
309 // prepare result
310 RabinAutomaton* pRes = &rRes;
311 if(&rRes== &rRAut || &rRes== &rBAut) {
312 pRes= rRes.New();
313 }
314 pRes->Clear();
315 pRes->Name(CollapsString(rRAut.Name()+".x."+rBAut.Name()));
316
317 // create res alphabet
318 pRes->InsEvents(rRAut.Alphabet());
319 pRes->InsEvents(rBAut.Alphabet());
320
321 // pick the one Rabin pair we care and prepare result
322 RabinPair rpair= *(rRAut.RabinAcceptance().Begin());
323 rpair.RSet().RestrictSet(rpair.ISet());
324 pRes->RabinAcceptance().Size(1);
325 RabinPair& resrpair= *(pRes->RabinAcceptance().Begin());
326 resrpair.Clear();
327
328 // reverse composition map
329 std::map< RPState, Idx> reverseCompositionMap;
330 // todo stack
331 std::stack< RPState > todo;
332 // current pair, new pair
333 RPState currentstates, newstates;
334 // state
335 Idx tmpstate;
336 StateSet::Iterator lit1, lit2;
337 TransSet::Iterator tit1, tit1_end, tit2, tit2_end;
338 std::map< RPState, Idx>::iterator rcit;
339 // push all combinations of initial states on todo stack
340 FD_DF("RabinBuechiProduct: push initial states to todo:");
341 for(lit1 = rRAut.InitStatesBegin(); lit1 != rRAut.InitStatesEnd(); ++lit1) {
342 for(lit2 = rBAut.InitStatesBegin(); lit2 != rBAut.InitStatesEnd(); ++lit2) {
343 currentstates = RPState(*lit1, *lit2, true);
344 todo.push(currentstates);
345 Idx tmpstate=pRes->InsInitState();
346 reverseCompositionMap[currentstates] = tmpstate;
347 FD_DF("RabinRabinBuechiProduct: " << currentstates.Str() << " -> " << tmpstate);
348 // figure, whether this state should be in the invariant
349 if(rpair.ISet().Exists(currentstates.q1))
350 resrpair.ISet().Insert(tmpstate);
351 // copy buechi marking
352 if(rBAut.ExistsMarkedState(currentstates.q2))
353 pRes->SetMarkedState(tmpstate);
354 }
355 }
356
357 // start algorithm
358 FD_DF("RabinBuechiProduct: processing reachable states:");
359 while (! todo.empty()) {
360 // allow for user interrupt
361 LoopCallback();
362 // get next reachable state from todo stack
363 currentstates = todo.top();
364 todo.pop();
365 FD_DF("RabinBuechiProduct: processing (" << currentstates.Str() << " -> " << reverseCompositionMap[currentstates]);
366 // iterate over all rRAut transitions
367 tit1 = rRAut.TransRelBegin(currentstates.q1);
368 tit1_end = rRAut.TransRelEnd(currentstates.q1);
369 for(; tit1 != tit1_end; ++tit1) {
370 // find transition in rBAut
371 tit2 = rBAut.TransRelBegin(currentstates.q2, tit1->Ev);
372 tit2_end = rBAut.TransRelEnd(currentstates.q2, tit1->Ev);
373 for (; tit2 != tit2_end; ++tit2) {
374 newstates = RPState(tit1->X2, tit2->X2,currentstates.m1required);
375 // figure whether marking was resolved
376 if(currentstates.m1required) {
377 if(rpair.RSet().Exists(currentstates.q1))
378 newstates.m1required=false;
379 } else {
380 if(rpair.ISet().Exists(currentstates.q1))
381 if(rBAut.ExistsMarkedState(currentstates.q2))
382 newstates.m1required=true;
383 }
384 // add to result and todo list if composition state is new
385 rcit = reverseCompositionMap.find(newstates);
386 if(rcit == reverseCompositionMap.end()) {
387 todo.push(newstates);
388 tmpstate = pRes->InsState();
389 // figure, whether this state should be recurrent
390 if(!newstates.m1required)
391 if(rBAut.ExistsMarkedState(newstates.q2))
392 resrpair.RSet().Insert(tmpstate);
393 // figure, whether this state should be invariant
394 if(rpair.ISet().Exists(newstates.q1))
395 resrpair.ISet().Insert(tmpstate);
396 // copy buechi marking
397 if(rBAut.ExistsMarkedState(newstates.q2))
398 pRes->SetMarkedState(tmpstate);
399 // record new state
400 reverseCompositionMap[newstates] = tmpstate;
401 FD_DF("RabinBuechiProduct: todo push: (" << newstates.Str() << ") -> " << reverseCompositionMap[newstates]);
402 }
403 else {
404 tmpstate = rcit->second;
405 }
406 pRes->SetTransition(reverseCompositionMap[currentstates],
407 tit1->Ev, tmpstate);
408 FD_DF("RabinBuechiProduct: add transition to new generator: " <<
409 pRes->TStr(Transition(reverseCompositionMap[currentstates], tit1->Ev, tmpstate)));
410 }
411 }
412 }
413
414 FD_DF("RabinBuechiProduct: recurrent states: " << pRes->StatesToString(resrpair.RSet()));
415
416
417 // fix statenames ...
418 if(rRAut.StateNamesEnabled() && rBAut.StateNamesEnabled() && pRes->StateNamesEnabled())
419 for(rcit=reverseCompositionMap.begin(); rcit!=reverseCompositionMap.end(); rcit++) {
420 Idx x1=rcit->first.q1;
421 Idx x2=rcit->first.q2;
422 bool m1requ=rcit->first.m1required;
423 Idx x12=rcit->second;
424 if(!pRes->ExistsState(x12)) continue;
425 std::string name1= rRAut.StateName(x1);
426 if(name1=="") name1=ToStringInteger(x1);
427 std::string name2= rBAut.StateName(x2);
428 if(name2=="") name1=ToStringInteger(x2);
429 std::string name12 = name1 + "|" + name2;
430 if(m1requ) name12 += "|r1m";
431 else name12 +="|r2m";
432 name12=pRes->UniqueStateName(name12);
433 pRes->StateName(x12,name12);
434 }
435
436 // .. or clear them (?)
437 if(!(rRAut.StateNamesEnabled() && rBAut.StateNamesEnabled() && pRes->StateNamesEnabled()))
438 pRes->StateNamesEnabled(false);
439
440 // be attribute aware
441 bool careattr=rRAut.Alphabet().EqualAttributes(rBAut.Alphabet());
442 if(careattr) {
443 pRes->EventAttributes(rRAut.Alphabet());
444 }
445
446 // copy result
447 if(pRes != &rRes) {
448 pRes->Move(rRes);
449 delete pRes;
450 }
451}
452
453
454
455} // namespace faudes
456
#define FD_DF(message)
const std::string & Name(void) const
RPState(const Idx &rq1, const Idx &rq2, const bool &rf)
bool operator<(const RPState &other) const
std::string Str(void)
void RestrictStates(const StateSet &rDomain)
StateSet & RSet(void)
StateSet & ISet(void)
virtual void Clear(void)
Iterator Begin(void)
StateSet PredecessorStates(Idx x2) const
StateSet SuccessorStates(Idx x1) const
virtual void Clear(void)
const TaStateSet< StateAttr > & States(void) const
virtual void Move(TaGenerator &rGen)
const TaEventSet< EventAttr > & Alphabet(void) const
bool SetTransition(Idx x1, Idx ev, Idx x2)
const ATransSet & TransRel(void) const
virtual void RestrictStates(const StateSet &rStates)
void RabinAcceptance(const faudes::RabinAcceptance &rRabAcc)
TrGenerator * New(void) const
virtual void EraseDoublets(void)
StateSet::Iterator InitStatesBegin(void) const
const TransSet & TransRel(void) const
const EventSet & Alphabet(void) const
std::string StatesToString(void) const
const StateSet & InitStates(void) const
TransSet::Iterator TransRelBegin(void) const
bool IsAccessible(void) const
void InsEvents(const EventSet &events)
void InsMarkedStates(const StateSet &rStates)
StateSet AccessibleSet(void) const
bool ExistsState(Idx index) const
StateSet::Iterator MarkedStatesBegin(void) const
std::string TStr(const Transition &rTrans) const
std::string StateName(Idx index) const
TransSet::Iterator TransRelEnd(void) const
StateSet::Iterator MarkedStatesEnd(void) const
void SetMarkedState(Idx index)
virtual void EventAttributes(const EventSet &rEventSet)
bool StateNamesEnabled(void) const
StateSet::Iterator InitStatesEnd(void) const
bool ExistsMarkedState(Idx index) const
std::string UniqueStateName(const std::string &rName) const
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
virtual void EraseSet(const TBaseSet &rOtherSet)
Idx Size(void) const
void Product(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
void RabinLifeStates(const TransSet &rTransRel, const TransSetX2EvX1 &rRevTransRel, const RabinPair &rRPair, StateSet &rLife)
void RabinTrimSet(const RabinAutomaton &rRAut, StateSet &rTrim)
void RabinBuechiAutomaton(const RabinAutomaton &rRAut, const Generator &rBAut, RabinAutomaton &rRes)
bool RabinTrim(RabinAutomaton &rRAut)
bool IsRabinLife(const RabinAutomaton &rRAut)
void RabinBuechiProduct(const RabinAutomaton &rRAut, const Generator &rBAut, RabinAutomaton &rRes)
bool IsRabinTrim(const RabinAutomaton &rRAut)
void RabinSimplify(RabinAutomaton &rRAut)
uint32_t Idx
void LoopCallback(void)
std::string ToStringInteger(Int number)
Definition cfl_utils.cpp:43
std::string CollapsString(const std::string &rString, unsigned int len)
Definition cfl_utils.cpp:91

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