def __init__(self, inputs, outputs, debug=False): if not inputs: raise ValueError("Inputs can't be empty") if not outputs: raise ValueError("Ouputs can't be empty") # FIXME: check we have the same number of inputs and output # and they have the same shape because we are using the ouputs as # the next inputs # set debug self.debug = debug # graph underlying structure self.graph = nx.DiGraph() # tracking structures self.idx2op = {} # op object self.idx2results = {} # op computation result self.idx2input_idx = defaultdict(set) # ops used as inputs self.idx2ouput_ops = defaultdict(set) # ops used as outputs self.inputs_idx = [] # track which op idx are inputs self.outputs_idx = [] # track what op idx are outputs self.fitness = None self.compiled = False self._results = None self.callback_collection = None # storing inputs tensors self.inputs = box(inputs) for ipt in self.inputs: self.inputs_idx.append(ipt.idx) # output self.outputs = box(outputs) for output in self.outputs: self.outputs_idx.append(output.idx) # build forward graph for output in self.outputs: self._add_op_to_graph(output, None, self.debug) # FIXME: check that the graph is fully connected from input to output # coerce exec_path as a list to allow reuse accros batches. self.execution_path = list(nx.topological_sort(self.graph))
def compile(self, selection_strategy, fitness_functions): """Configure evoluationary model for training """ # FIXME: check args validity self.selection_strategy = selection_strategy self.fitness_functions = box(fitness_functions) self.compiled = True self._results = Results(debug=self.debug)
def __call__(self, ops): # boxing inputs if needed ops = box(ops) # graph computation or eager? if self.debug: input_types = [type(op) for op in ops] self.print_debug('inputs type:%s' % (input_types)) if issubclass(type(ops[0]), OP): # graph mode self.print_debug('graph mode') input_shapes = [] for op in ops: assert issubclass(type(op), OP) self.input_ops.append(op) input_shapes.append(op.get_output_shapes()) self.compute_output_shape(input_shapes) return self else: # eager mode self.print_debug('eager mode') # check inputs are valis for op in ops: if not B.is_tensor(op): raise ValueError("Expecting list(tensors) or a tensor") # input shapes input_shapes = [] for op in ops: input_shapes.append(op.shape) self.compute_output_shape(input_shapes) # compute concrete results - dispatch iterate through populations. return unbox(self.dispatch(ops, self.EAGER))
def dispatch(self, populations, mode): return box(self.call(populations))
def _call_from_graph(self, populations): "Function called during graph executions" populations = box(populations) # dispatch take care of iterating through populations return unbox(self.dispatch(populations, self.GRAPH))
def evolve(self, populations, generations=1, callbacks=None, verbose=1): if not self.compiled: raise ValueError("compile() must be run before using the graph") return self.callback_collection = CallbackCollection(callbacks) populations = box(populations) self.print_debug("Initial Populations", populations) if not len(populations) == len(self.inputs): raise ValueError('The numbers of population must be equal\ to number of inputs') # assign initial value to inputs current_populations = [] for pop_idx, ipt in enumerate(self.inputs): self.inputs[pop_idx].assign(populations[pop_idx]) pop = self.inputs[pop_idx].get() current_populations.append(pop) num_populations = len(current_populations) self.print_debug('Initial current_populations', current_populations) # callbacks self.callback_collection.on_evolution_begin(current_populations) # progress bar if verbose: pb = tqdm(total=generations, unit='generation') # evolve loop for generation_idx in range(generations): # callbacks self.callback_collection.on_generation_begin(generation_idx) # perform evolution evolved_populations = self.perform_evolution() # assign evolved populations self.print_debug(generation_idx, 'evolved pop', evolved_populations) fitness_scores_list = [] # keep track of fitness scores metrics_list = [] # keep track of the metrics scores for pop_idx in range(num_populations): # find current informaiton current_pop = current_populations[pop_idx] evolved_pop = evolved_populations[pop_idx] fitness_function = self.fitness_functions[pop_idx] self.print_debug('current_population', pop_idx, current_pop) self.print_debug('evolved_population', pop_idx, evolved_pop) # select population new_population, fitness_scores, metrics = self.selection_strategy( # noqa fitness_function, current_pop, evolved_pop) # tracks metrics metrics_list.append(metrics) fitness_scores_list.append(fitness_scores) # update population tensor self.inputs[pop_idx].assign(new_population) # track current population current_populations[pop_idx] = new_population # record fitness scores self._results.record_fitness(fitness_scores_list) latest_metrics = self._results.get_latest_metrics(flatten=True) # callbacks self.callback_collection.on_generation_end(generation_idx, latest_metrics, fitness_scores_list, populations) # progress bar if verbose: formatted_metrics = {} for name, value in latest_metrics.items(): name = name.lower().replace(' ', '_') formatted_metrics[name] = value pb.set_postfix(formatted_metrics) pb.update() if verbose: pb.close() # final callback self.callback_collection.on_evolution_end(current_populations) # record last population self._results.set_population(current_populations) # return last evolution return self._results
def test_box_unbox_tensor(): val = tensor([1, 2, 3]) assert tensor_equal(unbox(box(val)), val)
def test_box_unbox(): vals = ['a', [1, 2, 3]] for val in vals: assert unbox(box(val)) == val