Showing
3 changed files
with
58 additions
and
31 deletions
.gitignore
0 → 100644
1 | +"""libDDD-based model-checker for (AR)CTL with(out) fairness | ||
2 | +""" | ||
3 | + | ||
1 | from ddd import ddd, sdd, shom | 4 | from ddd import ddd, sdd, shom |
2 | from tl import parse, Phi | 5 | from tl import parse, Phi |
3 | from functools import reduce, lru_cache | 6 | from functools import reduce, lru_cache |
... | @@ -12,17 +15,17 @@ def fixpoint(fonction, start): | ... | @@ -12,17 +15,17 @@ def fixpoint(fonction, start): |
12 | current = fonction(current) | 15 | current = fonction(current) |
13 | return current | 16 | return current |
14 | 17 | ||
15 | - | 18 | + |
16 | - | 19 | + |
17 | ####################### CTL ####################### | 20 | ####################### CTL ####################### |
18 | - | 21 | + |
19 | class CTL_model_checker(object): | 22 | class CTL_model_checker(object): |
20 | """ | 23 | """ |
21 | Object which can symbolically compute the subset of the universe where a given CTL formula is satisfied. | 24 | Object which can symbolically compute the subset of the universe where a given CTL formula is satisfied. |
22 | - | 25 | + |
23 | Attributes : | 26 | Attributes : |
24 | - variables : the list of the variables | 27 | - variables : the list of the variables |
25 | - | 28 | + |
26 | Methods : | 29 | Methods : |
27 | - check(formula) : returns the sdd representing the subset of the universe where the input formula is satisfied | 30 | - check(formula) : returns the sdd representing the subset of the universe where the input formula is satisfied |
28 | - atom(var) : returns the sdd representing the subset of the universe where var is True | 31 | - atom(var) : returns the sdd representing the subset of the universe where var is True |
... | @@ -38,12 +41,12 @@ class CTL_model_checker(object): | ... | @@ -38,12 +41,12 @@ class CTL_model_checker(object): |
38 | assert isinstance(succ, shom), "succ must be of type shom" | 41 | assert isinstance(succ, shom), "succ must be of type shom" |
39 | 42 | ||
40 | self.logic = "CTL" | 43 | self.logic = "CTL" |
41 | - | 44 | + |
42 | self.variables = next(iter(universe))[0].vars() # c'est tres sale mais je trouve pas d'autre solution pour l'instant | 45 | self.variables = next(iter(universe))[0].vars() # c'est tres sale mais je trouve pas d'autre solution pour l'instant |
43 | 46 | ||
44 | self.CTL_True = universe | 47 | self.CTL_True = universe |
45 | self.CTL_False = sdd.empty() | 48 | self.CTL_False = sdd.empty() |
46 | - self.neg = lambda phi : self.CTL_True - phi | 49 | + self.neg = lambda phi : self.CTL_True - phi |
47 | self.gfp = lambda fonction : fixpoint(fonction, self.CTL_True) | 50 | self.gfp = lambda fonction : fixpoint(fonction, self.CTL_True) |
48 | self.lfp = lambda fonction : fixpoint(fonction, self.CTL_False) | 51 | self.lfp = lambda fonction : fixpoint(fonction, self.CTL_False) |
49 | 52 | ||
... | @@ -53,7 +56,7 @@ class CTL_model_checker(object): | ... | @@ -53,7 +56,7 @@ class CTL_model_checker(object): |
53 | self.EG = lambda phi : self.gfp(lambda Z : phi & (self.EX(Z) | self.deadlock)) | 56 | self.EG = lambda phi : self.gfp(lambda Z : phi & (self.EX(Z) | self.deadlock)) |
54 | self.EU = lambda phi1, phi2 : self.lfp(lambda Z : phi2 | (phi1 & self.EX(Z))) | 57 | self.EU = lambda phi1, phi2 : self.lfp(lambda Z : phi2 | (phi1 & self.EX(Z))) |
55 | self.EW = lambda phi1, phi2 : self.gfp(lambda Z : phi2 | (phi1 & (self.EX(Z) | self.deadlock))) | 58 | self.EW = lambda phi1, phi2 : self.gfp(lambda Z : phi2 | (phi1 & (self.EX(Z) | self.deadlock))) |
56 | - self.ER = lambda phi1, phi2 : self.gfp(lambda Z : phi2 & (phi1 | self.EX(Z) | self.deadlock)) | 59 | + self.ER = lambda phi1, phi2 : self.gfp(lambda Z : phi2 & (phi1 | self.EX(Z) | self.deadlock)) |
57 | self.EM = lambda phi1, phi2 : self.lfp(lambda Z : phi2 & (phi1 | self.EX(Z))) | 60 | self.EM = lambda phi1, phi2 : self.lfp(lambda Z : phi2 & (phi1 | self.EX(Z))) |
58 | 61 | ||
59 | self.AX = lambda phi : self.EX(self.CTL_True) & self.neg(self.EX(self.neg(phi))) | 62 | self.AX = lambda phi : self.EX(self.CTL_True) & self.neg(self.EX(self.neg(phi))) |
... | @@ -72,7 +75,7 @@ class CTL_model_checker(object): | ... | @@ -72,7 +75,7 @@ class CTL_model_checker(object): |
72 | """ | 75 | """ |
73 | Input : | 76 | Input : |
74 | - var : string | 77 | - var : string |
75 | - | 78 | + |
76 | Output : | 79 | Output : |
77 | The sdd representing the subset of the universe where var is True. | 80 | The sdd representing the subset of the universe where var is True. |
78 | """ | 81 | """ |
... | @@ -85,7 +88,7 @@ class CTL_model_checker(object): | ... | @@ -85,7 +88,7 @@ class CTL_model_checker(object): |
85 | d = ddd.from_range(v, 0, 1, d) | 88 | d = ddd.from_range(v, 0, 1, d) |
86 | return sdd.mkz(d) & self.CTL_True | 89 | return sdd.mkz(d) & self.CTL_True |
87 | 90 | ||
88 | - | 91 | + |
89 | # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) | 92 | # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) |
90 | def _phi2sdd(self, phi): | 93 | def _phi2sdd(self, phi): |
91 | if phi.kind == 'bool': | 94 | if phi.kind == 'bool': |
... | @@ -113,12 +116,12 @@ class CTL_model_checker(object): | ... | @@ -113,12 +116,12 @@ class CTL_model_checker(object): |
113 | else: | 116 | else: |
114 | raise ValueError(repr(phi) + "is not a " + self.logic + " sub formula") | 117 | raise ValueError(repr(phi) + "is not a " + self.logic + " sub formula") |
115 | 118 | ||
116 | - | 119 | + |
117 | def check(self, formula): | 120 | def check(self, formula): |
118 | """ | 121 | """ |
119 | Input : | 122 | Input : |
120 | - formula : string or tl.Phi object | 123 | - formula : string or tl.Phi object |
121 | - | 124 | + |
122 | Output : | 125 | Output : |
123 | The sdd representing the subset of the universe where the formula is satisfied. | 126 | The sdd representing the subset of the universe where the formula is satisfied. |
124 | """ | 127 | """ |
... | @@ -130,7 +133,7 @@ class CTL_model_checker(object): | ... | @@ -130,7 +133,7 @@ class CTL_model_checker(object): |
130 | 133 | ||
131 | 134 | ||
132 | ####################### Fair CTL ####################### | 135 | ####################### Fair CTL ####################### |
133 | - | 136 | + |
134 | class FairCTL_model_checker(CTL_model_checker): | 137 | class FairCTL_model_checker(CTL_model_checker): |
135 | def __init__ (self, universe, succ, fairness): | 138 | def __init__ (self, universe, succ, fairness): |
136 | """ | 139 | """ |
... | @@ -158,10 +161,10 @@ class FairCTL_model_checker(CTL_model_checker): | ... | @@ -158,10 +161,10 @@ class FairCTL_model_checker(CTL_model_checker): |
158 | raise TypeError("Fairness constraints must be CTL formulae or SDD") | 161 | raise TypeError("Fairness constraints must be CTL formulae or SDD") |
159 | 162 | ||
160 | self.fairness = [fairness_preprocess(f) for f in fairness] | 163 | self.fairness = [fairness_preprocess(f) for f in fairness] |
161 | - | 164 | + |
162 | self.EG_fair = lambda phi : self.gfp(lambda Z : phi & (self.deadlock | self.EX(reduce(sdd.__and__ , [self.EU(Z, Z & f) for f in self.fairness], self.CTL_True)))) | 165 | self.EG_fair = lambda phi : self.gfp(lambda Z : phi & (self.deadlock | self.EX(reduce(sdd.__and__ , [self.EU(Z, Z & f) for f in self.fairness], self.CTL_True)))) |
163 | - CTL_model_checker.__init__(self, self.EG_fair(self.CTL_True), succ) | 166 | + CTL_model_checker.__init__(self, self.EG_fair(self.CTL_True), succ) |
164 | - | 167 | + |
165 | # EX, AX, EU, EF, EM do not need to be redefined once universe = EG_fair(True) | 168 | # EX, AX, EU, EF, EM do not need to be redefined once universe = EG_fair(True) |
166 | self.EX_fair = self.EX | 169 | self.EX_fair = self.EX |
167 | self.EF_fair = self.EF | 170 | self.EF_fair = self.EF |
... | @@ -179,7 +182,7 @@ class FairCTL_model_checker(CTL_model_checker): | ... | @@ -179,7 +182,7 @@ class FairCTL_model_checker(CTL_model_checker): |
179 | self.AW_fair = lambda phi1, phi2 : self.neg(self.EU_fair(self.neg(phi2), self.neg(phi1) & self.neg(phi2))) | 182 | self.AW_fair = lambda phi1, phi2 : self.neg(self.EU_fair(self.neg(phi2), self.neg(phi1) & self.neg(phi2))) |
180 | self.AR_fair = lambda phi1, phi2 : self.AW_fair(phi2, phi1 & phi2) | 183 | self.AR_fair = lambda phi1, phi2 : self.AW_fair(phi2, phi1 & phi2) |
181 | self.AM_fair = lambda phi1, phi2 : self.AU_fair(phi2, phi1 & phi2) | 184 | self.AM_fair = lambda phi1, phi2 : self.AU_fair(phi2, phi1 & phi2) |
182 | - | 185 | + |
183 | self.unarymod = {"EX" : self.EX_fair, "EF" : self.EF_fair, "EG" : self.EG_fair, "AX" : self.AX_fair, "AF" : self.AF_fair, "AG" : self.AG_fair} | 186 | self.unarymod = {"EX" : self.EX_fair, "EF" : self.EF_fair, "EG" : self.EG_fair, "AX" : self.AX_fair, "AF" : self.AF_fair, "AG" : self.AG_fair} |
184 | self.binarymod = {"EU" : self.EU_fair, "ER" : self.ER_fair, "EW" : self.EW_fair, "EM" : self.EM_fair, "AU" : self.AU_fair, "AR" : self.AR_fair, "AW" : self.AW_fair, "AM" : self.AM_fair} | 187 | self.binarymod = {"EU" : self.EU_fair, "ER" : self.ER_fair, "EW" : self.EW_fair, "EM" : self.EM_fair, "AU" : self.AU_fair, "AR" : self.AR_fair, "AW" : self.AW_fair, "AM" : self.AM_fair} |
185 | 188 | ||
... | @@ -187,22 +190,22 @@ class FairCTL_model_checker(CTL_model_checker): | ... | @@ -187,22 +190,22 @@ class FairCTL_model_checker(CTL_model_checker): |
187 | 190 | ||
188 | ####################### ARCTL ####################### | 191 | ####################### ARCTL ####################### |
189 | 192 | ||
190 | -import functools | 193 | +import functools |
191 | from ddd import shom | 194 | from ddd import shom |
192 | 195 | ||
193 | def build_labeled_succ(v): | 196 | def build_labeled_succ(v): |
194 | # v.model.spec.rules contains a list of Rule objects (ecco.rr.st.py) representing the rules | 197 | # v.model.spec.rules contains a list of Rule objects (ecco.rr.st.py) representing the rules |
195 | # v.g.m.transitions() returns a dict of {"R15" : shom} | 198 | # v.g.m.transitions() returns a dict of {"R15" : shom} |
196 | - | 199 | + |
197 | res = dict() | 200 | res = dict() |
198 | labels = list(set([label for r in v.model.spec.rules if r.label for label in r.label.split(",")]))# build a list of unique value of Rule labels | 201 | labels = list(set([label for r in v.model.spec.rules if r.label for label in r.label.split(",")]))# build a list of unique value of Rule labels |
199 | labels.sort() # sort this list for better display | 202 | labels.sort() # sort this list for better display |
200 | # add a special key "_None" in the dict associated to unlabeld | 203 | # add a special key "_None" in the dict associated to unlabeld |
201 | unlabeled_rules = [r.name() for r in v.model.spec.rules if not r.label] | 204 | unlabeled_rules = [r.name() for r in v.model.spec.rules if not r.label] |
202 | if unlabeled_rules: | 205 | if unlabeled_rules: |
203 | - assert "_None" not in labels, 'there is a rule labeled "_None" but it is a restricted label for rules without label' | 206 | + assert "_None" not in labels, 'there is a rule labeled "_None" but it is a restricted label for rules without label' |
204 | unlabeled_shoms = [v.g.m.transitions()[i] for i in unlabeled_rules] | 207 | unlabeled_shoms = [v.g.m.transitions()[i] for i in unlabeled_rules] |
205 | - # build the union of those shoms | 208 | + # build the union of those shoms |
206 | res["_None"] = functools.reduce(shom.__or__, unlabeled_shoms, shom.empty()) | 209 | res["_None"] = functools.reduce(shom.__or__, unlabeled_shoms, shom.empty()) |
207 | # rules with labels | 210 | # rules with labels |
208 | for label in labels: | 211 | for label in labels: |
... | @@ -229,7 +232,7 @@ class ARCTL_model_checker(CTL_model_checker): | ... | @@ -229,7 +232,7 @@ class ARCTL_model_checker(CTL_model_checker): |
229 | for e in succ_dict : # for each key of the dictionnary | 232 | for e in succ_dict : # for each key of the dictionnary |
230 | assert isinstance(e, str), "every key of succ_dict must be of type string" | 233 | assert isinstance(e, str), "every key of succ_dict must be of type string" |
231 | assert isinstance(succ_dict[e], shom), "every value of succ_dict must be of type shom" | 234 | assert isinstance(succ_dict[e], shom), "every value of succ_dict must be of type shom" |
232 | - | 235 | + |
233 | self.succ_dict = succ_dict | 236 | self.succ_dict = succ_dict |
234 | if tau_label in succ_dict: | 237 | if tau_label in succ_dict: |
235 | self.tau_label = tau_label | 238 | self.tau_label = tau_label |
... | @@ -238,7 +241,7 @@ class ARCTL_model_checker(CTL_model_checker): | ... | @@ -238,7 +241,7 @@ class ARCTL_model_checker(CTL_model_checker): |
238 | self.succ = functools.reduce(shom.__or__, self.succ_dict.values(), shom.empty()) | 241 | self.succ = functools.reduce(shom.__or__, self.succ_dict.values(), shom.empty()) |
239 | CTL_model_checker.__init__(self, universe, self.succ) | 242 | CTL_model_checker.__init__(self, universe, self.succ) |
240 | self.logic = "ARCTL" | 243 | self.logic = "ARCTL" |
241 | - | 244 | + |
242 | def build_succ_alpha(self, alpha): | 245 | def build_succ_alpha(self, alpha): |
243 | if alpha.kind == 'bool': | 246 | if alpha.kind == 'bool': |
244 | assert isinstance(alpha.value, bool), repr(alpha) + " is of kind bool but its value is not a boolean" | 247 | assert isinstance(alpha.value, bool), repr(alpha) + " is of kind bool but its value is not a boolean" |
... | @@ -256,12 +259,12 @@ class ARCTL_model_checker(CTL_model_checker): | ... | @@ -256,12 +259,12 @@ class ARCTL_model_checker(CTL_model_checker): |
256 | return reduce(shom.__or__, [self.build_succ_alpha(child) for child in alpha.children], shom.empty()) | 259 | return reduce(shom.__or__, [self.build_succ_alpha(child) for child in alpha.children], shom.empty()) |
257 | else: | 260 | else: |
258 | raise ValueError(repr(actions) + "is not an action sub formula") | 261 | raise ValueError(repr(actions) + "is not an action sub formula") |
259 | - | 262 | + |
260 | def check(self, formula): | 263 | def check(self, formula): |
261 | """ | 264 | """ |
262 | Input : | 265 | Input : |
263 | - formula : string or tl.Phi object | 266 | - formula : string or tl.Phi object |
264 | - | 267 | + |
265 | Output : | 268 | Output : |
266 | The sdd representing the subset of the universe where the formula is satisfied. | 269 | The sdd representing the subset of the universe where the formula is satisfied. |
267 | """ | 270 | """ |
... | @@ -269,7 +272,7 @@ class ARCTL_model_checker(CTL_model_checker): | ... | @@ -269,7 +272,7 @@ class ARCTL_model_checker(CTL_model_checker): |
269 | if isinstance(formula , str): | 272 | if isinstance(formula , str): |
270 | formula = parse(formula).arctl() | 273 | formula = parse(formula).arctl() |
271 | return self._phi2sdd(formula) | 274 | return self._phi2sdd(formula) |
272 | - | 275 | + |
273 | # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) | 276 | # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) |
274 | def _phi2sdd(self, phi): | 277 | def _phi2sdd(self, phi): |
275 | if phi.actions: | 278 | if phi.actions: |
... | @@ -283,15 +286,15 @@ class ARCTL_model_checker(CTL_model_checker): | ... | @@ -283,15 +286,15 @@ class ARCTL_model_checker(CTL_model_checker): |
283 | else: | 286 | else: |
284 | return CTL_model_checker._phi2sdd(self, phi) | 287 | return CTL_model_checker._phi2sdd(self, phi) |
285 | 288 | ||
286 | - | 289 | + |
287 | - | 290 | + |
288 | ####################### Fair ARCTL ####################### | 291 | ####################### Fair ARCTL ####################### |
289 | - | 292 | + |
290 | class FairARCTL_model_checker(ARCTL_model_checker, FairCTL_model_checker): | 293 | class FairARCTL_model_checker(ARCTL_model_checker, FairCTL_model_checker): |
291 | def __init__ (self, universe, succ_dict, fairness, true_label="_None"): | 294 | def __init__ (self, universe, succ_dict, fairness, true_label="_None"): |
292 | ARCTL_model_checker.__init__(self, universe, succ_dict, true_label) | 295 | ARCTL_model_checker.__init__(self, universe, succ_dict, true_label) |
293 | FairCTL_model_checker.__init__ (self, universe, self.succ, fairness) | 296 | FairCTL_model_checker.__init__ (self, universe, self.succ, fairness) |
294 | - | 297 | + |
295 | # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) | 298 | # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) |
296 | def _phi2sdd(self, phi): | 299 | def _phi2sdd(self, phi): |
297 | if phi.actions: | 300 | if phi.actions: |
... | @@ -303,4 +306,4 @@ class FairARCTL_model_checker(ARCTL_model_checker, FairCTL_model_checker): | ... | @@ -303,4 +306,4 @@ class FairARCTL_model_checker(ARCTL_model_checker, FairCTL_model_checker): |
303 | elif phi.kind in self.binarymod: | 306 | elif phi.kind in self.binarymod: |
304 | return FairCTL_model_checker(self.CTL_True, succ, self.fairness).binarymod[phi.kind](self._phi2sdd(phi.children[0]), self._phi2sdd(phi.children[1])) | 307 | return FairCTL_model_checker(self.CTL_True, succ, self.fairness).binarymod[phi.kind](self._phi2sdd(phi.children[0]), self._phi2sdd(phi.children[1])) |
305 | else: | 308 | else: |
306 | - return FairCTL_model_checker._phi2sdd(self, phi) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
309 | + return FairCTL_model_checker._phi2sdd(self, phi) | ... | ... |
setup.py
0 → 100644
1 | +from setuptools import setup, find_packages | ||
2 | +import pathlib, inspect | ||
3 | +import pymc | ||
4 | + | ||
5 | +long_description = pathlib.Path("README.md").read_text(encoding="utf-8") | ||
6 | +description = inspect.cleandoc(pymc.__doc__).splitlines()[0] | ||
7 | + | ||
8 | +setup(name="pymc", | ||
9 | + description=description, | ||
10 | + long_description=long_description, | ||
11 | + url="https://forge.ibisc.univ-evry.fr/cthomas/pyits_model_checker", | ||
12 | + author="Franck Pommereau", | ||
13 | + author_email="franck.pommereau@univ-evry.fr", | ||
14 | + classifiers=["Development Status :: 4 - Beta", | ||
15 | + "Intended Audience :: Developers", | ||
16 | + "Topic :: Scientific/Engineering", | ||
17 | + "Programming Language :: Python :: 3", | ||
18 | + "Operating System :: OS Independent"], | ||
19 | + packages=find_packages(where="."), | ||
20 | + python_requires=">=3.7", | ||
21 | +) |
-
Please register or login to post a comment