event_lattice.py
5.24 KB
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import math
from functools import cache
from itertools import accumulate
import operator
def uniform_op(x):
n = len(list(x))
return 1.0 if n == 0 else 1.0/n
def max_op(x):
return max(x)
def min_op(x):
return min(x)
def sum_op(x):
return sum(x)
def stableprod_op(x):
log_x = map(math.log, x)
return math.exp(sum(log_x))
def prod_op(x):
return list(accumulate(x, func=lambda a,b: a*b))[-1]
class Event:
""""Events."""
@staticmethod
def parse(text):
return frozenset(text)
@staticmethod
def from_str(text):
return Event(Event.parse(text))
def __init__(self, literals):
"""Instantiate from a (frozen) set of literals.
For example: e = Event(frozenset("abc"))."""
self._literals = literals
def literals(self):
return self._literals
def __iter__(self):
return self._literals.__iter__()
def co(self):
"""Negation is case based: A = not a; a = not A."""
return Event(x.swapcase() for x in self._literals)
@cache
def is_consistent(self):
return all(x.swapcase() not in self._literals for x in self._literals)
def __hash__(self) -> int:
return self._literals.__hash__()
def __eq__(self, other):
return self._literals.__eq__(other._literals)
def __repr__(self) -> str:
return ''.join(str(x) for x in sorted(self._literals))
def invert(self):
return self.co()
def __eq__(self, other):
return self._literals.__eq__(other._literals)
def __or__(self, other):
return Event(self._literals | other._literals)
def __le__(self, other):
return self._literals.__le__(other._literals)
def __lt__(self, other):
return self._literals.__lt__(other._literals)
def __ne__(self, other):
return self._literals.__ne__(other._literals)
def __ge__(self, other):
return self._literals.__ge__(other._literals)
def __gt__(self, other):
return self._literals.__gt__(other._literals)
class Lattice:
@staticmethod
def parse(d):
result = dict()
for k, v in d.items():
key = Event.from_str(k)
result[key] = v
return result
@staticmethod
def close_literals(smodels):
base_lits = list(accumulate(smodels, func=operator.or_))[-1]
lits = set()
for x in base_lits.literals():
lits.add(x)
lits.add(x.swapcase())
return lits
def __init__(self, smodels_dict):
"""Create base for Events Lattice."""
self._smodels = smodels_dict
self._literals = Lattice.close_literals(self._smodels.keys())
def literals(self):
return self._literals
def stable_models(self):
return list(map(set, self._smodels.keys()))
@cache
def lower_bound(self, event):
return list(set(filter(lambda sm: sm <= event, self._smodels)))
@cache
def upper_bound(self, event):
return list(set(filter(lambda sm: event <= sm, self._smodels)))
def event_class(self, event):
return EventsClass(
self.upper_bound(event),
self.lower_bound(event),
self)
def related(self, u, v):
u_consistent = u.is_consistent()
v_consistent = v.is_consistent()
if u_consistent and (u_consistent == v_consistent):
return \
self.lower_bound(u) == self.lower_bound(v) and \
self.upper_bound(u) == self.upper_bound(v)
else:
return u_consistent == v_consistent
def factors(self, event):
return [self.lower_bound(event), self.upper_bound(event)]
def propagated_value(self, event:Event,
lower_op=sum_op,
upper_op=prod_op):
value = 0
if not event.is_consistent():
return value
lb = self.lower_bound(event)
len_lb = len(lb)
if len_lb > 1:
value = lower_op(map(lambda sm: self._smodels[sm], lb))
elif len_lb == 1:
value = self._smodels[lb[0]]
else:
ub = self.upper_bound(event)
len_ub = len(ub)
if len_ub > 1:
value = upper_op(map(lambda sm: self._smodels[sm], ub))
elif len_ub == 1:
value = self._smodels[ub[0]]
return value
def __repr__(self):
smodels_repr = ',\n\t\t'.join(f"{Event.from_str(k)}: {v:>5}" for k,v in self._smodels.items())
lits_repr = ','.join(sorted(self._literals))
return "{\n" +\
f"\t'stable_models': {{\n\t\t {smodels_repr} \n\t}}\n" +\
f"\t'literals': {{ {lits_repr} }} \n" +\
"}"
class EventsClass:
def __init__(self, upper, lower, lattice:Lattice):
self._upper = upper
self._lower = lower
self._lattice = lattice
def __repr__(self):
upper_repr = "" if len(self._upper) == 0 else ",".join(str(x) for x in self._upper)
lower_repr = "" if len(self._lower) == 0 else ",".join(str(x) for x in self._lower)
return f"<{upper_repr}|{lower_repr}>"
def __contains__(self, event:Event):
return self.lattice.lower_bound(event) == self._lower and \
self.lattice.upper_bound(event) == self._upper