-
Notifications
You must be signed in to change notification settings - Fork 0
/
closure_operators.py
185 lines (151 loc) · 5.58 KB
/
closure_operators.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
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
# -*- coding: utf-8 -*-
"""
Author: Gaurav Sahu, 12:00 16th January, 2018
Derivation and closure operators"""
import copy
def oprime(objects, context):
"""
Compute the set of all attributes shared by objects in context.
NB: objects must be of type set
"""
attributes = set(context.attributes[:])
for o in objects:
attributes &= context.objectsPrime({o})
return attributes
def aprime(attributes, context):
"""
Compute the set of all objects sharing attributes in context.
NB: attributes must be of type set
"""
objects = set(context.objects[:])
for a in attributes:
if isinstance(a, str):
objects &= context.attributesPrime({a})
else:
objects &= context.attributesPrime(a)
return objects
def oclosure(objects, context):
"""Return the closure of objects in context as a sorted list"""
return sorted(aprime(oprime(objects, context), context))
def aclosure(attributes, context):
"""Return the closure of attributes in context as a sorted list"""
return sorted(oprime(aprime(attributes, context), context))
def simple_closure(s, implications):
"""
Input: A set of implications and an attribute set s
Output: The closure of s with respect to implications
Examples
========
>>> from implications import Implication
>>> cd2a = Implication(set(('c', 'd')), set(('a')))
>>> ad2c = Implication(set(('a', 'd')), set(('c')))
>>> ab2cd = Implication(set(('a', 'b')), set(('c', 'd')))
>>> imps = [cd2a, ad2c, ab2cd]
>>> print simple_closure(set('a'), imps)
set(['a'])
>>> print simple_closure(set(), imps)
set([])
>>> simple_closure(set(['b', 'c', 'd']), imps) == set(['a', 'b', 'c', 'd'])
True
>>> a2bc = Implication(set(('a')), set(('b', 'c')))
>>> ce2abd = Implication(set(('c', 'e')), set(('a', 'b', 'd')))
>>> de2abc = Implication(set(('d', 'e')), set(('a', 'b', 'c')))
>>> cd2abe = Implication(set(('c', 'd')), set(('a', 'b', 'e')))
>>> imps = [a2bc, ce2abd, de2abc, cd2abe]
>>> simple_closure(set(['b', 'a']), imps) == set(['a', 'b', 'c'])
True
>>> simple_closure(set(['a', 'e']), imps) == set(['a', 'b', 'c', 'd', 'e'])
True
>>> imps = [ce2abd, a2bc, de2abc, cd2abe]
>>> simple_closure(set(['a', 'e']), imps) == set(['a', 'b', 'c', 'd', 'e'])
True
"""
unused_imps = implications[:]
new_closure = s.copy()
changed = True
while changed:
changed = False
for imp in unused_imps:
if imp.premise <= new_closure:
new_closure |= imp.conclusion
changed = True
unused_imps.remove(imp)
return new_closure
def lin_closure(s, implications):
"""
Input: A set of implications and an attribute set s
Output: The closure of s with respect to implications
NB: This implementation is not linear-time.
Use lists instead of dicts to get a liner-time implementation.
Examples
========
>>> from implications import Implication
>>> cd2a = Implication(set(('c', 'd')), set(('a')))
>>> ad2c = Implication(set(('a', 'd')), set(('c')))
>>> ab2cd = Implication(set(('a', 'b')), set(('c', 'd')))
>>> imps = [cd2a, ad2c, ab2cd]
>>> print lin_closure(set('a'), imps)
set(['a'])
>>> print lin_closure(set(), imps)
set([])
>>> lin_closure(set(['b', 'c', 'd']), imps) == set(['a', 'b', 'c', 'd'])
True
>>> a2bc = Implication(set(('a')), set(('b', 'c')))
>>> ce2abd = Implication(set(('c', 'e')), set(('a', 'b', 'd')))
>>> de2abc = Implication(set(('d', 'e')), set(('a', 'b', 'c')))
>>> cd2abe = Implication(set(('c', 'd')), set(('a', 'b', 'e')))
>>> imps = [a2bc, ce2abd, de2abc, cd2abe]
>>> lin_closure(set(['b', 'a']), imps) == set(['a', 'b', 'c'])
True
>>> lin_closure(set(['a', 'e']), imps) == set(['a', 'b', 'c', 'd', 'e'])
True
>>> imps = [ce2abd, a2bc, de2abc, cd2abe]
>>> lin_closure(set(['a', 'e']), imps) == set(['a', 'b', 'c', 'd', 'e'])
True
"""
new_closure = s.copy()
count, imps = {}, {}
for imp in implications:
count[imp] = len(imp.premise)
if count[imp] == 0:
new_closure |= imp.conclusion
for a in imp.premise:
imps.setdefault(a, [])
imps[a].append(imp)
update = list(new_closure)
while update:
m = update[-1]
del update[-1]
imps.setdefault(m, [])
for imp in imps[m]:
count[imp] -= 1
if count[imp] == 0:
add = imp.conclusion - new_closure
new_closure |= add
update.extend(add)
return new_closure
def closure(current, base_set, implications, prefLen):
"""
return the closure of attributes
NB: current and base_set must be of type set
"""
unused_imps = copy.copy(implications)
old_closure = copy.copy(base_set)
new_closure = copy.copy(current)
while (old_closure != new_closure):
old_closure = copy.copy(new_closure)
delete_list = []
for imp in unused_imps:
if imp.get_premise() <= new_closure:
new_closure |= imp.get_conclusion()
for x in base_set[:prefLen]:
if (((x in new_closure) and not (x in current)) or
(not (x in new_closure) and (x in current))):
return False, set()
delete_list.append(imp)
for imp in delete_list:
unused_imps.remove(imp)
return True, new_closure
if __name__ == "__main__":
import doctest
doctest.testmod()