omg_rabinfnct.cpp
Go to the documentation of this file.
1 /** @file omg_rabinfnct.cpp
2 
3 Operations regarding omega languages accepted by Rabin automata
4 
5 */
6 
7 /* FAU Discrete Event Systems Library (libFAUDES)
8 
9 Copyright (C) 2025 Thomas Moor
10 
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version.
15 
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
20 
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
24 
25 
26 
27 #include "omg_rabinfnct.h"
28 
29 
30 namespace 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
126 void 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)
145 bool 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)
153 void 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)
162 bool RabinTrim(RabinAutomaton& rRAut) {
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
173 bool RabinTrim(const RabinAutomaton& rRAut, RabinAutomaton& rRes) {
174  rRes=rRAut;
175  return RabinTrim(rRes);
176 }
177 
178 // IsRabinTrim
179 // (returns true iff accessible and life)
180 bool 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
208 void 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))
215 void 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
273 class RPState {
274 public:
275  // minimal interface
276  RPState() {};
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
296  bool mresolved;
297 };
298 
299 
300 // product of rabin and buechi automata, langugae intersection (only one rabin pair)
301 void 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
Definition: cfl_types.cpp:422
RPState(const Idx &rq1, const Idx &rq2, const bool &rf)
bool operator<(const RPState &other) const
std::string Str(void)
StateSet & RSet(void)
Definition: omg_rabinacc.h:68
virtual void Clear(void)
StateSet & ISet(void)
Definition: omg_rabinacc.h:84
Iterator End(void)
Iterator Begin(void)
StateSet PredecessorStates(Idx x2) const
StateSet SuccessorStates(Idx x1) const
TBaseSet< Transition, TransSort::X1EvX2 >::Iterator Iterator
Definition: cfl_transset.h:273
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)
Definition: omg_rabinaut.h:355
void RabinAcceptance(const faudes::RabinAcceptance &rRabAcc)
Definition: omg_rabinaut.h:326
TrGenerator * New(void) const
Definition: omg_rabinaut.h:300
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
Definition: cfl_baseset.h:1904
bool Exists(const T &rElem) const
Definition: cfl_baseset.h:2322
virtual void Clear(void)
Definition: cfl_baseset.h:2104
Iterator End(void) const
Definition: cfl_baseset.h:2098
virtual void RestrictSet(const TBaseSet &rOtherSet)
Definition: cfl_baseset.h:2271
virtual void InsertSet(const TBaseSet &rOtherSet)
Definition: cfl_baseset.h:2194
Iterator Begin(void) const
Definition: cfl_baseset.h:2093
virtual void EraseSet(const TBaseSet &rOtherSet)
Definition: cfl_baseset.h:2249
Idx Size(void) const
Definition: cfl_baseset.h:1899
void Product(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
void RabinTrimSet(const RabinAutomaton &rRAut, StateSet &rTrim)
void RabinBuechiAutomaton(const RabinAutomaton &rRAut, const Generator &rBAut, RabinAutomaton &rRes)
bool RabinTrim(RabinAutomaton &rRAut)
void RabinLifeStates(const TransSet &rTransRel, const TransSetX2EvX1 &rRevTransRel, const RabinPair &rRPair, StateSet &rLife)
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(bool pBreak(void))
Definition: cfl_utils.cpp:693
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.33l --- 2025.09.16 --- c++ api documentaion by doxygen