Blame view

code/drafts/event_lattice.py 7.99 KB
aa257377   Francisco Coelho   Post meeting 2022...
1
2
import math
from functools import cache
d080b400   Francisco Coelho   Paper draft: Exte...
3
from itertools import accumulate, combinations, chain, groupby
aa257377   Francisco Coelho   Post meeting 2022...
4
5
6
import operator


677cc471   Francisco Coelho   Further progress ...
7
    
aa257377   Francisco Coelho   Post meeting 2022...
8
9
def uniform_op(x):
    n = len(list(x))
677cc471   Francisco Coelho   Further progress ...
10
    return 1.0 if n == 0 else 1.0/n
aa257377   Francisco Coelho   Post meeting 2022...
11
12
13
14
15
16
17
18
19
20
21
22
23
24


def max_op(x):
    return max(x)


def min_op(x):
    return min(x)


def sum_op(x):
    return sum(x)


677cc471   Francisco Coelho   Further progress ...
25
def stableprod_op(x):
aa257377   Francisco Coelho   Post meeting 2022...
26
27
28
29
    log_x = map(math.log, x)
    return math.exp(sum(log_x))


677cc471   Francisco Coelho   Further progress ...
30
31
32
33
34
def prod_op(x):
    return list(accumulate(x, func=lambda a,b: a*b))[-1]


class Event:
d080b400   Francisco Coelho   Paper draft: Exte...
35
36
37
38
39
40
41
    """Events.
     
    An event is a set of literals - atoms and negated atoms.

    The convention is that atoms are represented by lower case single letters
    and a negated atom by upper case single letters.
    """
677cc471   Francisco Coelho   Further progress ...
42
43

    @staticmethod
d080b400   Francisco Coelho   Paper draft: Exte...
44
    def _parse(text):
677cc471   Francisco Coelho   Further progress ...
45
46
47
        return frozenset(text)

    @staticmethod
d080b400   Francisco Coelho   Paper draft: Exte...
48
49
50
51
52
53
    def parse(text):
        """Convert a string to an event.

        Each letter in the string represents a literal.
        """
        return Event(Event._parse(text))
677cc471   Francisco Coelho   Further progress ...
54
55
56
57
58


    def __init__(self, literals):
        """Instantiate from a (frozen) set of literals.
        For example: e = Event(frozenset("abc"))."""
d080b400   Francisco Coelho   Paper draft: Exte...
59
        self._literals = frozenset(literals)
677cc471   Francisco Coelho   Further progress ...
60
61
62
63
64
65
66
67


    def literals(self):
        return self._literals


    def __iter__(self):
        return self._literals.__iter__()
677cc471   Francisco Coelho   Further progress ...
68
69
70
    
    @cache
    def is_consistent(self):
d080b400   Francisco Coelho   Paper draft: Exte...
71
        """True if this event is consistent."""
677cc471   Francisco Coelho   Further progress ...
72
73
        return all(x.swapcase() not in self._literals for x in self._literals)

677cc471   Francisco Coelho   Further progress ...
74

d080b400   Francisco Coelho   Paper draft: Exte...
75
76
77
78
79
    def co(self):
        """Negation of this event.
        
        Negation is case based: A = not a; a = not A."""
        return Event(x.swapcase() for x in self._literals)
677cc471   Francisco Coelho   Further progress ...
80

d080b400   Francisco Coelho   Paper draft: Exte...
81
82
    def invert(self):
        """Negation of this event.
677cc471   Francisco Coelho   Further progress ...
83

d080b400   Francisco Coelho   Paper draft: Exte...
84
85
86
        See the method "co"
        """
        return self.co()
677cc471   Francisco Coelho   Further progress ...
87
88

    def __repr__(self) -> str:
d080b400   Francisco Coelho   Paper draft: Exte...
89
        return ''.join(str(x) for x in sorted(self._literals)) if len(self._literals) > 0 else '0'
aa257377   Francisco Coelho   Post meeting 2022...
90

d080b400   Francisco Coelho   Paper draft: Exte...
91
92
93
94
95
96
97
98
99
100
101
102
103
    def latex(self):
        """LaTeX representation of this even.
        
        Negation is represented by overline and the empty event by 

        """
        return ''.join(
            (str(x) if x.islower() else f"\co{{{x.lower()}}}") \
                for x in sorted(self._literals)
            ) if len(self._literals) > 0 else "\set{}"

    def __hash__(self) -> int:
        return self._literals.__hash__()
aa257377   Francisco Coelho   Post meeting 2022...
104

677cc471   Francisco Coelho   Further progress ...
105
106

    def __eq__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
107
        """Event equality test."""
677cc471   Francisco Coelho   Further progress ...
108
109
110
        return self._literals.__eq__(other._literals)

    def __or__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
111
        """Event union operation."""
677cc471   Francisco Coelho   Further progress ...
112
113
        return Event(self._literals | other._literals)

677cc471   Francisco Coelho   Further progress ...
114
    def __le__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
115
        """Event subset test."""
677cc471   Francisco Coelho   Further progress ...
116
117
118
119
        return self._literals.__le__(other._literals)


    def __lt__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
120
        """Event strict subset test."""
677cc471   Francisco Coelho   Further progress ...
121
122
123
124
        return self._literals.__lt__(other._literals)


    def __ne__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
125
        """Event not-equal test."""
677cc471   Francisco Coelho   Further progress ...
126
127
128
129
        return self._literals.__ne__(other._literals)


    def __ge__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
130
        """Event superset test."""
677cc471   Francisco Coelho   Further progress ...
131
132
133
134
        return self._literals.__ge__(other._literals)


    def __gt__(self, other):
d080b400   Francisco Coelho   Paper draft: Exte...
135
        """Event strict superset test."""
677cc471   Francisco Coelho   Further progress ...
136
137
138
139
140
        return self._literals.__gt__(other._literals)


class Lattice:

677cc471   Francisco Coelho   Further progress ...
141
142
    @staticmethod
    def parse(d):
d080b400   Francisco Coelho   Paper draft: Exte...
143
144
145
146
147
148
149
150
151
152
153
154
155
        """Input stable models.
        
        The input format is a dictionary associating a stable model in string form to an weight.
        
        For example:
        
        input_dict = {
            "A": 0.3,
            "ab": 0.2,
            "ac": 0.5
        }
        smodels = Lattice.parse(input_dict)
        """
aa257377   Francisco Coelho   Post meeting 2022...
156
157
        result = dict()
        for k, v in d.items():
d080b400   Francisco Coelho   Paper draft: Exte...
158
            key = Event.parse(k)
aa257377   Francisco Coelho   Post meeting 2022...
159
160
            result[key] = v
        return result
aa257377   Francisco Coelho   Post meeting 2022...
161
162


aa257377   Francisco Coelho   Post meeting 2022...
163
    @staticmethod
d080b400   Francisco Coelho   Paper draft: Exte...
164
165
166
167
168
    def close_literals(events):
        """Closed set of literals entailed by a set of events.
        
        Includes the literals in the set of events and any missing negation."""
        base_lits = list(accumulate(events, func=operator.or_))[-1]
677cc471   Francisco Coelho   Further progress ...
169
170
        lits = set()
        for x in base_lits.literals():
aa257377   Francisco Coelho   Post meeting 2022...
171
172
            lits.add(x)
            lits.add(x.swapcase())
d080b400   Francisco Coelho   Paper draft: Exte...
173
        return sorted(lits)
aa257377   Francisco Coelho   Post meeting 2022...
174

677cc471   Francisco Coelho   Further progress ...
175
    def __init__(self, smodels_dict):
d080b400   Francisco Coelho   Paper draft: Exte...
176
        """Create an Events lattice."""
677cc471   Francisco Coelho   Further progress ...
177
178
        self._smodels = smodels_dict
        self._literals = Lattice.close_literals(self._smodels.keys())
aa257377   Francisco Coelho   Post meeting 2022...
179
180

    def literals(self):
d080b400   Francisco Coelho   Paper draft: Exte...
181
        """The literals in this lattice."""
aa257377   Francisco Coelho   Post meeting 2022...
182
183
        return self._literals

d080b400   Francisco Coelho   Paper draft: Exte...
184
    @cache
aa257377   Francisco Coelho   Post meeting 2022...
185
    def stable_models(self):
d080b400   Francisco Coelho   Paper draft: Exte...
186
187
        """The stable models that generate this lattice."""
        return self._smodels.keys()
aa257377   Francisco Coelho   Post meeting 2022...
188

d080b400   Francisco Coelho   Paper draft: Exte...
189
190
191
192
    #@cache
    def events(self):
        """All the events of this lattice."""
        return chain.from_iterable(map(Event, combinations(self._literals, r)) for r in range(len(self._literals)+1))
aa257377   Francisco Coelho   Post meeting 2022...
193
194

    @cache
d080b400   Francisco Coelho   Paper draft: Exte...
195
196
197
198
199
200
201
202
    def stable_core(self, event):
        """The stable core of an event in this lattice."""
        return set(filter(lambda sm: sm <= event or event <= sm, self.stable_models()))

    # @cache
    # def event_class(self, event):
    #     """The equivalence class of an event."""
    #     return EventsClass(self.stable_core(event), self)
677cc471   Francisco Coelho   Further progress ...
203

d080b400   Francisco Coelho   Paper draft: Exte...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    @cache
    def classes(self):
        """The classes of this lattice.
        
        Each class is presented as a key:value pair where the "key" is the stable core of the elements in "value"."""
        map_ev_classes = [(e, tuple(self.stable_core(e))) for e in self.events() if e.is_consistent()]
        groups = dict()
        for e,c in map_ev_classes:
            if c in groups.keys():
                groups[c].add(e)
            else:
                groups[c] = set([e])
        inconsistent = list(e for e in self.events() if not e.is_consistent())
        inconsistent_repr = inconsistent[0]
        groups[(inconsistent_repr,)] = set(inconsistent)
        return groups
677cc471   Francisco Coelho   Further progress ...
220

aa257377   Francisco Coelho   Post meeting 2022...
221
222

    def related(self, u, v):
d080b400   Francisco Coelho   Paper draft: Exte...
223
        """Tests if two events are related."""
677cc471   Francisco Coelho   Further progress ...
224
225
        u_consistent = u.is_consistent()
        v_consistent = v.is_consistent()
aa257377   Francisco Coelho   Post meeting 2022...
226
        if u_consistent and (u_consistent == v_consistent):
d080b400   Francisco Coelho   Paper draft: Exte...
227
            return self.stable_core(u) == self.stable_core(v)
aa257377   Francisco Coelho   Post meeting 2022...
228
229
230
        else:
            return u_consistent == v_consistent

d080b400   Francisco Coelho   Paper draft: Exte...
231
232
233
    def extended_value(self, event:Event, 
            op=prod_op):
        """TODO: well..."""
677cc471   Francisco Coelho   Further progress ...
234
        value = 0
d080b400   Francisco Coelho   Paper draft: Exte...
235
236
237
        #
        #   INCONSISTENT EVENTS
        #
677cc471   Francisco Coelho   Further progress ...
238
239
        if not event.is_consistent():
            return value
d080b400   Francisco Coelho   Paper draft: Exte...
240
241
242
243
244
245
246
247
248
249
        #
        #   CONSISTENT EVENTS
        #
        score = self.stable_core(event)
        len_score = len(score)
        #   CONSISTENT, INDEPENDENT
        if len_score == 0:
            value = 0
        elif len_score == 1:
            value = self._smodels[score[0]]
aa257377   Francisco Coelho   Post meeting 2022...
250
        else:
d080b400   Francisco Coelho   Paper draft: Exte...
251
            value = op(map(lambda sm: self._smodels[sm], score))
aa257377   Francisco Coelho   Post meeting 2022...
252
253
254

        return value

677cc471   Francisco Coelho   Further progress ...
255
    def __repr__(self):
d080b400   Francisco Coelho   Paper draft: Exte...
256
        smodels_repr = ',\n\t\t'.join(f"{k}: {v:<}" for k,v in self._smodels.items())
677cc471   Francisco Coelho   Further progress ...
257
        lits_repr = ','.join(sorted(self._literals))
aa257377   Francisco Coelho   Post meeting 2022...
258

677cc471   Francisco Coelho   Further progress ...
259
260
261
262
        return "{\n" +\
            f"\t'stable_models': {{\n\t\t {smodels_repr} \n\t}}\n" +\
            f"\t'literals': {{ {lits_repr} }} \n" +\
            "}"
aa257377   Francisco Coelho   Post meeting 2022...
263

d080b400   Francisco Coelho   Paper draft: Exte...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# class EventsClass:
#     def __init__(self, core, lattice:Lattice):
#         self._core = core
#         self._lattice = lattice

#     def __repr__(self):
#         core_repr = "" if len(self._core) == 0 else ",".join(str(x) for x in self._core)
#         return f"<{core_repr}>"

#     def __contains__(self, event:Event):
#         return self.lattice.stable_core(event) == self._core

if __name__ == "__main__":
    def zoom_event(event_str, lattice):
        event = Event.parse(event_str)
        event_class = lattice.event_class(event)
        propagated = lattice.extended_value(
            event)

        print(
            f"Event: {event}\n\tClass: {event_class} \n\tValue: {propagated}")

    smodels = Lattice.parse({
        "A": 2,
        "ab": 3,
        "ac": 5
    })

    lattice = Lattice(smodels)

    ev_classes = lattice.classes()
    for k,g in ev_classes.items():
        print(f"{tuple(s.latex() for s in k)} {set(e.latex() for e in g)}")
    # zoom_event("abc", lattice)
    # zoom_event("a", lattice)
    # zoom_event("b", lattice)
    # zoom_event("bc", lattice)
    # zoom_event("ac", lattice)