-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnode.py
263 lines (237 loc) · 8.39 KB
/
node.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
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
import error
T, I, F = TRUE, INDETERMINATE, FALSE = 1, 0, -1
def bool_to_tv(b):
return T if b else F
TV_TABLE = {I:'indeterminate', T:'true', F:'false'}
def tv_to_str(tv):
return TV_TABLE[tv]
class Node:
"""Represents a basic node in the knowledge graph"""
def __init__(self):
"""
Members:
skip: Ignore this node
"""
self.skip = False
class Atom(Node):
def __init__(self, name, graph, tv=INDETERMINATE):
super().__init__()
if not name.isalnum():
raise SyntaxError('Invalid atom name: {}'.format(name))
self.original_tv = tv if tv is not INDETERMINATE else None
self.tv = tv
self.tv_reason = None
self.name = name
self.graph = graph
self.inputs = []
self.outputs = []
def eval(self, child=None, verbose=False):
if self.tv != INDETERMINATE:
if verbose:
if self.tv_reason:
print("{} is {} because {}".format(self, tv_to_str(self.tv), self.tv_reason))
else:
print("{} is {}".format(self, tv_to_str(self.tv)))
return self.tv
all_indeterminate = True
for i in self.inputs:
if i.skip:
continue
tv = i.eval(self)
if tv != INDETERMINATE:
all_indeterminate = False
if tv == TRUE:
self.tv = TRUE
if verbose:
self.tv_reason = "{} is {}".format(i, tv_to_str(self.tv))
print("{} is {} because {}".format(
self, tv_to_str(self.tv), self.tv_reason))
return TRUE
if all_indeterminate:
self.tv = INDETERMINATE
else:
self.tv = FALSE
if verbose:
print("{} is {}".format(self, tv_to_str(self.tv)))
return self.tv
def __str__(self):
return self.name
class INot(Node):
def __init__(self, input_node):
super().__init__()
if isinstance(input_node, Atom):
input_node.outputs.append(self)
else:
input_node.output = self
self.input = input_node
self.output = None
def eval(self, child=None, verbose=False):
tv = self.input.eval(self)
if tv != INDETERMINATE:
out_tv = T if tv is F else F
if verbose:
print('{} is {} because {} is {}'.format(
self, tv_to_str(out_tv), self.input, tv_to_str(tv)))
return out_tv
if verbose:
print('{} is {} because {} is {}'.format(
self, tv_to_str(INDETERMINATE), self.input, tv_to_str(INDETERMINATE)))
return INDETERMINATE
def __str__(self):
return "not({})".format(str(self.input))
class BinaryInputNode(Node):
""" InputNode: has 2 inputs, one output """
def __init__(self, i1, i2, **kwargs):
super().__init__(**kwargs)
self.i1, self.i2 = i1, i2
self.output = None
class IAnd(BinaryInputNode):
def eval(self, child=None, verbose=False):
for i in (self.i1, self.i2):
if i.skip:
continue
tv = i.eval(self)
if tv != TRUE:
if verbose:
print('{} is {} because {} is {}'.format(
self, tv_to_str(FALSE), i, tv_to_str(tv)))
return tv
if verbose:
print('{} is {} because {} and {} are true'.format(
self, tv_to_str(TRUE), self.i1, self.i2))
return TRUE
def __str__(self):
return "({} and {})".format(str(self.i1), str(self.i2))
class IOr(BinaryInputNode):
def eval(self, child=None, verbose=False):
for i in (self.i1, self.i2):
if i.skip:
continue
tv = i.eval(self)
if tv == TRUE:
if verbose:
print('{} is {} because {} is {}'.format(
self, tv_to_str(TRUE), i, tv_to_str(TRUE)))
return TRUE
if verbose:
print('{} is {} because {} is {}'.format(
self, tv_to_str(FALSE), i, tv_to_str(tv)))
return FALSE
def __str__(self):
return "({} or {})".format(str(self.i1), str(self.i2))
class IXor(BinaryInputNode):
def eval(self, child=None, verbose=False):
tv1 = self.i1.eval() if not self.i1.skip else FALSE
tv2 = self.i2.eval() if not self.i2.skip else FALSE
if tv1 == INDETERMINATE or tv2 == INDETERMINATE:
if verbose:
print('{} is {} because {} is {}'.format(
self, tv_to_str(tv),
self.i1 if tv1 == INDETERMINATE else self.i2,
tv_to_str(tv)))
return INDETERMINATE
if tv1 == tv2:
if verbose:
print('{} is {} because {} and {} are {}'.format(
self, tv_to_str(FALSE), self.i1, self.i2, tv_to_str(tv1)))
return FALSE
if verbose:
print('{} is {} because {} is {} and {} is {}'.format(
self, tv_to_str(TRUE),
self.i1, tv_to_str(tv1),
self.i2, tv_to_str(tv1)))
return TRUE
def __str__(self):
return "({} xor {})".format(str(self.i1), str(self.i2))
class ONot(Node):
def __init__(self, output_node):
super().__init__()
if isinstance(output_node, Atom):
output_node.inputs.append(self)
else:
output_node.input = self
self.output = output_node
self.input = None
def eval(self, child=None, verbose=False):
tv = self.input.eval(self)
if tv == TRUE:
return FALSE
return tv
class BinaryOutputNode(Node):
"""OutputNode: has 2 outputs, one input"""
def __init__(self, o1, o2, **kwargs):
super().__init__(**kwargs)
self.o1, self.o2 = o1, o2
for o in (o1, o2):
if isinstance(o, Atom):
o.inputs.append(self)
else:
o.input = self
self.input = None
class OAnd(BinaryOutputNode):
def eval(self, child, verbose=False):
if self.input.skip:
return INDETERMINATE
return self.input.eval(self)
def __str__(self):
return '({} and {})'.format(self.o1, self.o2)
class OOr(BinaryOutputNode):
def eval(self, child, verbose=False):
if (child is not self.o1) and (child is not self.o2):
raise ValueError("Child {} is not an output node".format(child))
if child is None:
return INDETERMINATE
if self.input.skip:
return INDETERMINATE
tv = self.input.eval(self)
if tv != TRUE:
if isinstance(child, Atom):
child.tv = tv
return tv
other = self.o1 if child is self.o2 else self.o2
self.skip = True
other_tv = other.eval(None)#
self.skip = False
if other_tv == FALSE:
child.tv = TRUE
return TRUE
if isinstance(child, Atom):
child.tv = INDETERMINATE
return INDETERMINATE
def __str__(self):
return '({} or {})'.format(self.o1, self.o2)
class OXor(BinaryOutputNode):
def eval(self, child, verbose=False):
if (child is not self.o1) and (child is not self.o2):
raise ValueError("Child {} is not an output node".format(child))
if child is None:
return INDETERMINATE
tv = self.input.eval(self)
if tv == INDETERMINATE:
if isinstance(child, Atom):
child.tv = INDETERMINATE
return INDETERMINATE
other = self.o1 if child is self.o2 else self.o2
self.skip = True
other_tv = other.eval(None)
self.skip = False
if other_tv == INDETERMINATE:
if isinstance(child, Atom):
child.tv = INDETERMINATE
return FALSE
if tv == TRUE:
if other_tv == TRUE:
if isinstance(child, Atom):
child.tv = FALSE
return FALSE
child.tv = TRUE
return TRUE
if other_tv == TRUE:
if isinstance(child, Atom):
child.tv = TRUE
return TRUE
if isinstance(child, Atom):
child.tv = FALSE
return FALSE
def __str__(self):
return '({} or {})'.format(self.o1, self.o2)