def setUp(self): self.config = { "cartesian": { "rows": 4, "columns": 3, "levels_back": 1, "num_inputs": 4, "num_outputs": 1 }, "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} ], "data_file": "tests/data/sine.dat", "response_variables" : [{"name": "y"}], "input_variables": [ {"type": "INPUT", "name": "a"}, {"type": "INPUT", "name": "b"}, {"type": "INPUT", "name": "c"}, {"type": "INPUT", "name": "d"} ] } self.generator = CartesianGenerator(self.config)
def test_calculate_column_level(self): self.config["cartesian"]["num_inputs"] = 2 generator = CartesianGenerator(self.config) self.assertEquals(generator.calculate_column_level(2), 0) self.assertEquals(generator.calculate_column_level(5), 0) self.assertEquals(generator.calculate_column_level(6), 1) self.assertEquals(generator.calculate_column_level(9), 1) self.assertEquals(generator.calculate_column_level(10), 2) self.assertEquals(generator.calculate_column_level(13), 2) self.assertRaises( RuntimeError, self.generator.calculate_column_level, 0 ) self.assertRaises( RuntimeError, self.generator.calculate_column_level, 1 ) self.assertRaises( RuntimeError, self.generator.calculate_column_level, -1 ) self.assertRaises( RuntimeError, self.generator.calculate_column_level, 14 )
def __init__(self, config, **kwargs): self.config = config self.record_config = config.get("recorder", None) self.recorder = kwargs.get("recorder", None) self.generator = CartesianGenerator(config) # mutation stats self.method = None self.mutation_probability = None self.random_probability = None self.mutated = False self.before_mutation = None self.after_mutation = None
class CartesianGeneratorTests(unittest.TestCase): def setUp(self): self.config = { "cartesian": { "rows": 4, "columns": 3, "levels_back": 1, "num_inputs": 4, "num_outputs": 1 }, "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} ], "data_file": "tests/data/sine.dat", "response_variables" : [{"name": "y"}], "input_variables": [ {"type": "INPUT", "name": "a"}, {"type": "INPUT", "name": "b"}, {"type": "INPUT", "name": "c"}, {"type": "INPUT", "name": "d"} ] } self.generator = CartesianGenerator(self.config) def test_build_address_grid(self): result = self.generator.build_address_grid() self.assertEquals( result, [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15] ] ) self.config["cartesian"]["num_inputs"] = 2 result = self.generator.build_address_grid() self.assertEquals( result, [ [0, 1], [2, 3, 4, 5], [6, 7, 8, 9], [10, 11, 12, 13] ] ) def test_calculate_column_level(self): self.config["cartesian"]["num_inputs"] = 2 generator = CartesianGenerator(self.config) self.assertEquals(generator.calculate_column_level(2), 0) self.assertEquals(generator.calculate_column_level(5), 0) self.assertEquals(generator.calculate_column_level(6), 1) self.assertEquals(generator.calculate_column_level(9), 1) self.assertEquals(generator.calculate_column_level(10), 2) self.assertEquals(generator.calculate_column_level(13), 2) self.assertRaises( RuntimeError, self.generator.calculate_column_level, 0 ) self.assertRaises( RuntimeError, self.generator.calculate_column_level, 1 ) self.assertRaises( RuntimeError, self.generator.calculate_column_level, -1 ) self.assertRaises( RuntimeError, self.generator.calculate_column_level, 14 ) def test_get_valid_addresses(self): result = self.generator.get_valid_addresses(0) self.assertEquals(range(4), result) result = self.generator.get_valid_addresses(1) self.assertEquals(range(8), result) result = self.generator.get_valid_addresses(2) self.assertEquals( [4, 5, 6, 7, 8, 9, 10, 11], result ) self.assertRaises( RuntimeError, self.generator.get_valid_addresses, -1 ) self.assertRaises( RuntimeError, self.generator.get_valid_addresses, 3 ) def test_gen_random_conn_gene(self): for i in range(100): result = self.generator.gen_random_conn_gene(4) self.assertTrue(result >= 0, result <= 3) def test_gen_random_func_gene(self): num_funcs = len(self.config["function_nodes"]) for i in range(100): result = self.generator.gen_random_func_gene() self.assertTrue(result >= 0, result <= num_funcs - 1) def test_gen_random_func_node(self): for i in range(100): node_addr = 7 func_node = self.generator.gen_random_func_node(node_addr) func_index = func_node[0] func_conns = func_node[1:] # asserts # check number of connections arity = self.config["function_nodes"][func_index]["arity"] self.assertEquals(arity, len(func_node[1:])) # check what connections points to rows = self.config["cartesian"]["rows"] columns = self.config["cartesian"]["columns"] max_addr = (rows * columns) - 1 for conn in func_conns: self.assertTrue(conn >= 0 and conn <= max_addr) def test_gen_random_output_node(self): num_inputs = self.config["cartesian"]["num_inputs"] rows = self.config["cartesian"]["rows"] columns = self.config["cartesian"]["columns"] num_funcs = (rows * columns) max_addr = (num_funcs + num_inputs) - 1 for i in range(100): res = self.generator.gen_random_output_node() # asserts self.assertTrue(res >= 0 and res <= max_addr) def test_prep_input_nodes(self): input_nodes = self.generator.prep_input_nodes() self.assertEquals(input_nodes, ["a", "b", "c", "d"]) def test_generate_new_cartesian(self): cartesian = self.generator.generate_new_cartesian() cart_dict = cartesian.to_dict() # import pprint # pprint.pprint(cartesian.to_dict()) # asserts rows = self.config["cartesian"]["rows"] columns = self.config["cartesian"]["columns"] levels_back = self.config["cartesian"]["levels_back"] num_inputs = self.config["cartesian"]["num_inputs"] # assert graph config self.assertEquals(cartesian.rows, rows) self.assertEquals(cartesian.columns, columns) self.assertEquals(cartesian.levels_back, levels_back) # assert graph elements num_func_nodes = rows * columns self.assertEquals(len(cartesian.func_nodes), num_func_nodes) self.assertEquals(cart_dict["func_nodes_len"], num_func_nodes) self.assertEquals(len(cartesian.input_nodes), num_inputs) self.assertEquals(cart_dict["input_nodes_len"], num_inputs) num_output_nodes = self.config["cartesian"]["num_outputs"] self.assertEquals(len(cartesian.output_nodes), num_output_nodes) self.assertEquals(cart_dict["output_nodes_len"], num_output_nodes)
config["data"]["1.0"] = [1.0 for j in range(rows)] # import pprint # pprint.pprint(config) json_store = JSONStore(config) functions = [ funcs.add_function, funcs.sub_function, funcs.mul_function, funcs.div_function, funcs.sin_function, funcs.cos_function, funcs.rad_function ] generator = CartesianGenerator(config) # genetic operators selection = Selection(config, recorder=json_store) mutation = CartesianMutation(config) # run symbolic regression population = generator.init() start_time = time.time() details = play.play_details( population=population, evaluate=evaluate, functions=functions, selection=selection, mutation=mutation,
class CartesianMutation(object): def __init__(self, config, **kwargs): self.config = config self.record_config = config.get("recorder", None) self.recorder = kwargs.get("recorder", None) self.generator = CartesianGenerator(config) # mutation stats self.method = None self.mutation_probability = None self.random_probability = None self.mutated = False self.before_mutation = None self.after_mutation = None def mutate_new_func_gene(self, old_gene): retry = 0 retry_limit = 10 new_gene = old_gene while new_gene == old_gene and retry < retry_limit: new_gene = self.generator.gen_random_func_gene() retry += 1 if new_gene == old_gene: err = "Error! Failed to mutate new function gene!" raise RuntimeError(err) else: return new_gene def mutate_new_conn_gene(self, old_gene, node_addr): retry = 0 retry_limit = 20 new_gene = old_gene while new_gene == old_gene and retry < retry_limit: new_gene = self.generator.gen_random_conn_gene(node_addr) retry += 1 if new_gene == old_gene: err = "Error! Failed to mutate new function gene!" raise RuntimeError(err) else: return new_gene def mutate_function_node(self, node_addr, cartesian): # pick random function gene node = cartesian.graph()[node_addr] gene_index = randint(0, len(node) - 1) new_gene = None # mutate function gene if gene_index == 0: old_gene = node[gene_index] new_gene = self.mutate_new_func_gene(old_gene) old_arity = self.config["function_nodes"][old_gene]["arity"] new_arity = self.config["function_nodes"][new_gene]["arity"] if old_arity < new_arity: for i in range(old_arity - new_arity): conn_gene = self.generator.gen_random_conn_gene(node_addr) cartesian.graph()[node_addr].append(conn_gene) else: for i in range(new_arity - old_arity): cartesian.graph()[node_addr].pop() # mutate connection gene else: old_gene = node[gene_index] new_gene = self.mutate_new_conn_gene(old_gene, node_addr) # return cartesian.graph()[node_addr][gene_index] = new_gene return gene_index def mutate_output_node(self, node_index, cartesian): retry = 0 retry_limit = 20 old_node_addr = cartesian.output_nodes[node_index] new_node_addr = old_node_addr # pick random input node or func node while new_node_addr == old_node_addr and retry < retry_limit: new_node_addr = randint(0, len(cartesian.graph()) - 1) retry += 1 # mutate output node cartesian.output_nodes[node_index] = new_node_addr if retry == retry_limit: return None else: return new_node_addr def point_mutation(self, cartesian): result = None num_inputs = self.config["cartesian"]["num_inputs"] num_outputs = self.config["cartesian"]["num_outputs"] num_funcs = len(cartesian.func_nodes) max_addr = num_inputs + num_funcs + num_outputs - 1 # chose random node node_addr = randint(num_inputs, max_addr) # mutate function node if node_addr < num_inputs + num_funcs: # convert index to node_addr in cartesian graph and mutate gene_index = self.mutate_function_node(node_addr, cartesian) result = { "mutated_node": "FUNC_NODE", "node_addr": node_addr, "gene_index": gene_index } # mutate output node else: # convert index to output node index and mutate node_index = node_addr - num_inputs - num_funcs new_addr = self.mutate_output_node(node_index, cartesian) result = { "mutated_node": "OUTPUT_NODE", "output_node": node_index, "new_addr": new_addr } # check result if result is None: self.mutated = False else: self.mutated = True self.index = result def mutate(self, cartesian): mutation_methods = { "POINT_MUTATION": self.point_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 = copy.deepcopy(cartesian.program()) # mutate if self.mutation_probability >= self.random_probability: mutation_func = mutation_methods[self.method] mutation_func(cartesian) # record after mutation self.after_mutation = copy.deepcopy(cartesian.program()) # record if self.recorder is not None: self.recorder.record(RecordType.MUTATION, self) def to_dict(self): self_dict = { "method": self.method, "mutation_probability": self.mutation_probability, "random_probability": self.random_probability, "mutated": self.mutated, "index": self.index, "before_mutation": self.before_mutation, "after_mutation": self.after_mutation } return self_dict
# add constants rows = len(config["data"]["y"]) # for i in range(11): # config["data"][str(i) + ".0"] = [float(i) for j in range(rows)] config["data"]["1.0"] = [1.0 for j in range(rows)] # import pprint # pprint.pprint(config) json_store = JSONStore(config) functions = [ funcs.add_function, funcs.sub_function, funcs.mul_function, funcs.div_function, funcs.sin_function, funcs.cos_function, funcs.rad_function ] generator = CartesianGenerator(config) # genetic operators selection = Selection(config, recorder=json_store) mutation = CartesianMutation(config) # run symbolic regression population = generator.init() start_time = time.time() details = play.play_details(population=population, evaluate=evaluate, functions=functions, selection=selection, mutation=mutation, print_func=print_func,
class CartesianMutation(object): def __init__(self, config, **kwargs): self.config = config self.record_config = config.get("recorder", None) self.recorder = kwargs.get("recorder", None) self.generator = CartesianGenerator(config) # mutation stats self.method = None self.mutation_probability = None self.random_probability = None self.mutated = False self.before_mutation = None self.after_mutation = None def mutate_new_func_gene(self, old_gene): retry = 0 retry_limit = 10 new_gene = old_gene while new_gene == old_gene and retry < retry_limit: new_gene = self.generator.gen_random_func_gene() retry += 1 if new_gene == old_gene: err = "Error! Failed to mutate new function gene!" raise RuntimeError(err) else: return new_gene def mutate_new_conn_gene(self, old_gene, node_addr): retry = 0 retry_limit = 20 new_gene = old_gene while new_gene == old_gene and retry < retry_limit: new_gene = self.generator.gen_random_conn_gene(node_addr) retry += 1 if new_gene == old_gene: err = "Error! Failed to mutate new function gene!" raise RuntimeError(err) else: return new_gene def mutate_function_node(self, node_addr, cartesian): # pick random function gene node = cartesian.graph()[node_addr] gene_index = randint(0, len(node) - 1) new_gene = None # mutate function gene if gene_index == 0: old_gene = node[gene_index] new_gene = self.mutate_new_func_gene(old_gene) old_arity = self.config["function_nodes"][old_gene]["arity"] new_arity = self.config["function_nodes"][new_gene]["arity"] if old_arity < new_arity: for i in range(old_arity - new_arity): conn_gene = self.generator.gen_random_conn_gene(node_addr) cartesian.graph()[node_addr].append(conn_gene) else: for i in range(new_arity - old_arity): cartesian.graph()[node_addr].pop() # mutate connection gene else: old_gene = node[gene_index] new_gene = self.mutate_new_conn_gene(old_gene, node_addr) # return cartesian.graph()[node_addr][gene_index] = new_gene return gene_index def mutate_output_node(self, node_index, cartesian): retry = 0 retry_limit = 20 old_node_addr = cartesian.output_nodes[node_index] new_node_addr = old_node_addr # pick random input node or func node while new_node_addr == old_node_addr and retry < retry_limit: new_node_addr = randint(0, len(cartesian.graph()) - 1) retry += 1 # mutate output node cartesian.output_nodes[node_index] = new_node_addr if retry == retry_limit: return None else: return new_node_addr def point_mutation(self, cartesian): result = None num_inputs = self.config["cartesian"]["num_inputs"] num_outputs = self.config["cartesian"]["num_outputs"] num_funcs = len(cartesian.func_nodes) max_addr = num_inputs + num_funcs + num_outputs - 1 # chose random node node_addr = randint(num_inputs, max_addr) # mutate function node if node_addr < num_inputs + num_funcs: # convert index to node_addr in cartesian graph and mutate gene_index = self.mutate_function_node(node_addr, cartesian) result = { "mutated_node": "FUNC_NODE", "node_addr": node_addr, "gene_index": gene_index } # mutate output node else: # convert index to output node index and mutate node_index = node_addr - num_inputs - num_funcs new_addr = self.mutate_output_node(node_index, cartesian) result = { "mutated_node": "OUTPUT_NODE", "output_node": node_index, "new_addr": new_addr } # check result if result is None: self.mutated = False else: self.mutated = True self.index = result def mutate(self, cartesian): mutation_methods = {"POINT_MUTATION": self.point_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 = copy.deepcopy(cartesian.program()) # mutate if self.mutation_probability >= self.random_probability: mutation_func = mutation_methods[self.method] mutation_func(cartesian) # record after mutation self.after_mutation = copy.deepcopy(cartesian.program()) # record if self.recorder is not None: self.recorder.record(RecordType.MUTATION, self) def to_dict(self): self_dict = { "method": self.method, "mutation_probability": self.mutation_probability, "random_probability": self.random_probability, "mutated": self.mutated, "index": self.index, "before_mutation": self.before_mutation, "after_mutation": self.after_mutation } return self_dict