-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore.py
More file actions
297 lines (233 loc) · 8.64 KB
/
Copy pathcore.py
File metadata and controls
297 lines (233 loc) · 8.64 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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
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
from dataclasses import dataclass
from copy import deepcopy
frame_u = []
class Value:
def __init__(self, literal = None, type: str = None):
self.literal = literal if literal is not None else None
self.type = type if type is not None else ""
def matches(self, value: "Value"):
return self.get() == value.get()
def get(self):
return self.literal
def __str__(self):
return f"{'{'}{self.type} {self.get()}{'}'}"
def __repr__(self):
if isinstance(self.literal, str):
return f"Value('{self.literal}')"
return f"Value({self.literal})"
class Facet:
def __init__(self, name: str, value: Value):
self.name = name
self.value = value
def matches(self, facet: "Facet"):
return self.value.matches(facet.value) and self.name == facet.name
def __str__(self):
return f"[{self.name}: {self.value}]"
def __repr__(self):
return f"Facet('{self.name}', {self.value.__repr__()})"
@dataclass
class Rule:
trigger: "Frame"
effect: "Frame"
priority: int = 1
class Frame:
def __init__(self, name: str, *facets: "Facet"):
self.name = name
self.facets = list(facets)
global frame_u
frame_u.append(self)
def matches(self, frame: "Frame"):
"""
Checks if every facet matches it's corresponding facet in another frame
:param frame:
:return:
"""
print(f"match {self} -> {frame}")
for my_facet in self.facets:
for other_facet in frame.facets:
if other_facet.name == my_facet.name:
print(f"{my_facet} matches {other_facet}? ", end = "")
if not my_facet.matches(other_facet):
print("no")
return False
print("yes")
break # my_facet had a corresponding other_facet, go to next my_facet
else: # If my_facet didn't have a corresponding other_facet
print(f"{my_facet} had no matching facet")
return False
return True
def add(self, facet: Facet):
self.facets.append(facet)
def get_value(self, facet_name: str):
values = []
for facet in self.facets:
if facet.name == facet_name:
values.append(facet.value)
return values
def __str__(self):
return f"({self.name})"
def __repr__(self):
s = f"Frame('{self.name}'"
if self.facets:
s += f", {", ".join(facet.__repr__() for facet in self.facets)})"
else:
s += ")"
return s
def d(frame: Frame):
"""
Describe a frame
:param frame:
:return:
"""
print(frame)
for facet in frame.facets:
print(" ", end="")
print(facet)
def r(n):
print(n.__repr__())
return n.__repr__()
class SetValue(Value):
def __init__(self, *values: Value, type = "Set"):
super().__init__(values, type = type)
def __str__(self):
return f"{'{'}{self.type} {" ".join(str(value) for value in self.get())}{'}'}"
class UnionValue(SetValue):
def __init__(self, *values: Value):
super().__init__(*values, type = "Union")
def matches(self, value: Value):
for sub_value in self.get():
if sub_value.matches(value):
return True
return False
class IntersectionValue(SetValue):
def __init__(self, *values: Value):
super().__init__(*values, type = "Intersection")
def matches(self, value: Value):
for sub_value in self.get():
if not sub_value.matches(value):
return False
return True
class NotValue(Value):
def __init__(self, value: Value):
super().__init__(value, type = "Not")
def matches(self, value: Value):
return not self.literal.matches(value)
class AnyValue(Value):
def __init__(self):
super().__init__(type="Any")
def __str__(self):
return "{Any}"
def matches(self, value: "Value"):
return True
class StringValue(Value):
def __init__(self, literal, type="String"):
super().__init__(literal, type)
class NumValue(Value):
def __init__(self, literal, type = "num"):
super().__init__(literal, type)
class NumGreaterThanValue(NumValue):
def __init__(self, literal):
super().__init__(literal, "NumGreaterThan")
def matches(self, value: "NumValue"):
if not isinstance(value, NumValue):
raise ValueError("NumGreaterThanValue can only be applied to NumValue types")
return value.get() > self.get()
class NumLessThanValue(NumValue):
def __init__(self, literal):
super().__init__(literal, "NumLessThan")
def matches(self, value: "NumValue"):
if not isinstance(value, NumValue):
raise ValueError("NumGreaterThanValue can only be applied to NumValue types")
return value.get() < self.get()
class FrameValue(Value):
def __init__(self, literal: Frame, type = "Relation"):
super().__init__(literal, type)
def matches(self, value: "FrameValue"):
return self.get() is value.get()
class NumValueFacet(Facet):
def __init__(self, name, literal):
if not(isinstance(literal, float) or isinstance(literal, int)):
super().__init__(name, literal)
else:
super().__init__(name, NumValue(literal))
class StringValueFacet(Facet):
def __init__(self, name, literal):
if not(isinstance(literal, str)):
super().__init__(name, literal)
else:
super().__init__(name, StringValue(literal))
class FrameValueFacet(Facet):
def __init__(self, name, literal):
if not(isinstance(literal, Frame)):
super().__init__(name, literal)
else:
super().__init__(name, FrameValue(literal))
def find_matching_rules(frame: Frame, rules: list) -> list:
"""
Find all rules that have a trigger matching the given frame.
:param frame: The frame to match against rule triggers.
:param rules: A list of Rule objects.
:return: List of Rules whose triggers match the frame.
"""
matching_rules = []
for rule in rules:
if rule.trigger.matches(frame) and not rule.effect.matches(frame):
print(f"Frame {frame} matches trigger of Rule {rule}")
matching_rules.append(rule)
return matching_rules
def apply_effects(matching_rules: list, frame: Frame):
"""
Apply the effects of all matching rules to the given frame.
:param matching_rules: List of Rules whose effects will be applied.
:param frame: The frame to which the effects will be applied.
"""
for rule in matching_rules:
print(f"Applying effect from Rule {rule} to Frame {frame}")
for facet in rule.effect.facets:
if isinstance(facet, FrameValueFacet):
frame.add(Facet(name=facet.name, value=facet.value))
else:
frame.add(Facet(name=facet.name, value=deepcopy(facet.value)))
def infer(frame: Frame, rules: list):
"""
The main inference function.
:param frame: The initial frame to start with.
:param rules: A list of Rule objects.
"""
# Describe the initial frame
d(frame)
# Flag to indicate if any new effects were added in an iteration
new_effects_applied = True
while new_effects_applied:
new_effects_applied = False
# Find all matching rules for the current frame
matching_rules = find_matching_rules(frame, rules)
if not matching_rules:
print("No more matching rules found.")
break
# Apply effects of matching rules to the frame
apply_effects(matching_rules, frame)
# If any new effects were added, we need to check again for matches
if len(matching_rules) > 0:
new_effects_applied = True
# Describe the final frame after inference
print("\nFinal Frame:")
d(frame)
# Example usage
if __name__ == "__main__":
# Create frames
frame1 = Frame("Frame1", Facet("color", Value("red")), Facet("size", Value("large")))
frame2 = Frame("Frame2", Facet("color", Value("blue")), Facet("size", Value("medium")))
# Create rules
rule1 = Rule(
trigger=Frame("Trigger1", Facet("color", Value("red"))),
effect=Frame("Effect1", Facet("shape", Value("square")))
)
rule2 = Rule(
trigger=Frame("Trigger2", Facet("size", Value("large"))),
effect=Frame("Effect2", Facet("material", Value("wood")))
)
# List of rules
rules = [rule1, rule2]
# Run inference on frame1 with the given rules
infer(frame1, rules)