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  Exclusive copyright is granted to Klaus Schmidt
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Lesser General Public
10  License as published by the Free Software Foundation; either
11  version 2.1 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public
19  License along with this library; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
21 
22 
23 #include "syn_sscon.h"
24 #include <stack>
25 
26 
27 namespace faudes {
28 
29 
30 
31 
32 /*
33  ***************************************************************************************
34  ***************************************************************************************
35 
36  The core algorithm is implemented as the function IsStdSynthesisConsistentUnchecked.
37  The function assumes that the arguments satisfy their respecive conditions. For a
38  detailed discussion on the algorithm see
39 
40  Moor, T.: Natural projections for the synthesis of
41  non-conflicting supervisory controllers,
42  Workshop on Discrete Event Systems (WODES), Paris, 2014.
43 
44  and
45 
46  Moor, T., Baier, Ch., Wittmann, Th.:
47  Consistent abstractions for the purpose of supervisory control,
48  Proc. 52nd IEEE Conference on Decision and Control (CDC), pp. 7291-7196, Firenze, 2013.
49 
50  The initial version of this functions was implemented by Matthias Leinfelder in due course
51  of his bachelor thesis.
52 
53  ***************************************************************************************
54  ***************************************************************************************
55  ****
56  */
57 
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 siguc = sig - sigc;
71 
72  // prepare: construct rich plant representation by parallel composition with abstraction
73  // (implementation of Paralle guarantees a trim result --- we rely on this)
74  Generator gg0;
75  ProductCompositionMap gg0pmap;
76  Parallel(rPlantGen,rPlant0Gen,gg0pmap,gg0);
77 
78  // prepare: construct a so-called trace generator with exactly the same reachable
79  // states as the rich plant gg0, but with only one path to reach each state; thus,
80  // each class of strings that reaches any particular state in gg0 has exaclty
81  // one representative string in the generated language of the trace generator
82 
83  // initialize result as 'trace' and a todo-set for the loop invariant "todo states
84  // are reachable in trace by exactly one path"
85  Generator trace;
86  trace.InsEvents(sig);
87  std::stack<Idx> todo;
88  todo.push(gg0.InitState());
89  trace.InsInitState(gg0.InitState());
90 
91  // forward reachability search
92  while(!todo.empty()){
93  // pick state from todo
94  Idx q = todo.top();
95  todo.pop();
96  // iterate all reachable states
97  TransSet:: Iterator tit,tit_end;
98  tit = gg0.TransRelBegin(q);
99  tit_end = gg0.TransRelEnd(q);
100  for(;tit != tit_end; ++tit){
101  // by the invariant "tit->X2 not in trace" implies "tit->X2 is not on the stack"
102  if(!trace.ExistsState(tit->X2)){
103  todo.push(tit->X2);
104  trace.InsState(tit->X2);
105  trace.SetTransition(*tit);
106  }
107  }
108  }
109  // this completes the construction of the trace generator
110 
111  /*
112  rPlantGen.GraphWrite("tmp_gen.jpg");
113  rPlant0Gen.GraphWrite("tmp_gen0.jpg");
114  gg0.GraphWrite("tmp_gg0.jpg");
115  trace.GraphWrite("tmp_trace.jpg");
116  */
117 
118  // prepare: backward reach via reverse transistion relation
119  TTransSet<TransSort::X2EvX1> revtransrel;
120  gg0.TransRel().ReSort(revtransrel);
121 
122  // initialize target states by marked states
123  StateSet target = gg0.MarkedStates();
124  // provide restricted target to focus backward reach (performance gain is debateble)
125  StateSet rtarget = target;
126 
127  // grow target set by backward 'reach and test'
128  while(true) {
129  // prepare to sense when stuck / break on success
130  Idx tsz=target.Size();
131  if(tsz==gg0.Size()) break;
132 
133  // report
134  FD_DF("IsStdSynthesisConsistent(..): targets for one-transition tests: #" << target.Size());
135 
136  // inner loop to grow by fast one-transition tests first
137  while(true) {
138  // allow for user interrupt
139  FD_WPC(target.size(),gg0.Size(),"IsStdSynthesisConsistent(): processing one-step tests");
140  // prepare to sense when stuck / break on success
141  Idx tszi=target.Size();
142  if(tszi==gg0.Size()) break;
143  // iterate over all relevant target states
144  StateSet::Iterator sit = rtarget.Begin();
145  StateSet::Iterator sit_end = rtarget.End();
146  while(sit != sit_end){
147  Idx x2=*sit;
148  FD_DF("IsStdSynthesisConsistent(..): testing predecessors of target " << gg0.StateName(x2));
149  // sense if all predecessors pass
150  bool allpass=true;
151  // iterate over all target predecessors
152  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(x2);
153  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(x2);
154  for(;rit != rit_end; ++rit){
155  // predecessor technically passes if allready identified as target
156  if(target.Exists(rit->X1)) continue;
157  // test A and B from [ref2]: passes if uncontrollable/unobservable event leads to the target
158  if(siguc.Exists(rit->Ev)) {
159  target.Insert(rit->X1);
160  rtarget.Insert(rit->X1);
161  FD_DF("IsStdSynthesisConsistent(..): one-transition test AB passed: " << gg0.StateName(rit->X1));
162  continue;
163  }
164  // test C adapted from [ref2]: predecessor passes if abstraction state is not marked and all successors lead to the target
165  if(!rPlant0Gen.ExistsMarkedState(gg0pmap.Arg2State(rit->X1))){
166  if(gg0.SuccessorStates(rit->X1) <= target) {
167  target.Insert(rit->X1);
168  rtarget.Insert(rit->X1);
169  FD_DF("IsStdSynthesisConsistent(..): one-transition test C passed: " << gg0.StateName(rit->X1));
170  continue;
171  }
172  }
173  // record the fail
174  allpass=false;
175  }
176  // safely increment sit
177  ++sit;
178  // if all predecessors passed we can restrict the backward search
179  if(allpass) {
180  FD_DF("IsStdSynthesisConsistent(..): all predecessors of " << gg0.StateName(x2) << " have passed");
181  rtarget.Erase(x2);
182  }
183  }
184  // break inner loop when stuck
185  if(target.Size()==tszi) break;
186  }
187 
188  // break on success
189  if(target.Size()==gg0.Size()) break;
190 
191  FD_DF("IsStdSynthesisConsistent(..): targets for multi-transition tests: #" << target.Size());
192  // iterate over all relevant target states to apply the multi-transition test until success
193  bool onepass=false;
194  StateSet::Iterator sit = rtarget.Begin();
195  StateSet::Iterator sit_end = rtarget.End();
196  for(; (!onepass) && (sit != sit_end); ++sit){
197  // iterate over all target predecessors
198  TTransSet<TransSort::X2EvX1>:: Iterator rit =revtransrel.BeginByX2(*sit);
199  TTransSet<TransSort::X2EvX1>:: Iterator rit_end=revtransrel.EndByX2(*sit);
200  for(; (!onepass) && (rit != rit_end); ++rit){
201  // cannot gain target if predecessor is already identified
202  if(target.Exists(rit->X1)) continue;
203  // allow for user interrupt
204  FD_WPC(target.size(),gg0.Size(),"IsStdSynthesisConsistent(): processing star-step tests");
205 
206  // prepare tests D/E from [ref2]/[ref1]
207 
208  // Ls: a) s
209  Generator Ls(trace);
210  Ls.SetMarkedState(rit->X1);
211  // Ls: b) s . sig ^ * --- all future of s
212  Ls.ClrTransitions(rit->X1);
213  SelfLoopMarkedStates(Ls,sig);
214  // Ls: c) s . sigstar * L --- all successfull future of s
215  LanguageIntersection(rPlantGen,Ls,Ls);
216  // Ms0: projection of Ls --- this is where test D/E may become expensive
217  Generator Ms0;
218  Project(Ls,sigo,Ms0);
219  // Es0: L0-Ms0 --- evil specification to prevent succesful future od s
220  Generator E0s;
221  LanguageDifference(rPlant0Gen,Ms0,E0s);
222  Trim(E0s);
223 
224  // apply test D: does reachability of X1 directly contradict with the evil specification
225  Generator Es=E0s;
226  InvProject(Es,sig);
227  PrefixClosure(Es);
228  if(EmptyLanguageIntersection(Ls,Es)) {
229  // pass: evil specification renders X1 unreachable
230  FD_DF("IsStdSynthesisConsistent(..): multi-transition test D passed: " << gg0.StateName(rit->X1));
231  onepass=true;
232  target.Insert(rit->X1);
233  rtarget.Insert(rit->X1);
234  continue;
235  }
236 
237  // apply test E: use supremal evil supervisor to test whether reachability of X1 complies with E0s
238  Generator K0s;
239  SupRelativelyPrefixClosed(rPlant0Gen,E0s,E0s);
240  SupConNB(rPlant0Gen,sigco,E0s,K0s);
241  // test whether X1 is reachable under evil supervision
242  Generator Ks=K0s;
243  InvProject(Ks,sig);
244  PrefixClosure(Ks);
245  Product(Ls,Ks,Ks);
246  if(EmptyLanguageIntersection(Ls,Ks)) {
247  // pass: evil supervisor renders X1 unreachable
248  FD_DF("IsStdSynthesisConsistent(..): multi-transition test E passed: " << gg0.StateName(rit->X1));
249  onepass=true;
250  target.Insert(rit->X1);
251  rtarget.Insert(rit->X1);
252  continue;
253  }
254 
255  } // iterate target predecessors
256  } // iterate targets
257  // break when not able to gain one more target
258  if(!onepass) break;
259 
260  // break outer loop on stuck
261  if(target.Size()==tsz) break;
262  if(target.Size()==gg0.Size()) break;
263  }
264 
265  // return success
266  return target.Size()==gg0.Size();
267 }
268 
269 
270 
271 
272 /*
273  ****
274  Convenience interfaces are provided below
275  ****
276  */
277 
278 
279 // IsStdSynthesisConsistent(rPlantGen, rCAlph, rPlant0Gen)
281  const Generator& rPlantGen,
282  const EventSet& rCAlph,
283  const Generator& rPlant0Gen) {
284 
285  // test if the plant is deterministic
286  if(!IsDeterministic(rPlantGen)){
287  std::stringstream errstr;
288  errstr << "Plant generator must be deterministic, " << "but is nondeterministic";
289  throw Exception("IsStdSynthesisConsistent", errstr.str(), 501);
290  }
291  // test if the Alphabet with the controllable events matches the Alphabet of the Generator
292  if(! (rCAlph <= rPlant0Gen.Alphabet()) ){
293  std::stringstream errstr;
294  errstr << "Controllable events must be a subset of the abstraction alphabet specified by \"" << rPlant0Gen.Name() << "\"";
295  throw Exception("IsStdSynthesisConsistent", errstr.str(), 506);
296  }
297  // test if the abstraction alphabet matches the plant alphabet
298  const EventSet& sigo = rPlant0Gen.Alphabet();
299  if(! (sigo <= rPlantGen.Alphabet() ) ){
300  std::stringstream errstr;
301  errstr << "Abstraction alphabet must be a subset of the plant alphabet pescified by \"" << rPlantGen.Name() << "\"";
302  throw Exception("IsStdSynthesisConsistent", errstr.str(), 506);
303  }
304 
305  // test the consistency of the plant
306  return IsStdSynthesisConsistentUnchecked(rPlantGen,rCAlph,rPlant0Gen);
307 }
308 
309 
310 // IsStdSynthesisConsistent(rPlantGen,rPlant0Gen)
312  const System& rPlantGen,
313  const Generator& rPlant0Gen
314 ){
315  // extract controllable events
316  const EventSet& calph = rPlantGen.ControllableEvents();
317  // pass on
318  return IsStdSynthesisConsistent(rPlantGen,calph,rPlant0Gen);
319 }
320 
321 
322 } // name space

libFAUDES 2.24g --- 2014.09.15 --- c++ api documentaion by doxygen