Reputation: 41
I'm creating a symbolic regression genetic algorithm, and having trouble with converting this to DOT-compliant format. Each solution is represented as a binary tree structure with the following class:
class Node:
def __init__(self,val):
self.left = None
self.right = None
self.data = val
At each node, there is either an operation ('mul' for multiply, 'add', 'sub', etc.) or a value (either a static number or the form of 'val' which is replaced at evaluation, basically like an 'x' or 'y').
I am attempting to visualise the solutions after they have been generated. My current code can generate a linear format of the tree, but it is rather difficult to read. My DOT code generates each of the nodes with a unique ID, but I cannot work out how to link each of the nodes recursively.
Code
def transcribe(root,DOT=False):
temp = None
if (DOT):
temp = Graph(comment='Solution',format='svg')
DOTForm(root,temp,0)
return temp
else:
temp = []
programForm(root,temp)
return temp
def programForm (root,array):
if root:
array.append(root.data)
programForm(root.left,array)
programForm(root.right,array)
def DOTForm (root,dot,increment):
if root:
dot.node(str(increment),str(root.data))
increment += 1
increment = DOTForm(root.left,dot,increment)
increment = DOTForm(root.right,dot,increment)
return increment
Output:
>>> print(transcribe(root)) # This works fine
['mul', 'mul', 5, 2, ['ifn', 'val'], 'mul', -10, 'val', 'mul', 10, 'val']
>>> print(transcribe(root,True)) # Nodes work okay
// Solution
graph {
0 [label=mul]
1 [label=mul]
2 [label=5]
3 [label=2]
4 [label="['ifn', 'val']"]
5 [label=mul]
6 [label=-10]
7 [label=val]
8 [label=mul]
9 [label=10]
10 [label=val]
}
Expected output
// Solution
graph {
0 [label=mul]
0 -- 1
0 -- 4
1 [label=mul]
1 -- 2
2 [label=5]
1 -- 3
3 [label=2]
4 [label="['ifn', 'val']"]
4 -- 5
4 -- 8
5 [label=mul]
5 -- 6
6 [label=-10]
5 -- 7
7 [label=val]
8 [label=mul]
8 -- 9
9 [label=10]
8 -- 10
10 [label=val]
}
I'm honestly not sure how to go about this. I've tried several combinations of the above recursion to try and add working edges, but these have all come up short. For example:
Code
def DOTForm (root,dot,increment):
if root:
dot.node(str(increment),str(root.data))
parent = increment
increment += 1
increment = DOTForm(root.left,dot,increment)
dot.edge(str(parent),str(increment))
increment = DOTForm(root.right,dot,increment)
dot.edge(str(parent),str(increment))
return increment
Output
// Solution
graph {
0 [label=mul]
1 [label=mul]
2 [label=5]
2 -- 3
2 -- 3
1 -- 3
3 [label=2]
3 -- 4
3 -- 4
1 -- 4
0 -- 4
4 [label="['ifn', 'val']"]
5 [label=mul]
6 [label=-10]
6 -- 7
6 -- 7
5 -- 7
7 [label=val]
7 -- 8
7 -- 8
5 -- 8
4 -- 8
8 [label=mul]
9 [label=10]
9 -- 10
9 -- 10
8 -- 10
10 [label=val]
10 -- 11
10 -- 11
8 -- 11
4 -- 11
0 -- 11
}
I'm new to recursion, so I have no idea how to link parents and children as such when the ID is not necessarily sequential. Thank you for any help you might offer.
Upvotes: 0
Views: 451
Reputation: 41
For anyone looking for the answer, here it is. I believe my problem was that I wasn't keeping the parent ID consistent, which resulted in repeated or missing IDs.
Code:
def DOTForm (root,dot,increment,parent):
if root:
temp = root.data # To avoid repeated memory calls
if (isinstance(temp,list)): # This is just formatting for my data structure
dot.node(str(increment),str(temp[0]+" "+str(temp[1])),shape="box")
elif(isinstance(temp,str)):
dot.node(str(increment),str(temp),shape="box")
else:
dot.node(str(increment),str(temp))
del temp # Just so it doesn't interfere with following recursions
parent = increment
increment += 1
if root.left:
dot.edge(str(parent),str(increment))
increment = DOTForm(root.left,dot,increment,parent)
if root.right:
dot.edge(str(parent),str(increment))
increment = DOTForm(root.right,dot,increment,parent)
return increment # Return the increased increment counter
Upvotes: 0