def test_program_init_depth(): """Check 'full' creates constant depth programs for single depth limit""" params = {'function_set': [add2, sub2, mul2, div2, sqrt1, log1, abs1, max2, min2], 'arities': {1: [sqrt1, log1, abs1], 2: [add2, sub2, mul2, div2, max2, min2]}, 'init_depth': (6, 6), 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1} random_state = check_random_state(415) programs = [] for i in range(20): programs.append(_Program(init_method='full', random_state=random_state, **params)) full_depth = np.bincount([gp.depth_ for gp in programs]) programs = [] for _ in range(20): programs.append(_Program(init_method='half and half', random_state=random_state, **params)) hnh_depth = np.bincount([gp.depth_ for gp in programs]) programs = [] for i in range(20): programs.append(_Program(init_method='grow', random_state=random_state, **params)) grow_depth = np.bincount([gp.depth_ for gp in programs]) assert_true(full_depth[-1] == 20) assert_false(hnh_depth[-1] == 20) assert_false(grow_depth[-1] == 20)
def test_export_graphviz(): """Check output of a simple program to Graphviz""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) # Test for a small program test_gp = [mul2, div2, 8, 1, sub2, 9, .5] gp = _Program(random_state=random_state, program=test_gp, **params) output = gp.export_graphviz() tree = 'digraph program {\n' \ 'node [style=filled]0 [label="mul", fillcolor="#136ed4"] ;\n' \ '1 [label="div", fillcolor="#136ed4"] ;\n' \ '2 [label="X8", fillcolor="#60a6f6"] ;\n' \ '3 [label="X1", fillcolor="#60a6f6"] ;\n' \ '1 -> 3 ;\n1 -> 2 ;\n' \ '4 [label="sub", fillcolor="#136ed4"] ;\n' \ '5 [label="X9", fillcolor="#60a6f6"] ;\n' \ '6 [label="0.500", fillcolor="#60a6f6"] ;\n' \ '4 -> 6 ;\n4 -> 5 ;\n0 -> 4 ;\n0 -> 1 ;\n}' assert_true(output == tree) # Test with fade_nodes output = gp.export_graphviz(fade_nodes=[0, 1, 2, 3]) tree = 'digraph program {\n' \ 'node [style=filled]0 [label="mul", fillcolor="#cecece"] ;\n' \ '1 [label="div", fillcolor="#cecece"] ;\n' \ '2 [label="X8", fillcolor="#cecece"] ;\n' \ '3 [label="X1", fillcolor="#cecece"] ;\n' \ '1 -> 3 ;\n1 -> 2 ;\n' \ '4 [label="sub", fillcolor="#136ed4"] ;\n' \ '5 [label="X9", fillcolor="#60a6f6"] ;\n' \ '6 [label="0.500", fillcolor="#60a6f6"] ;\n' \ '4 -> 6 ;\n4 -> 5 ;\n0 -> 4 ;\n0 -> 1 ;\n}' assert_true(output == tree) # Test a degenerative single-node program test_gp = [1] gp = _Program(random_state=random_state, program=test_gp, **params) output = gp.export_graphviz() tree = 'digraph program {\n' \ 'node [style=filled]0 [label="X1", fillcolor="#60a6f6"] ;\n}' assert_true(output == tree)
def test_print_overloading(): """Check that printing a program object results in 'pretty' output""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) test_gp = [mul2, div2, 8, 1, sub2, 9, .5] gp = _Program(random_state=random_state, program=test_gp, **params) orig_stdout = sys.stdout try: out = StringIO() sys.stdout = out print(gp) output = out.getvalue().strip() finally: sys.stdout = orig_stdout lisp = "mul(div(X8, X1), sub(X9, 0.500))" assert_true(output == lisp) # Test with feature names params['feature_names'] = [str(n) for n in range(10)] gp = _Program(random_state=random_state, program=test_gp, **params) orig_stdout = sys.stdout try: out = StringIO() sys.stdout = out print(gp) output = out.getvalue().strip() finally: sys.stdout = orig_stdout lisp = "mul(div(8, 1), sub(9, 0.500))" assert_true(output == lisp)
def test_get_subtree(): """Check that get subtree does the same thing for self and new programs""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) # Test for a small program test_gp = [mul2, div2, 8, 1, sub2, 9, .5] gp = _Program(random_state=random_state, program=test_gp, **params) self_test = gp.get_subtree(check_random_state(0)) external_test = gp.get_subtree(check_random_state(0), test_gp) assert_equal(self_test, external_test)
def test_all_metrics(): """Check all supported metrics work""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) # Test for a small program test_gp = [mul2, div2, 8, 1, sub2, 9, .5] gp = _Program(random_state=random_state, program=test_gp, **params) X = np.reshape(random_state.uniform(size=50), (5, 10)) y = random_state.uniform(size=5) sample_weight = np.ones(5) expected = [ 1.48719809776, 1.82389179833, 1.76013763179, -0.2928200724, -0.5 ] result = [] for m in ['mean absolute error', 'mse', 'rmse', 'pearson', 'spearman']: gp.metric = _fitness_map[m] gp.raw_fitness_ = gp.raw_fitness(X, y, sample_weight) result.append(gp.fitness()) assert_array_almost_equal(result, expected)
def test_execute(): """Check executing the program works""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) # Test for a small program test_gp = [mul2, div2, 8, 1, sub2, 9, .5] X = np.reshape(random_state.uniform(size=50), (5, 10)) gp = _Program(random_state=random_state, program=test_gp, **params) result = gp.execute(X) expected = [-0.19656208, 0.78197782, -1.70123845, -0.60175969, -0.01082618] assert_array_almost_equal(result, expected)
def test_indices(): """Check that indices are stable when generated on the fly.""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) test_gp = [mul2, div2, 8, 1, sub2, 9, .5] gp = _Program(random_state=random_state, program=test_gp, **params) assert_raises(ValueError, gp.get_all_indices) assert_raises(ValueError, gp._indices) def get_indices_property(): return gp.indices_ assert_raises(ValueError, get_indices_property) indices, _ = gp.get_all_indices(10, 7, random_state) assert_array_equal(indices, gp.get_all_indices()[0]) assert_array_equal(indices, gp._indices()) assert_array_equal(indices, gp.indices_)
def test_validate_program(): """Check that valid programs are accepted & invalid ones raise error""" function_set = [add2, sub2, mul2, div2, sqrt1, log1, abs1, max2, min2] arities = {1: [sqrt1, log1, abs1], 2: [add2, sub2, mul2, div2, max2, min2]}, init_depth = (2, 6) init_method = 'half and half' n_features = 10 const_range = (-1.0, 1.0) metric = 'mean absolute error' p_point_replace = 0.05 parsimony_coefficient = 0.1 random_state = check_random_state(415) test_gp = [sub2, abs1, sqrt1, log1, log1, sqrt1, 7, abs1, abs1, abs1, log1, sqrt1, 2] # This one should be fine _ = _Program(function_set, arities, init_depth, init_method, n_features, const_range, metric, p_point_replace, parsimony_coefficient, random_state, program=test_gp) # Now try a couple that shouldn't be assert_raises(ValueError, _Program, function_set, arities, init_depth, init_method, n_features, const_range, metric, p_point_replace, parsimony_coefficient, random_state, program=test_gp[:-1]) assert_raises(ValueError, _Program, function_set, arities, init_depth, init_method, n_features, const_range, metric, p_point_replace, parsimony_coefficient, random_state, program=test_gp + [1])
def test_program_init_method(): """Check 'full' creates longer and deeper programs than other methods""" params = { 'function_set': [add2, sub2, mul2, div2, sqrt1, log1, abs1, max2, min2], 'arities': { 1: [sqrt1, log1, abs1], 2: [add2, sub2, mul2, div2, max2, min2] }, 'init_depth': (2, 6), 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) programs = [] for i in range(20): programs.append( _Program(init_method='full', random_state=random_state, **params)) full_length = np.mean([gp.length_ for gp in programs]) full_depth = np.mean([gp.depth_ for gp in programs]) programs = [] for i in range(20): programs.append( _Program(init_method='half and half', random_state=random_state, **params)) hnh_length = np.mean([gp.length_ for gp in programs]) hnh_depth = np.mean([gp.depth_ for gp in programs]) programs = [] for i in range(20): programs.append( _Program(init_method='grow', random_state=random_state, **params)) grow_length = np.mean([gp.length_ for gp in programs]) grow_depth = np.mean([gp.depth_ for gp in programs]) assert_greater(full_length, hnh_length) assert_greater(hnh_length, grow_length) assert_greater(full_depth, hnh_depth) assert_greater(hnh_depth, grow_depth)
def test_genetic_operations(): """Check all genetic operations are stable and don't change programs""" params = { 'function_set': [add2, sub2, mul2, div2], 'arities': { 2: [add2, sub2, mul2, div2] }, 'init_depth': (2, 6), 'init_method': 'half and half', 'n_features': 10, 'const_range': (-1.0, 1.0), 'metric': 'mean absolute error', 'p_point_replace': 0.05, 'parsimony_coefficient': 0.1 } random_state = check_random_state(415) # Test for a small program test_gp = [mul2, div2, 8, 1, sub2, 9, .5] donor = [add2, 0.1, sub2, 2, 7] gp = _Program(random_state=random_state, program=test_gp, **params) assert_equal( [f.name if isinstance(f, _Function) else f for f in gp.reproduce()], ['mul', 'div', 8, 1, 'sub', 9, 0.5]) assert_equal(gp.program, test_gp) assert_equal([ f.name if isinstance(f, _Function) else f for f in gp.crossover(donor, random_state)[0] ], ['sub', 2, 7]) assert_equal(gp.program, test_gp) assert_equal([ f.name if isinstance(f, _Function) else f for f in gp.subtree_mutation(random_state)[0] ], ['mul', 'div', 8, 1, 'sub', 'sub', 3, 5, 'add', 6, 3]) assert_equal(gp.program, test_gp) assert_equal([ f.name if isinstance(f, _Function) else f for f in gp.hoist_mutation(random_state)[0] ], ['div', 8, 1]) assert_equal(gp.program, test_gp) assert_equal([ f.name if isinstance(f, _Function) else f for f in gp.point_mutation(random_state)[0] ], ['mul', 'div', 8, 1, 'sub', 9, 0.5]) assert_equal(gp.program, test_gp)
def pse_generator(random, args): individual = _Program( args["function_set"], args["arities"], args["init_depth"], args["init_method"], args["n_features"], args["const_range"], args["metric"], args["p_point_replace"], args["parsimony_coefficient"], args["random_state"], ) individual.build_program(args["random_state"]) while not individual.validate_program(): individual.build_program(args["random_state"]) return individual
def _parallel_evolve(n_programs, parents, X, y, sample_weight, seeds, params): """Private function used to build a batch of programs within a job.""" n_samples, n_features = X.shape # Unpack parameters tournament_size = params['tournament_size'] function_set = params['function_set'] arities = params['arities'] init_depth = params['init_depth'] init_method = params['init_method'] const_range = params['const_range'] metric = params['_metric'] parsimony_coefficient = params['parsimony_coefficient'] method_probs = params['method_probs'] p_point_replace = params['p_point_replace'] max_samples = params['max_samples'] max_samples = int(max_samples * n_samples) def _tournament(): """Find the fittest individual from a sub-population.""" contenders = random_state.randint(0, len(parents), tournament_size) fitness = [parents[p].fitness_ for p in contenders] if metric.greater_is_better: parent_index = contenders[np.argmax(fitness)] else: parent_index = contenders[np.argmin(fitness)] return parents[parent_index], parent_index # Build programs programs = [] for i in range(n_programs): random_state = check_random_state(seeds[i]) if parents is None: program = None genome = None else: method = random_state.uniform() parent, parent_index = _tournament() if method < method_probs[0]: # crossover donor, donor_index = _tournament() program, removed, remains = parent.crossover( donor.program, random_state) genome = { 'method': 'Crossover', 'parent_idx': parent_index, 'parent_nodes': removed, 'donor_idx': donor_index, 'donor_nodes': remains } elif method < method_probs[1]: # subtree_mutation program, removed, _ = parent.subtree_mutation(random_state) genome = { 'method': 'Subtree Mutation', 'parent_idx': parent_index, 'parent_nodes': removed } elif method < method_probs[2]: # hoist_mutation program, removed = parent.hoist_mutation(random_state) genome = { 'method': 'Hoist Mutation', 'parent_idx': parent_index, 'parent_nodes': removed } elif method < method_probs[3]: # point_mutation program, mutated = parent.point_mutation(random_state) genome = { 'method': 'Point Mutation', 'parent_idx': parent_index, 'parent_nodes': mutated } else: # reproduction program = parent.reproduce() genome = { 'method': 'Reproduction', 'parent_idx': parent_index, 'parent_nodes': [] } program = _Program(function_set=function_set, arities=arities, init_depth=init_depth, init_method=init_method, n_features=n_features, metric=metric, const_range=const_range, p_point_replace=p_point_replace, parsimony_coefficient=parsimony_coefficient, random_state=random_state, program=program) program.parents = genome # Draw samples, using sample weights, and then fit if sample_weight is None: curr_sample_weight = np.ones((n_samples, )) else: curr_sample_weight = sample_weight.copy() oob_sample_weight = curr_sample_weight.copy() indices, not_indices = program.get_all_indices(n_samples, max_samples, random_state) curr_sample_weight[not_indices] = 0 oob_sample_weight[indices] = 0 program.raw_fitness_ = program.raw_fitness(X, y, curr_sample_weight) if max_samples < n_samples: # Calculate OOB fitness program.oob_fitness_ = program.raw_fitness(X, y, oob_sample_weight) programs.append(program) return programs
def pse_variator(random, parent1, parent2, args): children = [] #parent1 = copy.deepcopy(parent1) #parent2 = copy.deepcopy(parent2) program1 = None program2 = None # TODO some default hard-coded probabilities...to be changed p_crossover = 0.8 p_subtree_mutation = 0.01 p_hoist_mutation = 0.01 p_point_mutation = 0.01 # TODO understand better how the different functions work; apparently, they do not # modify the individual, but return a new 'program' that is later used to initialize an individual if random.random() < p_crossover: program1, removed, remains = parent1.crossover(parent2.program, args["random_state"]) program2, removed, remains = parent2.crossover(parent1.program, args["random_state"]) elif random.random() < p_crossover + p_subtree_mutation: program1, removed, _ = parent1.subtree_mutation(args["random_state"]) program2, removed, _ = parent2.subtree_mutation(args["random_state"]) elif random.random() < p_crossover + p_subtree_mutation + p_hoist_mutation: program1, removed = parent1.hoist_mutation(args["random_state"]) program2, removed = parent2.hoist_mutation(args["random_state"]) # TODO: point_mutation has an issue with 'arity' (that should be a list or something, it's an int instead) #if random.random() < p_point_mutation : # program1, mutated = parent1.point_mutation(args["random_state"]) # program2, mutated = parent2.point_mutation(args["random_state"]) if program1 != None and program2 != None: child1 = _Program(args["function_set"], args["arities"], args["init_depth"], args["init_method"], args["n_features"], args["const_range"], args["metric"], args["p_point_replace"], args["parsimony_coefficient"], args["random_state"], program=program1) child2 = _Program(args["function_set"], args["arities"], args["init_depth"], args["init_method"], args["n_features"], args["const_range"], args["metric"], args["p_point_replace"], args["parsimony_coefficient"], args["random_state"], program=program2) #print("child1=", child1) #print("child2=", child2) #if child1.validate_program() : children.append( copy.deepcopy(child1) ) #if child2.validate_program() : children.append( copy.deepcopy(child2) ) if child1.validate_program(): children.append(child1) if child2.validate_program(): children.append(child2) return children