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 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