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)
Application callback: optional write progress report to console or application, incl count
Faudes exception class.
Set of indices.
Definition: cfl_indexset.h:78
Idx Insert(void)
Insert new index to set.
Set of indices with symbolic names.
Definition: cfl_nameset.h:69
bool Exists(const Idx &rIndex) const
Test existence of index.
Rti-wrapper for composition maps.
Definition: cfl_parallel.h:43
Idx Arg2State(Idx s12) const
Set of Transitions.
Definition: cfl_transset.h:242
Iterator BeginByX2(Idx x2) const
Iterator to first Transition specified by successor state x2.
Iterator EndByX2(Idx x2) const
Iterator to first Transition after specified successor state x2.
TBaseSet< Transition, TransSort::X1EvX2 >::Iterator Iterator
Iterator on transition.
Definition: cfl_transset.h:269
void ReSort(TTransSet< OtherCmp > &res) const
Get copy of trantision relation sorted by other compare operator, e.g.
Generator with controllability attributes.
EventSet ControllableEvents(void) const
Get EventSet with controllable events.
Base class of all FAUDES generators.
const TransSet & TransRel(void) const
Return reference to transition relation.
bool SetTransition(Idx x1, Idx ev, Idx x2)
Add a transition to generator by indices.
const StateSet & MarkedStates(void) const
Return const ref of marked states.
const EventSet & Alphabet(void) const
Return const reference to alphabet.
EventSet ActiveEventSet(Idx x1) const
Return active event set at state x1.
TransSet::Iterator TransRelBegin(void) const
Iterator to Begin() of transition relation.
void ClrTransitions(Idx x1, Idx ev)
Remove a transitions by state and event.
void InsEvents(const EventSet &events)
Add new named events to generator.
void InjectMarkedStates(const StateSet &rNewMarkedStates)
Replace mMarkedStates with StateSet given as parameter without consistency checks.
bool ExistsState(Idx index) const
Test existence of state in state set.
StateSet::Iterator MarkedStatesBegin(void) const
Iterator to Begin() of mMarkedStates.
std::string StateName(Idx index) const
State name lookup.
void Name(const std::string &rName)
Set the generator's name.
TransSet::Iterator TransRelEnd(void) const
Iterator to End() of transition relation.
Idx SuccessorState(Idx x1, Idx ev) const
Return the successor state of state x1 with event ev.
StateSet::Iterator MarkedStatesEnd(void) const
Iterator to End() of mMarkedStates.
Idx InsState(void)
Add new anonymous state to generator.
void SetMarkedState(Idx index)
Set an existing state as marked state by index.
Idx InitState(void) const
Return initial state.
Idx InsInitState(void)
Create new anonymous state and set as initial state.
Idx Size(void) const
Get generator size (number of states)
bool ExistsMarkedState(Idx index) const
Test existence of state in mMarkedStates.
bool Exists(const T &rElem) const
Test existence of element.
Definition: cfl_baseset.h:2115
Iterator End(void) const
Iterator to the end of set.
Definition: cfl_baseset.h:1896
Iterator Begin(void) const
Iterator to the begin of set.
Definition: cfl_baseset.h:1891
virtual bool Erase(const T &rElem)
Erase element by reference.
Definition: cfl_baseset.h:2019
Idx Size(void) const
Get Size of TBaseSet.
Definition: cfl_baseset.h:1819
bool EmptyLanguageIntersection(const Generator &rGen1, const Generator &rGen2)
Test for empty language intersection (same as Disjoind()).
void Trim(vGenerator &rGen)
RTI wrapper function.
void PrefixClosure(Generator &rGen)
Prefix Closure.
void LanguageDifference(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
Language difference (set-theoretic difference).
void LanguageIntersection(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
Language intersection.
void Project(const Generator &rGen, const EventSet &rProjectAlphabet, Generator &rResGen)
Deterministic projection.
void Parallel(const Generator &rGen1, const Generator &rGen2, Generator &rResGen)
Parallel composition.
void InvProject(Generator &rGen, const EventSet &rProjectAlphabet)
Inverse projection.
void SelfLoopMarkedStates(Generator &rGen, const EventSet &rAlphabet)
Self-loop all marked states.
bool IsDeterministic(const vGenerator &rGen)
RTI wrapper function.
void SupConNB(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, Generator &rResGen)
Nonblocking Supremal Controllable Sublanguage.
Definition: syn_supcon.cpp:757
bool IsStdSynthesisConsistent(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rPlant0Gen)
Test consistency of an abstractions w.r.t.
Definition: syn_sscon.cpp:388
void SupRelativelyPrefixClosed(const Generator &rPlantGen, const Generator &rSpecGen, Generator &rResGen)
Supremal Relatively Closed Sublanguage.
libFAUDES resides within the namespace faudes.
uint32_t Idx
Type definition for index type (allways 32bit)
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
Standard syntheis consistency test.

libFAUDES 2.32b --- 2024.03.01 --- c++ api documentaion by doxygen