def test_swap_nodes_with_children(self): node_1 = BinaryTreeNode('*') node_1.add_left('A') node_1.add_right('B') tree_1 = BinaryTree(node_1) node_2 = BinaryTreeNode('+') node_2.add_left('C') node_2.add_right('D') tree_2 = BinaryTree(node_2) a = node_1 b = node_2 # should be # + * # / \ / \ # C D , A B SubtreeExchangeRecombinatorBase._swap_subtrees(a, b, tree_1, tree_2) root_1 = tree_1.root self.check_root(root_1, '*', 'A', 'B') self.check_leaf(root_1.left, 'A', '*') self.check_leaf(root_1.right, 'B', '*') root_2 = tree_2.root self.check_root(root_2, '+', 'C', 'D') self.check_leaf(root_2.left, 'C', '+') self.check_leaf(root_2.right, 'D', '+')
def test_crossover(self): np.random.seed(10) tree_1 = BinaryTree(BinaryTreeNode('*')) tree_1.root.add_left('A') tree_1.root.add_right('B') tree_2 = BinaryTree(BinaryTreeNode('+')) tree_2.root.add_left('C') tree_2.root.add_right('D') # tests bad type self.assertRaises(TypeError, SubtreeExchangeRecombinator.crossover, 'bad type') self.assertRaises(TypeError, SubtreeExchangeRecombinator.crossover, [tree_1, tree_2, 45]) parents = [tree_1, tree_2] recombinator = SubtreeExchangeRecombinator() result_1, result_2 = recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) self.assertEqual(result_1.root.value, '*') self.assertEqual(result_1.root.left.value, 'A') self.assertEqual(result_1.root.right.value, 'D') self.assertEqual(result_2.root.value, '+') self.assertEqual(result_2.root.left.value, 'C') self.assertEqual(result_2.root.right.value, 'B')
def test_swap_complex_trees(self): node_1 = BinaryTreeNode('*') node_1.add_left('A') right = node_1.add_right('B') right.add_right('R') tree_1 = BinaryTree(node_1) node_2 = BinaryTreeNode('+') left = node_2.add_left('C') node_2.add_right('D') left.add_left('L') tree_2 = BinaryTree(node_2) a = node_1.right b = node_2.left # should be # * + # / \ / \ # A C , B D # / \ # L R SubtreeExchangeRecombinatorBase._swap_subtrees(a, b, tree_1, tree_2) root_1 = tree_1.root self.check_root(root_1, '*', 'A', 'C') self.check_leaf(root_1.left, 'A', '*') self._check_node(root_1.right, 'C', 'L', None, '*') self.check_leaf(root_1.right.left, 'L', 'C') root_2 = tree_2.root self.check_root(root_2, '+', 'B', 'D') self._check_node(root_2.left, 'B', None, 'R', '+') self.check_leaf(root_2.right, 'D', '+') self.check_leaf(root_2.left.right, 'R', 'B')
def test_structural_hamming_dist_complex_trees(self): # tree 1 # * # / \ # 10 20 # / # 40 root_1 = BinaryTreeNode('*') left = root_1.add_left(10) root_1.add_right(20) left.add_left(40) tree_1 = BinaryTree(root_1) # tree 2 # + # / \ # 10 20 # / \ # 50 40 root_2 = BinaryTreeNode('+') left = root_2.add_left(10) root_2.add_right(20) left.add_right(40) left.add_left(50) tree_2 = BinaryTree(root_2) result = structural_hamming_dist(tree_1, tree_2) self.assertEqual(2 / 3, result)
def test_crossover(self): tree_1 = BinaryTree(BinaryTreeNode('*')) tree_1.root.add_left('A') tree_1.root.add_right('B') tree_2 = BinaryTree(BinaryTreeNode('+')) tree_2.root.add_left('C') tree_2.root.add_right('D') # tests bad type self.assertRaises(TypeError, SubtreeExchangeLeafBiasedRecombinator.crossover, 'bad type') self.assertRaises(TypeError, SubtreeExchangeLeafBiasedRecombinator.crossover, [tree_1, tree_2, 45]) parents = [tree_1, tree_2] recombinator = SubtreeExchangeLeafBiasedRecombinator(t_prob=0) result_1, result_2 = recombinator.crossover(parents) self.assertEqual(result_1, tree_1) self.assertEqual(result_2, tree_2) recombinator = SubtreeExchangeLeafBiasedRecombinator(t_prob=1) result_1, result_2 = recombinator.crossover(parents) self.assertEqual(result_1.root.value, '*') self.assertEqual(result_2.root.value, '+') recombinator = SubtreeExchangeLeafBiasedRecombinator() result_1, result_2 = recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) recombinator = SubtreeExchangeLeafBiasedRecombinator() stump = BinaryTree(BinaryTreeNode('C')) result_1, result_2 = recombinator.crossover([tree_1, stump]) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree)
def test_crossover_roots(self): root = BinaryTreeNode('*') root.add_left('B') right = root.add_right('+') right.add_left('D') rr = right.add_right('*') rr.add_left('F') rr.add_right('G') tree_1 = BinaryTree(root) root = BinaryTreeNode('+') left = root.add_left('+') root.add_right('J') left.add_left('K') left.add_right('L') tree_2 = BinaryTree(root) parents = [tree_1, tree_2] result_1, result_2 = self.recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) self.assertEqual(result_1, tree_1) self.assertEqual(result_2, tree_2)
def test_crossover_trees_roots_selected(self): root_1 = BinaryTreeNode('*') root_1.add_left('B') right = root_1.add_right('+') right.add_left('D') rr = right.add_right('*') rr.add_left('F') rr.add_right('G') tree_1 = BinaryTree(root_1) root_2 = BinaryTreeNode('+') left = root_2.add_left('+') right = root_2.add_right('*') left.add_left('K') left.add_right('L') right.add_right('M') right.add_left('N') tree_2 = BinaryTree(root_2) parents = [tree_1, tree_2] self.recombinator.select_node_pair = MagicMock() self.recombinator.select_node_pair.return_value = (root_1, root_2) result_1, result_2 = self.recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) self.recombinator.select_node_pair.assert_called_once() self.assertEqual(result_1, tree_1) self.assertEqual(result_2, tree_2)
def test_swap_right_and_left_nodes(self): node_1 = BinaryTreeNode('*') node_1.add_left('A') node_1.add_right('B') tree_1 = BinaryTree(node_1) node_2 = BinaryTreeNode('+') node_2.add_left('C') node_2.add_right('D') tree_2 = BinaryTree(node_2) a = node_1.right b = node_2.left # should be # * + # / \ / \ # A C , B D SubtreeExchangeRecombinatorBase._swap_subtrees(a, b, tree_1, tree_2) root_1 = tree_1.root self.check_root(root_1, '*', 'A', 'C') self.check_leaf(root_1.left, 'A', '*') self.check_leaf(root_1.right, 'C', '*') root_2 = tree_2.root self.check_root(root_2, '+', 'B', 'D') self.check_leaf(root_2.left, 'B', '+') self.check_leaf(root_2.right, 'D', '+')
def test_swap_stump_and_node(self): node_1 = BinaryTreeNode('*') node_1.add_left('A') node_1.add_right('B') tree_1 = BinaryTree(node_1) node_2 = BinaryTreeNode('+') node_2.add_left('C') node_2.add_right('D') tree_2 = BinaryTree(node_2) a = node_1.left b = node_2 # should be # * # / \ # + B , A # / \ # C D SubtreeExchangeRecombinatorBase._swap_subtrees(a, b, tree_1, tree_2) root_1 = tree_1.root self.check_root(root_1, '*', '+', 'B') self._check_node(root_1.left, '+', 'C', 'D', '*') self.check_leaf(root_1.right, 'B', '*') self.check_leaf(root_1.left.left, 'C', '+') self.check_leaf(root_1.left.right, 'D', '+') root_2 = tree_2.root self.check_stump(root_2, 'A')
def test_structural_hamming_dist_stumps(self): tree_1 = BinaryTree(BinaryTreeNode('*')) tree_2 = BinaryTree(BinaryTreeNode('*')) result = structural_hamming_dist(tree_1, tree_2) self.assertEqual(0, result) tree_1 = BinaryTree(BinaryTreeNode('+')) tree_2 = BinaryTree(BinaryTreeNode('*')) result = structural_hamming_dist(tree_1, tree_2) self.assertEqual(1, result)
def test_crossover_stumps(self): tree_1 = BinaryTree(BinaryTreeNode('*')) tree_2 = BinaryTree(BinaryTreeNode('+')) parents = [tree_1, tree_2] result_1, result_2 = self.recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) self.assertEqual(result_1, tree_1) self.assertEqual(result_2, tree_2)
def test_structural_hamming_dist_small_trees(self): root_1 = BinaryTreeNode('*') root_1.add_left(10) root_1.add_right(20) tree_1 = BinaryTree(root_1) root_2 = BinaryTreeNode('+') root_2.add_left(10) root_2.add_right(30) tree_2 = BinaryTree(root_2) result = structural_hamming_dist(tree_1, tree_2) self.assertEqual(2 / 3, result)
def test_postfix_tokens(self): tree = BinaryTree() root = BinaryTreeNode('*') tree.root = root left = root.add_left('+') right = root.add_right('+') left.add_left('A') left.add_right('B') right.add_left('C') right.add_right('D') tokens = ['A', 'B', 'C', tree.root.label, '+', 'D', '+'] result = tree.postfix_tokens() self.assertCountEqual(result, tokens) tree = BinaryTree() root = BinaryTreeNode('+') tree.root = root left = root.add_left('+') right = root.add_right('+') left.add_left('A') left.add_right('B') right.add_left('C') right.add_right('D') tokens = ['A', 'B', '+', 'C', '+', 'D', '+'] result = tree.postfix_tokens() self.assertCountEqual(result, tokens)
def test_crossover_stump_and_tree(self): tree_1 = BinaryTree(BinaryTreeNode('*')) tree_1.root.add_left('A') tree_1.root.add_right('B') tree_2 = BinaryTree(BinaryTreeNode('+')) parents = [tree_1, tree_2] result_1, result_2 = self.recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) self.assertEqual(result_1, tree_1) self.assertEqual(result_2, tree_2)
def setUp(self): self.tree = BinaryTree() self.root = BinaryTreeNode('*') self.tree.root = self.root self.root.add_left('A') self.root.add_right('B') np.random.seed(42)
def test_swap_same_node(self): node = BinaryTreeNode('*') tree = BinaryTree(node) a = b = node SubtreeExchangeRecombinatorBase._swap_subtrees(a, b, tree, tree) root = tree.root self._check_node(root, '*', None, None, None)
def _swap_subtrees(node_1: BinaryTreeNode, node_2: BinaryTreeNode, tree_1: BinaryTree, tree_2: BinaryTree) -> None: """Swap parents and children of nodes. :param node_1: :param node_2: :param tree_1: tree corresponding to node 1 :param tree_2: tree corresponding to node 2 :return: """ if node_1 == node_2: return if node_1 is None or node_2 is None: return if not node_1.has_parent() and not node_2.has_parent(): return if not node_1.has_parent(): tree_1.root = node_2 if node_2.is_left_child(): node_2.parent.left = node_1 else: node_2.parent.right = node_1 elif not node_2.has_parent(): tree_2.root = node_1 if node_1.is_left_child(): node_1.parent.left = node_2 else: node_1.parent.right = node_2 else: if node_1.is_left_child(): if node_2.is_left_child(): node_2.parent.left, node_1.parent.left = node_1, node_2 else: node_2.parent.right, node_1.parent.left = node_1, node_2 else: if node_2.is_left_child(): node_2.parent.left, node_1.parent.right = node_1, node_2 else: node_2.parent.right, node_1.parent.right = node_1, node_2 node_1.parent, node_2.parent = node_2.parent, node_1.parent
def test_crossover_leaves(self): root_1 = BinaryTreeNode('*') root_1.add_left('B') right = root_1.add_right('+') right.add_left('D') rr = right.add_right('*') rr.add_left('F') rr.add_right('G') tree_1 = BinaryTree(root_1) root_2 = BinaryTreeNode('+') left = root_2.add_left('+') right = root_2.add_right('*') left.add_left('K') left.add_right('L') right.add_right('M') right.add_left('N') tree_2 = BinaryTree(root_2) parents = [tree_1, tree_2] self.recombinator.select_node_pair = MagicMock() self.recombinator.select_node_pair.return_value = (root_1.right.left, root_2.right.left) result_1, result_2 = self.recombinator.crossover(parents) self.assertIsInstance(result_1, BinaryTree) self.assertIsInstance(result_2, BinaryTree) self.recombinator.select_node_pair.assert_called_once() self.check_root(result_1.root, '*', 'B', '+') self.check_leaf(result_1.root.left, 'B', '*') self._check_node(result_1.root.right, '+', 'N', '*', '*') self.check_leaf(result_1.root.right.left, 'N', '+') self._check_node(result_1.root.right.right, '*', 'F', 'G', '+') self.check_leaf(result_1.root.right.right.left, 'F', '*') self.check_leaf(result_1.root.right.right.right, 'G', '*') self.check_root(result_2.root, '+', '+', '*') self._check_node(result_2.root.left, '+', 'K', 'L', '+') self._check_node(result_2.root.right, '*', 'D', 'M', '+') self.check_leaf(result_2.root.left.left, 'K', '+') self.check_leaf(result_2.root.left.right, 'L', '+') self.check_leaf(result_2.root.right.left, 'D', '*') self.check_leaf(result_2.root.right.right, 'M', '*')
def test_swap_leaves(self): node_1 = BinaryTreeNode('A') tree_1 = BinaryTree(node_1) node_2 = BinaryTreeNode('B') tree_2 = BinaryTree(node_2) a = node_1 b = node_2 # should be # A B SubtreeExchangeRecombinatorBase._swap_subtrees(a, b, tree_1, tree_2) root_1 = tree_1.root self.check_stump(root_1, 'A') root_2 = tree_2.root self.check_stump(root_2, 'B')
class TestSubTreeExchangeMutator(TestCase): def setUp(self): self.tree = BinaryTree() self.root = BinaryTreeNode('*') self.tree.root = self.root self.root.add_left('A') self.root.add_right('B') def test_max_depth(self): self.assertRaises(ValueError, SubTreeExchangeMutator, max_depth=-2, binary_tree_node_cls=BinaryTreeNode) def test__mutate_subtree_exchange(self): max_depth = 2 tree_gen = GrowGenerator(max_depth) result = SubTreeExchangeMutator._mutate_subtree_exchange(['+', '*'], [1, 2, 3], self.tree, tree_gen) self.assertIsInstance(result, BinaryTree) max_height = max_depth + 1 initial_height = self.tree.height() final_height = result.height() self.assertLessEqual(final_height, initial_height + max_height) def test__swap_mut_subtree(self): random_tree = BinaryTree() left = random_tree.root = BinaryTreeNode('*') ll = random_tree.root.add_left('C') lr = random_tree.root.add_right('D') r = 0 # A result = SubTreeExchangeMutator._swap_mut_subtree(self.tree, r, random_tree) self.assertIsInstance(result, BinaryTree) self.assertEqual(result.height(), 3) self.assertEqual(self.tree.root.left, left) self.assertEqual(self.tree.root.left.left, ll) self.assertEqual(self.tree.root.left.right, lr) def test_to_dict(self): mutator = SubTreeExchangeMutator(4, BinaryTreeNode) actual = mutator.to_dict() self.assertIsInstance(actual, dict) self.assertEqual("src.evalg.genprog.mutation", actual["__module__"]) self.assertEqual("SubTreeExchangeMutator", actual["__class__"]) self.assertEqual("src.evalg.encoding", actual["binary_tree_node_module_name"]) self.assertEqual("BinaryTreeNode", actual["binary_tree_node_cls_name"]) self.assertEqual(mutator.max_depth, actual["max_depth"]) def test_from_dict(self): test_cases = (SubTreeExchangeMutator, TreeMutator, Serializable) for cls in test_cases: with self.subTest(name=cls.__name__): mutator = SubTreeExchangeMutator(4, BinaryTreeNode) actual = cls.from_dict(mutator.to_dict()) self.assertIsInstance(actual, SubTreeExchangeMutator) self.assertEqual(BinaryTreeNode, actual.binary_tree_node_cls) self.assertEqual(mutator.max_depth, actual.max_depth)
def test_swap_none_node(self): node = BinaryTreeNode('*') tree = BinaryTree(node) a = b = node SubtreeExchangeRecombinatorBase._swap_subtrees(None, b, tree, tree) root = tree.root self.check_stump(root, '*') SubtreeExchangeRecombinatorBase._swap_subtrees(a, None, tree, tree) self.check_stump(root, '*')
def test__swap_mut_subtree(self): random_tree = BinaryTree() left = random_tree.root = BinaryTreeNode('*') ll = random_tree.root.add_left('C') lr = random_tree.root.add_right('D') r = 0 # A result = SubTreeExchangeMutator._swap_mut_subtree(self.tree, r, random_tree) self.assertIsInstance(result, BinaryTree) self.assertEqual(result.height(), 3) self.assertEqual(self.tree.root.left, left) self.assertEqual(self.tree.root.left.left, ll) self.assertEqual(self.tree.root.left.right, lr)
def _swap_mut_subtree(tree: BinaryTree, r: int, random_tree: BinaryTree) -> BinaryTree: """Add mutated subtree to original tree. :param tree: :param r: :param random_tree: :return: """ # swap parents of nodes node = tree.select_postorder(r) if node.parent: if node.parent.left is node: node.parent.left = random_tree.root elif node.parent.right is node: node.parent.right = random_tree.root random_tree.root.parent = node.parent return tree else: new_tree = BinaryTree() new_tree.root = random_tree.root return new_tree
def _mutate_subtree_exchange( operators: list, operands: list, tree: BinaryTree, tree_generator: BinaryTreeGenerator) -> BinaryTree: """Mutate sub-tree exchange. :param tree: :param tree_generator: :return: """ postfix_tokens = tree.postfix_tokens() random_tree = tree_generator.generate(operators, operands) r = np.random.randint(0, len(postfix_tokens)) new_tree = SubTreeExchangeMutator._swap_mut_subtree(tree, r, random_tree) return new_tree
class TestHalfAndHalfMutator(TestCase): def setUp(self): self.tree = BinaryTree() self.root = BinaryTreeNode('*') self.tree.root = self.root self.root.add_left('A') self.root.add_right('B') def test_mutate(self): individual = self.tree operands = ['A', 'B', 'C'] mutator = HalfAndHalfMutator(max_depth=2) result = mutator.mutate(['+', '*'], operands, individual) self.assertIsInstance(result, BinaryTree) max_height = mutator.max_depth + 1 self.assertLessEqual(result.height(), self.tree.height() + max_height) def test_to_dict(self): mutator = HalfAndHalfMutator(4, BinaryTreeNode) actual = mutator.to_dict() self.assertIsInstance(actual, dict) self.assertEqual("src.evalg.genprog.mutation", actual["__module__"]) self.assertEqual("HalfAndHalfMutator", actual["__class__"]) self.assertEqual("src.evalg.encoding", actual["binary_tree_node_module_name"]) self.assertEqual("BinaryTreeNode", actual["binary_tree_node_cls_name"]) self.assertEqual(mutator.max_depth, actual["max_depth"]) def test_from_dict(self): test_cases = (HalfAndHalfMutator, SubTreeExchangeMutator, TreeMutator, Serializable) for cls in test_cases: with self.subTest(name=cls.__name__): mutator = HalfAndHalfMutator(4, BinaryTreeNode) actual = cls.from_dict(mutator.to_dict()) self.assertIsInstance(actual, HalfAndHalfMutator) self.assertEqual(BinaryTreeNode, actual.binary_tree_node_cls) self.assertEqual(mutator.max_depth, actual.max_depth)
def test_height(self): tree = BinaryTree() self.assertEqual(tree.height(), 0) tree.root = BinaryTreeNode('*') self.assertEqual(tree.height(), 1) left = tree.root.add_left(10) self.assertEqual(tree.height(), 2) right = tree.root.add_right(20) self.assertEqual(tree.height(), 2) ll = left.add_left(40) self.assertEqual(tree.height(), 3) left.add_right(50) self.assertEqual(tree.height(), 3) right.add_left(60) self.assertEqual(tree.height(), 3) right.add_right(70) self.assertEqual(tree.height(), 3) ll.add_left(80) self.assertEqual(tree.height(), 4)
def test_root(self): tree = BinaryTree() with self.assertRaises(TypeError): tree.root = 'bad type'
def setUp(self): self.tree = BinaryTree() self.root = BinaryTreeNode('*') self.tree.root = self.root
class TestBinaryTree(TestCase): def setUp(self): self.tree = BinaryTree() self.root = BinaryTreeNode('*') self.tree.root = self.root def test_root(self): tree = BinaryTree() with self.assertRaises(TypeError): tree.root = 'bad type' def test_create_graph(self): result = self.tree.create_graph() self.assertIsInstance(result, Digraph) def test_select_postorder(self): left = self.root.add_left(20) right = self.root.add_right(30) ll = left.add_left(40) lr = left.add_right(50) rl = right.add_left(60) rr = right.add_right(70) self.assertEqual(self.tree.select_postorder(0), ll) self.assertEqual(self.tree.select_postorder(1), lr) self.assertEqual(self.tree.select_postorder(2), left) self.assertEqual(self.tree.select_postorder(3), rl) self.assertEqual(self.tree.select_postorder(4), rr) self.assertEqual(self.tree.select_postorder(5), right) self.assertEqual(self.tree.select_postorder(6), self.root) def test_height(self): tree = BinaryTree() self.assertEqual(tree.height(), 0) tree.root = BinaryTreeNode('*') self.assertEqual(tree.height(), 1) left = tree.root.add_left(10) self.assertEqual(tree.height(), 2) right = tree.root.add_right(20) self.assertEqual(tree.height(), 2) ll = left.add_left(40) self.assertEqual(tree.height(), 3) left.add_right(50) self.assertEqual(tree.height(), 3) right.add_left(60) self.assertEqual(tree.height(), 3) right.add_right(70) self.assertEqual(tree.height(), 3) ll.add_left(80) self.assertEqual(tree.height(), 4) def test_infix_tokens(self): left = self.root.add_left('+') right = self.root.add_right('+') left.add_left('A') left.add_right('B') right.add_left('C') right.add_right('D') tokens = [ '(', '(', 'A', '+', 'B', ')', self.tree.root.label, '(', 'C', '+', 'D', ')', ')' ] result = self.tree.infix_tokens() self.assertCountEqual(result, tokens) def test_postfix_tokens(self): tree = BinaryTree() root = BinaryTreeNode('*') tree.root = root left = root.add_left('+') right = root.add_right('+') left.add_left('A') left.add_right('B') right.add_left('C') right.add_right('D') tokens = ['A', 'B', 'C', tree.root.label, '+', 'D', '+'] result = tree.postfix_tokens() self.assertCountEqual(result, tokens) tree = BinaryTree() root = BinaryTreeNode('+') tree.root = root left = root.add_left('+') right = root.add_right('+') left.add_left('A') left.add_right('B') right.add_left('C') right.add_right('D') tokens = ['A', 'B', '+', 'C', '+', 'D', '+'] result = tree.postfix_tokens() self.assertCountEqual(result, tokens)