def get_actives(self, block_material: BlockMaterial): ''' method will go through and set the attributes block_material.active_nodes and active_args. active_nodes will include all output_nodes, a subset of main_nodes and input_nodes. ''' ezLogging.info("%s - Inside get_actives" % (block_material.id)) block_material.active_nodes = set(np.arange(self.main_count, self.main_count+self.output_count)) block_material.active_args = set() #block_material.active_ftns = set() # add feeds into the output_nodes for node_input in range(self.main_count, self.main_count+self.output_count): block_material.active_nodes.update([block_material[node_input]]) for node_index in reversed(range(self.main_count)): if node_index in block_material.active_nodes: # then add the input nodes to active list block_material.active_nodes.update(block_material[node_index]["inputs"]) block_material.active_args.update(block_material[node_index]["args"]) else: pass # sort block_material.active_nodes = sorted(list(block_material.active_nodes)) ezLogging.debug("%s - active nodes: %s" % (block_material.id, block_material.active_nodes)) block_material.active_args = sorted(list(block_material.active_args)) ezLogging.debug("%s - active args: %s" % (block_material.id, block_material.active_args))
def evaluate(self, block_material: BlockMaterial, block_def,#: BlockDefinition, training_datapair: ezData, validation_datapair: ezData): ''' stuff the old code has but unclear why gpus = tf.config.experimental.list_physical_devices('GPU') #tf.config.experimental.set_virtual_device_configuration(gpus[0],[ tf.config.experimental.VirtualDeviceConfiguration(memory_limit = 1024*3) ]) ''' ezLogging.info("%s - Start evaluating..." % (block_material.id)) try: self.build_graph(block_material, block_def, training_datapair) except Exception as err: ezLogging.critical("%s - Build Graph; Failed: %s" % (block_material.id, err)) block_material.dead = True import pdb; pdb.set_trace() return try: output = self.train_graph(block_material, block_def, training_datapair, validation_datapair) except Exception as err: ezLogging.critical("%s - Train Graph; Failed: %s" % (block_material.id, err)) block_material.dead = True import pdb; pdb.set_trace() return block_material.output = output # TODO make sure it is a list
def fill_args(self, block_def: BlockDefinition, block_material: BlockMaterial): ''' TODO ''' block_material.args = [None] * block_def.arg_count for arg_index, arg_type in enumerate(block_def.arg_types): block_material.args[arg_index] = arg_type()
def build_block(self, block_def: BlockDefinition, indiv_id): ''' TODO ''' block_material = BlockMaterial(block_def.nickname) block_material.set_id(indiv_id) self.fill_args(block_def, block_material) self.fill_genome(block_def, block_material) block_def.get_actives(block_material) return block_material
def standard_build_graph(self, block_material: BlockMaterial, block_def,#: BlockDefinition, input_layers = None): ''' trying to generalize the graph building process similar to standard_evaluate() For Transfer Learning: we expect input_layers to be None, and later when we call function(*inputs, *args), we want to pass in an empty list for inputs. This also guarentees that no matter how many 'active nodes' we have for our transfer learning block, we will only ever use one pretrained model...no inputs are shared between nodes so the models never connect! ''' # add input data if input_layers is not None: for i, input_layer in enumerate(input_layers): block_material.evaluated[-1*(i+1)] = input_layer # go solve for node_index in block_material.active_nodes: if node_index < 0: # do nothing. at input node continue elif node_index >= block_def.main_count: # do nothing NOW. at output node. we'll come back to grab output after this loop continue else: # main node. this is where we evaluate function = block_material[node_index]["ftn"] inputs = [] if input_layers is not None: node_input_indices = block_material[node_index]["inputs"] for node_input_index in node_input_indices: inputs.append(block_material.evaluated[node_input_index]) ezLogging.debug("%s - Eval %i; input index: %s" % (block_material.id, node_index, node_input_indices)) args = [] node_arg_indices = block_material[node_index]["args"] for node_arg_index in node_arg_indices: args.append(block_material.args[node_arg_index].value) ezLogging.debug("%s - Eval %i; arg index: %s, value: %s" % (block_material.id, node_index, node_arg_indices, args)) ezLogging.debug("%s - Eval %i; Function: %s, Inputs: %s, Args: %s" % (block_material.id, node_index, function, inputs, args)) block_material.evaluated[node_index] = function(*inputs, *args) output = [] if not block_material.dead: for output_index in range(block_def.main_count, block_def.main_count+block_def.output_count): output.append(block_material.evaluated[block_material.genome[output_index]]) ezLogging.info("%s - Ending evaluating...%i output" % (block_material.id, len(output))) return output
def evaluate(self, block_material: BlockMaterial, block_def,#: BlockDefinition, training_datapair: ezData, validation_datapair: ezData): ezLogging.info("%s - Start evaluating..." % (block_material.id)) try: self.build_graph(block_material, block_def, training_datapair) except Exception as err: ezLogging.critical("%s - Build Graph; Failed: %s" % (block_material.id, err)) block_material.dead = True import pdb; pdb.set_trace() return block_material.output = [training_datapair, validation_datapair]
def mutate_single_argvalue(mutant_material: BlockMaterial, block_def): #: BlockDefinition): ''' instead of looking for a different arg index in .args with the same arg type, mutate the value stored in this arg index. ''' ezLogging.info("%s - Inside mutate_single_argvalue" % (mutant_material.id)) if len(mutant_material.active_args) > 0: # if block has arguments, then there is something to mutate choices = np.arange(block_def.arg_count) choices = rnd.choice(choices, size=len(choices), replace=False) #randomly reorder for arg_index in choices: mutant_material.args[arg_index].mutate() ezLogging.info("%s - Mutated node %i; new arg value: %s" % (mutant_material.id, arg_index, mutant_material.args[arg_index])) if arg_index in mutant_material.active_args: # active_arg finally mutated ezLogging.debug("%s - Mutated node %i - active" % (mutant_material.id, arg_index)) mutant_material.need_evaluate = True break else: ezLogging.debug("%s - Mutated node %i - inactive" % (mutant_material.id, arg_index)) else: # won't actually mutate ezLogging.warning("%s - No active args to mutate" % (mutant_material.id))
def evaluate(self, block_material: BlockMaterial, block_def,#: BlockDefinition, training_datapair: ezData, validation_datapair: ezData=None): ezLogging.info("%s - Start evaluating..." % (block_material.id)) output_list = self.standard_evaluate(block_material, block_def, training_datapair.x) #training_datapair.x = output_list[0] #block_material.output = training_datapair block_material.output = output_list
def evaluate(self, block_material: BlockMaterial, block_def, #: BlockDefinition, training_datapair: ezData, validation_datapair: ezData): ezLogging.info("%s - Start evaluating..." % (block_material.id)) output_list = self.standard_evaluate(block_material, block_def, [training_datapair.pipeline]) training_datapair.pipeline = output_list[0] #assuming only outputs the pipeline block_material.output = [training_datapair, validation_datapair]
def evaluate(self, block_material: BlockMaterial, block_def,#: BlockDefinition, training_datapair: ezData, validation_datapair: ezData): ezLogging.info("%s - Start evaluating..." % (block_material.id)) try: # check if this fails our conditions if block_def.main_count != 1: raise self.TooManyMainNodes(block_def.main_count) if 0 not in block_material.active_nodes: raise self.NoActiveMainNodes(block_material.active_nodes) # good to continue to build self.build_graph(block_material, block_def, training_datapair) except Exception as err: ezLogging.critical("%s - Build Graph; Failed: %s" % (block_material.id, err)) block_material.dead = True import pdb; pdb.set_trace() return block_material.output = [training_datapair, validation_datapair]
def evaluate(self, block_material: BlockMaterial, block_def,#: BlockDefinition, training_datapair: ezData, validation_datapair: ezData): ezLogging.info("%s - Start evaluating..." % (block_material.id)) try: self.build_graph(block_material, block_def, training_datapair.pipeline_wrapper) except Exception as err: ezLogging.critical("%s - Build Graph; Failed: %s" % (block_material.id, err)) block_material.dead = True import pdb; pdb.set_trace() return try: # outputs a list of the validation metrics output = self.train_graph(block_material, block_def, training_datapair, validation_datapair) except Exception as err: ezLogging.critical("%s - Train Graph; Failed: %s" % (block_material.id, err)) block_material.dead = True import pdb; pdb.set_trace() return block_material.output = [None, output] # TODO make sure it is a list
def mutate_single_argindex(mutant_material: BlockMaterial, block_def): #: BlockDefinition): ''' search through the args and try to find a matching arg_type and use that arg index instead ''' ezLogging.info("%s - Inside mutate_single_argindex" % (mutant_material.id)) if len(mutant_material.active_args) > 0: # then there is something to mutate choices = [] # need to find those nodes with 'args' filled #weights = [] # option to sample node_index by the number of args for each node for node_index in range(block_def.main_count): if len(mutant_material[node_index]["args"]) > 0: choices.append(node_index) #weights.append(len(mutant_material[node_index]["args"])) else: pass choices = rnd.choice(choices, size=len(choices), replace=False) #randomly reorder for node_index in choices: ith_arg = rnd.choice( np.arange(len(mutant_material[node_index]["args"]))) current_arg = mutant_material[node_index]["args"][ith_arg] arg_dtype = block_def.get_node_dtype(mutant_material, node_index, "args")[ith_arg] new_arg = block_def.get_random_arg(arg_dtype, exclude=[current_arg]) if new_arg is None: # failed to find a new_arg continue else: mutant_material[node_index]["args"][ith_arg] = new_arg ezLogging.info( "%s - Mutated node %i; ori arg index: %i, new arg index: %i" % (mutant_material.id, node_index, current_arg, new_arg)) if node_index in mutant_material.active_nodes: # active_node finally mutated ezLogging.debug("%s - Mutated node %i - active" % (mutant_material.id, node_index)) mutant_material.need_evaluate = True break else: ezLogging.debug("%s - Mutated node %i - inactive" % (mutant_material.id, node_index)) else: # won't actually mutate ezLogging.warning("%s - No active args to mutate" % (mutant_material.id))
def evaluate(self, block_material: BlockMaterial, block_def,#: BlockDefinition, training_datapair: ezData, validation_datapair: ezData): ezLogging.info("%s - Start evaluating..." % (block_material.id)) # going to treat training + validation as separate block_materials! output = [] for datapair in [training_datapair, validation_datapair]: single_output_list = self.standard_evaluate(block_material, block_def, [datapair.pipeline]) datapair.pipeline = single_output_list[0] if block_material.dead: return [] else: output.append(datapair) self.preprocess_block_evaluate(block_material) #prep for next loop through datapair block_material.output = output
def get_lisp(self, block_material: BlockMaterial): ''' the idea is to help with seeding, we have the ability to collapse an individual into a tree and then collapse into a lisp string representation note we'll be using active nodes a lot here...and don't forget that "active_nodes will include all output_nodes, a subset of main_nodes and input_nodes." ''' block_material.lisp = [] # get actives just in case it has changed since last evaluated...but it really shouldn't have! self.get_actives(block_material) # each output will have it's own tree #for ith_output in range(self.output_count): # first going to create a dictionary of the active nodes inputs _active_dict = {} #output_node = self.main_count+ith_output #_active_dict['output'] = block_material[output_node] for ith_node in reversed(block_material.active_nodes): if (ith_node<0) or (ith_node>=self.main_count): #input or ouptput node continue func = block_material[ith_node]['ftn'] inputs = block_material[ith_node]['inputs'] args = block_material[ith_node]['args'] # now start to shape it into a lisp lisp = ['%s' % func.__name__] for _input in inputs: # attach an 'n' to remind us that this is a node number and not an arg # later we'll go through and replace each node with it's own entry in _active_dict lisp.append('%in' % _input) for _arg in args: lisp.append('%s' % str(block_material.args[_arg])) # and now throw the lisp into our _active_dict (tree) _active_dict['%i' % ith_node] = lisp # at this point we have the ith node and arg values for each active node # now we'll go through the list and replace each node with each entry from the dict # this is how we slowly build out the branches of the trees for ith_node in block_material.active_nodes: if (ith_node<0) or (ith_node>=self.main_count): #input or ouptput node continue lisp = _active_dict[str(ith_node)] new_lisp = [] for i, val in enumerate(lisp): if i == 0: # 0th position in lisp should be the function. keep and append. pass elif val.endswith('n'): if int(val[:-1]) < 0: ''' decided not to use the datatype and just assume that input datatypes match ...this stuff unecessarily complicates loading in an individual so going to just pass it as -1n or -2n etc #input node so we want to instead pass in the datatype we expect # -1th genome is 0th input # -2nd genome is 1th input... genome_node*-1 - 1 = input_node val = self.input_dtypes[(int(val[:-1])*-1)-1]''' pass else: # then it's a node number, replace with that node's new_lisp val = _active_dict[str(val[:-1])] #[:-1] to remove 'n' else: # then it's an arg and we pass it as such pass new_lisp.append(val) # replace lisp with new_lisp in the dict _active_dict[str(ith_node)] = new_lisp # now all that should be left are the output nodes # each output node produces it's own tree so it's own final lisp for ith_output in range(self.output_count): final_node = block_material[self.main_count+ith_output] # convert the final list into a string lisp_str = str(_active_dict[str(final_node)]) # since it was a list of strings, there will be a mess of quotes inside # so replace any quotes, and spaces while we're at it lisp_str = lisp_str.replace("'","").replace('"','').replace(" ", "") # that's our tree. so append to lisp block_material.lisp.append(lisp_str)
def fill_genome(self, block_def: BlockDefinition, block_material: BlockMaterial): ''' TODO ''' block_material.genome = [None] * block_def.genome_count block_material.genome[( -1 * block_def.input_count):] = ["InputPlaceholder" ] * block_def.input_count # fill main nodes for node_index in range(block_def.main_count): ftns = block_def.get_random_ftn(return_all=True) for ftn in ftns: # find inputs input_dtypes = block_def.operator_dict[ftn]["inputs"] input_index = [None] * len(input_dtypes) for ith_input, input_dtype in enumerate(input_dtypes): input_index[ith_input] = block_def.get_random_input( block_material, req_dtype=input_dtype, _max=node_index) if None in input_index: # failed to fill it in; try another ftn continue else: pass # find args arg_dtypes = block_def.operator_dict[ftn]["args"] arg_index = [None] * len(arg_dtypes) for ith_arg, arg_dtype in enumerate(arg_dtypes): arg_index[ith_arg] = block_def.get_random_arg( req_dtype=arg_dtype) if None in arg_index: # failed to fill it in; try another ftn continue else: pass # all complete block_material[node_index] = { "ftn": ftn, "inputs": input_index, "args": arg_index } break # error check that node got filled if block_material[node_index] is None: print( "GENOME ERROR: no primitive was able to fit into current genome arrangment" ) import pdb pdb.set_trace() exit() # fill output nodes for ith_output, node_index in enumerate( range(block_def.main_count, block_def.main_count + block_def.output_count)): req_dtype = block_def.output_dtypes[ith_output] block_material[node_index] = block_def.get_random_input( block_material, req_dtype=req_dtype, _min=0, _max=block_def.main_count)
def mutate_single_ftn(mutant_material: BlockMaterial, block_def): #: BlockDefinition): ''' pick nodes at random and mutate the ftn-index until an active node is selected will mutate the function to anything with matching input/arg dtype. if the expected input datatypes don't match the current genome, it will find a new input/arg that will match ''' ezLogging.info("%s - Inside mutate_single_ftn" % (mutant_material.id)) choices = np.arange(block_def.main_count) choices = rnd.choice(choices, size=len(choices), replace=False) #randomly reorder for node_index in choices: # note, always will be a main_node current_ftn = mutant_material[node_index]["ftn"] req_output_dtype = block_def.operator_dict[current_ftn]["output"] new_ftn = block_def.get_random_ftn(req_dtype=req_output_dtype, exclude=[current_ftn]) ezLogging.debug("%s - Mutated node %i - possible new ftn: %s" % (mutant_material.id, node_index, new_ftn)) # make sure input_dtypes match req_input_dtypes = block_def.operator_dict[new_ftn]["inputs"] new_inputs = [None] * len(req_input_dtypes) for input_index in mutant_material[node_index]["inputs"]: exist_input_dtype = block_def.get_node_dtype( mutant_material, input_index, "output") #instead of verify from current node, goes to input for ith_input, (new_input, input_dtype) in enumerate( zip(new_inputs, req_input_dtypes)): if (new_input is None) and (input_dtype == exist_input_dtype): # then we can match an existing input with one of our required inputs new_inputs[ith_input] = input_index else: pass # now try and fill in anything still None for ith_input, (new_input, input_dtype) in enumerate( zip(new_inputs, req_input_dtypes)): if new_input is None: new_inputs[ith_input] = block_def.get_random_input( mutant_material, req_dtype=input_dtype, _max=node_index) # if there is still 'None' then we failed to fit this ftn in...try another ftn if None in new_inputs: continue else: ezLogging.debug("%s - Mutated node %i - possible new inputs: %s" % (mutant_material.id, node_index, new_inputs)) # make sure arg_dtypes match req_arg_dtypes = block_def.operator_dict[new_ftn]["args"] new_args = [None] * len(req_arg_dtypes) exist_arg_dtypes = block_def.get_node_dtype(mutant_material, node_index, "args") for arg_index, exist_arg_dtype in zip( mutant_material[node_index]["args"], exist_arg_dtypes): for ith_arg, (new_arg, req_arg_dtype) in enumerate( zip(new_args, req_arg_dtypes)): if (new_arg is None) and (req_arg_dtypes == exist_arg_dtype): new_args[ith_arg] = arg_index else: pass # now try and fill in anything still None for ith_arg, (new_arg, req_arg_dtype) in enumerate(zip(new_args, req_arg_dtypes)): if new_arg is None: new_args[ith_arg] = block_def.get_random_arg( req_dtype=req_arg_dtype) # if there is still 'None' then we failed to fit this ftn ...try another ftn if None in new_args: continue else: ezLogging.debug("%s - Mutated node %i - possible new args: %s" % (mutant_material.id, node_index, new_args)) # at this point we found a ftn and fit inputs and args mutant_material[node_index]["ftn"] = new_ftn mutant_material[node_index]["inputs"] = new_inputs mutant_material[node_index]["args"] = new_args ezLogging.debug( "%s - Mutated node %i; old_ftn: %s, new_ftn: %s, new_inputs: %s, new_args %s" % (mutant_material.id, node_index, current_ftn, new_ftn, new_inputs, new_args)) if node_index in mutant_material.active_nodes: # active_node finally mutated ezLogging.debug("%s - Mutated node %i - active" % (mutant_material.id, node_index)) mutant_material.need_evaluate = True break else: ezLogging.debug("%s - Mutated node %i - inactive" % (mutant_material.id, node_index)) pass
def mutate_single_input(mutant_material: BlockMaterial, block_def): #: BlockDefinition): ''' pick nodes at random and mutate the input-index until an active node is selected when mutating inputs, it will look for a node that outputs the matching datatype of the current node's input so it can fail at doing so and won't mutate that node ''' ezLogging.info("%s - Inside mutate_single_input" % (mutant_material.id)) choices = np.arange(block_def.main_count + block_def.output_count) choices = rnd.choice(choices, size=len(choices), replace=False) #randomly reorder for node_index in choices: if node_index < block_def.main_count: # then we are mutating a main-node (expect a node-dict) num_inputs_into_node = len(mutant_material[node_index]["inputs"]) ith_input = rnd.choice(np.arange(num_inputs_into_node)) current_input_index = mutant_material[node_index]["inputs"][ ith_input] req_dtype = block_def.get_node_dtype(mutant_material, node_index, "inputs")[ith_input] new_input = block_def.get_random_input( mutant_material, req_dtype=req_dtype, _max=node_index, exclude=[current_input_index]) if new_input is None: # failed to find new input, will have to try another node to mutate continue else: mutant_material[node_index]["inputs"][ith_input] = new_input ezLogging.debug( "%s - Mutated node %i; ori_input: %i, new_input: %i" % (mutant_material.id, node_index, current_input_index, new_input)) if node_index in mutant_material.active_nodes: # active_node finally mutated ezLogging.debug("%s - Mutated node %i - active" % (mutant_material.id, node_index)) mutant_material.need_evaluate = True break else: ezLogging.debug("%s - Mutated node %i - inactive" % (mutant_material.id, node_index)) pass else: # then we are mtuating an output-node (expect a int index value) current_output_index = mutant_material[node_index] req_dtype = block_def.output_dtypes[node_index - block_def.main_count] new_output_index = block_def.get_random_input( mutant_material, req_dtype=req_dtype, _min=0, exclude=[current_output_index]) if new_output_index is None: # failed to find new node continue else: mutant_material[node_index] = new_output_index ezLogging.debug( "%s - Mutated node %i; ori_input: %i, new_input: %i" % (mutant_material.id, node_index, current_output_index, new_output_index)) # active_node finally mutated ezLogging.debug("%s - Mutated node %i - active" % (mutant_material.id, node_index)) mutant_material.need_evaluate = True break
def build_block_from_lisp(self, block_def: BlockDefinition, lisp: str, indiv_id): ''' the expectation here is that lisp is the string tree representation (not a file holding the str) that follows the format of how we build out a lisp in codes.block_definitions.block_definition.get_lisp() shoud look something like: [func1,[func2,-2n,-1n],-1n] we also can handle cases where we are 'reusing' the output of a node...thanks to this line ```lisp = lisp.replace(_active_dict[ith_node], "%in" % ith_node)``` try with: lisp = '[mult,-1n,[mult,[sub,[mult,-1n,-1n],-2n],[sub,[mult,-1n,-1n],-2n]]]' NOTE: I think this currently only works if the block has 1 output! ''' _active_dict = {} ith_node = 0 while True: # from the start of the string, keep looking for lists [] match = re.search("\[[0-9A-Za-z_\-\s.,']+\]", lisp) if match is None: # no more lists inside lisp. so we're done break else: # get the single element lisp _active_dict[ith_node] = lisp[match.start():match.end()] # now replace that element with the node number # add 'n' to distinguish from arg value lisp = lisp.replace(_active_dict[ith_node], "%in" % ith_node) # increment to next node ith_node += 1 if ith_node >= 10**3: # very unlikely to have more than 1000 nodes...prob something went wrong ezLogging.error("something went wrong") break # now build the individual block_material = BlockMaterial(block_def.nickname) block_material.set_id(indiv_id) block_material.args = [None] * block_def.arg_count block_material.genome = [None] * block_def.genome_count block_material.genome[( -1 * block_def.input_count):] = ["InputPlaceholder" ] * block_def.input_count ith_active_node = -1 args_used = [] # so we don't overwrite an arg we already used active_main_nodes = sorted( np.random.choice(range(block_def.main_count), size=len(_active_dict), replace=False)) for node_index in range(block_def.main_count): if node_index in active_main_nodes: ith_active_node += 1 # fill node with what we got from the lisp lisp = _active_dict[ith_active_node].strip('][').split(',') for ftn in block_def.operator_dict.keys(): if ftn.__name__ == lisp[0]: # we matched our lisp ftn with entry in operatordict input_index = [] arg_index = [] ith_input = -1 ith_arg = -1 # now grab what we have in the lisp and make sure they match for val in lisp[ 1:]: # [1:] #ignores the ftn in 0th element if val.endswith('n'): # then it's a node index ith_input += 1 extracted_val = int( val[:-1]) # [:-1] to remove 'n' if extracted_val >= 0: # then it's a main node input_index.append( active_main_nodes[int(extracted_val)]) # verify that the data types match incoming_dtype = block_def.get_node_dtype( block_material, node_index=input_index[ith_input], key='output') expected_dtype = block_def.operator_dict[ ftn]["inputs"][ith_input] if incoming_dtype != expected_dtype: ezLogging.error( "error in genome seeding...mismatching incoming + given data types" ) import pdb pdb.set_trace() return None else: # all good pass else: # then it's an input node input_index.append(extracted_val) else: # then it's an arg value ith_arg += 1 req_arg_type = block_def.operator_dict[ftn][ "args"][ith_arg] poss_arg_index = block_def.get_random_arg( req_arg_type, exclude=args_used) if poss_arg_index is None: ezLogging.error( "can't find matching arg type in seeding" ) import pdb pdb.set_trace() return None arg_index.append(poss_arg_index) args_used.append(poss_arg_index) # have to convert val which is still a string to expected datatype! # kinda hacky but should work if 'float' in req_arg_type.__name__.lower(): val = float(val) elif 'int' in req_arg_type.__name__.lower(): val = int(val) elif 'bool' in req_arg_type.__name__.lower(): val = bool(val) else: pass block_material.args[ poss_arg_index] = req_arg_type(value=val) block_material[node_index] = { "ftn": ftn, "inputs": input_index, "args": arg_index } break else: # ftn doesn't match our lisp continue else: # then this is not an active main node...just fill it normally then # copy paste from fill_nodes ftns = block_def.get_random_ftn(return_all=True) for ftn in ftns: # find inputs input_dtypes = block_def.operator_dict[ftn]["inputs"] input_index = [None] * len(input_dtypes) for ith_input, input_dtype in enumerate(input_dtypes): input_index[ith_input] = block_def.get_random_input( block_material, req_dtype=input_dtype, _max=node_index) if None in input_index: # failed to fill it in; try another ftn continue else: pass # find args arg_dtypes = block_def.operator_dict[ftn]["args"] arg_index = [None] * len(arg_dtypes) for ith_arg, arg_dtype in enumerate(arg_dtypes): arg_index[ith_arg] = block_def.get_random_arg( req_dtype=arg_dtype) if None in arg_index: # failed to fill it in; try another ftn continue else: pass # all complete block_material[node_index] = { "ftn": ftn, "inputs": input_index, "args": arg_index } break # error check that node got filled if block_material[node_index] is None: print( "GENOME ERROR: no primitive was able to fit into current genome arrangment" ) import pdb pdb.set_trace() return None # output node # currently only works for 1 output node block_material[block_def.main_count] = active_main_nodes[-1] # now finish filling in the args for arg_index, arg_type in enumerate(block_def.arg_types): if block_material.args[arg_index] is None: block_material.args[arg_index] = arg_type() else: continue block_def.get_actives(block_material) return block_material