Showing
1 changed file
with
165 additions
and
0 deletions
pymc/__init__.py
0 → 100644
1 | +from ddd import ddd, sdd, shom | ||
2 | +from tl import parse, Phi | ||
3 | +from functools import reduce, lru_cache | ||
4 | + | ||
5 | +def fixpoint(fonction, start): | ||
6 | + current = start | ||
7 | + previous = None | ||
8 | + while previous != current: | ||
9 | + previous = current | ||
10 | + current = fonction(current) | ||
11 | + return current | ||
12 | + | ||
13 | + | ||
14 | + | ||
15 | +####################### CTL ####################### | ||
16 | + | ||
17 | +class CTL_model_checker(object): | ||
18 | + def __init__ (self, universe, pred): | ||
19 | + """ | ||
20 | + Input : | ||
21 | + - universe is a sdd representing the state space (for example v.g.reachable) | ||
22 | + - pred is a shom representing the inverse of succ (for exemple v.g.m.pred()) | ||
23 | + """ | ||
24 | + # TODO : mettre en cache les output de phi2sdd ? | ||
25 | + assert isinstance(universe, sdd), "universe must be of type sdd" | ||
26 | + assert isinstance(pred, shom), "pred must be of type shom" | ||
27 | + | ||
28 | + self.variables = next(iter(universe))[0].vars() # c'est tres sale mais je trouve pas d'autre solution pour l'instant | ||
29 | + | ||
30 | + self.CTL_True = universe | ||
31 | + self.CTL_False = sdd.empty() | ||
32 | + self.neg = lambda phi : self.CTL_True - phi | ||
33 | + self.gfp = lambda fonction : fixpoint(fonction, self.CTL_True) | ||
34 | + self.lfp = lambda fonction : fixpoint(fonction, self.CTL_False) | ||
35 | + | ||
36 | + self.EX = pred & universe | ||
37 | + self.EF = lambda phi : self.lfp(lambda Z : phi | self.EX(Z)) | ||
38 | + self.EG = lambda phi : self.gfp(lambda Z : phi & self.EX(Z)) | ||
39 | + self.EU = lambda phi1, phi2 : self.lfp(lambda Z : phi2 | (phi1 & self.EX(Z))) | ||
40 | + self.ER = lambda phi1, phi2 : self.gfp(lambda Z : phi2 & (phi1 | self.EX(Z))) | ||
41 | + self.EW = lambda phi1, phi2 : self.gfp(lambda Z : phi2 | (phi1 & self.EX(Z))) | ||
42 | + | ||
43 | + # definition of universal operators in term of fix points | ||
44 | + self.AX = lambda phi : self.EX(phi) - self.EX(neg(phi)) | ||
45 | + self.AF = lambda phi : self.lfp(lambda Z : phi | self.AX(Z)) | ||
46 | + self.AG = lambda phi : self.gfp(lambda Z : phi & self.AX(Z)) | ||
47 | + self.AU = lambda phi1, phi2 : self.lfp(lambda Z : phi2 | (phi1 & self.AX(Z))) | ||
48 | + self.AR = lambda phi1, phi2 : self.gfp(lambda Z : phi2 & (phi1 | self.AX(Z))) | ||
49 | + self.AW = lambda phi1, phi2 : self.gfp(lambda Z : phi2 | (phi1 & self.AX(Z))) | ||
50 | + | ||
51 | + # redefinition of universal operators in term of negation of existential ones | ||
52 | + self.AX = lambda phi : self.neg(self.EX(self.neg(phi))) # /!\ if there is no path from s, then s |= AX(phi) because there is no path that falsifies phi | ||
53 | + self.AF = lambda phi : self.neg(self.EG(self.neg(phi))) | ||
54 | + self.AG = lambda phi : self.neg(self.EF(self.neg(phi))) | ||
55 | + self.AU = lambda phi1, phi2 : self.neg(self.EU(self.neg(phi2),self.neg(phi1) & self.neg(phi2)) | self.EG(self.neg(phi2))) | ||
56 | + | ||
57 | + self.unarymod = {"EX" : self.EX, "EF" : self.EF, "EG" : self.EG, "AX" : self.AX, "AF" : self.AF, "AG" : self.AG} | ||
58 | + self.binarymod = {"EU" : self.EU, "ER" : self.ER, "EW" : self.EW, "AU" : self.AU, "AR" : self.AR, "AW" : self.AW} | ||
59 | + | ||
60 | + @lru_cache(maxsize=None) # si la version de python etait 3.9 on pourrait utiliser functools.cache directement | ||
61 | + def atom(self, var): | ||
62 | + """ | ||
63 | + | ||
64 | + """ | ||
65 | + assert var in self.variables, var + " is not a variable" | ||
66 | + d = ddd.one() | ||
67 | + for v in reversed(self.variables): | ||
68 | + if v == var : | ||
69 | + d = ddd.from_range(var, 1, 1, d) | ||
70 | + else : | ||
71 | + d = ddd.from_range(v, 0, 1, d) | ||
72 | + return sdd.mkz(d) & self.CTL_True | ||
73 | + | ||
74 | + | ||
75 | + # recursive function that builds the sdd along the syntax tree (bottom-up, going-up from the tree leaves) | ||
76 | + def _phi2sdd(self, phi): | ||
77 | + if phi.kind == 'bool': | ||
78 | + assert isinstance(phi.value, bool), repr(phi) + " is of kind bool but its value is not a boolean" | ||
79 | + if phi.value: | ||
80 | + return self.CTL_True | ||
81 | + else: | ||
82 | + return self.CTL_False | ||
83 | + elif phi.kind == 'name': | ||
84 | + return self.atom(phi.value) | ||
85 | + elif phi.kind == 'not': | ||
86 | + return self.neg(self._phi2sdd(phi.children[0])) | ||
87 | + elif phi.kind =='and': | ||
88 | + return reduce(sdd.__and__, [self._phi2sdd(child) for child in phi.children], self.CTL_True) | ||
89 | + elif phi.kind =='or': | ||
90 | + return reduce(sdd.__or__, [self._phi2sdd(child) for child in phi.children], self.CTL_False) | ||
91 | + elif phi.kind in self.unarymod: | ||
92 | + return self.unarymod[phi.kind](self._phi2sdd(phi.children[0])) | ||
93 | + elif phi.kind in self.binarymod: | ||
94 | + return self.binarymod[phi.kind](self._phi2sdd(phi.children[0]), self._phi2sdd(phi.children[1])) | ||
95 | + else: | ||
96 | + raise ValueError(repr(phi) + "is not a CTL sub formula") | ||
97 | + | ||
98 | + | ||
99 | + def check(self, formula): | ||
100 | + assert isinstance(formula , str) or isinstance(formula , Phi), "The formula given in input must be a string or a parsed formula" | ||
101 | + if isinstance(formula , str): | ||
102 | + formula = parse(formula).ctl() | ||
103 | + return self._phi2sdd(formula) | ||
104 | + | ||
105 | + | ||
106 | + | ||
107 | +####################### Fair CTL ####################### | ||
108 | + | ||
109 | +class FairCTL_model_checker(CTL_model_checker): | ||
110 | + def __init__ (self, universe, pred, fairness): | ||
111 | + """ | ||
112 | + Input : | ||
113 | + - universe is a sdd representing the state space (for example v.g.reachable) | ||
114 | + - pred is a shom representing the inverse of succ (for exemple v.g.m.pred()) | ||
115 | + - fairness is a list of CTL formulae (or sdd) to be used as fairness constraints (the fair trajectory must satisfy and[GF f for f in fairness]) | ||
116 | + """ | ||
117 | + super().__init__(universe, pred) | ||
118 | + if isinstance(fairness, list): | ||
119 | + pass | ||
120 | + elif isinstance(fairness, str) or isinstance(fairness, sdd) : | ||
121 | + fairness = [fairness] | ||
122 | + else: | ||
123 | + raise TypeError("fairness must be a list or a string expressing a CTL formula") | ||
124 | + if fairness == []: | ||
125 | + print("The list of fairness constraints is empty, you should used the CTL_model_checker instead") | ||
126 | + | ||
127 | + def fairness_preprocess(f): | ||
128 | + if isinstance(f, str): | ||
129 | + return CTL_model_checker(universe, pred).check(f) | ||
130 | + elif isinstance(f, sdd): | ||
131 | + return f | ||
132 | + else: | ||
133 | + raise TypeError("Fairness constraints must be CTL formulae or SDD") | ||
134 | + | ||
135 | + self.fairness = [fairness_preprocess(f) for f in fairness] | ||
136 | + | ||
137 | + # see Symbolic Model Checking, McMillan 1993, section 6.4 or Symbolic model checking: 1020 states and beyond, Burch et al 1992, section 6.2 | ||
138 | + self.EG_fair = lambda phi : self.gfp(lambda Z : phi & self.EX(reduce(sdd.__and__ , [self.EU(Z, Z & f) for f in self.fairness], self.CTL_True))) | ||
139 | + self.EG_fair_True = self.EG_fair(self.CTL_True) | ||
140 | + self.EX_fair = lambda phi : self.EX(phi & self.EG_fair_True) | ||
141 | + self.EF_fair = lambda phi : self.EF(phi & self.EG_fair_True) | ||
142 | + self.EU_fair = lambda phi1, phi2 : self.EU(phi1, phi2 & self.EG_fair_True) | ||
143 | + | ||
144 | + # definition of universal operators as negation of existential ones (not sure of myself here) | ||
145 | + self.AX_fair = lambda phi : self.neg(self.EX_fair(self.neg(phi))) | ||
146 | + self.AF_fair = lambda phi : self.neg(self.EG_fair(self.neg(phi))) | ||
147 | + self.AG_fair = lambda phi : self.neg(self.EF_fair(self.neg(phi))) | ||
148 | + self.AU_fair = lambda phi1, phi2 : self.neg(self.EU_fair(self.neg(phi2), self.neg(phi1) & self.neg(phi2)) | self.EG_fair(self.neg(phi2))) | ||
149 | + | ||
150 | + 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} | ||
151 | + self.binarymod = {"EU" : self.EU_fair, "AU" : self.AU_fair}#, "ER" : ER, "EW" : EW, "AR" : AR, "AW" : AW} | ||
152 | + | ||
153 | + | ||
154 | + | ||
155 | +####################### ACTL ####################### | ||
156 | + | ||
157 | +class ACTL_model_checker(CTL_model_checker): | ||
158 | + pass | ||
159 | + | ||
160 | + | ||
161 | + | ||
162 | +####################### ARCTL ####################### | ||
163 | + | ||
164 | +class ARCTL_model_checker(CTL_model_checker): | ||
165 | + pass |
-
Please register or login to post a comment