Skip to content

Commit b92c33d

Browse files
EzetowersEzequiel Torres
authored andcommitted
Added draw method to LispTreeExpr class
* Create a graph with pygraphviz and networkx tools
1 parent ff0a723 commit b92c33d

File tree

3 files changed

+117
-58
lines changed

3 files changed

+117
-58
lines changed

MLC/Common/LispTreeExpr/LispTreeExpr.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@
2020
# along with this program. If not, see <http://www.gnu.org/licenses/>
2121

2222
import MLC.Log.log as lg
23+
import networkx as nx
24+
import matplotlib.pyplot as plt
2325
import re
26+
2427
from MLC.mlc_parameters.mlc_parameters import Config
2528
from MLC.Common.Operations import Operations
2629
from MLC.Common.LispTreeExpr.TreeNodes import LeafNode, InternalNode
2730
from MLC.Common.LispTreeExpr.OperationNodes import OpNodeFactory
31+
from PyQt5.QtCore import Qt
2832

2933

3034
class ExprException(Exception):
@@ -75,10 +79,18 @@ def __init__(self, expression):
7579

7680

7781
class LispTreeExpr(object):
82+
class NodeIdGenerator(object):
83+
def __init__(self):
84+
self._node_id_counter = 0
85+
86+
def next_node_id(self):
87+
node_id = self._node_id_counter
88+
self._node_id_counter += 1
89+
return node_id
7890

7991
def __init__(self, expr):
92+
self._node_id_generator = LispTreeExpr.NodeIdGenerator()
8093
self._nodes = []
81-
8294
self._expanded_tree = expr
8395

8496
# Remove the root part of the node
@@ -193,6 +205,28 @@ def simplify_tree(self):
193205
self._simplified_tree = '(root ' + self._root.to_string() + ')'
194206
lg.logger_.debug("[LISP_TREE_EXPR] Simplified Expression: " + self._simplified_tree)
195207

208+
def draw(self):
209+
tree = nx.DiGraph()
210+
self._root.construct_tree(tree)
211+
212+
fig = plt.figure()
213+
# Put figure window on top of all other windows
214+
fig.canvas.manager.window.setWindowModality(Qt.ApplicationModal)
215+
fig.canvas.manager.window.setWindowTitle("Individual Tree Representation")
216+
ax = fig.add_axes([0, 0, 1, 1])
217+
ax.axis('off')
218+
219+
labels = {}
220+
for node_id in tree:
221+
labels[node_id] = tree.node[node_id]['value']
222+
pos = nx.nx_pydot.graphviz_layout(tree, prog='dot')
223+
224+
nodes = nx.draw_networkx_nodes(tree, pos, node_size=1000, node_color='#D3D3D3')
225+
nodes.set_edgecolor('k')
226+
nx.draw_networkx_edges(tree, pos, arrows=False)
227+
nx.draw_networkx_labels(tree, pos, labels, font_size=12)
228+
plt.show()
229+
196230
def calculate_expression(self, sensor_replacement_list):
197231
class Replace_Sensors_Visitor(TreeVisitor):
198232

@@ -291,23 +325,27 @@ def _generate_leaf_node(self, expr, parent_depth, expr_index):
291325
else:
292326
param_len = len(expr)
293327

294-
leaf = LeafNode(expr[:param_len])
328+
leaf = LeafNode(self._node_id_generator.next_node_id(), expr[:param_len])
295329
leaf.set_depth(parent_depth)
296330
leaf.set_expr_index(expr_index)
297331
leaf.set_subtreedepth(0)
298332
self._nodes.append(leaf)
299333
return leaf, param_len + 1
300334

301335
# As a precondition, the expression must be well-formed
302-
def _generate_node(self, expr, is_root_expression=False, parent_depth=0, expr_index=0):
336+
def _generate_node(self,
337+
expr,
338+
is_root_expression=False,
339+
parent_depth=0,
340+
expr_index=0):
303341
if expr[0] != '(':
304342
return self._generate_leaf_node(expr, parent_depth, expr_index)
305343

306344
# We are in the presence of an internal node. Get the operation
307345
op = self._get_operation(expr, is_root_expression)
308346

309347
# Generate the arguments of the internal node as Child Nodes
310-
node = OpNodeFactory.make(op["op"])
348+
node = OpNodeFactory.make(op["op"], self._node_id_generator.next_node_id())
311349
node.set_depth(parent_depth + 1)
312350
node.set_expr_index(expr_index)
313351
expr_offset = 0
@@ -320,11 +358,13 @@ def _generate_node(self, expr, is_root_expression=False, parent_depth=0, expr_in
320358
next_arg_pos = 1 + len(op["op"]) + 1 + expr_offset
321359

322360
if expr[next_arg_pos] == '(':
323-
child_node, offset = self._generate_node(
324-
expr[next_arg_pos:], parent_depth=parent_depth + 1, expr_index=expr_index + next_arg_pos)
361+
child_node, offset = self._generate_node(expr[next_arg_pos:],
362+
parent_depth=parent_depth + 1,
363+
expr_index=expr_index + next_arg_pos)
325364
else:
326-
child_node, offset = self._generate_leaf_node(
327-
expr[next_arg_pos:], parent_depth=parent_depth + 1, expr_index=expr_index + next_arg_pos)
365+
child_node, offset = self._generate_leaf_node(expr[next_arg_pos:],
366+
parent_depth=parent_depth + 1,
367+
expr_index=expr_index + next_arg_pos)
328368

329369
node.add_child(child_node)
330370
child_subtreedepth = max(child_subtreedepth, child_node.get_subtreedepth())

MLC/Common/LispTreeExpr/OperationNodes.py

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,23 @@ def execute_op_without_warnings(op, log_prefix, exception_msg, arg1, arg2=None):
5353

5454
class PlusNode(InternalNode):
5555

56-
def __init__(self):
57-
InternalNode.__init__(self, "+", 1)
56+
def __init__(self, node_id):
57+
InternalNode.__init__(self, node_id, "+", 1)
5858

5959
def formal(self):
6060
return "(" + self._nodes[0].formal() + " + " + self._nodes[1].formal() + ")"
6161

6262
def op_simplify(self):
6363
# If one of the arguments is zero, avoid the operation
6464
if self._node_arg_x_is_y(0, 0):
65-
return LeafNode(self._nodes[1].to_string())
65+
return LeafNode(self._node_id, self._nodes[1].to_string())
6666
elif self._node_arg_x_is_y(1, 0):
67-
return LeafNode(self._nodes[0].to_string())
67+
return LeafNode(self._node_id, self._nodes[0].to_string())
6868

6969
# Non of the arguments are zero. Make the operation if they are not sensors
7070
if not self._nodes[0].is_sensor() and not self._nodes[1].is_sensor():
7171
arg = float(self._nodes[0].to_string()) + float(self._nodes[1].to_string())
72-
return LeafNode(process_float(arg))
72+
return LeafNode(self._node_id, process_float(arg))
7373
else:
7474
return self
7575

@@ -86,25 +86,25 @@ def op_compute(self, arg_list):
8686

8787
class MinusNode(InternalNode):
8888

89-
def __init__(self):
90-
InternalNode.__init__(self, "-", 1)
89+
def __init__(self, node_id):
90+
InternalNode.__init__(self, node_id, "-", 1)
9191

9292
def formal(self):
9393
return "(" + self._nodes[0].formal() + " - " + self._nodes[1].formal() + ")"
9494

9595
def op_simplify(self):
9696
# if both arguments are equals, return 0
9797
if self._nodes[0].to_string() == self._nodes[1].to_string():
98-
return LeafNode(process_float(0))
98+
return LeafNode(self._node_id, process_float(0))
9999

100100
# If the second argument is zero, avoid the operation.
101101
if self._node_arg_x_is_y(1, 0):
102-
return LeafNode(self._nodes[0].to_string())
102+
return LeafNode(self._node_id, self._nodes[0].to_string())
103103

104104
# Non of the arguments are zero. Make the operation if they are not sensors
105105
if not self._nodes[0].is_sensor() and not self._nodes[1].is_sensor():
106106
arg = float(self._nodes[0].to_string()) - float(self._nodes[1].to_string())
107-
return LeafNode(process_float(arg))
107+
return LeafNode(self._node_id, process_float(arg))
108108
else:
109109
return self
110110

@@ -121,26 +121,26 @@ def op_compute(self, arg_list):
121121

122122
class MultNode(InternalNode):
123123

124-
def __init__(self):
125-
InternalNode.__init__(self, "*", 1)
124+
def __init__(self, node_id):
125+
InternalNode.__init__(self, node_id, "*", 1)
126126

127127
def formal(self):
128128
return "(" + self._nodes[0].formal() + " .* " + self._nodes[1].formal() + ")"
129129

130130
def op_simplify(self):
131131
# If one or both of the arguments are zero, return zero
132132
if self._node_arg_x_is_y(0, 0) or self._node_arg_x_is_y(1, 0):
133-
return LeafNode(process_float(0))
133+
return LeafNode(self._node_id, process_float(0))
134134

135135
# If one of the arguments is zero, avoid the operation
136136
if self._node_arg_x_is_y(0, 1):
137-
return LeafNode(self._nodes[1].to_string())
137+
return LeafNode(self._node_id, self._nodes[1].to_string())
138138
elif self._node_arg_x_is_y(1, 1):
139-
return LeafNode(self._nodes[0].to_string())
139+
return LeafNode(self._node_id, self._nodes[0].to_string())
140140

141141
if not self._nodes[0].is_sensor() and not self._nodes[1].is_sensor():
142142
arg = float(self._nodes[0].to_string()) * float(self._nodes[1].to_string())
143-
return LeafNode(process_float(arg))
143+
return LeafNode(self._node_id, process_float(arg))
144144
else:
145145
return self
146146

@@ -159,8 +159,8 @@ class DivisionNode(InternalNode):
159159
PROTECTION = 0.001
160160
SIMPLIFY_PROTECTION = 0.01
161161

162-
def __init__(self):
163-
InternalNode.__init__(self, "/", 1)
162+
def __init__(self, node_id):
163+
InternalNode.__init__(self, node_id, "/", 1)
164164

165165
def formal(self):
166166
return "(my_div(" + self._nodes[0].formal() + "," + self._nodes[1].formal() + "))"
@@ -178,19 +178,19 @@ def _process_division(self, dividend, divisor):
178178
def op_simplify(self):
179179
# If the first argument is zero, return zero
180180
if self._node_arg_x_is_y(0, 0):
181-
return LeafNode(process_float(0))
181+
return LeafNode(self._node_id, process_float(0))
182182

183183
# If the second argument is one, return the first argument
184184
if self._node_arg_x_is_y(1, 1):
185-
return LeafNode(self._nodes[0].to_string())
185+
return LeafNode(self._node_id, self._nodes[0].to_string())
186186

187187
if not self._nodes[0].is_sensor() and not self._nodes[1].is_sensor():
188188
# FIXME: Harcoded number. Change it
189189
if abs(float(self._nodes[1].to_string())) < DivisionNode.SIMPLIFY_PROTECTION:
190-
return LeafNode(process_float(0))
190+
return LeafNode(self._node_id, process_float(0))
191191
else:
192192
arg = float(self._nodes[0].to_string()) / float(self._nodes[1].to_string())
193-
return LeafNode(process_float(arg))
193+
return LeafNode(self._node_id, process_float(arg))
194194
else:
195195
return self
196196

@@ -207,16 +207,16 @@ def op_compute(self, arg_list):
207207

208208
class SineNode(InternalNode):
209209

210-
def __init__(self):
211-
InternalNode.__init__(self, "sin", 3)
210+
def __init__(self, node_id):
211+
InternalNode.__init__(self, node_id, "sin", 3)
212212

213213
def formal(self):
214214
return "sin(" + self._nodes[0].formal() + ")"
215215

216216
def op_simplify(self):
217217
if not self._nodes[0].is_sensor():
218218
arg = np.sin(float(self._nodes[0].to_string()))
219-
return LeafNode(process_float(arg))
219+
return LeafNode(self._node_id, process_float(arg))
220220
else:
221221
return self
222222

@@ -232,16 +232,16 @@ def op_compute(self, arg_list):
232232

233233
class CosineNode(InternalNode):
234234

235-
def __init__(self):
236-
InternalNode.__init__(self, "cos", 3)
235+
def __init__(self, node_id):
236+
InternalNode.__init__(self, node_id, "cos", 3)
237237

238238
def formal(self):
239239
return "cos(" + self._nodes[0].formal() + ")"
240240

241241
def op_simplify(self):
242242
if not self._nodes[0].is_sensor():
243243
arg = np.cos(float(self._nodes[0].to_string()))
244-
return LeafNode(process_float(arg))
244+
return LeafNode(self._node_id, process_float(arg))
245245
else:
246246
return self
247247

@@ -259,8 +259,8 @@ class LogarithmNode(InternalNode):
259259
PROTECTION = 0.00001
260260
SIMPLIFY_PROTECTION = 0.01
261261

262-
def __init__(self):
263-
InternalNode.__init__(self, "log", 5)
262+
def __init__(self, node_id):
263+
InternalNode.__init__(self, node_id, "log", 5)
264264

265265
def formal(self):
266266
return "my_log(" + self._nodes[0].formal() + ")"
@@ -281,7 +281,7 @@ def op_simplify(self):
281281
else:
282282
arg = np.log(float(self._nodes[0].to_string()))
283283

284-
return LeafNode(process_float(arg))
284+
return LeafNode(self._node_id, process_float(arg))
285285
else:
286286
return self
287287

@@ -297,8 +297,8 @@ def op_compute(self, arg_list):
297297

298298
class ExponentialNode(InternalNode):
299299

300-
def __init__(self):
301-
InternalNode.__init__(self, "exp", 5)
300+
def __init__(self, node_id):
301+
InternalNode.__init__(self, node_id, "exp", 5)
302302

303303
def formal(self):
304304
return "exp(" + self._nodes[0].formal() + ")"
@@ -311,9 +311,9 @@ def op_simplify(self):
311311
except OverflowError:
312312
# FIXME: See what to do with this expression, because there are problems when
313313
# an infinite value is the argument of a sinusoidal function
314-
return LeafNode(process_float(float("inf")))
314+
return LeafNode(self._node_id, process_float(float("inf")))
315315

316-
return LeafNode(process_float(arg))
316+
return LeafNode(self._node_id, process_float(arg))
317317
else:
318318
return self
319319

@@ -329,16 +329,16 @@ def op_compute(self, arg_list):
329329

330330
class TanhNode(InternalNode):
331331

332-
def __init__(self):
333-
InternalNode.__init__(self, "tanh", 5)
332+
def __init__(self, node_id):
333+
InternalNode.__init__(self, node_id, "tanh", 5)
334334

335335
def formal(self):
336336
return "tanh(" + self._nodes[0].formal() + ")"
337337

338338
def op_simplify(self):
339339
if not self._nodes[0].is_sensor():
340340
arg = np.tanh(float(self._nodes[0].to_string()))
341-
return LeafNode(process_float(arg))
341+
return LeafNode(self._node_id, process_float(arg))
342342
else:
343343
return self
344344

@@ -354,8 +354,8 @@ def op_compute(self, arg_list):
354354

355355
class RootNode(InternalNode):
356356

357-
def __init__(self):
358-
InternalNode.__init__(self, "", 0)
357+
def __init__(self, node_id):
358+
InternalNode.__init__(self, node_id, "", 0)
359359

360360
def to_string(self):
361361
return " ".join([n.to_string() for n in self._nodes])
@@ -389,9 +389,9 @@ def accept(self, visitor):
389389
class OpNodeFactory:
390390

391391
@staticmethod
392-
def make(op):
392+
def make(op, node_id):
393393
if op == 'root':
394-
return RootNode()
394+
return RootNode(node_id)
395395

396396
# instatiate node from string
397397
operation = Operations.get_instance().get_operation_from_op_string(op)
@@ -401,4 +401,4 @@ def make(op):
401401
node_module = importlib.import_module(node_module_name)
402402
node_class = getattr(node_module, node_class_name)
403403

404-
return node_class()
404+
return node_class(node_id)

0 commit comments

Comments
 (0)