-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBESSolver.py
152 lines (111 loc) · 5.34 KB
/
BESSolver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import copy
import os
from FormulaReader import FormulaNode, OperatorNode
class BooleanEquation:
def __init__(self, sign, lhs, rhs):
self.sign = sign
self.lhs = lhs
self.rhs = simplify(rhs)
def __repr__(self):
return self.sign + ' ' + self.lhs + ' = ' + str(self.rhs)
class BooleanEquationSystem:
def __init__(self, equations):
self.equations = equations
def __repr__(self):
return '\n'.join(str(e) for e in self.equations)
model = None
printInfo = False
# creates the right-hand side of a boolean equation
def RHS(state, formula):
# in case operator is VAL, we just return itself
newFormula = formula
# in case variable or fixpoint, its just a variable
if formula.op.type in ["VAR", "LEASTFP", "GREATESTFP"]:
newVar = [formula.op.var + str(state)]
newFormula = FormulaNode([OperatorNode(newVar, "VAR")])
# in case binary (OR, AND, PRODUCT, COPRODUCT, TCOSUM, TSUM) use corresponding boolean operator (OR, AND)
elif formula.type == "BINARY":
op = "OR" if formula.op.type in ["OR", "COPRODUCT", "TSUM"] else "AND"
subformulas = [RHS(state, formula.subformulas[i]) for i in [0, 1]]
newFormula = FormulaNode([[subformulas[0], OperatorNode([], op), subformulas[1]]])
# in case diamond or box, create formula with subformula for each outgoing transition with given action
elif formula.op.type in ["DIAMOND", "BOX"]:
subformulas = [RHS(t.enddist.keys()[0], formula.subformulas[0]) for t in model.outgoing(state, formula.op.action)]
# in case no transitions, return 0 if diamond, else 1
if not subformulas:
return FormulaNode([OperatorNode(["0"] if formula.op.type == "DIAMOND" else ["1"], "VAL")])
# in case one transition, return that RHS
elif len(subformulas) == 1:
return subformulas[0]
# in case more than one transition, create disjunction if diamond, else conjunction (from left to right)
else:
op = "OR" if formula.op.type == "DIAMOND" else "AND"
newFormula = subformulas[0]
for i in range(1, len(subformulas)):
newFormula = FormulaNode([[newFormula, OperatorNode([], op), subformulas[i]]])
return newFormula
def createBES(formula):
fixpoints = formula.getSubFormulas(["LEASTFP", "GREATESTFP"])
equations = []
for fixf in fixpoints:
sign = "mu" if fixf.op.type == "LEASTFP" else "nu"
for state in range(0, model.numstates):
equations += [BooleanEquation(sign, fixf.op.var + str(state), RHS(state, fixf.subformulas[0]))]
return BooleanEquationSystem(equations)
# applies boolean simplifications
def simplify(formula):
for i in range(0, len(formula.subformulas)):
formula.subformulas[i] = simplify(formula.subformulas[i])
if formula.type is "BINARY":
subfVars = [subf.op.var for subf in formula.subformulas if subf.op.type == "VAR"]
subfIsFalse = [subf.op.type == "VAL" and subf.op.val == 0 for subf in formula.subformulas]
subfIsTrue = [subf.op.type == "VAL" and subf.op.val == 1 for subf in formula.subformulas]
# idempotence
if len(subfVars) == 2 and subfVars[0] == subfVars[1]:
return formula.subformulas[0]
# zero and unit element for AND
if formula.op.type in ["AND", "PRODUCT", "TCOSUM"]:
if any(subfIsFalse):
return FormulaNode([OperatorNode(["0"], "VAL")])
elif any(subfIsTrue):
return formula.subformulas[0] if subfIsTrue[1] else formula.subformulas[1]
# zero and unit element for OR
else:
if any(subfIsTrue):
return FormulaNode([OperatorNode(["1"], "VAL")])
elif any(subfIsFalse):
return formula.subformulas[0] if subfIsFalse[1] else formula.subformulas[1]
return formula
# substitutes a formula 'new' for a variable 'var'
def substituteVar(formula, var, new):
# print("substituting " + str(var) + " for " + str(new) + " in " + str(formula))
if formula.op.type == "VAR" and formula.op.var == var:
# not using copy is not wrong, but it is ugly
# without it this will also substitute in equations below sometimes due to same reference
return copy.deepcopy(new)
else:
for i in range(0, len(formula.subformulas)):
formula.subformulas[i] = substituteVar(formula.subformulas[i], var, new)
return formula
# solves BES using Gauss elimination
def solveBES(bes):
for i in reversed(range(0, len(bes.equations))):
equation = bes.equations[i]
var = equation.lhs
equation.rhs = substituteVar(equation.rhs, var, FormulaNode([OperatorNode(["0"] if equation.sign == "mu" else ["1"], "VAL")]))
equation.rhs = simplify(equation.rhs)
for j in reversed(range(0, i)):
bes.equations[j].rhs = substituteVar(bes.equations[j].rhs, var, equation.rhs)
if printInfo:
print(str(bes) + '\n')
return bool(bes.equations[0].rhs.op.val)
def initBESSolver(ts, formula, store, verbose):
global model, printInfo
model = ts
printInfo = verbose
bes = createBES(formula)
if store:
f = open(os.path.sep.join([os.path.split(model.file)[0], model.name + "_" + formula.name + "_BES.bes"]), 'w')
f.write(str(bes))
f.close()
return solveBES(bes)