· 6 years ago · Jun 01, 2019, 02:44 AM
1# Léo Duret - May 2019
2# For AGFLOW
3
4
5class RuleSet(object):
6 """
7 Holds Dependencies and Conflict rules adding the abilitty
8 to check coherence of them.
9 """
10
11 def __init__(self):
12 # Will hold the element of the set and their matrix indexes.
13 self.elements = dict()
14
15 # Holding the conflicts and dependencies.
16 self.confs = list()
17 self.deps = list()
18
19 # Will be filled when matrix is created.
20 self.matrix = None
21
22 def _update_table(self, one):
23 # Adds new elements to table by checking if the key exists.
24 if one not in self.elements:
25 self.elements[one] = len(self.elements)
26
27 def addDep(self, one, other):
28 # Making sure the dependencies have a element in the set.
29 self._update_table(one)
30 self._update_table(other)
31
32 self.deps.append((one, other))
33
34 def addConflict(self, one, other):
35 # Making sure the conflicts have a element in the set.
36 self._update_table(one)
37 self._update_table(other)
38
39 self.confs.append((one, other))
40
41 def _add_deps(self):
42 # Adding the dependencies the the matrix by fetching the index
43 # and setting the coordinates to 1.
44 for one, other in self.deps:
45 y = self.elements.get(one)
46 x = self.elements.get(other)
47
48 # Coordinates should never be None but maintaining intergrity.
49 assert(x is not None and y is not None)
50 self.matrix[y][x] = 1
51
52 def _add_conflicts(self):
53 # Adding the conflicts to the matrix by fetching the index
54 # and setting the coordinates to 2.
55 for one, other in self.confs:
56 y = self.elements.get(one)
57 x = self.elements.get(other)
58
59 # Coordinates should never be None but maintaining intergrity.
60 assert(x is not None and y is not None)
61 self.matrix[y][x] = 2
62 self.matrix[x][y] = 2
63
64 def make_matrix(self):
65 # Create the matrix.
66
67 # Using double list comprehension to create identity matrix because
68 # values depend on themsleves.
69 self.matrix = [
70 [
71 0 if o != i else 1
72 for o in self.elements
73 ]
74 for i in self.elements
75 ]
76
77 # See fonction definition
78 self._add_deps()
79
80 # Extending dependencies such as A->B, B->C then A->C.
81 for y in range(len(self.matrix)):
82 for x in range(len(self.matrix[y])):
83 if self.matrix[y][x] == 1:
84 for i in range(len(self.matrix[x])):
85 if self.matrix[x][i] == 1:
86 self.matrix[y][i] = 1
87
88 def isCoherent(self):
89 # Checking coherence such that if A->B and B-/>A then it's false.
90
91 # Reset matrix.
92 self.make_matrix()
93
94 # Iterating conflicts
95 for one, other in self.confs:
96 y = self.elements.get(one)
97 x = self.elements.get(other)
98 assert(x is not None and y is not None)
99
100 # Checking if a conflict lands on a dependency -> False
101 if self.matrix[x][y] == 1 or self.matrix[y][x] == 1:
102 return False
103 else:
104 # Cant' have dependencies between elements in conflict
105 for line in self.matrix:
106 if line[x] == 1 and line[y] == 1:
107 return False
108 # Adding conflicts
109 self._add_conflicts()
110
111 # No coherence errors found
112 return True
113
114
115class Options(object):
116 """
117 Container for selected elements, used the RuleSet matrix to
118 determin dependencies and conflicts
119 """
120
121 def __init__(self, rs):
122 # Assuring the class the used in the right context.
123 assert(isinstance(rs, RuleSet))
124 self.rs = rs
125
126 # Selected elements set.
127 self.opts = set()
128
129 def _get_chain(self, arg):
130 # Get all inline dependencies.
131 ret_set = set()
132
133 index = self.rs.elements.get(arg)
134 line = self.rs.matrix[index]
135
136 for i in range(len(line)):
137 key = list(self.rs.elements.keys())[i]
138 if line[i] == 1 and key not in ret_set:
139 ret_set.add(key)
140
141 return ret_set
142
143 def toggle(self, arg):
144 # Keep the matrix up to date.
145 self.rs.make_matrix()
146
147 # Set of elements that will be removed.
148 rm = set()
149
150 # Set of dependencies of the argument.
151 toolchain = self._get_chain(arg)
152
153 # Case in which we need to unselect.
154 if arg in self.opts:
155 index = self.rs.elements.get(arg)
156 # Going through the column of the argument to remove
157 # every thing that depend on it.
158 for i in range(len(self.rs.matrix)):
159 key = list(self.rs.elements.keys())[i]
160 # if dependency in toolchain remove it
161 if self.rs.matrix[i][index] == 1 and key in toolchain:
162 rm.add(key)
163 self.opts -= rm
164
165 # Case in which we select and element.
166 else:
167 # Checking for conflict in the dependency chain.
168 for dep in toolchain:
169 for conf in self.rs.confs:
170 # If there is a conflict get the dependencies
171 # of the conflictuous argument and add them to remove set.
172 rest = set(conf) - set(dep)
173 other = rest.pop()
174 if other in self.opts:
175 rm = rm.union(self._get_chain(other))
176 self.opts -= rm
177
178 # add all the new items dependencies.
179 self.opts = self.opts.union(toolchain)
180
181 def selection(self):
182 # Returning all the selected options as a set of strings.
183 return set(str(key) for key in self.opts)