syn_sscon.cpp
Go to the documentation of this file.
1 /** @file syn_sscon.cpp Standard synthesis consistency test */
2 
3 /* FAU Discrete Event Systems Library (libfaudes)
4 
5  Copyright (C) 2014 Matthias Leinfelder, 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 #include "syn_sscon.h"
23 #include <stack>
24 
25 
26 namespace faudes {
27 
28 
29 
30 
31 /*
32  ***************************************************************************************
33  ***************************************************************************************
34 
35  The core algorithm is implemented as the function IsStdSynthesisConsistentUnchecked(...)
36  The function assumes that the arguments satisfy their respecive conditions. For a
37  detailed discussion on the algorithm see
38 
39  Moor, T.: Natural projections for the synthesis of
40  non-conflicting supervisory controllers,
41  Workshop on Discrete Event Systems (WODES), Paris, 2014.
42 
43  and
44 
45  Moor, T., Baier, Ch., Wittmann, Th.:
46  Consistent abstractions for the purpose of supervisory control,
47  Proc. 52nd IEEE Conference on Decision and Control (CDC), pp. 7291-7196, Firenze, 2013.
48 
49  The initial version of this function was implemented by Matthias Leinfelder in course
50  of his bachelor thesis.
51 
52  ***************************************************************************************
53  ***************************************************************************************
54  */
55 
56 #undef FD_DF
57 #define FD_DF(msg) FD_WARN(msg)
58 
59 //IsStdSynthesisConsistentUnchecked (rPlantGen, rPlant0Gen, rCAlph)
61  const Generator& rPlantGen,
62  const EventSet& rCAlph,
63  const Generator& rPlant0Gen) {
64 
65  // prepare: extract relevant alpahbets
66  const EventSet& sig = rPlantGen.Alphabet();
67  const EventSet& sigo = rPlant0Gen.Alphabet();
68  const EventSet& sigc = rCAlph;
69  EventSet sigco = sigc * sigo;
70  EventSet siguco = sig - sigco;
71 
72  // prepare: construct rich plant representation by parallel composition with abstraction
73  // (the implementation of Parallel guarantees a trim result --- we rely on this)
74  Generator gg0;
75  ProductCompositionMap gg0pmap;
76  Parallel(rPlantGen,rPlant0Gen,gg0pmap,gg0);
77  FD_DF("IsStdSynthesisConsistent(..): relevant states: " << gg0.Size());
78 
79  // prepare: construct a so-called trace generator with exactly the same reachable
80  // states as the rich plant gg0, but with only one path to reach each state; thus,
81  // each class of strings that reaches any particular state in gg0 has exaclty
82  // one representative string in the generated language of the trace generator
83 
84  // initialize result and prepare the todo-stack for the loop invariant "todo states
85  // are reachable in trace by exactly one path"
86  Generator trace;
87  trace.InsEvents(sig);
88  std::stack<Idx> todo;
89  todo.push(gg0.InitState());
90  trace.InsInitState(gg0.InitState());
91 
92  // forward reachability search
93  while(!todo.empty()){
94  // pick state from todo
95  Idx q = todo.top();
96  todo.pop();
97  // iterate all reachable states
98  TransSet::Iterator tit,tit_end;
99  tit = gg0.TransRelBegin(q);
100  tit_end = gg0.TransRelEnd(q);
101  for(;tit != tit_end; ++tit){
102  // by the invariant "tit->X2 not in trace" implies "tit->X2 is not on the stack"
103  if(!trace.ExistsState(tit->X2)){
104  todo.push(tit->X2);
105  trace.InsState(tit->X2);
106  trace.SetTransition(*tit);
107  }
108  }
109  }
110  // this completes the construction of the trace generator
111 
112  /*
113  rPlantGen.GraphWrite("tmp_gen.jpg");
114  rPlant0Gen.GraphWrite("tmp_gen0.jpg");
115  gg0.GraphWrite("tmp_gg0.jpg");
116  trace.GraphWrite("tmp_trace.jpg");
117  */
118 
119  // prepare: backward reach via reverse transistion relation
120  TTransSet<TransSort::X2EvX1> revtransrel;
121  gg0.TransRel().ReSort(revtransrel);
122 
123  // initialize target states by marked states
124  StateSet target = gg0.MarkedStates();
125  // provide boundary to focus backward reach
126  StateSet btarget = target;
127 
128  // grow target set by backward 'reach and test'
129  while(true) {
130 
131  // break on success
132  if(target.Size()==gg0.Size()) break;
133 
134  // report
135  FD_DF("IsStdSynthesisConsistent(..): targets for one-transition tests A,B and C: #" << target.Size() << " (b" << btarget.Size() << ")");
136 
137  // inner loop to grow by fast one-transition tests A and B first
138  while(true) {
139  // allow for user interrupt
140  FD_WPC(target.Size(),gg0.Size(),"IsStdSynthesisConsistent(): processing one-step tests");
141  // prepare to sense when stuck
142  Idx tszi=target.Size();
143  // iterate over all relevant target states (using boundary)
144  StateSet::Iterator sit = btarget.Begin();
145  StateSet::Iterator sit_end = btarget.End();
146  while(sit != sit_end){
147  // break on success
148  if(target.Size()==gg0.Size()) break;
149  // pick target state
150  Idx x2=*sit;
151  FD_DF("IsStdSynthesisConsistent(..): testing predecessors of target " << gg0.StateName(x2));
152  // sense if all predecessors pass
153  bool allpass=true;
154  // iterate over all target predecessors
155  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(x2);
156  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(x2);
157  for(;rit != rit_end; ++rit){
158  // predecessor technically passes if allready identified as target
159  if(target.Exists(rit->X1)) continue;
160  // test A and B from [ref2]: passes if uncontrollable or unobservable event leads to the target
161  if(siguco.Exists(rit->Ev)) {
162  target.Insert(rit->X1);
163  btarget.Insert(rit->X1);
164  FD_DF("IsStdSynthesisConsistent(..): one-transition test AB passed at: " << gg0.StateName(rit->X1));
165  continue;
166  }
167  // test C adapted from [ref2]: predecessor passes if (i) the abstraction state is not marked,
168  // (ii) all events in the abstraction are controllable, and (iii) each event leads the low level
169  // plant to a successor within the target (as of 2017/06)
170  if(!rPlant0Gen.ExistsMarkedState(gg0pmap.Arg2State(rit->X1))){
171  EventSet act0 = rPlant0Gen.ActiveEventSet(gg0pmap.Arg2State(rit->X1));
172  EventSet::Iterator eit=act0.Begin();
173  EventSet::Iterator eit_end=act0.End();
174  for(;eit != eit_end; ++eit)
175  if(!target.Exists(gg0.SuccessorState(rit->X1,*eit))) break;
176  if(eit == eit_end) {
177  target.Insert(rit->X1);
178  btarget.Insert(rit->X1);
179  FD_DF("IsStdSynthesisConsistent(..): one-transition test C passed at: " << gg0.StateName(rit->X1));
180  continue;
181  }
182  }
183  // record the fail
184  allpass=false;
185  }
186  // safely increment sit
187  ++sit;
188  // remove from boundary if if all predecessors passed
189  if(allpass) {
190  FD_DF("IsStdSynthesisConsistent(..): all predecessors of " << gg0.StateName(x2) << " have passed");
191  btarget.Erase(x2);
192  }
193  }
194  // break inner loop when stuck
195  if(target.Size()==tszi) break;
196  }
197 
198  // break on success
199  if(target.Size()==gg0.Size()) break;
200 
201  FD_DF("IsStdSynthesisConsistent(..): targets for multi-transition tests: #" << target.Size() << " (b" << btarget.Size() << ")");
202 
203  // looking for one pass only
204  bool onepass=false;
205 
206  // inner loop to grow by comperativly fast multi-transition test D
207  StateSet::Iterator sit = btarget.Begin();
208  StateSet::Iterator sit_end = btarget.End();
209  for(; (!onepass) && (sit != sit_end); ++sit){
210  // iterate over all target predecessors
211  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(*sit);
212  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(*sit);
213  for(; rit != rit_end; ++rit){
214  // cannot gain target if predecessor is already identified
215  if(target.Exists(rit->X1)) continue;
216  // allow for user interrupt
217  FD_WPC(target.Size(),gg0.Size(),"IsStdSynthesisConsistent(): processing fast star-step test D");
218  // test D from [ref2]
219  // (i) compute low-level reach without attaining a low-level target state (evil reach);
220  // (ii) test whether being constraint to the evil reach implies high-level blocking;
221  // (iii) test whether each exist from the high-level reach leads the low level to the target (as of 2017/06)
222  // skip high-level marked
223  StateSet ereach;
224  StateSet ereach0;
225  std::stack<Idx> etodo;
226  etodo.push(rit->X1);
227  bool fail=false;
228  while((!etodo.empty()) && (!fail)){
229  // pick a state from todo stack
230  Idx x1 = etodo.top();
231  etodo.pop();
232  // not evil reach
233  if(target.Exists(x1)) continue;
234  // high-level marked
235  if(rPlant0Gen.ExistsMarkedState(gg0pmap.Arg2State(x1))) {
236  fail=true;
237  continue;
238  }
239  // extend evil reach
240  ereach.Insert(x1);
241  ereach0.Insert(gg0pmap.Arg2State(x1));
242  // iterate all reachable states
243  TransSet::Iterator tit = gg0.TransRelBegin(x1);
244  TransSet::Iterator tit_end = gg0.TransRelEnd(x1);
245  for(;tit != tit_end; ++tit)
246  if(!ereach.Exists(tit->X2))
247  etodo.push(tit->X2);
248  }
249  // passed (ii), need to check (iii)
250  if(!fail) {
251  FD_DF("IsStdSynthesisConsistent(..): multi-transition test *D: evil reach #" << ereach.Size());
252  StateSet::Iterator xit = ereach.Begin();
253  StateSet::Iterator xit_end = ereach.End();
254  for(; (xit!= xit_end) && (!fail); ++xit) {
255  Idx x1=*xit;
256  Idx x10=gg0pmap.Arg2State(x1);
257  TransSet::Iterator tit0=rPlant0Gen.TransRelBegin(x10);
258  TransSet::Iterator tit0_end=rPlant0Gen.TransRelEnd(x10);
259  for(;tit0 != tit0_end; ++tit0) {
260  if(ereach0.Exists(tit0->X2)) continue;
261  if(!target.Exists(gg0.SuccessorState(x1,tit0->Ev))) break;
262  }
263  if(tit0 != tit0_end) fail=true;
264  }
265  if(!fail) {
266  FD_DF("IsStdSynthesisConsistent(..): multi-transition test *D passed at: " << gg0.StateName(rit->X1));
267  onepass=true;
268  //target.InsertSet(ereach);
269  //btarget.InsertSet(ereach);
270  target.Insert(rit->X1);
271  btarget.Insert(rit->X1);
272  break;
273  }
274  }
275  }
276  }
277 
278  // sense success in test D
279  if(onepass) continue;
280 
281 
282  // iterate over thourough tests D from [ref2] E from [ref1]
283  FD_DF("IsStdSynthesisConsistent(..): running multi-transition tests D and E");
284  StateSet ftarget;
285  StateSet::Iterator sit2 = btarget.Begin();
286  StateSet::Iterator sit2_end = btarget.End();
287  for(; (!onepass) && (sit2 != sit2_end); ++sit2){
288  // iterate over all target predecessors
289  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(*sit2);
290  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(*sit2);
291  for(; (!onepass) && (rit != rit_end); ++rit){
292  // cannot gain target if predecessor is already identified
293  if(target.Exists(rit->X1)) continue;
294  // we know this to fail
295  if(ftarget.Exists(rit->X1)) continue;
296  // allow for user interrupt
297  FD_WPC(target.Size(),gg0.Size(),"IsStdSynthesisConsistent(): processing star-step tests");
298 
299  // prepare tests D/E from [ref2]/[ref1]
300 
301  // Ls: a) s
302  Generator Ls(trace);
303  Ls.SetMarkedState(rit->X1);
304  // Ls: b) s . sig^* --- all future of s
305  Ls.ClrTransitions(rit->X1);
306  SelfLoopMarkedStates(Ls,sig);
307  // Ms: a) M --- all successful future of s
308  Generator Ms=gg0;
309  Ms.InjectMarkedStates(target);
310  // Ms: b) Ls ^ M
311  LanguageIntersection(Ls,Ms,Ms);
312  // M0s: projection of Ms --- all successful future of s from the perspective of the abstraction
313  Generator M0s;
314  Project(Ms,sigo,M0s);
315  // Es0: a) Ms0 . sigo^* --- evil specification to prevent succesful future of s
316  Generator E0s=M0s;
317  StateSet::Iterator ssit = E0s.MarkedStatesBegin();
318  StateSet::Iterator ssit_end = E0s.MarkedStatesEnd();
319  for(; ssit!=ssit_end; ++ssit)
320  E0s.ClrTransitions(*ssit);
321  SelfLoopMarkedStates(E0s,sigo);
322  // Es0: b) L0 - M0s . sigo^*
323  LanguageDifference(rPlant0Gen,E0s,E0s);
324  Trim(E0s);
325 
326  // apply test D: does reachability of X1 contradict with the evil specification ?
327  Generator Es=E0s;
328  InvProject(Es,sig);
329  PrefixClosure(Es);
330  if(EmptyLanguageIntersection(Ls,Es)) {
331  // pass: evil specification renders X1 unreachable
332  FD_DF("IsStdSynthesisConsistent(..): multi-transition test D passed at: " << gg0.StateName(rit->X1));
333  onepass=true;
334  target.Insert(rit->X1);
335  btarget.Insert(rit->X1);
336  continue;
337  }
338 
339  // apply test E: use the supremal evil supervisor to test whether reachability of X1 complies with E0s
340  Generator K0s;
341  SupRelativelyPrefixClosed(rPlant0Gen,E0s,E0s);
342  SupConNB(rPlant0Gen,sigco,E0s,K0s);
343  // test whether X1 is reachable under evil supervision
344  Generator Ks=K0s;
345  InvProject(Ks,sig);
346  PrefixClosure(Ks);
347  if(EmptyLanguageIntersection(Ls,Ks)) {
348  // pass: evil supervisor renders X1 unreachable
349  FD_DF("IsStdSynthesisConsistent(..): multi-transition test E passed at: " << gg0.StateName(rit->X1));
350  onepass=true;
351  target.Insert(rit->X1);
352  btarget.Insert(rit->X1);
353  continue;
354  }
355 
356  // record failure
357  ftarget.Insert(rit->X1);
358 
359 
360  } // iterate target predecessors
361  } // iterate targets
362 
363 
364  // break outer loop if tests D/E did not gain one more target
365  if(!onepass) break;
366 
367  }
368 
369  // return success
370  return target.Size()==gg0.Size();
371 }
372 
373 
374 
375 
376 /*
377  ***************************************************************************************
378  ***************************************************************************************
379 
380  Application Interface
381 
382  ***************************************************************************************
383  ***************************************************************************************
384  */
385 
386 
387 // IsStdSynthesisConsistent(rPlantGen, rCAlph, rPlant0Gen)
389  const Generator& rPlantGen,
390  const EventSet& rCAlph,
391  const Generator& rPlant0Gen)
392 {
393  // test if the plant is deterministic
394  if(!IsDeterministic(rPlantGen)){
395  std::stringstream errstr;
396  errstr << "Plant generator must be deterministic, " << "but is nondeterministic";
397  throw Exception("IsStdSynthesisConsistent", errstr.str(), 501);
398  }
399  // test if the Alphabet with the controllable events matches the Alphabet of the Generator
400  if(! (rCAlph <= rPlantGen.Alphabet()) ){
401  std::stringstream errstr;
402  errstr << "Controllable events must be a subset of the alphabet specified by \"" << rPlantGen.Name() << "\"";
403  throw Exception("IsStdSynthesisConsistent", errstr.str(), 506);
404  }
405  // test if the abstraction alphabet matches the plant alphabet
406  const EventSet& sigo = rPlant0Gen.Alphabet();
407  if(! (sigo <= rPlantGen.Alphabet() ) ){
408  std::stringstream errstr;
409  errstr << "Abstraction alphabet must be a subset of the plant alphabet pescified by \"" << rPlantGen.Name() << "\"";
410  throw Exception("IsStdSynthesisConsistent", errstr.str(), 506);
411  }
412 
413  // test the consistency of the plant
414  return IsStdSynthesisConsistentUnchecked(rPlantGen,rCAlph,rPlant0Gen);
415 }
416 
417 
418 // IsStdSynthesisConsistent(rPlantGen,rPlant0Gen)
420  const System& rPlantGen,
421  const Generator& rPlant0Gen)
422 {
423  // extract controllable events
424  const EventSet& calph = rPlantGen.ControllableEvents();
425  // pass on
426  return IsStdSynthesisConsistent(rPlantGen,calph,rPlant0Gen);
427 }
428 
429 
430 } // name space
#define FD_WPC(cntnow, contdone, message)
bool Exists(const Idx &rIndex) const
Idx Arg2State(Idx s12) const
Iterator BeginByX2(Idx x2) const
Iterator EndByX2(Idx x2) const
TBaseSet< Transition, TransSort::X1EvX2 >::Iterator Iterator
Definition: cfl_transset.h:273
void ReSort(TTransSet< OtherCmp > &res) const
EventSet ControllableEvents(void) const
const TransSet & TransRel(void) const
bool SetTransition(Idx x1, Idx ev, Idx x2)
const StateSet & MarkedStates(void) const
const EventSet & Alphabet(void) const
EventSet ActiveEventSet(Idx x1) const
TransSet::Iterator TransRelBegin(void) const
void ClrTransitions(Idx x1, Idx ev)
void InsEvents(const EventSet &events)
void InjectMarkedStates(const StateSet &rNewMarkedStates)
bool ExistsState(Idx index) const
StateSet::Iterator MarkedStatesBegin(void) const
std::string StateName(Idx index) const
void Name(const std::string &rName)
TransSet::Iterator TransRelEnd(void) const
Idx SuccessorState(Idx x1, Idx ev) const
StateSet::Iterator MarkedStatesEnd(void) const
void SetMarkedState(Idx index)
Idx InitState(void) const
Idx Size(void) const
bool ExistsMarkedState(Idx index) const
bool Exists(const T &rElem) const
Definition: cfl_baseset.h:2132
Iterator End(void) const
Definition: cfl_baseset.h:1913
Iterator Begin(void) const
Definition: cfl_baseset.h:1908
virtual bool Erase(const T &rElem)
Definition: cfl_baseset.h:2036
Idx Size(void) const
Definition: cfl_baseset.h:1836
bool EmptyLanguageIntersection(const Generator &rGen1, const Generator &rGen2)
void Trim(vGenerator &rGen)
void PrefixClosure(Generator &rGen)
void LanguageDifference(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
void LanguageIntersection(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
void Project(const Generator &rGen, const EventSet &rProjectAlphabet, Generator &rResGen)
void Parallel(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
void InvProject(Generator &rGen, const EventSet &rProjectAlphabet)
void SelfLoopMarkedStates(Generator &rGen, const EventSet &rAlphabet)
bool IsDeterministic(const vGenerator &rGen)
void SupConNB(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, Generator &rResGen)
Definition: syn_supcon.cpp:757
bool IsStdSynthesisConsistent(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rPlant0Gen)
Definition: syn_sscon.cpp:388
void SupRelativelyPrefixClosed(const Generator &rPlantGen, const Generator &rSpecGen, Generator &rResGen)
uint32_t Idx
bool IsStdSynthesisConsistentUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rPlant0Gen)
Definition: syn_sscon.cpp:60
#define FD_DF(msg)
Definition: syn_sscon.cpp:57

libFAUDES 2.33b --- 2025.05.07 --- c++ api documentaion by doxygen