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
26namespace 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;
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 SupRelativelyClosed(rPlant0Gen,E0s,E0s);
342 SupCon(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)
#define FD_DF(message)
const std::string & Name(void) const
bool Exists(const Idx &rIndex) const
Iterator BeginByX2(Idx x2) const
Iterator EndByX2(Idx x2) const
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
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
Iterator End(void) const
Iterator Begin(void) const
virtual bool Erase(const T &rElem)
Idx Size(void) const
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 SupCon(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rSpecGen, Generator &rResGen)
void SupRelativelyClosed(const Generator &rPlantGen, const Generator &rSpecGen, Generator &rResGen)
bool IsStdSynthesisConsistent(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rPlant0Gen)
uint32_t Idx
bool IsStdSynthesisConsistentUnchecked(const Generator &rPlantGen, const EventSet &rCAlph, const Generator &rPlant0Gen)
Definition syn_sscon.cpp:60

libFAUDES 2.33k --- 2025.09.16 --- c++ api documentaion by doxygen