Commit 85318ed0 authored by Adam P. Goucher's avatar Adam P. Goucher

Functionality to simplify ruletrees

parent 300dd11c
Pipeline #49745015 passed with stages
in 7 minutes and 41 seconds
from .parsetable import GetNumberOfInputs
from ast import literal_eval
from .writetree import RuleTree
# Named neighbourhoods:
nhoods = {'2': [(-1, 0), (1, 0), (0, 0), (0, 0)],
'4': [(0, -1), (-1, 0), (1, 0), (0, 1), (0, 0), (0, 0)],
'8': [(-1, -1), (1, -1), (-1, 1), (1, 1), (0, -1), (-1, 0), (1, 0), (0, 1), (0, 0), (0, 0)]}
def ReadRuleTree(list_of_lines):
def ParseRuleTree(list_of_lines):
numInputs = 0
num_nodes = 0
......@@ -52,19 +54,86 @@ def ReadRuleTree(list_of_lines):
# Tree node
numbers = [int(x) for x in line.split()]
depth = numbers[0]
children = numbers[1:]
if depth > 1:
# Convert node number to index into uint32_t array:
children = [n_states * (num_nodes - 1 - x) for x in children]
list_of_nodes.append(children)
list_of_nodes.append(numbers)
if (len(list_of_nodes) != num_nodes):
raise RuntimeError('%d nodes found; %d expected' % (len(list_of_nodes), num_nodes))
# We want the root to be position zero:
list_of_nodes = list_of_nodes[::-1]
return n_states, nhood, list_of_nodes
def compress_tree(list_of_nodes, n_states, numInputs):
rt = RuleTree(n_states, numInputs, initialise=False)
rt.seq = list(map(tuple, list_of_nodes))
rt._shrink()
return list(map(list, rt.seq))
def optimise_tree(n_states, nhood, list_of_nodes):
numInputs = GetNumberOfInputs(nhood)
# Perform optimisation of this tree:
list_of_nodes = compress_tree(list_of_nodes, n_states, numInputs)
# Determine which inputs are essential:
essentials = [False] * numInputs
for node in list_of_nodes:
if len(set(node[1:])) > 1:
essentials[-node[0]] = True
# Flatten unnecessary layers:
for node in list_of_nodes:
depth = node[0]
if (depth > 1):
next_depth = list_of_nodes[node[-1]][0]
if not essentials[-next_depth]:
for j in range(1, len(node)):
node[j] = list_of_nodes[node[j]][-1]
while not essentials[-list_of_nodes[-1][0]]:
x = list_of_nodes[-1][-1]
list_of_nodes = list_of_nodes[:x+1]
# Rename inputs:
emap = [0] * (numInputs + 1)
for i in range(1, numInputs + 1):
emap[i] = emap[i-1] + (1 if essentials[-i] else 0)
for node in list_of_nodes:
node[0] = max(1, emap[node[0]])
# Shrink neighbourhood:
new_inputs = [t for (x, t) in zip(essentials, nhood[:numInputs]) if x]
nhood = new_inputs + nhood[numInputs:]
numInputs = len(new_inputs)
# Reoptimise:
list_of_nodes = compress_tree(list_of_nodes, n_states, numInputs)
return n_states, nhood, list_of_nodes
def ReadRuleTree(list_of_lines):
# Parse the actual file into a rule tree:
ruletree = ParseRuleTree(list_of_lines)
# Optimise the resulting rule tree:
n_states, nhood, list_of_nodes = optimise_tree(*ruletree)
# Serialise tree to an array:
num_nodes = len(list_of_nodes)
def serialise_node(numbers):
depth = numbers[0]
children = numbers[1:]
if depth > 1:
# Convert node number to index into uint32_t array:
children = [n_states * (num_nodes - 1 - x) for x in children]
return children
list_of_nodes = [serialise_node(data) for data in list_of_nodes[::-1]]
return n_states, nhood, list_of_nodes
......@@ -6,7 +6,7 @@ class RuleTree:
written by Tim Hutton, Tom Rokicki, and Andrew Trevorrow.
'''
def __init__(self, numStates, numInputs):
def __init__(self, numStates, numInputs, initialise=True):
self.numInputs = numInputs;
......@@ -22,7 +22,8 @@ class RuleTree:
self.cache = {}
self.shrinksize = 100
self._init_tree()
if initialise:
self._init_tree()
def _init_tree(self):
self.curndd = -1
......
# Automatically generated by make-ruletree.py.
num_states=2
num_neighbors=8
num_nodes=31
1 0 0
2 0 0
1 1 1
2 0 2
3 1 3
1 0 1
2 5 0
3 3 6
4 4 7
2 0 5
2 2 0
3 9 10
3 10 1
4 11 12
5 8 13
3 3 10
4 15 12
3 1 1
4 12 17
5 16 18
6 14 19
7 20 20
8 21 21
3 6 1
4 23 17
4 17 17
5 24 25
6 19 26
7 27 27
8 28 28
9 22 29
import os
import lifelib
import unittest
import lifelib.genera.rulefiles.parsetree as parsetree
class TestTreeduce(unittest.TestCase):
def test_reduce(self):
'''
Tests that we can recognise an hexagonal rule embedded as a Moore CA.
'''
filename = os.path.join(lifelib.lifelib_dir, 'rules', 'source', 'Hex-B2omS2.tree')
with open(filename, 'r') as f:
list_of_lines = list(f)
ruletree = parsetree.ParseRuleTree(list_of_lines)
self.assertEqual(ruletree[1], [(-1, -1), (1, -1), (-1, 1), (1, 1), (0, -1), (-1, 0), (1, 0), (0, 1), (0, 0), (0, 0)])
self.assertEqual(len(ruletree[2]), 31)
ruletree2 = parsetree.optimise_tree(*ruletree)
self.assertEqual(ruletree2[1], [(-1, -1), (1, 1), (0, -1), (-1, 0), (1, 0), (0, 1), (0, 0), (0, 0)])
self.assertEqual(len(ruletree2[2]), 27)
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment