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 
57 //IsStdSynthesisConsistentUnchecked (rPlantGen, rPlant0Gen, rCAlph)
59  const Generator& rPlantGen,
60  const EventSet& rCAlph,
61  const Generator& rPlant0Gen) {
62 
63  // prepare: extract relevant alpahbets
64  const EventSet& sig = rPlantGen.Alphabet();
65  const EventSet& sigo = rPlant0Gen.Alphabet();
66  const EventSet& sigc = rCAlph;
67  EventSet sigco = sigc * sigo;
68  EventSet siguco = sig - sigco;
69 
70  // prepare: construct rich plant representation by parallel composition with abstraction
71  // (the implementation of Parallel guarantees a trim result --- we rely on this)
72  Generator gg0;
73  ProductCompositionMap gg0pmap;
74  Parallel(rPlantGen,rPlant0Gen,gg0pmap,gg0);
75  FD_DF("IsStdSynthesisConsistent(..): relevant states: " << gg0.Size());
76 
77  // prepare: construct a so-called trace generator with exactly the same reachable
78  // states as the rich plant gg0, but with only one path to reach each state; thus,
79  // each class of strings that reaches any particular state in gg0 has exaclty
80  // one representative string in the generated language of the trace generator
81 
82  // initialize result and prepare the todo-stack for the loop invariant "todo states
83  // are reachable in trace by exactly one path"
84  Generator trace;
85  trace.InsEvents(sig);
86  std::stack<Idx> todo;
87  todo.push(gg0.InitState());
88  trace.InsInitState(gg0.InitState());
89 
90  // forward reachability search
91  while(!todo.empty()){
92  // pick state from todo
93  Idx q = todo.top();
94  todo.pop();
95  // iterate all reachable states
96  TransSet::Iterator tit,tit_end;
97  tit = gg0.TransRelBegin(q);
98  tit_end = gg0.TransRelEnd(q);
99  for(;tit != tit_end; ++tit){
100  // by the invariant "tit->X2 not in trace" implies "tit->X2 is not on the stack"
101  if(!trace.ExistsState(tit->X2)){
102  todo.push(tit->X2);
103  trace.InsState(tit->X2);
104  trace.SetTransition(*tit);
105  }
106  }
107  }
108  // this completes the construction of the trace generator
109 
110  /*
111  rPlantGen.GraphWrite("tmp_gen.jpg");
112  rPlant0Gen.GraphWrite("tmp_gen0.jpg");
113  gg0.GraphWrite("tmp_gg0.jpg");
114  trace.GraphWrite("tmp_trace.jpg");
115  */
116 
117  // prepare: backward reach via reverse transistion relation
118  TTransSet<TransSort::X2EvX1> revtransrel;
119  gg0.TransRel().ReSort(revtransrel);
120 
121  // initialize target states by marked states
122  StateSet target = gg0.MarkedStates();
123  // provide boundary to focus backward reach
124  StateSet btarget = target;
125 
126  // grow target set by backward 'reach and test'
127  while(true) {
128 
129  // break on success
130  if(target.Size()==gg0.Size()) break;
131 
132  // report
133  FD_DF("IsStdSynthesisConsistent(..): targets for one-transition tests A,B and C: #" << target.Size() << " (b" << btarget.Size() << ")");
134 
135  // inner loop to grow by fast one-transition tests A and B first
136  while(true) {
137  // allow for user interrupt
138  FD_WPC(target.Size(),gg0.Size(),"IsStdSynthesisConsistent(): processing one-step tests");
139  // prepare to sense when stuck
140  Idx tszi=target.Size();
141  // iterate over all relevant target states (using boundary)
142  StateSet::Iterator sit = btarget.Begin();
143  StateSet::Iterator sit_end = btarget.End();
144  while(sit != sit_end){
145  // break on success
146  if(target.Size()==gg0.Size()) break;
147  // pick target state
148  Idx x2=*sit;
149  FD_DF("IsStdSynthesisConsistent(..): testing predecessors of target " << gg0.StateName(x2));
150  // sense if all predecessors pass
151  bool allpass=true;
152  // iterate over all target predecessors
153  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(x2);
154  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(x2);
155  for(;rit != rit_end; ++rit){
156  // predecessor technically passes if allready identified as target
157  if(target.Exists(rit->X1)) continue;
158  // test A and B from [ref2]: passes if uncontrollable/unobservable event leads to the target
159  if(siguco.Exists(rit->Ev)) {
160  target.Insert(rit->X1);
161  btarget.Insert(rit->X1);
162  FD_DF("IsStdSynthesisConsistent(..): one-transition test AB passed at: " << gg0.StateName(rit->X1));
163  continue;
164  }
165  // test C adapted from [ref2]: predecessor passes if abstraction state is not marked and all successors lead to the target
166  if(!rPlant0Gen.ExistsMarkedState(gg0pmap.Arg2State(rit->X1))){
167  if(gg0.SuccessorStates(rit->X1) <= target) {
168  target.Insert(rit->X1);
169  btarget.Insert(rit->X1);
170  FD_DF("IsStdSynthesisConsistent(..): one-transition test C passed at: " << gg0.StateName(rit->X1));
171  continue;
172  }
173  }
174  // record the fail
175  allpass=false;
176  }
177  // safely increment sit
178  ++sit;
179  // remove from boundary if if all predecessors passed
180  if(allpass) {
181  FD_DF("IsStdSynthesisConsistent(..): all predecessors of " << gg0.StateName(x2) << " have passed");
182  btarget.Erase(x2);
183  }
184  }
185  // break inner loop when stuck
186  if(target.Size()==tszi) break;
187  }
188 
189  // break on success
190  if(target.Size()==gg0.Size()) break;
191 
192  FD_DF("IsStdSynthesisConsistent(..): targets for multi-transition tests: #" << target.Size() << " (b" << btarget.Size() << ")");
193 
194  // looking for one pass only
195  bool onepass=false;
196 
197  // inner loop to grow by comperativly fast multi-transition test D
198  StateSet::Iterator sit = btarget.Begin();
199  StateSet::Iterator sit_end = btarget.End();
200  for(; (!onepass) && (sit != sit_end); ++sit){
201  // iterate over all target predecessors
202  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(*sit);
203  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(*sit);
204  for(; rit != rit_end; ++rit){
205  // cannot gain target if predecessor is already identified
206  if(target.Exists(rit->X1)) continue;
207  // allow for user interrupt
208  FD_WPC(target.Size(),gg0.Size(),"IsStdSynthesisConsistent(): processing fast star-step test D");
209  // test D from [ref2]
210  // a) compute low-level reach without attaining a low-level target state (evil reach);
211  // b) test whether being constraint to the evil reach implies high-level blocking
212  // (thus we fail if we attain a high-level marking without passing a low-level marking)
213  // skip high-level marked
214  StateSet ereach;
215  std::stack<Idx> etodo;
216  etodo.push(rit->X1);
217  bool fail=false;
218  while((!etodo.empty()) && (!fail)){
219  // pick a state from todo stack
220  Idx x1 = etodo.top();
221  etodo.pop();
222  // not evil reach
223  if(target.Exists(x1)) continue;
224  // high-level marked
225  if(rPlant0Gen.ExistsMarkedState(gg0pmap.Arg2State(x1))) {
226  fail=true;
227  continue;
228  }
229  // extend evil reach
230  ereach.Insert(x1);
231  // iterate all reachable states
232  TransSet::Iterator tit = gg0.TransRelBegin(x1);
233  TransSet::Iterator tit_end = gg0.TransRelEnd(x1);
234  for(;tit != tit_end; ++tit)
235  if(!ereach.Exists(tit->X2))
236  etodo.push(tit->X2);
237  }
238  // pass
239  if(!fail) {
240  FD_DF("IsStdSynthesisConsistent(..): multi-transition test *D passed: gain #" << ereach.Size());
241  FD_DF("IsStdSynthesisConsistent(..): multi-transition test *D passed at: " << rit->X1);
242  onepass=true;
243  //target.InsertSet(ereach);
244  //btarget.InsertSet(ereach);
245  target.Insert(rit->X1);
246  btarget.Insert(rit->X1);
247  break;
248  }
249  }
250  }
251 
252  // sense success in test D
253  if(onepass) continue;
254 
255 
256  // iterate over thourough tests D from [ref2] E from [ref1]
257  FD_DF("IsStdSynthesisConsistent(..): running multi-transition tests D and E");
258  StateSet ftarget;
259  StateSet::Iterator sit2 = btarget.Begin();
260  StateSet::Iterator sit2_end = btarget.End();
261  for(; (!onepass) && (sit2 != sit2_end); ++sit2){
262  FD_DF("IsStdSynthesisConsistent(..): at " << *sit2);
263  // iterate over all target predecessors
264  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(*sit2);
265  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(*sit2);
266  for(; (!onepass) && (rit != rit_end); ++rit){
267  // cannot gain target if predecessor is already identified
268  if(target.Exists(rit->X1)) continue;
269  // we know this to fail
270  if(ftarget.Exists(rit->X1)) continue;
271  // allow for user interrupt
272  FD_WPC(target.Size(),gg0.Size(),"IsStdSynthesisConsistent(): processing star-step tests");
273 
274  // prepare tests D/E from [ref2]/[ref1]
275 
276  // Ls: a) s
277  Generator Ls(trace);
278  Ls.SetMarkedState(rit->X1);
279  // Ls: b) s . sig^* --- all future of s
280  Ls.ClrTransitions(rit->X1);
281  SelfLoopMarkedStates(Ls,sig);
282  // Ms: a) M --- all successful future of s
283  Generator Ms=gg0;
284  Ms.InjectMarkedStates(target);
285  // Ms: b) Ls ^ M
286  LanguageIntersection(Ls,Ms,Ms);
287  // M0s: projection of Ms --- all successful future of s from the perspective of the abstraction
288  Generator M0s;
289  Project(Ms,sigo,M0s);
290  // Es0: a) Ms0 . sigo^* --- evil specification to prevent succesful future of s
291  Generator E0s=M0s;
292  StateSet::Iterator ssit = E0s.MarkedStatesBegin();
293  StateSet::Iterator ssit_end = E0s.MarkedStatesEnd();
294  for(; ssit!=ssit_end; ++ssit)
295  E0s.ClrTransitions(*ssit);
296  SelfLoopMarkedStates(E0s,sigo);
297  // Es0: b) L0 - M0s . sigo^*
298  LanguageDifference(rPlant0Gen,E0s,E0s);
299  Trim(E0s);
300 
301  // apply test D: does reachability of X1 contradict with the evil specification ?
302  Generator Es=E0s;
303  InvProject(Es,sig);
304  PrefixClosure(Es);
305  if(EmptyLanguageIntersection(Ls,Es)) {
306  // pass: evil specification renders X1 unreachable
307  FD_DF("IsStdSynthesisConsistent(..): multi-transition test D passed: " << gg0.StateName(rit->X1));
308  onepass=true;
309  target.Insert(rit->X1);
310  btarget.Insert(rit->X1);
311  continue;
312  }
313 
314  // apply test E: use the supremal evil supervisor to test whether reachability of X1 complies with E0s
315  Generator K0s;
316  SupRelativelyPrefixClosed(rPlant0Gen,E0s,E0s);
317  SupConNB(rPlant0Gen,sigco,E0s,K0s);
318  // test whether X1 is reachable under evil supervision
319  Generator Ks=K0s;
320  InvProject(Ks,sig);
321  PrefixClosure(Ks);
322  if(EmptyLanguageIntersection(Ls,Ks)) {
323  // pass: evil supervisor renders X1 unreachable
324  FD_DF("IsStdSynthesisConsistent(..): multi-transition test E passed: " << gg0.StateName(rit->X1));
325  onepass=true;
326  target.Insert(rit->X1);
327  btarget.Insert(rit->X1);
328  continue;
329  }
330 
331  // record failure
332  ftarget.Insert(rit->X1);
333 
334 
335  } // iterate target predecessors
336  } // iterate targets
337 
338 
339  // break outer loop if tests D/E did not gain one more target
340  if(!onepass) break;
341 
342  }
343 
344  // return success
345  return target.Size()==gg0.Size();
346 }
347 
348 
349 
350 
351 /*
352  ***************************************************************************************
353  ***************************************************************************************
354 
355  Application Interface
356 
357  ***************************************************************************************
358  ***************************************************************************************
359  */
360 
361 
362 // IsStdSynthesisConsistent(rPlantGen, rCAlph, rPlant0Gen)
364  const Generator& rPlantGen,
365  const EventSet& rCAlph,
366  const Generator& rPlant0Gen)
367 {
368  // test if the plant is deterministic
369  if(!IsDeterministic(rPlantGen)){
370  std::stringstream errstr;
371  errstr << "Plant generator must be deterministic, " << "but is nondeterministic";
372  throw Exception("IsStdSynthesisConsistent", errstr.str(), 501);
373  }
374  // test if the Alphabet with the controllable events matches the Alphabet of the Generator
375  if(! (rCAlph <= rPlantGen.Alphabet()) ){
376  std::stringstream errstr;
377  errstr << "Controllable events must be a subset of the alphabet specified by \"" << rPlantGen.Name() << "\"";
378  throw Exception("IsStdSynthesisConsistent", errstr.str(), 506);
379  }
380  // test if the abstraction alphabet matches the plant alphabet
381  const EventSet& sigo = rPlant0Gen.Alphabet();
382  if(! (sigo <= rPlantGen.Alphabet() ) ){
383  std::stringstream errstr;
384  errstr << "Abstraction alphabet must be a subset of the plant alphabet pescified by \"" << rPlantGen.Name() << "\"";
385  throw Exception("IsStdSynthesisConsistent", errstr.str(), 506);
386  }
387 
388  // test the consistency of the plant
389  return IsStdSynthesisConsistentUnchecked(rPlantGen,rCAlph,rPlant0Gen);
390 }
391 
392 
393 // IsStdSynthesisConsistent(rPlantGen,rPlant0Gen)
395  const System& rPlantGen,
396  const Generator& rPlant0Gen)
397 {
398  // extract controllable events
399  const EventSet& calph = rPlantGen.ControllableEvents();
400  // pass on
401  return IsStdSynthesisConsistent(rPlantGen,calph,rPlant0Gen);
402 }
403 
404 
405 } // name space

libFAUDES 2.28c --- 2016.09.30 --- c++ api documentaion by doxygen