def __init__(self, f, allowed_functions=None, selection_strategy=None, init_list=None, depth=None, build_strategy='grow'): # build_strategy: "grow"/"full" self.f = f self.rtype = f.rtype allowed_children = self.f.allowed_children() if allowed_functions is not None: allowed_children = [[ child for child in child_list if child in allowed_functions ] for child_list in allowed_children] type_table = grow_type_table if build_strategy == 'grow' else full_type_table allowed_children = [[ child for child in child_list if child in type_table[depth][child.rtype] ] for child_list in allowed_children] if not all(allowed_children): raise UnsatisfiableType( "{} has a parameter that cannot be satisfied.".format( self.f.__name__)) if init_list is not None: self.num_children = len(init_list) self.children = [] for i, (fn, sublist) in enumerate(init_list): if fn not in allowed_children[i]: convert_fn = lookup_convert( f.__getattribute__('__params')[i], fn.rtype) if convert_fn is not None: self.children.append( Node(convert_fn, init_list=[(fn, sublist)])) else: raise UnsatisfiableType( "Invalid initialization for {}. {} expected, {} ({}) provided." .format(self.f.__name__, self.rtype, fn.__name__, fn.rtype)) else: self.children.append(Node(fn, init_list=sublist)) return if selection_strategy is not None: child_choices = selection_strategy( parent=self.f, children=allowed_children, ) else: child_choices = (random.choice(child_list) for child_list in allowed_children) self.children = [ Node(choice, allowed_functions=allowed_functions, selection_strategy=selection_strategy, depth=depth - 1, build_strategy=build_strategy) for choice in child_choices ] self.num_children = len(self.children)
def optimize(scoring_function, population_size=250, iterations=25, build_tree=build_tree, next_generation=next_generation, show_scores=True): print("Creating initial population of {}.".format(population_size)) sys.stdout.flush() population = [] for __ in xrange(population_size): try: tree = build_tree_to_requirements(scoring_function) population.append(tree) except UnsatisfiableType: raise UnsatisfiableType( "Could not meet input requirements. Found only {} satisfying trees." .format(len(population))) best_tree = [random.choice(population)] early_stop = [] def score_callback(iteration, scores): if not show_scores: return non_failure_scores = [ score for score in scores.values() if score != -sys.maxsize ] try: average_score = sum(non_failure_scores) / len(non_failure_scores) except ZeroDivisionError: average_score = -sys.maxsize best_score = max(scores.values()) best_tree.append(max(scores, key=scores.get)) print("Iteration {}:\tBest: {:.2f}\tAverage: {:.2f}".format( iteration + 1, best_score, average_score, )) sys.stdout.flush() if best_score == getattr(scoring_function, '__max_score', None): early_stop.append(True) print("Optimizing...") with recursion_limit(600): for iteration in xrange(iterations): callback = functools.partial(score_callback, iteration) population = next_generation(population, scoring_function, score_callback=callback) if early_stop: break best_tree = max(best_tree, key=scoring_function) return best_tree
def find_functions(return_type, allowed_functions=None, convert=True): functions = lookup_rtype(return_type, convert) if allowed_functions is None: return functions allowable = frozenset(functions) & frozenset(allowed_functions) if not allowable: raise UnsatisfiableType("No allowable functions satisfying {}.".format( (prettify_converted_type if not convert else str)(return_type))) return list(allowable)
def build_tree_to_requirements(scoring_function): params = getattr(scoring_function, '__params', ()) if len(params) != 1: raise ValueError("Scoring function must accept a single parameter.") return_type, = params for __ in xrange(9999): with recursion_limit(500): tree = build_tree(return_type, convert=False) requirements = getattr(scoring_function, 'required_inputs', ()) if not all(req in tree for req in requirements): continue return tree raise UnsatisfiableType("Could not meet input requirements.")
def crossover(first_tree, second_tree=None): first_tree_info = get_tree_info(first_tree) if second_tree is None: sending_tree_info, receiving_tree_info = first_tree_info, first_tree_info else: sending_tree_info, receiving_tree_info = (first_tree_info, get_tree_info(second_tree))[::random.choice((-1, 1))] mutual_rtypes = list(frozenset(sending_tree_info.nodes_by_rtype) & frozenset(receiving_tree_info.nodes_by_rtype)) if not mutual_rtypes: raise UnsatisfiableType("Trees are not compatible.") chosen_rtype = random.choice(mutual_rtypes) chosen_node = random.choice(receiving_tree_info.nodes_by_rtype[chosen_rtype]) chosen_replacement = random.choice(sending_tree_info.nodes_by_rtype[chosen_rtype]) chosen_node.parent.children[chosen_node.index] = copy.deepcopy(chosen_replacement.node) if second_tree is None: return first_tree return first_tree if first_tree_info is receiving_tree_info else second_tree
def __init__(self, f, allowed_functions=None, selection_strategy=None): self.f = f self.rtype = f.rtype allowed_children = self.f.allowed_children() if allowed_functions is not None: allowed_children = [ [child for child in child_list if child in allowed_functions] for child_list in allowed_children ] if not all(allowed_children): raise UnsatisfiableType( "{} has a parameter that cannot be satisfied.".format(self.f.__name__) ) if selection_strategy is not None: child_choices = selection_strategy( parent=self.f, children=allowed_children, ) else: child_choices = ( random.choice(child_list) for child_list in allowed_children ) self.children = [ Node( choice, allowed_functions=allowed_functions, selection_strategy=selection_strategy, ) for choice in child_choices ] self.num_children = len(self.children)