Beispiel #1
0
    def test_generate_func_node(self):
        # SYMBOLIC REGRESSION TREES
        for i in range(100):
            node = self.generator.generate_func_node()
            self.assertEquals(node.node_type, NodeType.FUNCTION)

        # CLASSIFICATION TREES
        self.config["tree_generation"]["tree_type"] = "CLASSIFICATION_TREE"
        self.config["function_nodes"] = [
            {
                "type": "CLASS_FUNCTION",
                "name": "GREATER_THAN",
                "arity": 2,

                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 1
                }
            }
        ]
        self.config["class_attributes"] = [
            "attrubte_1",
            "attrubte_2",
            "attrubte_3"
        ]
        generator = TreeGenerator(self.config)
        for i in range(100):
            node = generator.generate_func_node()
            class_attribute = node.class_attribute
            self.assertEquals(node.node_type, NodeType.CLASS_FUNCTION)
            self.assertTrue(class_attribute in self.config["class_attributes"])
    def test_generate_func_node(self):
        # SYMBOLIC REGRESSION TREES
        for i in range(100):
            node = self.generator.generate_func_node()
            self.assertEquals(node.node_type, NodeType.FUNCTION)

        # CLASSIFICATION TREES
        self.config["tree_generation"]["tree_type"] = "CLASSIFICATION_TREE"
        self.config["function_nodes"] = [{
            "type": "CLASS_FUNCTION",
            "name": "GREATER_THAN",
            "arity": 2,
            "data_range": {
                "lower_bound": 0.0,
                "upper_bound": 10.0,
                "decimal_places": 1
            }
        }]
        self.config["class_attributes"] = [
            "attrubte_1", "attrubte_2", "attrubte_3"
        ]
        generator = TreeGenerator(self.config)
        for i in range(100):
            node = generator.generate_func_node()
            class_attribute = node.class_attribute
            self.assertEquals(node.node_type, NodeType.CLASS_FUNCTION)
            self.assertTrue(class_attribute in self.config["class_attributes"])
Beispiel #3
0
def evaluate_trees():
    results = []
    response = {}

    # parse incomming data
    if request.data is not None:
        incomming = json.loads(request.data)
        config = incomming["config"]
        individuals = incomming["individuals"]

        # convert dict to trees
        parser = TreeGenerator(config)
        for individual in list(individuals):
            tree = parser.generate_tree_from_dict(individual)
            individuals.append(tree)
            individuals.remove(individual)

        evaluate(individuals, functions, config, results)

        # jsonify results
        response["results"] = []
        for individual in results:
            result = {
                "id": individual.tree_id,
                "score": individual.score,
            }
            response["results"].append(result)

    else:
        response = {"status": PlayNodeStatus.ERROR}

    return jsonify(response)
Beispiel #4
0
    def __init__(self, config, **kwargs):
        self.config = config
        self.recorder = kwargs.get("recorder", None)
        self.generator = TreeGenerator(self.config)

        # mutation stats
        self.method = None
        self.index = None
        self.mutation_probability = None
        self.random_probability = None
        self.mutated = False
        self.before_mutation = None
        self.after_mutation = None
Beispiel #5
0
    def test_greedy_over_selection(self):
        print "GREEDY-OVER SELECTION"

        # create population of size 1000
        self.config["max_population"] = 1000
        generator = TreeGenerator(self.config)
        population = generator.init()

        # greedy over selection
        old_pop_size = self.print_population("OLD", population)
        self.selection.greedy_over_selection(population)
        new_pop_size = self.print_population("NEW", population)

        self.assertEquals(old_pop_size, new_pop_size)
Beispiel #6
0
    def test_greedy_over_selection(self):
        print "GREEDY-OVER SELECTION"

        # create population of size 1000
        self.config["max_population"] = 1000
        generator = TreeGenerator(self.config)
        population = generator.init()

        # greedy over selection
        old_pop_size = self.print_population("OLD", population)
        self.selection.greedy_over_selection(population)
        new_pop_size = self.print_population("NEW", population)

        self.assertEquals(old_pop_size, new_pop_size)
Beispiel #7
0
    def __init__(self, config, **kwargs):
        self.config = config
        self.recorder = kwargs.get("recorder", None)
        self.generator = TreeGenerator(self.config)

        # mutation stats
        self.method = None
        self.index = None
        self.mutation_probability = None
        self.random_probability = None
        self.mutated = False
        self.before_mutation = None
        self.after_mutation = None
Beispiel #8
0
    def setUp(self):
        self.config = {
            "max_population" : 10,

            "tree_generation" : {
                "method" : "FULL_METHOD",
                "initial_max_depth" : 3
            },

            "selection" : {
                    "method" : "ROULETTE_SELECTION"
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 3.0},
                {"type": "CONSTANT", "value": 4.0},
                {"type": "CONSTANT", "value": 5.0},
                {"type": "CONSTANT", "value": 6.0},
                {"type": "CONSTANT", "value": 7.0},
                {"type": "CONSTANT", "value": 8.0},
                {"type": "CONSTANT", "value": 9.0},
                {"type": "CONSTANT", "value": 10.0}
            ],

            "input_variables" : [
                {"type": "INPUT", "name": "x"}
            ]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config)
        self.population = self.generator.init()

        # give population random scores
        for inidividual in self.population.individuals:
            inidividual.score = random.triangular(1, 100)
    def setUp(self):
        self.config = {
            "max_population": 50,

            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },

            "evaluator": {
                "use_cache": True
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
            ],

            "input_variables": [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file": "tests/data/sine.dat",

            "response_variables": [{"name": "y"}]
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)
Beispiel #10
0
    def setUp(self):
        self.config = {
            "max_population": 50,

            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },

            "evaluator": {
                "use_cache": True
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
            ],

            "input_variables": [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file": "tests/data/sine.dat",

            "response_variables": [{"name": "y"}]
        }
        config.load_data(self.config)

        self.functions = {
            "ADD": "+",
            "SUB": "-",
            "MUL": "*",
            "DIV": "/",
            "POW": "**",
            "SIN": "math.sin",
            "COS": "math.cos",
            "RAD": "math.radians",
            "LN": "math.ln",
            "LOG": "math.log"
        }
        self.generator = TreeGenerator(self.config)
Beispiel #11
0
    def setUp(self):
        self.config = {
            "max_population": 10,

            "tree_generation": {
                "tree_type": "SYMBOLIC_REGRESSION",
                "method": "RAMPED_HALF_AND_HALF_METHOD",
                "initial_max_depth": 3
            },

            "function_nodes": [
                {"type": "FUNCTION", "arity": 2, "name": "ADD"},
                {"type": "FUNCTION", "arity": 2, "name": "SUB"},
                {"type": "FUNCTION", "arity": 2, "name": "MUL"},
                {"type": "FUNCTION", "arity": 2, "name": "DIV"},
                {"type": "FUNCTION", "arity": 1, "name": "COS"},
                {"type": "FUNCTION", "arity": 1, "name": "SIN"}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "INPUT", "name": "x"},
                {"type": "INPUT", "name": "y"},
                {
                    "type": "RANDOM_CONSTANT",
                    "data_range": {
                        "upper_bound": 10.0,
                        "lower_bound": -10.0,
                        "decimal_places": 1
                    }
                }
            ],

            "input_variables": [
                {"name": "x"},
                {"name": "y"}
            ]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)
        self.parser = TreeParser()
Beispiel #12
0
    def setUp(self):
        self.config = {
            "max_population":
            5,
            "tree_generation": {
                "tree_type": "CLASSIFICATION_TREE",
                "method": "FULL_METHOD",
                "initial_max_depth": 2
            },
            "evaluator": {
                "use_cache": True
            },
            "function_nodes": [{
                "type": "CLASS_FUNCTION",
                "name": "GREATER_THAN",
                "arity": 2,
                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 0,
                }
            }, {
                "type": "CLASS_FUNCTION",
                "name": "LESS_THAN",
                "arity": 2,
                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 0,
                }
            }, {
                "type": "CLASS_FUNCTION",
                "name": "EQUALS",
                "arity": 2,
                "decimal_precision": 2,
                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 0,
                }
            }],
            "terminal_nodes": [
                {
                    "type": "RANDOM_CONSTANT",
                    "name": "species",
                    "range": [1.0, 2.0, 3.0]
                },
            ],
            "input_variables": [{
                "name": "sepal_length"
            }, {
                "name": "sepal_width"
            }, {
                "name": "petal_length"
            }, {
                "name": "petal_width"
            }],
            "class_attributes":
            ["sepal_length", "sepal_width", "petal_length", "petal_width"],
            "data_file":
            "tests/data/iris.dat",
            "response_variables": [{
                "name": "species"
            }]
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("CLASSIFICATION")
        self.generator = TreeGenerator(self.config)
        self.population = self.generator.init()
Beispiel #13
0
def gp_predict(train_data, test_data, train_cat, xx, yy):
    # setup
    config = {
        "max_population": 800,
        "max_generation": 30,
        "stale_limit": 10,

        "tree_generation": {
            "tree_type": "CLASSIFICATION_TREE",
            "method": "RAMPED_HALF_AND_HALF_METHOD",
            "depth_ranges": [
                {"size": 1, "percentage": 1.0}
            ]
        },

        "evaluator": {"use_cache": True},

        "selection": {
            "method": "TOURNAMENT_SELECTION",
            "tournament_size": 100
        },

        "crossover": {
            "method": "POINT_CROSSOVER",
            "probability": 0.8
        },

        "mutation": {
            "methods": [
                "SUBTREE_MUTATION"
            ],
            "probability": 0.8
        },

        "function_nodes": [
            {
                "type": "CLASS_FUNCTION",
                "name": "GREATER_THAN",
                "arity": 2,

                "data_range": {
                    "lower_bound": -1.0,
                    "upper_bound": 1.0,
                    "decimal_places": 2,
                }
            },
            {
                "type": "CLASS_FUNCTION",
                "name": "LESS_THAN",
                "arity": 2,

                "data_range": {
                    "lower_bound": -1.0,
                    "upper_bound": 1.0,
                    "decimal_places": 2,
                }
            },
            {
                "type": "CLASS_FUNCTION",
                "name": "EQUALS",
                "arity": 2,

                "data_range": {
                    "lower_bound": -1.0,
                    "upper_bound": 1.0,
                    "decimal_places": 2
                }
            }
        ],

        "terminal_nodes": [
            {
                "type": "RANDOM_CONSTANT",
                "name": "category",
                "range": [
                    0.0,
                    1.0
                ]
            },
        ],

        "class_attributes": [
            "x",
            "y"
        ],

        "input_variables": [
            {"name": "x"},
            {"name": "y"}
        ],
        "response_variables": [{"name": "category"}]
    }

    # load data
    config["data"] = {}
    config["data"]["rows"] = len(train_data)
    config["data"]["x"] = []
    config["data"]["y"] = []
    config["data"]["category"] = train_cat
    for row in train_data:
        config["data"]["x"].append(row[0])
        config["data"]["y"].append(row[1])

    functions = GPFunctionRegistry("CLASSIFICATION")
    generator = TreeGenerator(config)

    # genetic operators
    selection = Selection(config)
    crossover = TreeCrossover(config)
    mutation = TreeMutation(config)

    # run symbolic regression
    population = generator.init()

    details = play.play_details(
        population=population,
        evaluate=evaluate,
        functions=functions,
        selection=selection,
        crossover=crossover,
        mutation=mutation,
        print_func=print_func,
        stop_func=default_stop_func,
        config=config,
        editor=edit_trees,
    )
    play.play(details)

    best_tree = population.best_individuals[0]
    # gp_plot_dt(best_tree, True)

    # load test data
    config["data"] = {}
    config["data"]["rows"] = len(test_data)
    config["data"]["x"] = []
    config["data"]["y"] = []
    for row in test_data:
        config["data"]["x"].append(row[0])
        config["data"]["y"].append(row[1])

    # predict
    predicted = gp_eval.predict_tree(best_tree, functions, config)

    # load test data
    config["data"] = {}
    config["data"]["rows"] = xx.shape[0] * xx.shape[1]
    config["data"]["x"] = np.reshape(xx, xx.shape[0] * xx.shape[1])
    config["data"]["y"] = np.reshape(yy, yy.shape[0] * yy.shape[1])

    contour = gp_eval.predict_tree(best_tree, functions, config)
    contour = np.array(contour)
    contour = contour.reshape(xx.shape)

    return predicted, contour
Beispiel #14
0
class TreeMutation(object):
    def __init__(self, config, **kwargs):
        self.config = config
        self.recorder = kwargs.get("recorder", None)
        self.generator = TreeGenerator(self.config)

        # mutation stats
        self.method = None
        self.index = None
        self.mutation_probability = None
        self.random_probability = None
        self.mutated = False
        self.before_mutation = None
        self.after_mutation = None

    def generate_new_node(self, details):
        if details is None:
            return None

        elif details["type"] == NodeType.FUNCTION:
            return Node(
                NodeType.FUNCTION,
                name=details["name"],
                arity=details["arity"],
                branches=[]
            )

        elif details["type"] == NodeType.CLASS_FUNCTION:
            return Node(
                NodeType.CLASS_FUNCTION,
                name=details["name"],
                arity=details["arity"],
                branches=[]
            )

        elif details["type"] == NodeType.INPUT:
            return Node(
                NodeType.INPUT,
                name=details["name"]
            )

        elif details["type"] == NodeType.CONSTANT:
            return Node(
                NodeType.CONSTANT,
                name=details.get("name", None),
                value=details["value"]
            )

        elif details["type"] == NodeType.RANDOM_CONSTANT:
            resolved_details = self.generator.resolve_random_constant(details)
            return Node(
                NodeType.CONSTANT,
                name=resolved_details.get("name", None),
                value=resolved_details["value"]
            )

    def mutate_new_node_details(self, old_node):
        # determine what kind of old_node it is
        node_pool = []
        if old_node.is_function() or old_node.is_class_function():
            tmp = list(self.config["function_nodes"])
            tmp = [n for n in tmp if n["arity"] == old_node.arity]
            node_pool.extend(tmp)

        elif old_node.is_terminal():
            node_pool.extend(self.config["terminal_nodes"])

        # check the node and return
        retry = 0
        retry_limit = 100
        while True:
            if retry == retry_limit:
                return None
            else:
                retry += 1

            n_details = sample(node_pool, 1)[0]
            if n_details["type"] == NodeType.RANDOM_CONSTANT:
                n_details = self.generator.resolve_random_constant(n_details)
            elif n_details["type"] == NodeType.CLASS_FUNCTION:
                n_details = self.generator.resolve_class_function(n_details)
            new_node = self.generate_new_node(n_details)

            if old_node.equals(new_node) is False:
                return n_details

    def point_mutation(self, tree, mutation_index=None):
        # mutate node
        self.index = randint(0, len(tree.program) - 1)
        node = tree.program[self.index]
        new_node = self.mutate_new_node_details(node)

        if new_node is None:
            return

        elif node.is_function():
            node.name = new_node["name"]

        elif node.is_terminal():
            node.node_type = new_node.get("type")
            node.name = new_node.get("name", None)
            node.value = new_node.get("value", None)

        tree.update()
        self.mutated = True

    def hoist_mutation(self, tree, mutation_index=None):
        # new indivdiaul generated from subtree
        node = None
        if mutation_index is None:
            self.index = randint(0, len(tree.program) - 2)
            node = tree.program[self.index]
        else:
            self.index = mutation_index
            node = tree.program[mutation_index]

        tree.root = node
        tree.update()
        self.mutated = True

    def subtree_mutation(self, tree, mutation_index=None):
        # subtree exchanged against external random subtree
        node = None
        if mutation_index is None:
            self.index = randint(0, len(tree.program) - 1)
            node = tree.program[self.index]
        else:
            self.index = mutation_index
            node = tree.program[mutation_index]

        self.generator.max_depth = randint(1, 3)
        sub_tree = self.generator.generate_tree()
        if node is not tree.root:
            tree.replace_node(node, sub_tree.root)
        else:
            tree.root = sub_tree.root
        tree.update()
        self.mutated = True

    def shrink_mutation(self, tree, mutation_index=None):
        # replace subtree with terminal
        if len(tree.func_nodes):
            node = None
            if mutation_index is None:
                self.index = randint(0, len(tree.func_nodes) - 1)
                node = tree.func_nodes[self.index]

                while node is tree.root:
                    self.index = randint(0, len(tree.func_nodes) - 1)
                    node = tree.func_nodes[self.index]
            else:
                self.index = mutation_index
                node = tree.program[mutation_index]

            candidate_nodes = tree.term_nodes
            candidate_nodes.extend(tree.input_nodes)
            new_node_detail = sample(candidate_nodes, 1)[0]
            node_details = self.mutate_new_node_details(new_node_detail)
            new_node = self.generate_new_node(node_details)

            if new_node:
                tree.replace_node(node, new_node)
                tree.update()
                self.mutated = True

    def expansion_mutation(self, tree, mutation_index=None):
        # terminal exchanged against external random subtree
        node = None
        if mutation_index is None:
            prob = random()

            if tree.size == 1:
                return

            elif prob > 0.5 and len(tree.term_nodes) > 0:
                self.index = randint(0, len(tree.term_nodes) - 1)
                node = tree.term_nodes[self.index]

            elif prob < 0.5 and len(tree.input_nodes) > 0:
                self.index = randint(0, len(tree.input_nodes) - 1)
                node = tree.input_nodes[self.index]

            elif len(tree.term_nodes) > 0:
                self.index = randint(0, len(tree.term_nodes) - 1)
                node = tree.term_nodes[self.index]

            elif len(tree.input_nodes) > 0:
                self.index = randint(0, len(tree.input_nodes) - 1)
                node = tree.input_nodes[self.index]

        else:
            self.index = mutation_index
            node = tree.program[mutation_index]

        sub_tree = self.generator.generate_tree()
        tree.replace_node(node, sub_tree.root)
        tree.update()
        self.mutated = True

    def mutate(self, tree):
        mutation_methods = {
            "POINT_MUTATION": self.point_mutation,
            "HOIST_MUTATION": self.hoist_mutation,
            "SUBTREE_MUTATION": self.subtree_mutation,
            "SHRINK_MUTATION": self.shrink_mutation,
            "EXPAND_MUTATION": self.expansion_mutation
        }

        self.method = sample(self.config["mutation"]["methods"], 1)[0]
        self.index = None
        self.mutation_probability = self.config["mutation"]["probability"]
        self.random_probability = random()
        self.mutated = False
        self.before_mutation = None
        self.after_mutation = None

        # record before mutation
        self.before_mutation = tree.to_dict()["program"]

        # mutate
        if self.mutation_probability >= self.random_probability:
            mutation_func = mutation_methods[self.method]
            mutation_func(tree)

        # record after mutation
        self.after_mutation = tree.to_dict()["program"]

        # record
        if self.recorder is not None:
            self.recorder.record(RecordType.MUTATION, self)

    def to_dict(self):
        self_dict = {
            "method": self.method,
            "mutation_index": self.index,
            "mutation_probability": self.mutation_probability,
            "random_probability": self.random_probability,
            "mutated": self.mutated,
            "before_mutation": self.before_mutation,
            "after_mutation": self.after_mutation
        }

        return self_dict
Beispiel #15
0
    def setUp(self):
        self.config = {
            "max_population":
            10,
            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },
            "evaluator": {
                "use_cache": True
            },
            "selection": {
                "method": "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },
            "crossover": {
                "method": "POINT_CROSSOVER",
                "probability": 0.6
            },
            "mutation": {
                "methods": ["POINT_MUTATION"],
                "probability": 0.8
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }],
            "terminal_nodes": [
                {
                    "type": "CONSTANT",
                    "value": 1.0
                },
            ],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }],
            "data_file":
            "tests/data/sine.dat",
            "response_variables": [{
                "name": "y"
            }],
            "recorder": {
                "store_file": "json_store_test.json",
                "compress": True
            }
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.json_store = JSONStore(self.config)
        self.json_store.setup_store()

        self.population = self.generator.init()
        results = []
        cache = {}
        evaluate(self.population.individuals, self.functions, self.config,
                 results, cache, self.json_store)
        self.population.sort_individuals()

        self.selection = Selection(self.config, recorder=self.json_store)
        self.crossover = TreeCrossover(self.config, recorder=self.json_store)
        self.mutation = TreeMutation(self.config, recorder=self.json_store)
    def setUp(self):
        self.config = {
            "max_population": 5,

            "tree_generation": {
                "tree_type": "CLASSIFICATION_TREE",
                "method": "FULL_METHOD",
                "initial_max_depth": 2
            },

            "evaluator": {
                "use_cache": True
            },

            "function_nodes": [
                {
                    "type": "CLASS_FUNCTION",
                    "name": "GREATER_THAN",
                    "arity": 2,

                    "data_range": {
                        "lower_bound": 0.0,
                        "upper_bound": 10.0,
                        "decimal_places": 0,
                    }
                },
                {
                    "type": "CLASS_FUNCTION",
                    "name": "LESS_THAN",
                    "arity": 2,

                    "data_range": {
                        "lower_bound": 0.0,
                        "upper_bound": 10.0,
                        "decimal_places": 0,
                    }
                },
                {
                    "type": "CLASS_FUNCTION",
                    "name": "EQUALS",
                    "arity": 2,
                    "decimal_precision": 2,
                    "data_range": {
                        "lower_bound": 0.0,
                        "upper_bound": 10.0,
                        "decimal_places": 0,
                    }
                }
            ],

            "terminal_nodes": [
                {
                    "type": "RANDOM_CONSTANT",
                    "name": "species",
                    "range": [
                        1.0,
                        2.0,
                        3.0
                    ]
                },
            ],

            "input_variables": [
                {"name": "sepal_length"},
                {"name": "sepal_width"},
                {"name": "petal_length"},
                {"name": "petal_width"}
            ],

            "class_attributes": [
                "sepal_length",
                "sepal_width",
                "petal_length",
                "petal_width"
            ],

            "data_file": "tests/data/iris.dat",

            "response_variables": [{"name": "species"}]
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("CLASSIFICATION")
        self.generator = TreeGenerator(self.config)
        self.population = self.generator.init()
Beispiel #17
0
    def setUp(self):
        random.seed(0)
        self.config = {
            "max_population" : 20,
            "max_generation" : 5,

            "tree_generation" : {
                "method" : "GROW_METHOD",
                "initial_max_depth" : 4
            },

            "evaluator": {
                "use_cache" : True
            },

            "selection" : {
                "method" : "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },

            "crossover" : {
                "method" : "POINT_CROSSOVER",
                "probability" : 0.8
            },

            "mutation" : {
                "methods": [
                    "POINT_MUTATION",
                    "HOIST_MUTATION",
                    "SUBTREE_MUTATION",
                    "SHRINK_MUTATION",
                    "EXPAND_MUTATION"
                ],
                "probability" : 1.0
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 3.0},
                {"type": "CONSTANT", "value": 4.0},
                {"type": "CONSTANT", "value": 5.0},
                {"type": "CONSTANT", "value": 6.0},
                {"type": "CONSTANT", "value": 7.0},
                {"type": "CONSTANT", "value": 8.0},
                {"type": "CONSTANT", "value": 9.0},
                {"type": "CONSTANT", "value": 10.0}
            ],

            "input_variables" : [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file" : "tests/data/sine.dat",

            "response_variables" : [{"name": "y"}]
        }
        config.load_data(self.config)
        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config, recorder=None)
        self.crossover = TreeCrossover(self.config, recorder=None)
        self.mutation = TreeMutation(self.config, recorder=None)
class TreeGeneratorTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population":
            10,
            "tree_generation": {
                "tree_type": "SYMBOLIC_REGRESSION",
                "method": "RAMPED_HALF_AND_HALF_METHOD",
                "initial_max_depth": 3
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "arity": 2,
                "name": "ADD"
            }, {
                "type": "FUNCTION",
                "arity": 2,
                "name": "SUB"
            }, {
                "type": "FUNCTION",
                "arity": 2,
                "name": "MUL"
            }, {
                "type": "FUNCTION",
                "arity": 2,
                "name": "DIV"
            }, {
                "type": "FUNCTION",
                "arity": 1,
                "name": "COS"
            }, {
                "type": "FUNCTION",
                "arity": 1,
                "name": "SIN"
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "INPUT",
                "name": "x"
            }, {
                "type": "INPUT",
                "name": "y"
            }, {
                "type": "RANDOM_CONSTANT",
                "data_range": {
                    "upper_bound": 10.0,
                    "lower_bound": -10.0,
                    "decimal_places": 1
                }
            }],
            "input_variables": [{
                "name": "x"
            }, {
                "name": "y"
            }]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)
        self.parser = TreeParser()

    def tearDown(self):
        del self.config
        del self.generator
        del self.parser

    def test_generate_func_node(self):
        # SYMBOLIC REGRESSION TREES
        for i in range(100):
            node = self.generator.generate_func_node()
            self.assertEquals(node.node_type, NodeType.FUNCTION)

        # CLASSIFICATION TREES
        self.config["tree_generation"]["tree_type"] = "CLASSIFICATION_TREE"
        self.config["function_nodes"] = [{
            "type": "CLASS_FUNCTION",
            "name": "GREATER_THAN",
            "arity": 2,
            "data_range": {
                "lower_bound": 0.0,
                "upper_bound": 10.0,
                "decimal_places": 1
            }
        }]
        self.config["class_attributes"] = [
            "attrubte_1", "attrubte_2", "attrubte_3"
        ]
        generator = TreeGenerator(self.config)
        for i in range(100):
            node = generator.generate_func_node()
            class_attribute = node.class_attribute
            self.assertEquals(node.node_type, NodeType.CLASS_FUNCTION)
            self.assertTrue(class_attribute in self.config["class_attributes"])

    def test_resolve_random_constant(self):
        upper_bound = 10.0
        lower_bound = -10.0
        decimal_places = 0

        for i in range(100):
            n_details = {
                "type": "RANDOM_CONSTANT",
                "data_range": {
                    "lower_bound": lower_bound,
                    "upper_bound": upper_bound,
                    "decimal_places": decimal_places
                }
            }
            new_n_details = self.generator.resolve_random_constant(n_details)
            node_type = new_n_details["type"]
            node_value = new_n_details["value"]

            self.assertEquals(node_type, "CONSTANT")
            self.assertTrue(upper_bound >= node_value)
            self.assertTrue(lower_bound <= node_value)
            self.assertEquals(node_value, int(node_value))

        upper_bound = 100.0
        lower_bound = -100.0
        decimal_places = 1

        for i in range(100):
            n_details = {
                "type": "RANDOM_CONSTANT",
                "data_range": {
                    "lower_bound": lower_bound,
                    "upper_bound": upper_bound,
                    "decimal_places": decimal_places
                }
            }
            new_n_details = self.generator.resolve_random_constant(n_details)
            node_type = new_n_details["type"]
            node_value = new_n_details["value"]

            self.assertEquals(node_type, "CONSTANT")
            self.assertTrue(upper_bound >= node_value)
            self.assertTrue(lower_bound <= node_value)

            node_value = decimal.Decimal(str(node_value))
            node_decimal_places = abs(node_value.as_tuple().exponent)
            self.assertEquals(decimal_places, node_decimal_places)

    def test_generate_term_node(self):
        for i in range(100):
            node = self.generator.generate_term_node()
            self.assertTrue(node.node_type == NodeType.CONSTANT
                            or NodeType.INPUT)

    def test_full_method(self):
        tests = 1

        for i in xrange(tests):
            tree = self.generator.full_method()

            # asserts
            init_max = self.config["tree_generation"]["initial_max_depth"]
            self.assertEquals(tree.depth, init_max)
            self.assertTrue(tree.size > init_max)

    def test_grow_method(self):
        tests = 1000

        for i in xrange(tests):
            tree = self.generator.grow_method()

            # asserts
            init_max = self.config["tree_generation"]["initial_max_depth"]
            self.assertEquals(tree.depth, init_max)
            self.assertTrue(tree.size > init_max)

    def test_generate_tree_from_dict(self):
        population = self.generator.init()
        tree = population.individuals[0]
        tree_dict = self.parser.tree_to_dict(tree, tree.root)
        tree_generated = self.generator.generate_tree_from_dict(tree_dict)

        program_str = ""
        for i in tree.program:
            if i.name is not None:
                program_str += i.name
            else:
                program_str += str(i.value)

        generated_str = ""
        for i in tree_generated.program:
            if i.name is not None:
                generated_str += i.name
            else:
                generated_str += str(i.value)

        self.assertEquals(program_str, generated_str)

    def test_init(self):
        population = self.generator.init()
        self.assertEquals(len(population.individuals), 10)
Beispiel #19
0
class TreeMutation(object):
    def __init__(self, config, **kwargs):
        self.config = config
        self.recorder = kwargs.get("recorder", None)
        self.generator = TreeGenerator(self.config)

        # mutation stats
        self.method = None
        self.index = None
        self.mutation_probability = None
        self.random_probability = None
        self.mutated = False
        self.before_mutation = None
        self.after_mutation = None

    def generate_new_node(self, details):
        if details is None:
            return None

        elif details["type"] == NodeType.FUNCTION:
            return Node(NodeType.FUNCTION,
                        name=details["name"],
                        arity=details["arity"],
                        branches=[])

        elif details["type"] == NodeType.CLASS_FUNCTION:
            return Node(NodeType.CLASS_FUNCTION,
                        name=details["name"],
                        arity=details["arity"],
                        branches=[])

        elif details["type"] == NodeType.INPUT:
            return Node(NodeType.INPUT, name=details["name"])

        elif details["type"] == NodeType.CONSTANT:
            return Node(NodeType.CONSTANT,
                        name=details.get("name", None),
                        value=details["value"])

        elif details["type"] == NodeType.RANDOM_CONSTANT:
            resolved_details = self.generator.resolve_random_constant(details)
            return Node(NodeType.CONSTANT,
                        name=resolved_details.get("name", None),
                        value=resolved_details["value"])

    def mutate_new_node_details(self, old_node):
        # determine what kind of old_node it is
        node_pool = []
        if old_node.is_function() or old_node.is_class_function():
            tmp = list(self.config["function_nodes"])
            tmp = [n for n in tmp if n["arity"] == old_node.arity]
            node_pool.extend(tmp)

        elif old_node.is_terminal():
            node_pool.extend(self.config["terminal_nodes"])

        # check the node and return
        retry = 0
        retry_limit = 100
        while True:
            if retry == retry_limit:
                return None
            else:
                retry += 1

            n_details = sample(node_pool, 1)[0]
            if n_details["type"] == NodeType.RANDOM_CONSTANT:
                n_details = self.generator.resolve_random_constant(n_details)
            elif n_details["type"] == NodeType.CLASS_FUNCTION:
                n_details = self.generator.resolve_class_function(n_details)
            new_node = self.generate_new_node(n_details)

            if old_node.equals(new_node) is False:
                return n_details

    def point_mutation(self, tree, mutation_index=None):
        # mutate node
        self.index = randint(0, len(tree.program) - 1)
        node = tree.program[self.index]
        new_node = self.mutate_new_node_details(node)

        if new_node is None:
            return

        elif node.is_function():
            node.name = new_node["name"]

        elif node.is_terminal():
            node.node_type = new_node.get("type")
            node.name = new_node.get("name", None)
            node.value = new_node.get("value", None)

        tree.update()
        self.mutated = True

    def hoist_mutation(self, tree, mutation_index=None):
        # new indivdiaul generated from subtree
        node = None
        if mutation_index is None:
            self.index = randint(0, len(tree.program) - 2)
            node = tree.program[self.index]
        else:
            self.index = mutation_index
            node = tree.program[mutation_index]

        tree.root = node
        tree.update()
        self.mutated = True

    def subtree_mutation(self, tree, mutation_index=None):
        # subtree exchanged against external random subtree
        node = None
        if mutation_index is None:
            self.index = randint(0, len(tree.program) - 1)
            node = tree.program[self.index]
        else:
            self.index = mutation_index
            node = tree.program[mutation_index]

        self.generator.max_depth = randint(1, 3)
        sub_tree = self.generator.generate_tree()
        if node is not tree.root:
            tree.replace_node(node, sub_tree.root)
        else:
            tree.root = sub_tree.root
        tree.update()
        self.mutated = True

    def shrink_mutation(self, tree, mutation_index=None):
        # replace subtree with terminal
        if len(tree.func_nodes):
            node = None
            if mutation_index is None:
                self.index = randint(0, len(tree.func_nodes) - 1)
                node = tree.func_nodes[self.index]

                while node is tree.root:
                    self.index = randint(0, len(tree.func_nodes) - 1)
                    node = tree.func_nodes[self.index]
            else:
                self.index = mutation_index
                node = tree.program[mutation_index]

            candidate_nodes = tree.term_nodes
            candidate_nodes.extend(tree.input_nodes)
            new_node_detail = sample(candidate_nodes, 1)[0]
            node_details = self.mutate_new_node_details(new_node_detail)
            new_node = self.generate_new_node(node_details)

            if new_node:
                tree.replace_node(node, new_node)
                tree.update()
                self.mutated = True

    def expansion_mutation(self, tree, mutation_index=None):
        # terminal exchanged against external random subtree
        node = None
        if mutation_index is None:
            prob = random()

            if tree.size == 1:
                return

            elif prob > 0.5 and len(tree.term_nodes) > 0:
                self.index = randint(0, len(tree.term_nodes) - 1)
                node = tree.term_nodes[self.index]

            elif prob < 0.5 and len(tree.input_nodes) > 0:
                self.index = randint(0, len(tree.input_nodes) - 1)
                node = tree.input_nodes[self.index]

            elif len(tree.term_nodes) > 0:
                self.index = randint(0, len(tree.term_nodes) - 1)
                node = tree.term_nodes[self.index]

            elif len(tree.input_nodes) > 0:
                self.index = randint(0, len(tree.input_nodes) - 1)
                node = tree.input_nodes[self.index]

        else:
            self.index = mutation_index
            node = tree.program[mutation_index]

        sub_tree = self.generator.generate_tree()
        tree.replace_node(node, sub_tree.root)
        tree.update()
        self.mutated = True

    def mutate(self, tree):
        mutation_methods = {
            "POINT_MUTATION": self.point_mutation,
            "HOIST_MUTATION": self.hoist_mutation,
            "SUBTREE_MUTATION": self.subtree_mutation,
            "SHRINK_MUTATION": self.shrink_mutation,
            "EXPAND_MUTATION": self.expansion_mutation
        }

        self.method = sample(self.config["mutation"]["methods"], 1)[0]
        self.index = None
        self.mutation_probability = self.config["mutation"]["probability"]
        self.random_probability = random()
        self.mutated = False
        self.before_mutation = None
        self.after_mutation = None

        # record before mutation
        self.before_mutation = tree.to_dict()["program"]

        # mutate
        if self.mutation_probability >= self.random_probability:
            mutation_func = mutation_methods[self.method]
            mutation_func(tree)

        # record after mutation
        self.after_mutation = tree.to_dict()["program"]

        # record
        if self.recorder is not None:
            self.recorder.record(RecordType.MUTATION, self)

    def to_dict(self):
        self_dict = {
            "method": self.method,
            "mutation_index": self.index,
            "mutation_probability": self.mutation_probability,
            "random_probability": self.random_probability,
            "mutated": self.mutated,
            "before_mutation": self.before_mutation,
            "after_mutation": self.after_mutation
        }

        return self_dict
Beispiel #20
0
class PlayTests(unittest.TestCase):
    def setUp(self):
        random.seed(0)
        self.config = {
            "max_population":
            20,
            "max_generation":
            5,
            "tree_generation": {
                "method": "GROW_METHOD",
                "initial_max_depth": 4
            },
            "evaluator": {
                "use_cache": True
            },
            "selection": {
                "method": "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },
            "crossover": {
                "method": "POINT_CROSSOVER",
                "probability": 0.8
            },
            "mutation": {
                "methods": [
                    "POINT_MUTATION", "HOIST_MUTATION", "SUBTREE_MUTATION",
                    "SHRINK_MUTATION", "EXPAND_MUTATION"
                ],
                "probability":
                1.0
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 3.0
            }, {
                "type": "CONSTANT",
                "value": 4.0
            }, {
                "type": "CONSTANT",
                "value": 5.0
            }, {
                "type": "CONSTANT",
                "value": 6.0
            }, {
                "type": "CONSTANT",
                "value": 7.0
            }, {
                "type": "CONSTANT",
                "value": 8.0
            }, {
                "type": "CONSTANT",
                "value": 9.0
            }, {
                "type": "CONSTANT",
                "value": 10.0
            }],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }],
            "data_file":
            "tests/data/sine.dat",
            "response_variables": [{
                "name": "y"
            }]
        }
        config.load_data(self.config)
        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config, recorder=None)
        self.crossover = TreeCrossover(self.config, recorder=None)
        self.mutation = TreeMutation(self.config, recorder=None)

    def tearDown(self):
        del self.config

        del self.generator

        del self.selection
        del self.crossover
        del self.mutation

    def test_reproduce(self):
        tests = 1

        for i in range(tests):
            population = self.generator.init()

            res = []
            evaluate(population.individuals, self.functions, self.config, res)
            population.individuals = res

            # print "POPULATION"
            # for i in population.individuals:
            #     print i, i.score
            # print "\n"

            self.selection.select(population)

            # print "SELECTION"
            # for i in population.individuals:
            #     print i, i.score
            # print "\n"

            # reproduce
            play_details = play.play_details(population=population,
                                             selection=self.selection,
                                             crossover=self.crossover,
                                             mutation=self.mutation,
                                             evaluate=None,
                                             config=self.config)
            play.play_ga_reproduce(play_details)

            # print "REPRODUCE"
            # for i in population.individuals:
            #     print i, i.score

            # assert
            max_pop = self.config["max_population"]
            self.assertEquals(len(population.individuals), max_pop)
            self.assertTrue(population.config is self.config)
Beispiel #21
0
    def setUp(self):
        random.seed(0)
        self.config = {
            "max_population":
            20,
            "max_generation":
            5,
            "tree_generation": {
                "method": "GROW_METHOD",
                "initial_max_depth": 4
            },
            "evaluator": {
                "use_cache": True
            },
            "selection": {
                "method": "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },
            "crossover": {
                "method": "POINT_CROSSOVER",
                "probability": 0.8
            },
            "mutation": {
                "methods": [
                    "POINT_MUTATION", "HOIST_MUTATION", "SUBTREE_MUTATION",
                    "SHRINK_MUTATION", "EXPAND_MUTATION"
                ],
                "probability":
                1.0
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 3.0
            }, {
                "type": "CONSTANT",
                "value": 4.0
            }, {
                "type": "CONSTANT",
                "value": 5.0
            }, {
                "type": "CONSTANT",
                "value": 6.0
            }, {
                "type": "CONSTANT",
                "value": 7.0
            }, {
                "type": "CONSTANT",
                "value": 8.0
            }, {
                "type": "CONSTANT",
                "value": 9.0
            }, {
                "type": "CONSTANT",
                "value": 10.0
            }],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }],
            "data_file":
            "tests/data/sine.dat",
            "response_variables": [{
                "name": "y"
            }]
        }
        config.load_data(self.config)
        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config, recorder=None)
        self.crossover = TreeCrossover(self.config, recorder=None)
        self.mutation = TreeMutation(self.config, recorder=None)
Beispiel #22
0
class SelectionTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population" : 10,

            "tree_generation" : {
                "method" : "FULL_METHOD",
                "initial_max_depth" : 3
            },

            "selection" : {
                    "method" : "ROULETTE_SELECTION"
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 3.0},
                {"type": "CONSTANT", "value": 4.0},
                {"type": "CONSTANT", "value": 5.0},
                {"type": "CONSTANT", "value": 6.0},
                {"type": "CONSTANT", "value": 7.0},
                {"type": "CONSTANT", "value": 8.0},
                {"type": "CONSTANT", "value": 9.0},
                {"type": "CONSTANT", "value": 10.0}
            ],

            "input_variables" : [
                {"type": "INPUT", "name": "x"}
            ]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config)
        self.population = self.generator.init()

        # give population random scores
        for inidividual in self.population.individuals:
            inidividual.score = random.triangular(1, 100)

    def print_population(self, phase, population):
        print "{0}[{1}]:".format(phase, len(population.individuals))
        for individual in population.individuals:
            print individual, individual.score
        print '\n\n'

        return len(population.individuals)

    def test_normalize_scores(self):
        self.selection._normalize_scores(self.population)

        total_sum = 0
        for individual in self.population.individuals:
            total_sum += individual.score

        self.assertEquals(round(total_sum, 2), 1.0)

    def test_roulette_selection(self):
        print "ROULETTE SELECTION"

        old_pop_size = len(self.population.individuals)
        self.selection.roulette_wheel_selection(self.population)
        new_pop_size = len(self.population.individuals)

        # assert
        # check for object uniqueness
        individual_ids = []
        for i in self.population.individuals:
            self.assertFalse(id(i) in individual_ids)
            individual_ids.append(id(i))

        self.assertEquals(new_pop_size, old_pop_size)

    def test_tournament_selection(self):
        print "TOURNAMENT SELECTION"

        # tournament selection
        old_pop_size = self.print_population("OLD", self.population)
        self.selection.tournament_selection(self.population)
        new_pop_size = self.print_population("NEW", self.population)

        # assert
        # check for object uniqueness
        individual_ids = []
        for i in self.population.individuals:
            self.assertFalse(id(i) in individual_ids)
            individual_ids.append(id(i))

        self.assertEqual(old_pop_size, new_pop_size)

    def test_elitest_selection(self):
        print "ELITEST SELECTION"

        # elitest selection
        old_pop_size = self.print_population("OLD", self.population)
        self.selection.elitest_selection(self.population)
        new_pop_size = self.print_population("NEW", self.population)

        self.assertEquals(old_pop_size, new_pop_size)

    def test_greedy_over_selection(self):
        print "GREEDY-OVER SELECTION"

        # create population of size 1000
        self.config["max_population"] = 1000
        generator = TreeGenerator(self.config)
        population = generator.init()

        # greedy over selection
        old_pop_size = self.print_population("OLD", population)
        self.selection.greedy_over_selection(population)
        new_pop_size = self.print_population("NEW", population)

        self.assertEquals(old_pop_size, new_pop_size)

    def test_select(self):
        old_pop_size = len(self.population.individuals)
        self.selection.select(self.population)
        new_pop_size = len(self.population.individuals)

        # assert
        # check for object uniqueness
        individual_ids = []
        for i in self.population.individuals:
            self.assertFalse(id(i) in individual_ids)
            individual_ids.append(id(i))

        self.assertEquals(old_pop_size, new_pop_size)
Beispiel #23
0
    def setUp(self):
        self.config = {
            "max_population" : 10,

            "tree_generation" : {
                "method" : "FULL_METHOD",
                "initial_max_depth" : 4
            },

            "evaluator" : {
                "use_cache": True
            },

            "selection" : {
                "method" : "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },

            "crossover" : {
                "method" : "POINT_CROSSOVER",
                "probability" : 0.6
            },

            "mutation" : {
                "methods": ["POINT_MUTATION"],
                "probability" : 0.8
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
            ],

            "input_variables" : [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file" : "tests/data/sine.dat",
            "response_variables" : [{"name": "y"}],


            "recorder" : {
                "store_file": "json_store_test.json",
                "compress": True
            }
        }
        config.load_data(self.config)


        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.json_store = JSONStore(self.config)
        self.json_store.setup_store()

        self.population = self.generator.init()
        results = []
        cache = {}
        evaluate(
            self.population.individuals,
            self.functions,
            self.config,
            results,
            cache,
            self.json_store
        )
        self.population.sort_individuals()

        self.selection = Selection(self.config, recorder=self.json_store)
        self.crossover = TreeCrossover(self.config, recorder=self.json_store)
        self.mutation = TreeMutation(self.config, recorder=self.json_store)
Beispiel #24
0
class JSONStoreTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population" : 10,

            "tree_generation" : {
                "method" : "FULL_METHOD",
                "initial_max_depth" : 4
            },

            "evaluator" : {
                "use_cache": True
            },

            "selection" : {
                "method" : "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },

            "crossover" : {
                "method" : "POINT_CROSSOVER",
                "probability" : 0.6
            },

            "mutation" : {
                "methods": ["POINT_MUTATION"],
                "probability" : 0.8
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
            ],

            "input_variables" : [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file" : "tests/data/sine.dat",
            "response_variables" : [{"name": "y"}],


            "recorder" : {
                "store_file": "json_store_test.json",
                "compress": True
            }
        }
        config.load_data(self.config)


        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.json_store = JSONStore(self.config)
        self.json_store.setup_store()

        self.population = self.generator.init()
        results = []
        cache = {}
        evaluate(
            self.population.individuals,
            self.functions,
            self.config,
            results,
            cache,
            self.json_store
        )
        self.population.sort_individuals()

        self.selection = Selection(self.config, recorder=self.json_store)
        self.crossover = TreeCrossover(self.config, recorder=self.json_store)
        self.mutation = TreeMutation(self.config, recorder=self.json_store)

    def tearDown(self):
        self.json_store.delete_store()

        del self.config
        del self.functions
        del self.generator
        del self.population
        del self.json_store

    def test_setup_store(self):
        # assert
        file_exists = os.path.exists(self.config["recorder"]["store_file"])
        self.assertEquals(file_exists, True)

    def test_purge_store(self):
        # write something to store file
        self.json_store.store_file.write("Hello World\n")
        self.json_store.store_file.close()

        # purge store file
        self.json_store.purge_store()

        # assert
        store_file = open(self.config["recorder"]["store_file"], "r").read()
        self.assertEquals(len(store_file), 0)

    def test_delete_store(self):
        # delete store
        self.json_store.delete_store()

        # assert
        file_exists = os.path.exists(self.config["recorder"]["store_file"])
        self.assertEquals(file_exists, False)

    def test_record_population(self):
        self.json_store.record_population(self.population)

        record = self.json_store.generation_record
        self.assertNotEquals(record, {})
        self.assertEquals(record["population"]["generation"], 0)

    def test_record_selection(self):
        # record selection
        self.selection.select(self.population)

        # assert
        record = self.json_store.generation_record
        # import pprint
        # pprint.pprint(record)
        self.assertNotEquals(record, {})
        self.assertEquals(record["selection"]["selected"], 10)

    def test_record_crossover(self):
        # record crossover
        tree_1 = self.population.individuals[0]
        tree_2 = self.population.individuals[1]
        self.crossover.crossover(tree_1, tree_2)

        # assert
        record = self.json_store.generation_record
        self.assertNotEquals(record, {})

    def test_record_mutation(self):
        # record mutation
        tree = self.population.individuals[0]
        self.mutation.mutate(tree)

        # assert
        record = self.json_store.generation_record
        # pprint.pprint(record)
        self.assertNotEquals(record, {})

    def test_record_evaulation(self):
        # record evaluation
        results = []
        evaluate(
            self.population.individuals,
            self.functions,
            self.config,
            results,
            recorder=self.json_store
        )

        # assert
        record = self.json_store.generation_record
        # import pprint
        # pprint.pprint(record)
        self.assertEquals(record["evaluation"]["cache_size"], 10)
        self.assertEquals(record["evaluation"]["match_cached"], 0)

    def test_record_to_file(self):
        # write record to file and close
        self.json_store.record_population(self.population)
        self.json_store.record_to_file()
        self.json_store.store_file.close()

        # open up the file and restore json to dict
        store_file = open(self.config["recorder"]["store_file"], "r").read()
        data = json.loads(store_file)

        # assert tests
        self.assertNotEquals(data, {})
        self.assertEquals(data["population"]["generation"], 0)

    def test_summarize_store(self):
        # write record to file and close
        self.json_store.setup_store()
        self.json_store.record_population(self.population)

        for i in range(5):
            tree_1 = self.population.individuals[0]
            tree_2 = self.population.individuals[1]
            self.crossover.crossover(tree_1, tree_2)

        for i in range(10):
            tree = self.population.individuals[0]
            self.mutation.mutate(tree)

        self.json_store.record_to_file()
        self.json_store.store_file.close()

        # summarize
        self.json_store.summarize_store()

        # assert
        store_file = open(self.config["recorder"]["store_file"], "r")
        line = json.loads(store_file.read())
        store_file.close()

        self.assertIsNotNone(line)

    def test_finalize(self):
        # write record to file and close
        self.json_store.setup_store()
        self.json_store.record_population(self.population)
        self.json_store.record_to_file()
        self.json_store.store_file.close()

        # zip the store file
        self.json_store.finalize()

        # assert
        store_fp = self.config["recorder"]["store_file"]
        store_fp = list(os.path.splitext(store_fp))  # split ext
        store_fp[1] = ".zip"  # change ext to zip
        store_fp = "".join(store_fp)

        file_exists = os.path.exists(store_fp)
        self.assertEquals(file_exists, True)
def gp_benchmark_loop(config):
    try:
        # setup
        random.seed(config["random_seed"])  # VERY IMPORTANT!
        load_data(config, config["call_path"])
        json_store = JSONStore(config)
        # functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        generator = TreeGenerator(config)

        # genetic operators
        selection = Selection(config, recorder=json_store)
        crossover = TreeCrossover(config, recorder=json_store)
        mutation = TreeMutation(config, recorder=json_store)

        # setup the initial random population
        population = generator.init()

        # create play details
        details = play.play_details(
            population=population,
            functions=config["functions"],
            evaluate=evaluate,
            selection=selection,
            crossover=crossover,
            mutation=mutation,
            editor=edit_trees,
            stop_func=default_stop_func,
            # print_func=print_func,
            config=config,
            recorder=json_store)

        # run symbolic regression
        start_time = time.time()
        play.play(details)
        end_time = time.time()
        time_taken = end_time - start_time

        # print msg
        print("DONE -> pop: {0} cross: {1} mut: {2} seed: {3} [{4}s]".format(
            config["max_population"], config["crossover"]["probability"],
            config["mutation"]["probability"], config["random_seed"],
            round(time_taken, 2)))

        # log on completion
        if config.get("log_path", False):
            config.pop("data")
            msg = {
                "timestamp": time.mktime(datetime.now().timetuple()),
                "status": "DONE",
                "config": config,
                "runtime": time_taken,
                "best_score": population.find_best_individuals()[0].score,
                "best": str(population.find_best_individuals()[0])
            }
            log_path = os.path.expandvars(config["log_path"])
            log_file = open(log_path, "a+")
            log_file.write(json.dumps(msg) + "\n")
            log_file.close()

    except Exception as err_msg:
        import traceback
        traceback.print_exc()

        # log exception
        if config.get("log_path", False):
            msg = {
                "timestamp": time.mktime(datetime.now().timetuple()),
                "status": "ERROR",
                "config": config,
                "error": err_msg
            }
            log_path = os.path.expandvars(config["log_path"])
            log_file = open(log_path, "a+")
            log_file.write(json.dumps(msg) + "\n")
            log_file.close()

        raise  # raise the exception

    return config
Beispiel #26
0
def gp_predict(train_data, test_data, train_cat, xx, yy):
    # setup
    config = {
        "max_population":
        800,
        "max_generation":
        30,
        "stale_limit":
        10,
        "tree_generation": {
            "tree_type": "CLASSIFICATION_TREE",
            "method": "RAMPED_HALF_AND_HALF_METHOD",
            "depth_ranges": [{
                "size": 1,
                "percentage": 1.0
            }]
        },
        "evaluator": {
            "use_cache": True
        },
        "selection": {
            "method": "TOURNAMENT_SELECTION",
            "tournament_size": 100
        },
        "crossover": {
            "method": "POINT_CROSSOVER",
            "probability": 0.8
        },
        "mutation": {
            "methods": ["SUBTREE_MUTATION"],
            "probability": 0.8
        },
        "function_nodes": [{
            "type": "CLASS_FUNCTION",
            "name": "GREATER_THAN",
            "arity": 2,
            "data_range": {
                "lower_bound": -1.0,
                "upper_bound": 1.0,
                "decimal_places": 2,
            }
        }, {
            "type": "CLASS_FUNCTION",
            "name": "LESS_THAN",
            "arity": 2,
            "data_range": {
                "lower_bound": -1.0,
                "upper_bound": 1.0,
                "decimal_places": 2,
            }
        }, {
            "type": "CLASS_FUNCTION",
            "name": "EQUALS",
            "arity": 2,
            "data_range": {
                "lower_bound": -1.0,
                "upper_bound": 1.0,
                "decimal_places": 2
            }
        }],
        "terminal_nodes": [
            {
                "type": "RANDOM_CONSTANT",
                "name": "category",
                "range": [0.0, 1.0]
            },
        ],
        "class_attributes": ["x", "y"],
        "input_variables": [{
            "name": "x"
        }, {
            "name": "y"
        }],
        "response_variables": [{
            "name": "category"
        }]
    }

    # load data
    config["data"] = {}
    config["data"]["rows"] = len(train_data)
    config["data"]["x"] = []
    config["data"]["y"] = []
    config["data"]["category"] = train_cat
    for row in train_data:
        config["data"]["x"].append(row[0])
        config["data"]["y"].append(row[1])

    functions = GPFunctionRegistry("CLASSIFICATION")
    generator = TreeGenerator(config)

    # genetic operators
    selection = Selection(config)
    crossover = TreeCrossover(config)
    mutation = TreeMutation(config)

    # run symbolic regression
    population = generator.init()

    details = play.play_details(
        population=population,
        evaluate=evaluate,
        functions=functions,
        selection=selection,
        crossover=crossover,
        mutation=mutation,
        print_func=print_func,
        stop_func=default_stop_func,
        config=config,
        editor=edit_trees,
    )
    play.play(details)

    best_tree = population.best_individuals[0]
    # gp_plot_dt(best_tree, True)

    # load test data
    config["data"] = {}
    config["data"]["rows"] = len(test_data)
    config["data"]["x"] = []
    config["data"]["y"] = []
    for row in test_data:
        config["data"]["x"].append(row[0])
        config["data"]["y"].append(row[1])

    # predict
    predicted = gp_eval.predict_tree(best_tree, functions, config)

    # load test data
    config["data"] = {}
    config["data"]["rows"] = xx.shape[0] * xx.shape[1]
    config["data"]["x"] = np.reshape(xx, xx.shape[0] * xx.shape[1])
    config["data"]["y"] = np.reshape(yy, yy.shape[0] * yy.shape[1])

    contour = gp_eval.predict_tree(best_tree, functions, config)
    contour = np.array(contour)
    contour = contour.reshape(xx.shape)

    return predicted, contour
Beispiel #27
0
    def setUp(self):
        self.config = {
            "max_population":
            50,
            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },
            "evaluator": {
                "use_cache": True
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }],
            "terminal_nodes": [
                {
                    "type": "CONSTANT",
                    "value": 1.0
                },
            ],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }],
            "data_file":
            "tests/data/sine.dat",
            "response_variables": [{
                "name": "y"
            }]
        }
        config.load_data(self.config)

        self.functions = {
            "ADD": "+",
            "SUB": "-",
            "MUL": "*",
            "DIV": "/",
            "POW": "**",
            "SIN": "math.sin",
            "COS": "math.cos",
            "RAD": "math.radians",
            "LN": "math.ln",
            "LOG": "math.log"
        }
        self.generator = TreeGenerator(self.config)
    def setUp(self):
        self.config = {
            "max_population":
            10,
            "tree_generation": {
                "tree_type": "SYMBOLIC_REGRESSION",
                "method": "RAMPED_HALF_AND_HALF_METHOD",
                "initial_max_depth": 3
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "arity": 2,
                "name": "ADD"
            }, {
                "type": "FUNCTION",
                "arity": 2,
                "name": "SUB"
            }, {
                "type": "FUNCTION",
                "arity": 2,
                "name": "MUL"
            }, {
                "type": "FUNCTION",
                "arity": 2,
                "name": "DIV"
            }, {
                "type": "FUNCTION",
                "arity": 1,
                "name": "COS"
            }, {
                "type": "FUNCTION",
                "arity": 1,
                "name": "SIN"
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "INPUT",
                "name": "x"
            }, {
                "type": "INPUT",
                "name": "y"
            }, {
                "type": "RANDOM_CONSTANT",
                "data_range": {
                    "upper_bound": 10.0,
                    "lower_bound": -10.0,
                    "decimal_places": 1
                }
            }],
            "input_variables": [{
                "name": "x"
            }, {
                "name": "y"
            }]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)
        self.parser = TreeParser()
Beispiel #29
0
class TreeEvaluatorTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population": 50,

            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },

            "evaluator": {
                "use_cache": True
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
            ],

            "input_variables": [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file": "tests/data/sine.dat",

            "response_variables": [{"name": "y"}]
        }
        config.load_data(self.config)

        self.functions = {
            "ADD": "+",
            "SUB": "-",
            "MUL": "*",
            "DIV": "/",
            "POW": "**",
            "SIN": "math.sin",
            "COS": "math.cos",
            "RAD": "math.radians",
            "LN": "math.ln",
            "LOG": "math.log"
        }
        self.generator = TreeGenerator(self.config)

    def tearDown(self):
        del self.config
        del self.generator

    def test_generate_eq_function(self):
        # create terminal nodes
        term_node = Node(NodeType.CONSTANT, value=100.0)
        input_node = Node(NodeType.INPUT, name="x")

        # create function nodes
        mul_func = Node(
            NodeType.FUNCTION,
            name="MUL",
            arity=2,
            branches=[input_node, term_node]
        )

        rad_func = Node(
            NodeType.FUNCTION,
            name="RAD",
            arity=1,
            branches=[mul_func]
        )

        sin_func = Node(
            NodeType.FUNCTION,
            name="SIN",
            arity=1,
            branches=[rad_func]
        )

        # create tree
        tree = Tree()
        tree.root = sin_func
        tree.update()

        # generate equation function
        eq_func = evaluator.generate_eq_function(
            tree,
            self.functions,
            self.config
        )

        # assert
        self.assertIsNotNone(eq_func)
        self.assertEquals(round(eq_func(1), 4), 0.9848)

    def test_generate_eq_function_multivars(self):
        # create terminal nodes
        term_node = Node(NodeType.INPUT, name="var2")
        input_node = Node(NodeType.INPUT, name="var1")

        # create function nodes
        div_func = Node(
            NodeType.FUNCTION,
            name="DIV",
            arity=2,
            branches=[input_node, term_node]
        )

        # create tree
        tree = Tree()
        tree.root = div_func
        tree.update()

        # generate equation function
        config = {
            "input_variables": [
                {
                    "type": "INPUT",
                    "name": "var1"
                },
                {
                    "type": "INPUT",
                    "name": "var2"
                }
            ],

            "functions": {
                "ADD": "+",
                "SUB": "-",
                "MUL": "*",
                "DIV": "/",
                "POW": "**",
                "SIN": "math.sin",
                "COS": "math.cos",
                "RAD": "math.radians",
                "LN": "math.ln",
                "LOG": "math.log"
            }
        }
        eq_func = evaluator.generate_eq_function(tree, self.functions, config)

        # assert
        self.assertIsNotNone(eq_func)
        self.assertEquals(eq_func(1.0, 2.0), 0.5)

    def test_eval_tree(self):
        # create terminal nodes
        term_node = Node(NodeType.CONSTANT, value=100.0)
        input_node = Node(NodeType.INPUT, name="x")

        # create function nodes
        mul_func = Node(
            NodeType.FUNCTION,
            name="MUL",
            arity=2,
            branches=[input_node, term_node]
        )

        rad_func = Node(
            NodeType.FUNCTION,
            name="RAD",
            arity=1,
            branches=[mul_func]
        )

        sin_func = Node(
            NodeType.FUNCTION,
            name="SIN",
            arity=1,
            branches=[rad_func]
        )

        # create tree
        tree = Tree()
        tree.root = sin_func
        tree.update()

        # evaluate tree
        score, output = evaluator.eval_tree(tree, self.functions, self.config)
        self.assertEquals(round(score, 7), 0.5000001)

    def test_evaluate(self):
        population = self.generator.init()
        results = []

        start_time = time.time()
        evaluator.evaluate(
            population.individuals,
            self.functions,
            self.config,
            results
        )
        end_time = time.time()
        print("GP run took: %2.2fsecs\n" % (end_time - start_time))

        population.individuals = results

        # assert
        for individual in population.individuals:
            self.assertTrue(individual.score is not None)
            self.assertTrue(individual.score > 0)
class ClassifierEvaluationTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population": 5,

            "tree_generation": {
                "tree_type": "CLASSIFICATION_TREE",
                "method": "FULL_METHOD",
                "initial_max_depth": 2
            },

            "evaluator": {
                "use_cache": True
            },

            "function_nodes": [
                {
                    "type": "CLASS_FUNCTION",
                    "name": "GREATER_THAN",
                    "arity": 2,

                    "data_range": {
                        "lower_bound": 0.0,
                        "upper_bound": 10.0,
                        "decimal_places": 0,
                    }
                },
                {
                    "type": "CLASS_FUNCTION",
                    "name": "LESS_THAN",
                    "arity": 2,

                    "data_range": {
                        "lower_bound": 0.0,
                        "upper_bound": 10.0,
                        "decimal_places": 0,
                    }
                },
                {
                    "type": "CLASS_FUNCTION",
                    "name": "EQUALS",
                    "arity": 2,
                    "decimal_precision": 2,
                    "data_range": {
                        "lower_bound": 0.0,
                        "upper_bound": 10.0,
                        "decimal_places": 0,
                    }
                }
            ],

            "terminal_nodes": [
                {
                    "type": "RANDOM_CONSTANT",
                    "name": "species",
                    "range": [
                        1.0,
                        2.0,
                        3.0
                    ]
                },
            ],

            "input_variables": [
                {"name": "sepal_length"},
                {"name": "sepal_width"},
                {"name": "petal_length"},
                {"name": "petal_width"}
            ],

            "class_attributes": [
                "sepal_length",
                "sepal_width",
                "petal_length",
                "petal_width"
            ],

            "data_file": "tests/data/iris.dat",

            "response_variables": [{"name": "species"}]
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("CLASSIFICATION")
        self.generator = TreeGenerator(self.config)
        self.population = self.generator.init()

    def test_evaluate_tree(self):
        for i in range(100):
            tree = self.generator.generate_tree()
            score, output = evaluate_tree(tree, self.functions, self.config)

            # self.assertTrue(score < 1.0)
            self.assertEquals(len(output), self.config["data"]["rows"])

    def test_evaluate(self):
        results = []
        evaluate(self.population.individuals, self.functions, self.config, results)
        self.assertTrue(len(results), len(self.population.individuals))
Beispiel #31
0
                "petal_length",
                "petal_width"
            ],

            "data_file": "data/iris.dat",
            "input_variables": [
                {"name": "sepal_length"},
                {"name": "sepal_width"},
                {"name": "petal_length"},
                {"name": "petal_width"}
            ],
            "response_variables": [{"name": "species"}]
        }
        load_data(config, script_path)
        functions = GPFunctionRegistry("CLASSIFICATION")
        generator = TreeGenerator(config)

        # genetic operators
        selection = Selection(config)
        crossover = TreeCrossover(config)
        mutation = TreeMutation(config)

        # run symbolic regression
        population = generator.init()

        start_time = time.time()
        details = play.play_details(
            population=population,
            evaluate=evaluate,
            functions=functions,
            selection=selection,
Beispiel #32
0
class JSONStoreTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population":
            10,
            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },
            "evaluator": {
                "use_cache": True
            },
            "selection": {
                "method": "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },
            "crossover": {
                "method": "POINT_CROSSOVER",
                "probability": 0.6
            },
            "mutation": {
                "methods": ["POINT_MUTATION"],
                "probability": 0.8
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }],
            "terminal_nodes": [
                {
                    "type": "CONSTANT",
                    "value": 1.0
                },
            ],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }],
            "data_file":
            "tests/data/sine.dat",
            "response_variables": [{
                "name": "y"
            }],
            "recorder": {
                "store_file": "json_store_test.json",
                "compress": True
            }
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.json_store = JSONStore(self.config)
        self.json_store.setup_store()

        self.population = self.generator.init()
        results = []
        cache = {}
        evaluate(self.population.individuals, self.functions, self.config,
                 results, cache, self.json_store)
        self.population.sort_individuals()

        self.selection = Selection(self.config, recorder=self.json_store)
        self.crossover = TreeCrossover(self.config, recorder=self.json_store)
        self.mutation = TreeMutation(self.config, recorder=self.json_store)

    def tearDown(self):
        self.json_store.delete_store()

        del self.config
        del self.functions
        del self.generator
        del self.population
        del self.json_store

    def test_setup_store(self):
        # assert
        file_exists = os.path.exists(self.config["recorder"]["store_file"])
        self.assertEquals(file_exists, True)

    def test_purge_store(self):
        # write something to store file
        self.json_store.store_file.write("Hello World\n")
        self.json_store.store_file.close()

        # purge store file
        self.json_store.purge_store()

        # assert
        store_file = open(self.config["recorder"]["store_file"], "r").read()
        self.assertEquals(len(store_file), 0)

    def test_delete_store(self):
        # delete store
        self.json_store.delete_store()

        # assert
        file_exists = os.path.exists(self.config["recorder"]["store_file"])
        self.assertEquals(file_exists, False)

    def test_record_population(self):
        self.json_store.record_population(self.population)

        record = self.json_store.generation_record
        self.assertNotEquals(record, {})
        self.assertEquals(record["population"]["generation"], 0)

    def test_record_selection(self):
        # record selection
        self.selection.select(self.population)

        # assert
        record = self.json_store.generation_record
        # import pprint
        # pprint.pprint(record)
        self.assertNotEquals(record, {})
        self.assertEquals(record["selection"]["selected"], 10)

    def test_record_crossover(self):
        # record crossover
        tree_1 = self.population.individuals[0]
        tree_2 = self.population.individuals[1]
        self.crossover.crossover(tree_1, tree_2)

        # assert
        record = self.json_store.generation_record
        self.assertNotEquals(record, {})

    def test_record_mutation(self):
        # record mutation
        tree = self.population.individuals[0]
        self.mutation.mutate(tree)

        # assert
        record = self.json_store.generation_record
        # pprint.pprint(record)
        self.assertNotEquals(record, {})

    def test_record_evaulation(self):
        # record evaluation
        results = []
        evaluate(self.population.individuals,
                 self.functions,
                 self.config,
                 results,
                 recorder=self.json_store)

        # assert
        record = self.json_store.generation_record
        # import pprint
        # pprint.pprint(record)
        self.assertEquals(record["evaluation"]["cache_size"], 10)
        self.assertEquals(record["evaluation"]["match_cached"], 0)

    def test_record_to_file(self):
        # write record to file and close
        self.json_store.record_population(self.population)
        self.json_store.record_to_file()
        self.json_store.store_file.close()

        # open up the file and restore json to dict
        store_file = open(self.config["recorder"]["store_file"], "r").read()
        data = json.loads(store_file)

        # assert tests
        self.assertNotEquals(data, {})
        self.assertEquals(data["population"]["generation"], 0)

    def test_summarize_store(self):
        # write record to file and close
        self.json_store.setup_store()
        self.json_store.record_population(self.population)

        for i in range(5):
            tree_1 = self.population.individuals[0]
            tree_2 = self.population.individuals[1]
            self.crossover.crossover(tree_1, tree_2)

        for i in range(10):
            tree = self.population.individuals[0]
            self.mutation.mutate(tree)

        self.json_store.record_to_file()
        self.json_store.store_file.close()

        # summarize
        self.json_store.summarize_store()

        # assert
        store_file = open(self.config["recorder"]["store_file"], "r")
        line = json.loads(store_file.read())
        store_file.close()

        self.assertIsNotNone(line)

    def test_finalize(self):
        # write record to file and close
        self.json_store.setup_store()
        self.json_store.record_population(self.population)
        self.json_store.record_to_file()
        self.json_store.store_file.close()

        # zip the store file
        self.json_store.finalize()

        # assert
        store_fp = self.config["recorder"]["store_file"]
        store_fp = list(os.path.splitext(store_fp))  # split ext
        store_fp[1] = ".zip"  # change ext to zip
        store_fp = "".join(store_fp)

        file_exists = os.path.exists(store_fp)
        self.assertEquals(file_exists, True)
Beispiel #33
0
    def setUp(self):
        self.config = {
            "max_population":
            10,
            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 3
            },
            "selection": {
                "method": "ROULETTE_SELECTION"
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 3.0
            }, {
                "type": "CONSTANT",
                "value": 4.0
            }, {
                "type": "CONSTANT",
                "value": 5.0
            }, {
                "type": "CONSTANT",
                "value": 6.0
            }, {
                "type": "CONSTANT",
                "value": 7.0
            }, {
                "type": "CONSTANT",
                "value": 8.0
            }, {
                "type": "CONSTANT",
                "value": 9.0
            }, {
                "type": "CONSTANT",
                "value": 10.0
            }],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config)
        self.population = self.generator.init()

        # give population random scores
        for inidividual in self.population.individuals:
            inidividual.score = random.triangular(1, 100)
    def setUp(self):
        self.config = {
            "tree_generation": {
                "initial_max_depth": 4
            },
            "crossover": {
                "method": "POINT_CROSSOVER",
                "probability": 1.0
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 3.0
            }, {
                "type": "CONSTANT",
                "value": 4.0
            }, {
                "type": "CONSTANT",
                "value": 5.0
            }, {
                "type": "CONSTANT",
                "value": 6.0
            }, {
                "type": "CONSTANT",
                "value": 7.0
            }, {
                "type": "CONSTANT",
                "value": 8.0
            }, {
                "type": "CONSTANT",
                "value": 9.0
            }, {
                "type": "CONSTANT",
                "value": 10.0
            }],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.crossover = TreeCrossover(self.config)
        self.parser = TreeParser()

        # create nodes
        left_node_1 = Node(NodeType.INPUT, name="x")
        right_node_1 = Node(NodeType.CONSTANT, value=2.0)
        node = Node(NodeType.CONSTANT, value=2.0)

        left_node_2 = Node(NodeType.CONSTANT, value=3.0)
        right_node_2 = Node(NodeType.CONSTANT, value=4.0)

        cos_func_1 = Node(NodeType.FUNCTION,
                          name="ADD",
                          arity=2,
                          branches=[left_node_1, right_node_1])

        sin_func_1 = Node(NodeType.FUNCTION,
                          name="SIN",
                          arity=1,
                          branches=[node])

        cos_func_2 = Node(NodeType.FUNCTION,
                          name="COS",
                          arity=1,
                          branches=[left_node_2])
        sin_func_2 = Node(NodeType.FUNCTION,
                          name="SIN",
                          arity=1,
                          branches=[right_node_2])

        add_func = Node(NodeType.FUNCTION,
                        name="ADD",
                        arity=2,
                        branches=[cos_func_1, sin_func_1])

        sub_func = Node(NodeType.FUNCTION,
                        name="SUB",
                        arity=2,
                        branches=[sin_func_2, cos_func_2])

        # create tree_1
        self.tree_1 = Tree()
        self.tree_1.root = add_func
        self.tree_1.update()

        print self.tree_1

        # create tree_2
        self.tree_2 = Tree()
        self.tree_2.root = sub_func
        self.tree_2.update()
Beispiel #35
0
class ClassifierEvaluationTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population":
            5,
            "tree_generation": {
                "tree_type": "CLASSIFICATION_TREE",
                "method": "FULL_METHOD",
                "initial_max_depth": 2
            },
            "evaluator": {
                "use_cache": True
            },
            "function_nodes": [{
                "type": "CLASS_FUNCTION",
                "name": "GREATER_THAN",
                "arity": 2,
                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 0,
                }
            }, {
                "type": "CLASS_FUNCTION",
                "name": "LESS_THAN",
                "arity": 2,
                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 0,
                }
            }, {
                "type": "CLASS_FUNCTION",
                "name": "EQUALS",
                "arity": 2,
                "decimal_precision": 2,
                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 0,
                }
            }],
            "terminal_nodes": [
                {
                    "type": "RANDOM_CONSTANT",
                    "name": "species",
                    "range": [1.0, 2.0, 3.0]
                },
            ],
            "input_variables": [{
                "name": "sepal_length"
            }, {
                "name": "sepal_width"
            }, {
                "name": "petal_length"
            }, {
                "name": "petal_width"
            }],
            "class_attributes":
            ["sepal_length", "sepal_width", "petal_length", "petal_width"],
            "data_file":
            "tests/data/iris.dat",
            "response_variables": [{
                "name": "species"
            }]
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("CLASSIFICATION")
        self.generator = TreeGenerator(self.config)
        self.population = self.generator.init()

    def test_evaluate_tree(self):
        for i in range(100):
            tree = self.generator.generate_tree()
            score, output = evaluate_tree(tree, self.functions, self.config)

            # self.assertTrue(score < 1.0)
            self.assertEquals(len(output), self.config["data"]["rows"])

    def test_evaluate(self):
        results = []
        evaluate(self.population.individuals, self.functions, self.config,
                 results)
        self.assertTrue(len(results), len(self.population.individuals))
Beispiel #36
0
class PlayTests(unittest.TestCase):
    def setUp(self):
        random.seed(0)
        self.config = {
            "max_population" : 20,
            "max_generation" : 5,

            "tree_generation" : {
                "method" : "GROW_METHOD",
                "initial_max_depth" : 4
            },

            "evaluator": {
                "use_cache" : True
            },

            "selection" : {
                "method" : "TOURNAMENT_SELECTION",
                "tournament_size": 2
            },

            "crossover" : {
                "method" : "POINT_CROSSOVER",
                "probability" : 0.8
            },

            "mutation" : {
                "methods": [
                    "POINT_MUTATION",
                    "HOIST_MUTATION",
                    "SUBTREE_MUTATION",
                    "SHRINK_MUTATION",
                    "EXPAND_MUTATION"
                ],
                "probability" : 1.0
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 3.0},
                {"type": "CONSTANT", "value": 4.0},
                {"type": "CONSTANT", "value": 5.0},
                {"type": "CONSTANT", "value": 6.0},
                {"type": "CONSTANT", "value": 7.0},
                {"type": "CONSTANT", "value": 8.0},
                {"type": "CONSTANT", "value": 9.0},
                {"type": "CONSTANT", "value": 10.0}
            ],

            "input_variables" : [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file" : "tests/data/sine.dat",

            "response_variables" : [{"name": "y"}]
        }
        config.load_data(self.config)
        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config, recorder=None)
        self.crossover = TreeCrossover(self.config, recorder=None)
        self.mutation = TreeMutation(self.config, recorder=None)

    def tearDown(self):
        del self.config

        del self.generator

        del self.selection
        del self.crossover
        del self.mutation

    def test_reproduce(self):
        tests = 1

        for i in range(tests):
            population = self.generator.init()

            res = []
            evaluate(population.individuals, self.functions, self.config, res)
            population.individuals = res

            # print "POPULATION"
            # for i in population.individuals:
            #     print i, i.score
            # print "\n"

            self.selection.select(population)

            # print "SELECTION"
            # for i in population.individuals:
            #     print i, i.score
            # print "\n"

            # reproduce
            play_details = play.play_details(
                population=population,
                selection=self.selection,
                crossover=self.crossover,
                mutation=self.mutation,
                evaluate=None,
                config=self.config
            )
            play.play_ga_reproduce(play_details)

            # print "REPRODUCE"
            # for i in population.individuals:
            #     print i, i.score

            # assert
            max_pop = self.config["max_population"]
            self.assertEquals(len(population.individuals), max_pop)
            self.assertTrue(population.config is self.config)
Beispiel #37
0
class TreeEvaluatorTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population":
            50,
            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },
            "evaluator": {
                "use_cache": True
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }],
            "terminal_nodes": [
                {
                    "type": "CONSTANT",
                    "value": 1.0
                },
            ],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }],
            "data_file":
            "tests/data/sine.dat",
            "response_variables": [{
                "name": "y"
            }]
        }
        config.load_data(self.config)

        self.functions = {
            "ADD": "+",
            "SUB": "-",
            "MUL": "*",
            "DIV": "/",
            "POW": "**",
            "SIN": "math.sin",
            "COS": "math.cos",
            "RAD": "math.radians",
            "LN": "math.ln",
            "LOG": "math.log"
        }
        self.generator = TreeGenerator(self.config)

    def tearDown(self):
        del self.config
        del self.generator

    def test_generate_eq_function(self):
        # create terminal nodes
        term_node = Node(NodeType.CONSTANT, value=100.0)
        input_node = Node(NodeType.INPUT, name="x")

        # create function nodes
        mul_func = Node(NodeType.FUNCTION,
                        name="MUL",
                        arity=2,
                        branches=[input_node, term_node])

        rad_func = Node(NodeType.FUNCTION,
                        name="RAD",
                        arity=1,
                        branches=[mul_func])

        sin_func = Node(NodeType.FUNCTION,
                        name="SIN",
                        arity=1,
                        branches=[rad_func])

        # create tree
        tree = Tree()
        tree.root = sin_func
        tree.update()

        # generate equation function
        eq_func = evaluator.generate_eq_function(tree, self.functions,
                                                 self.config)

        # assert
        self.assertIsNotNone(eq_func)
        self.assertEquals(round(eq_func(1), 4), 0.9848)

    def test_generate_eq_function_multivars(self):
        # create terminal nodes
        term_node = Node(NodeType.INPUT, name="var2")
        input_node = Node(NodeType.INPUT, name="var1")

        # create function nodes
        div_func = Node(NodeType.FUNCTION,
                        name="DIV",
                        arity=2,
                        branches=[input_node, term_node])

        # create tree
        tree = Tree()
        tree.root = div_func
        tree.update()

        # generate equation function
        config = {
            "input_variables": [{
                "type": "INPUT",
                "name": "var1"
            }, {
                "type": "INPUT",
                "name": "var2"
            }],
            "functions": {
                "ADD": "+",
                "SUB": "-",
                "MUL": "*",
                "DIV": "/",
                "POW": "**",
                "SIN": "math.sin",
                "COS": "math.cos",
                "RAD": "math.radians",
                "LN": "math.ln",
                "LOG": "math.log"
            }
        }
        eq_func = evaluator.generate_eq_function(tree, self.functions, config)

        # assert
        self.assertIsNotNone(eq_func)
        self.assertEquals(eq_func(1.0, 2.0), 0.5)

    def test_eval_tree(self):
        # create terminal nodes
        term_node = Node(NodeType.CONSTANT, value=100.0)
        input_node = Node(NodeType.INPUT, name="x")

        # create function nodes
        mul_func = Node(NodeType.FUNCTION,
                        name="MUL",
                        arity=2,
                        branches=[input_node, term_node])

        rad_func = Node(NodeType.FUNCTION,
                        name="RAD",
                        arity=1,
                        branches=[mul_func])

        sin_func = Node(NodeType.FUNCTION,
                        name="SIN",
                        arity=1,
                        branches=[rad_func])

        # create tree
        tree = Tree()
        tree.root = sin_func
        tree.update()

        # evaluate tree
        score, output = evaluator.eval_tree(tree, self.functions, self.config)
        self.assertEquals(round(score, 7), 0.5000001)

    def test_evaluate(self):
        population = self.generator.init()
        results = []

        start_time = time.time()
        evaluator.evaluate(population.individuals, self.functions, self.config,
                           results)
        end_time = time.time()
        print("GP run took: %2.2fsecs\n" % (end_time - start_time))

        population.individuals = results

        # assert
        for individual in population.individuals:
            self.assertTrue(individual.score is not None)
            self.assertTrue(individual.score > 0)
Beispiel #38
0
    def setUp(self):
        self.config = {
            "tree_generation": {
                "method": "GROW_METHOD",
                "initial_max_depth": 4
            },
            "mutation": {
                "methods": [
                    "POINT_MUTATION", "HOIST_MUTATION", "SUBTREE_MUTATION",
                    "SHRINK_MUTATION", "EXPAND_MUTATION"
                ],
                "probability":
                1.0
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "INPUT",
                "name": "x"
            }],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }]
        }
        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.parser = TreeParser()
        self.mutation = TreeMutation(self.config)

        # create nodes
        left_node = Node(NodeType.CONSTANT, value=1.0)
        right_node = Node(NodeType.INPUT, name="x")

        cos_func = Node(NodeType.FUNCTION,
                        name="COS",
                        arity=1,
                        branches=[left_node])

        sin_func = Node(NodeType.FUNCTION,
                        name="SIN",
                        arity=1,
                        branches=[right_node])

        add_func = Node(NodeType.FUNCTION,
                        name="ADD",
                        arity=2,
                        branches=[cos_func, sin_func])

        # create tree
        self.tree = Tree()
        self.tree.root = add_func
        self.tree.update_program()
        self.tree.update_func_nodes()
        self.tree.update_term_nodes()
Beispiel #39
0
class TreeGeneratorTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population": 10,

            "tree_generation": {
                "tree_type": "SYMBOLIC_REGRESSION",
                "method": "RAMPED_HALF_AND_HALF_METHOD",
                "initial_max_depth": 3
            },

            "function_nodes": [
                {"type": "FUNCTION", "arity": 2, "name": "ADD"},
                {"type": "FUNCTION", "arity": 2, "name": "SUB"},
                {"type": "FUNCTION", "arity": 2, "name": "MUL"},
                {"type": "FUNCTION", "arity": 2, "name": "DIV"},
                {"type": "FUNCTION", "arity": 1, "name": "COS"},
                {"type": "FUNCTION", "arity": 1, "name": "SIN"}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "INPUT", "name": "x"},
                {"type": "INPUT", "name": "y"},
                {
                    "type": "RANDOM_CONSTANT",
                    "data_range": {
                        "upper_bound": 10.0,
                        "lower_bound": -10.0,
                        "decimal_places": 1
                    }
                }
            ],

            "input_variables": [
                {"name": "x"},
                {"name": "y"}
            ]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)
        self.parser = TreeParser()

    def tearDown(self):
        del self.config
        del self.generator
        del self.parser

    def test_generate_func_node(self):
        # SYMBOLIC REGRESSION TREES
        for i in range(100):
            node = self.generator.generate_func_node()
            self.assertEquals(node.node_type, NodeType.FUNCTION)

        # CLASSIFICATION TREES
        self.config["tree_generation"]["tree_type"] = "CLASSIFICATION_TREE"
        self.config["function_nodes"] = [
            {
                "type": "CLASS_FUNCTION",
                "name": "GREATER_THAN",
                "arity": 2,

                "data_range": {
                    "lower_bound": 0.0,
                    "upper_bound": 10.0,
                    "decimal_places": 1
                }
            }
        ]
        self.config["class_attributes"] = [
            "attrubte_1",
            "attrubte_2",
            "attrubte_3"
        ]
        generator = TreeGenerator(self.config)
        for i in range(100):
            node = generator.generate_func_node()
            class_attribute = node.class_attribute
            self.assertEquals(node.node_type, NodeType.CLASS_FUNCTION)
            self.assertTrue(class_attribute in self.config["class_attributes"])

    def test_resolve_random_constant(self):
        upper_bound = 10.0
        lower_bound = -10.0
        decimal_places = 0

        for i in range(100):
            n_details = {
                "type": "RANDOM_CONSTANT",
                "data_range": {
                    "lower_bound": lower_bound,
                    "upper_bound": upper_bound,
                    "decimal_places": decimal_places
                }
            }
            new_n_details = self.generator.resolve_random_constant(n_details)
            node_type = new_n_details["type"]
            node_value = new_n_details["value"]

            self.assertEquals(node_type, "CONSTANT")
            self.assertTrue(upper_bound >= node_value)
            self.assertTrue(lower_bound <= node_value)
            self.assertEquals(node_value, int(node_value))

        upper_bound = 100.0
        lower_bound = -100.0
        decimal_places = 1

        for i in range(100):
            n_details = {
                "type": "RANDOM_CONSTANT",
                "data_range": {
                    "lower_bound": lower_bound,
                    "upper_bound": upper_bound,
                    "decimal_places": decimal_places
                }
            }
            new_n_details = self.generator.resolve_random_constant(n_details)
            node_type = new_n_details["type"]
            node_value = new_n_details["value"]

            self.assertEquals(node_type, "CONSTANT")
            self.assertTrue(upper_bound >= node_value)
            self.assertTrue(lower_bound <= node_value)

            node_value = decimal.Decimal(str(node_value))
            node_decimal_places = abs(node_value.as_tuple().exponent)
            self.assertEquals(decimal_places, node_decimal_places)

    def test_generate_term_node(self):
        for i in range(100):
            node = self.generator.generate_term_node()
            self.assertTrue(
                node.node_type == NodeType.CONSTANT or NodeType.INPUT
            )

    def test_full_method(self):
        tests = 1

        for i in xrange(tests):
            tree = self.generator.full_method()

            # asserts
            init_max = self.config["tree_generation"]["initial_max_depth"]
            self.assertEquals(tree.depth, init_max)
            self.assertTrue(tree.size > init_max)

    def test_grow_method(self):
        tests = 1000

        for i in xrange(tests):
            tree = self.generator.grow_method()

            # asserts
            init_max = self.config["tree_generation"]["initial_max_depth"]
            self.assertEquals(tree.depth, init_max)
            self.assertTrue(tree.size > init_max)

    def test_generate_tree_from_dict(self):
        population = self.generator.init()
        tree = population.individuals[0]
        tree_dict = self.parser.tree_to_dict(tree, tree.root)
        tree_generated = self.generator.generate_tree_from_dict(tree_dict)

        program_str = ""
        for i in tree.program:
            if i.name is not None:
                program_str += i.name
            else:
                program_str += str(i.value)

        generated_str = ""
        for i in tree_generated.program:
            if i.name is not None:
                generated_str += i.name
            else:
                generated_str += str(i.value)

        self.assertEquals(program_str, generated_str)

    def test_init(self):
        population = self.generator.init()
        self.assertEquals(len(population.individuals), 10)
Beispiel #40
0
                "value": 10.0
            },
        ],
        "input_variables": [{
            "type": "INPUT",
            "name": "var1"
        }],
        "response_variables": [{
            "name": "answer"
        }],
        "data_file":
        "arabas_et_al-f1.dat"
    }
    config["max_population"] = 100000
    load_data(config, data_dir)
    generator = TreeGenerator(config)
    population = generator.init()
    results = []

    # TREE EVALUTOR 1
    start_time = time.time()
    tree_eval_1.evaluate(copy.deepcopy(population.individuals),
                         GPFunctionRegistry("SYMBOLIC_REGRESSION"), config,
                         results)
    end_time = time.time()
    time_taken = end_time - start_time
    print "Evaluator 1 took:", str(round(time_taken, 2)) + "s"

    # TREE EVALUTOR 2
    # functions = {
    #     "ADD": "+",
Beispiel #41
0
    def setUp(self):
        random.seed(10)

        self.config = {
            "max_population": 10,

            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "INPUT", "name": "x"},
                {"type": "INPUT", "name": "y"},
                {"type": "INPUT", "name": "z"}
            ],

            "input_variables": [
                {"name": "x"},
                {"name": "y"},
                {"name": "z"}
            ]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)
        self.parser = TreeParser()

        # create nodes
        left_node = Node(NodeType.CONSTANT, value=1.0)
        right_node = Node(NodeType.CONSTANT, value=2.0)

        cos_func = Node(
            NodeType.FUNCTION,
            name="COS",
            arity=1,
            branches=[left_node]
        )
        sin_func = Node(
            NodeType.FUNCTION,
            name="SIN",
            arity=1,
            branches=[right_node]
        )

        add_func = Node(
            NodeType.FUNCTION,
            name="ADD",
            arity=2,
            branches=[cos_func, sin_func]
        )

        # create tree
        self.tree = Tree()
        self.tree.root = add_func
        self.tree.update_program()
        self.tree.update_func_nodes()
        self.tree.update_term_nodes()
Beispiel #42
0
class SelectionTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population":
            10,
            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 3
            },
            "selection": {
                "method": "ROULETTE_SELECTION"
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "terminal_nodes": [{
                "type": "CONSTANT",
                "value": 1.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 2.0
            }, {
                "type": "CONSTANT",
                "value": 3.0
            }, {
                "type": "CONSTANT",
                "value": 4.0
            }, {
                "type": "CONSTANT",
                "value": 5.0
            }, {
                "type": "CONSTANT",
                "value": 6.0
            }, {
                "type": "CONSTANT",
                "value": 7.0
            }, {
                "type": "CONSTANT",
                "value": 8.0
            }, {
                "type": "CONSTANT",
                "value": 9.0
            }, {
                "type": "CONSTANT",
                "value": 10.0
            }],
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }]
        }

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

        self.selection = Selection(self.config)
        self.population = self.generator.init()

        # give population random scores
        for inidividual in self.population.individuals:
            inidividual.score = random.triangular(1, 100)

    def print_population(self, phase, population):
        print "{0}[{1}]:".format(phase, len(population.individuals))
        for individual in population.individuals:
            print individual, individual.score
        print '\n\n'

        return len(population.individuals)

    def test_normalize_scores(self):
        self.selection._normalize_scores(self.population)

        total_sum = 0
        for individual in self.population.individuals:
            total_sum += individual.score

        self.assertEquals(round(total_sum, 2), 1.0)

    def test_roulette_selection(self):
        print "ROULETTE SELECTION"

        old_pop_size = len(self.population.individuals)
        self.selection.roulette_wheel_selection(self.population)
        new_pop_size = len(self.population.individuals)

        # assert
        # check for object uniqueness
        individual_ids = []
        for i in self.population.individuals:
            self.assertFalse(id(i) in individual_ids)
            individual_ids.append(id(i))

        self.assertEquals(new_pop_size, old_pop_size)

    def test_tournament_selection(self):
        print "TOURNAMENT SELECTION"

        # tournament selection
        old_pop_size = self.print_population("OLD", self.population)
        self.selection.tournament_selection(self.population)
        new_pop_size = self.print_population("NEW", self.population)

        # assert
        # check for object uniqueness
        individual_ids = []
        for i in self.population.individuals:
            self.assertFalse(id(i) in individual_ids)
            individual_ids.append(id(i))

        self.assertEqual(old_pop_size, new_pop_size)

    def test_elitest_selection(self):
        print "ELITEST SELECTION"

        # elitest selection
        old_pop_size = self.print_population("OLD", self.population)
        self.selection.elitest_selection(self.population)
        new_pop_size = self.print_population("NEW", self.population)

        self.assertEquals(old_pop_size, new_pop_size)

    def test_greedy_over_selection(self):
        print "GREEDY-OVER SELECTION"

        # create population of size 1000
        self.config["max_population"] = 1000
        generator = TreeGenerator(self.config)
        population = generator.init()

        # greedy over selection
        old_pop_size = self.print_population("OLD", population)
        self.selection.greedy_over_selection(population)
        new_pop_size = self.print_population("NEW", population)

        self.assertEquals(old_pop_size, new_pop_size)

    def test_select(self):
        old_pop_size = len(self.population.individuals)
        self.selection.select(self.population)
        new_pop_size = len(self.population.individuals)

        # assert
        # check for object uniqueness
        individual_ids = []
        for i in self.population.individuals:
            self.assertFalse(id(i) in individual_ids)
            individual_ids.append(id(i))

        self.assertEquals(old_pop_size, new_pop_size)
Beispiel #43
0
    def test_evaluate(self):
        random.seed(10)
        # solution = {
        #     "results":
        #     [
        #         {"score": 15726642.002161335},
        #         {"score": 359.25843589015597},
        #         {"score": 92155571.22132382},
        #         {"score": 26186.46142920347},
        #         {"score": 15649304.847552022},
        #         {"score": 188.86069156360125},
        #         {"score": 23439.33097274221},
        #     ]
        # }

        # setup
        config = {
            "max_population" : 10,
            "max_generation" : 5,

            "tree_generation" : {
                "method" : "GROW_METHOD",
                "initial_max_depth" : 3
            },

            "evaluator": {
                "use_cache" : True
            },

            "selection" : {
                "method" : "TOURNAMENT_SELECTION",
                "tournament_size": 5
            },

            "crossover" : {
                "method" : "POINT_CROSSOVER",
                "probability" : 0.8
            },

            "mutation" : {
                "methods": [
                    "POINT_MUTATION",
                    "HOIST_MUTATION",
                    "SUBTREE_MUTATION",
                    "SHRINK_MUTATION",
                    "EXPAND_MUTATION"
                ],
                "probability" : 0.9
            },

            "function_nodes" : [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1}
            ],

            "terminal_nodes" : [
                {"type": "CONSTANT", "value": 1.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 2.0},
                {"type": "CONSTANT", "value": 3.0},
                {"type": "CONSTANT", "value": 4.0},
                {"type": "CONSTANT", "value": 5.0},
                {"type": "CONSTANT", "value": 6.0},
                {"type": "CONSTANT", "value": 7.0},
                {"type": "CONSTANT", "value": 8.0},
                {"type": "CONSTANT", "value": 9.0},
                {"type": "CONSTANT", "value": 10.0}
            ],


            "data_file" : "tests/data/sine.dat",

            "input_variables" : [{"type": "INPUT", "name": "x"}],
            "response_variables" : [{"name": "y"}]
        }
        parser = TreeParser()
        population = TreeGenerator(config).init()

        # create a dictionary of trees
        data = {"config": config, "individuals": []}
        for individual in population.individuals:
            tree_json = parser.tree_to_dict(individual, individual.root)
            data["individuals"].append(tree_json)

        # make sure population size is equals to number of trees
        population_size = len(population.individuals)
        individuals = len(data["individuals"])
        self.assertEquals(population_size, individuals)

        # evaluating individuals
        data = json.dumps(data)
        host = "localhost"
        port = 8080
        req_type = "POST"
        path = "evaluate"
        response = self.transmit(host, port, req_type, path, data)
        response = json.loads(response)
        print response
class TreeEvaluatorTests(unittest.TestCase):
    def setUp(self):
        self.config = {
            "max_population": 50,

            "tree_generation": {
                "method": "FULL_METHOD",
                "initial_max_depth": 4
            },

            "evaluator": {
                "use_cache": True
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1}
            ],

            "terminal_nodes": [
                {"type": "CONSTANT", "value": 1.0},
            ],

            "input_variables": [
                {"type": "INPUT", "name": "x"}
            ],

            "data_file": "tests/data/sine.dat",

            "response_variables": [{"name": "y"}]
        }
        config.load_data(self.config)

        self.functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        self.generator = TreeGenerator(self.config)

    def tearDown(self):
        del self.config
        del self.generator

    # def test_generate_eq_function(self):
    #     # create terminal nodes
    #     term_node = Node(NodeType.CONSTANT, value=100.0)
    #     input_node = Node(NodeType.INPUT, name="x")

    #     # create function nodes
    #     mul_func = Node(
    #         NodeType.FUNCTION,
    #         name="MUL",
    #         arity=2,
    #         branches=[input_node, term_node]
    #     )

    #     rad_func = Node(
    #         NodeType.FUNCTION,
    #         name="RAD",
    #         arity=1,
    #         branches=[mul_func]
    #     )

    #     sin_func = Node(
    #         NodeType.FUNCTION,
    #         name="SIN",
    #         arity=1,
    #         branches=[rad_func]
    #     )

    #     # create tree
    #     tree = Tree()
    #     tree.root = sin_func
    #     tree.update()

    #     # generate equation function
    #     eq_func = evaluator.generate_eq_function(
    #         tree,
    #         self.functions,
    #         self.config
    #     )

    #     # assert
    #     self.assertIsNotNone(eq_func)
    #     self.assertEquals(round(eq_func(1), 4), 0.9848)

    # def test_generate_eq_function_multivars(self):
    #     # create terminal nodes
    #     term_node = Node(NodeType.INPUT, name="var2")
    #     input_node = Node(NodeType.INPUT, name="var1")

    #     # create function nodes
    #     div_func = Node(
    #         NodeType.FUNCTION,
    #         name="DIV",
    #         arity=2,
    #         branches=[input_node, term_node]
    #     )

    #     # create tree
    #     tree = Tree()
    #     tree.root = div_func
    #     tree.update()

    #     # generate equation function
    #     config = {
    #         "input_variables": [
    #             {
    #                 "type": "INPUT",
    #                 "name": "var1"
    #             },
    #             {
    #                 "type": "INPUT",
    #                 "name": "var2"
    #             }
    #         ],

    #         "functions": {
    #             "ADD": "+",
    #             "SUB": "-",
    #             "MUL": "*",
    #             "DIV": "/",
    #             "POW": "**",
    #             "SIN": "math.sin",
    #             "COS": "math.cos",
    #             "RAD": "math.radians",
    #             "LN": "math.ln",
    #             "LOG": "math.log"
    #         }
    #     }
    #     eq_func = evaluator.generate_eq_function(tree, self.functions, config)

    #     # assert
    #     self.assertIsNotNone(eq_func)
    #     self.assertEquals(eq_func(1.0, 2.0), 0.5)

    # def test_eval_tree(self):
    #     # create terminal nodes
    #     term_node = Node(NodeType.CONSTANT, value=100.0)
    #     input_node = Node(NodeType.INPUT, name="x")

    #     # create function nodes
    #     mul_func = Node(
    #         NodeType.FUNCTION,
    #         name="MUL",
    #         arity=2,
    #         branches=[input_node, term_node]
    #     )

    #     rad_func = Node(
    #         NodeType.FUNCTION,
    #         name="RAD",
    #         arity=1,
    #         branches=[mul_func]
    #     )

    #     sin_func = Node(
    #         NodeType.FUNCTION,
    #         name="SIN",
    #         arity=1,
    #         branches=[rad_func]
    #     )

    #     # create tree
    #     tree = Tree()
    #     tree.root = sin_func
    #     tree.update()

    #     # evaluate tree
    #     result = evaluator.eval_tree(tree, self.functions, self.config)
    #     self.assertEquals(round(result, 7), 0.5000001)

    def test_evaluate(self):
        population = self.generator.init()
        results = []

        start_time = time.time()
        evaluator.evaluate(
            population.individuals,
            self.functions,
            self.config,
            results
        )
        end_time = time.time()
        print("GP run took: %2.2fsecs\n" % (end_time - start_time))

        population.individuals = results

        # assert
        for individual in population.individuals:
            self.assertTrue(individual.score is not None)
            self.assertTrue(individual.score > 0)
Beispiel #45
0
def gp_benchmark_loop(config):
    try:
        # setup
        random.seed(config["random_seed"])  # VERY IMPORTANT!
        load_data(config, config["call_path"])
        json_store = JSONStore(config)
        # functions = GPFunctionRegistry("SYMBOLIC_REGRESSION")
        generator = TreeGenerator(config)

        # genetic operators
        selection = Selection(config, recorder=json_store)
        crossover = TreeCrossover(config, recorder=json_store)
        mutation = TreeMutation(config, recorder=json_store)

        # setup the initial random population
        population = generator.init()

        # create play details
        details = play.play_details(
            population=population,
            functions=config["functions"],
            evaluate=evaluate,
            selection=selection,
            crossover=crossover,
            mutation=mutation,
            editor=edit_trees,
            stop_func=default_stop_func,
            # print_func=print_func,
            config=config,
            recorder=json_store
        )

        # run symbolic regression
        start_time = time.time()
        play.play(details)
        end_time = time.time()
        time_taken = end_time - start_time

        # print msg
        print(
            "DONE -> pop: {0} cross: {1} mut: {2} seed: {3} [{4}s]".format(
                config["max_population"],
                config["crossover"]["probability"],
                config["mutation"]["probability"],
                config["random_seed"],
                round(time_taken, 2)
            )
        )

        # log on completion
        if config.get("log_path", False):
            config.pop("data")
            msg = {
                "timestamp": time.mktime(datetime.now().timetuple()),
                "status": "DONE",
                "config": config,
                "runtime": time_taken,
                "best_score": population.find_best_individuals()[0].score,
                "best": str(population.find_best_individuals()[0])
            }
            log_path = os.path.expandvars(config["log_path"])
            log_file = open(log_path, "a+")
            log_file.write(json.dumps(msg) + "\n")
            log_file.close()

    except Exception as err_msg:
        import traceback
        traceback.print_exc()

        # log exception
        if config.get("log_path", False):
            msg = {
                "timestamp": time.mktime(datetime.now().timetuple()),
                "status": "ERROR",
                "config": config,
                "error": err_msg
            }
            log_path = os.path.expandvars(config["log_path"])
            log_file = open(log_path, "a+")
            log_file.write(json.dumps(msg) + "\n")
            log_file.close()

        raise  # raise the exception

    return config