def create(genome, config): """ Receives a genome and returns its phenotype (a RecurrentNetwork). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {} for cg in genome.connections.values(): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) node_evals = [] for node_key, inputs in node_inputs.items(): node = genome.nodes[node_key] activation_function = genome_config.activation_defs.get(node.activation) aggregation_function = genome_config.aggregation_function_defs.get(node.aggregation) node_evals.append((node_key, activation_function, aggregation_function, node.bias, node.response, inputs)) return RecurrentNetwork(genome_config.input_keys, genome_config.output_keys, node_evals)
def create(genome, config): """ Receives a genome and returns its phenotype (a neural network). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {} for cg in genome.connections.values(): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) neurons = {} for node_key in required: ng = genome.nodes[node_key] inputs = node_inputs.get(node_key, []) neurons[node_key] = IZNeuron(ng.bias, ng.a, ng.b, ng.c, ng.d, inputs) genome_config = config.genome_config return IZNN(neurons, genome_config.input_keys, genome_config.output_keys)
def create(genome, config, time_constant): """ Receives a genome and returns its phenotype (a CTRNN). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {} for cg in itervalues(genome.connections): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) node_evals = {} for node_key, inputs in iteritems(node_inputs): node = genome.nodes[node_key] activation_function = genome_config.activation_defs.get(node.activation) aggregation_function = genome_config.aggregation_function_defs.get(node.aggregation) node_evals[node_key] = CTRNNNodeEval(time_constant, activation_function, aggregation_function, node.bias, node.response, inputs) return CTRNN(genome_config.input_keys, genome_config.output_keys, node_evals)
def test_fuzz_required(): for _ in range(1000): n_hidden = random.randint(10, 100) n_in = random.randint(1, 10) n_out = random.randint(1, 10) nodes = list( set( random.randint(0, 1000) for _ in range(n_in + n_out + n_hidden))) random.shuffle(nodes) inputs = nodes[:n_in] outputs = nodes[n_in:n_in + n_out] connections = [] for _ in range(n_hidden * 2): a = random.choice(nodes) b = random.choice(nodes) if a == b: continue if a in inputs and b in inputs: continue if a in outputs and b in outputs: continue connections.append((a, b)) required = required_for_output(inputs, outputs, connections) for o in outputs: assert o in required
def map_back_to_genome(self, genome, config, leaf_names, node_names): genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {i: {} for i in genome_config.output_keys} for cg in genome.connections.values(): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if i in genome_config.output_keys: continue if o not in node_inputs: node_inputs[o] = {} node_inputs[o][i] = cg if i not in node_inputs: node_inputs[i] = {} nodes = {i: Leaf(i) for i in genome_config.input_keys} assert len(leaf_names) == len(genome_config.input_keys) leaves = { name: nodes[i] for name, i in zip(leaf_names, genome_config.input_keys) } def map_back(idx, current_node): if isinstance(current_node, Leaf): return conns = node_inputs[idx] for idx, c in enumerate(current_node.children): conns[c.genome_idx].weight = list(current_node.weights)[idx] map_back(c.genome_idx, c) return if (type(self.cppn) != list): map_back(self.cppn.genome_idx, self.cppn) else: for o in self.cppn: map_back(o.genome_idx, o) return
def test_required_for_output(): inputs = [0, 1] outputs = [2] connections = [(0, 2), (1, 2)] required = required_for_output(inputs, outputs, connections) assert {2} == required inputs = [0, 1] outputs = [2] connections = [(0, 3), (1, 4), (3, 2), (4, 2)] required = required_for_output(inputs, outputs, connections) assert {2, 3, 4} == required inputs = [0, 1] outputs = [3] connections = [(0, 2), (1, 2), (2, 3)] required = required_for_output(inputs, outputs, connections) assert {2, 3} == required inputs = [0, 1] outputs = [4] connections = [(0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)] required = required_for_output(inputs, outputs, connections) assert {2, 3, 4} == required inputs = [0, 1] outputs = [4] connections = [(0, 2), (1, 3), (2, 3), (3, 4), (4, 2)] required = required_for_output(inputs, outputs, connections) assert {2, 3, 4} == required inputs = [0, 1] outputs = [4] connections = [(0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4), (2, 5)] required = required_for_output(inputs, outputs, connections) assert {2, 3, 4} == required
def create_cppn(genome, config, leaf_names, node_names, output_activation=None): genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {i: [] for i in genome_config.output_keys} for cg in genome.connections.values(): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if i in genome_config.output_keys: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) if i not in node_inputs: node_inputs[i] = [] nodes = {i: Leaf(i) for i in genome_config.input_keys} assert len(leaf_names) == len(genome_config.input_keys) leaves = { name: nodes[i] for name, i in zip(leaf_names, genome_config.input_keys) } def build_node(idx): if idx in nodes: return nodes[idx] node = genome.nodes[idx] conns = node_inputs[idx] children = [build_node(i) for i, w in conns] weights = [w for i, w in conns] if idx in genome_config.output_keys and output_activation is not None: activation = output_activation else: activation = str_to_activation[node.activation] aggregation = str_to_aggregation[node.aggregation] nodes[idx] = Node( children, weights, node.response, node.bias, activation, aggregation, genome_idx=idx, leaves=leaves, ) return nodes[idx] for idx in genome_config.output_keys: build_node(idx) outputs = [nodes[i] for i in genome_config.output_keys] for name in leaf_names: leaves[name].name = name for i, name in zip(genome_config.output_keys, node_names): nodes[i].name = name return outputs
def draw_net(config, genome, view=False, filename=None, node_names=None, show_disabled=True, prune_unused=True, node_colors=None, fmt='pdf'): """ Receives a genome and draws a neural network with arbitrary topology. """ if node_names is None: node_names = {} assert type(node_names) is dict if node_colors is None: node_colors = {} assert type(node_colors) is dict node_attrs = { 'shape': 'circle', 'fontsize': '9', 'height': '0.2', 'width': '0.2' } dot = graphviz.Digraph(format=fmt, node_attr=node_attrs) def node_to_string(n): try: no = genome.nodes[n] return '\n'.join( [str(n), str(no.activation), '{:0.2f}'.format(no.bias)]) except KeyError: return str(n) inputs = set() for k in config.genome_config.input_keys: inputs.add(k) name = node_names.get(k, node_to_string(k)) input_attrs = {'style': 'filled', 'shape': 'box'} input_attrs['fillcolor'] = node_colors.get(k, 'lightgray') dot.node(name, _attributes=input_attrs) outputs = set() for k in config.genome_config.output_keys: outputs.add(k) name = node_names.get(k, node_to_string(k)) node_attrs = {'style': 'filled'} node_attrs['fillcolor'] = node_colors.get(k, 'lightblue') dot.node(name, _attributes=node_attrs) from neat.graphs import required_for_output required = required_for_output(config.genome_config.input_keys, config.genome_config.output_keys, genome.connections) required |= inputs # print(required) if prune_unused: connections = set() for cg in genome.connections.values(): # print(cg) f, t = cg.key if f not in required or t not in required: # print('removed') continue if not cg.enabled and not show_disabled: # print('remove') continue # print('kept') connections.add(cg.key) # for c in connections: # print(c) used_nodes = inputs | outputs pending = copy.copy(outputs) while pending: new_pending = set() for a, b in connections: if b in used_nodes and b in pending and a not in used_nodes: new_pending.add(a) used_nodes.add(a) # print(pending, new_pending) pending = new_pending # used_nodes = required else: used_nodes = set(genome.nodes.keys()) for n in used_nodes: if n in inputs or n in outputs: continue attrs = {'style': 'filled'} attrs['fillcolor'] = node_colors.get(n, 'white') dot.node(node_to_string(n), _attributes=attrs) for cg in genome.connections.values(): if cg.enabled or show_disabled: #if cg.input not in used_nodes or cg.output not in used_nodes: # continue input, output = cg.key if input not in used_nodes or output not in used_nodes: continue a = node_names.get(input, node_to_string(input)) b = node_names.get(output, node_to_string(output)) style = 'solid' if cg.enabled else 'dotted' color = 'green' if cg.weight > 0 else 'red' width = str(0.1 + abs(cg.weight / 5.0)) dot.edge(a, b, _attributes={ 'style': style, 'color': color, 'penwidth': width, 'label': '{:0.2f}'.format(cg.weight), 'fontsize': '9' }) dot.render(filename, view=view, cleanup=True) return dot
def create(genome, config, batch_size=1, activation=sigmoid_activation, prune_empty=False, use_current_activs=False, n_internal_steps=1): from neat.graphs import required_for_output genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) if prune_empty: nonempty = { conn.key[1] for conn in genome.connections.values() if conn.enabled }.union(set(genome_config.input_keys)) input_keys = list(genome_config.input_keys) hidden_keys = [ k for k in genome.nodes.keys() if k not in genome_config.output_keys ] output_keys = list(genome_config.output_keys) hidden_responses = [genome.nodes[k].response for k in hidden_keys] output_responses = [genome.nodes[k].response for k in output_keys] hidden_biases = [genome.nodes[k].bias for k in hidden_keys] output_biases = [genome.nodes[k].bias for k in output_keys] if prune_empty: for i, key in enumerate(output_keys): if key not in nonempty: output_biases[i] = 0.0 n_inputs = len(input_keys) n_hidden = len(hidden_keys) n_outputs = len(output_keys) input_key_to_idx = {k: i for i, k in enumerate(input_keys)} hidden_key_to_idx = {k: i for i, k in enumerate(hidden_keys)} output_key_to_idx = {k: i for i, k in enumerate(output_keys)} def key_to_idx(key): if key in input_keys: return input_key_to_idx[key] elif key in hidden_keys: return hidden_key_to_idx[key] elif key in output_keys: return output_key_to_idx[key] input_to_hidden = ([], []) hidden_to_hidden = ([], []) output_to_hidden = ([], []) input_to_output = ([], []) hidden_to_output = ([], []) output_to_output = ([], []) for conn in genome.connections.values(): if not conn.enabled: continue i_key, o_key = conn.key if o_key not in required and i_key not in required: continue if prune_empty and i_key not in nonempty: print('Pruned {}'.format(conn.key)) continue i_idx = key_to_idx(i_key) o_idx = key_to_idx(o_key) if i_key in input_keys and o_key in hidden_keys: idxs, vals = input_to_hidden elif i_key in hidden_keys and o_key in hidden_keys: idxs, vals = hidden_to_hidden elif i_key in output_keys and o_key in hidden_keys: idxs, vals = output_to_hidden elif i_key in input_keys and o_key in output_keys: idxs, vals = input_to_output elif i_key in hidden_keys and o_key in output_keys: idxs, vals = hidden_to_output elif i_key in output_keys and o_key in output_keys: idxs, vals = output_to_output else: raise ValueError( 'Invalid connection from key {} to key {}'.format( i_key, o_key)) idxs.append((o_idx, i_idx)) # to, from vals.append(conn.weight) return RecurrentNet(n_inputs, n_hidden, n_outputs, input_to_hidden, hidden_to_hidden, output_to_hidden, input_to_output, hidden_to_output, output_to_output, hidden_responses, output_responses, hidden_biases, output_biases, batch_size=batch_size, activation=activation, use_current_activs=use_current_activs, n_internal_steps=n_internal_steps)
def create(genome, config, map_size): """ Receives a genome and returns its phenotype (a SwitchNeuronNetwork). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) input_keys = genome_config.input_keys output_keys = genome_config.output_keys # Gather inputs and expressed connections. std_inputs = {} mod_inputs = {} children = {} node_keys = set(genome.nodes.keys()) # + list(genome_config.input_keys[:]) # Here we populate the children dictionay for each unique not isolated node. for n in genome.nodes.keys(): children[n] = [] if not genome.nodes[n].is_isolated: for _ in range(1, map_size): new_idx = max(node_keys) + 1 children[n].append(new_idx) node_keys.add(new_idx) # We don't scale the output with the map size to keep passing the parameters of the network easy. # This part can be revised in the future for n in chain(input_keys, output_keys): children[n] = [] #Iterate over every connection for cg in itervalues(genome.connections): #If it's not enabled don't include it if not cg.enabled: continue i, o = cg.key #If neither node is required for output then skip the connection if o not in required and i not in required: continue #Find the map corresponding to each node of the connection in_map = [i] + children[i] out_map = [o] + children[o] #If the connection is modulatory and the output neuron a switch neuron then the new weights are stored #in the mod dictionary. We assume that only switch neurons have a modulatory part. if cg.is_mod and genome.nodes[o].is_switch: node_inputs = mod_inputs else: node_inputs = std_inputs for n in out_map: if n not in node_inputs.keys(): node_inputs[n] = [] if len(in_map) == map_size and len(out_map) == map_size: # Map to map connectivity if cg.one2one: # 1-to-1 mapping weight = cg.weight for i in range(map_size): node_inputs[out_map[i]].append((in_map[i], weight)) else: # 1-to-all if not cg.uniform: # Step start = -cg.weight end = cg.weight step = (end - start) / (map_size - 1) for o_n in out_map: s = start for i_n in in_map: node_inputs[o_n].append((i_n, s)) s += step else: # Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, cg.weight)) else: # Map-to-isolated or isolated-to-isolated if not cg.uniform: # Step start = -cg.weight end = cg.weight step = (end - start) / (map_size - 1) for o_n in out_map: s = start for i_n in in_map: node_inputs[o_n].append((i_n, s)) s += step else: # Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, cg.weight)) nodes = [] #Sometimes the output neurons end up not having any connections during the evolutionary process. While we do not #desire such networks, we should still allow them to make predictions to avoid fatal errors. for okey in output_keys: if okey not in node_keys: node_keys.add(okey) std_inputs[okey] = [] # While we cannot deduce the order of activations of the neurons due to the fact that we allow for arbitrary connection # schemes, we certainly want the output neurons to activate last. input_keys = genome_config.input_keys output_keys = genome_config.output_keys conns = {} for k in genome.nodes.keys(): if k not in std_inputs: std_inputs[k] = [] if k in children: for c in children[k]: std_inputs[c] = [] conns[k] = [i for i, _ in std_inputs[k]] sorted_keys = order_of_activation(conns, input_keys, output_keys) for node_key in sorted_keys: #if the node we are examining is not in our keys set then skip it. It means that it is not required for output. if node_key not in node_keys: continue node = genome.nodes[node_key] node_map = [node_key] + children[node_key] if node.is_switch: #If the node doesn't have any inputs then it is not needed if node_key not in std_inputs.keys( ) and node_key not in mod_inputs.keys(): continue # if the switch neuron only has modulatory weights then we copy those weights for the standard part as well. # this is not the desired behaviour but it is done to avoid errors during forward pass. if node_key not in std_inputs.keys( ) and node_key in mod_inputs.keys(): for n in node_map: std_inputs[n] = mod_inputs[n] if node_key not in mod_inputs: for n in node_map: mod_inputs[n] = [] for n in node_map: nodes.append(SwitchNeuron(n, std_inputs[n], mod_inputs[n])) continue for n in node_map: if n not in std_inputs: std_inputs[n] = [] # Create the standard part dictionary for the neuron for n in node_map: params = { 'activation_function': genome_config.activation_defs.get(node.activation), 'integration_function': genome_config.aggregation_function_defs.get(node.aggregation), 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': std_inputs[n] } nodes.append(Neuron(n, params)) return SwitchNeuronNetwork(input_keys, output_keys, nodes)
def create(genome, config): genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) input_keys = genome_config.input_keys output_keys = genome_config.output_keys #A dictionary where we keep the modulatory weights for every node mod_weights = {} #A dictionary where we keep the standard weights for every node std_weights = {} #Create a set with the keys of the nodes in the network keys = set() #Iterate over the connections for cg in itervalues(genome.connections): #if not cg.enabled: # continue i, o = cg.key #If neither of the nodes in the connection are required for output then skip this connection if o not in required and i not in required: continue if i not in input_keys: keys.add(i) keys.add(o) #In this implementation, only switch neurons have a modulatory part if genome.nodes[o].is_switch: #Add the weight to the modulatory part of the o node and continue with the next connection if cg.is_mod: if o not in mod_weights.keys(): mod_weights[o] = [(i, cg.weight)] else: mod_weights[o].append((i, cg.weight)) continue #If the connection is not modulatory #Add the weight to the standard weight of the o node. if o not in std_weights.keys(): std_weights[o] = [(i, cg.weight)] else: std_weights[o].append((i, cg.weight)) #Create the array with the network's nodes nodes = [] #Sometimes the output neurons end up not having any connections during the evolutionary process. While we do not #desire such networks, we should still allow them to make predictions to avoid fatal errors. for okey in output_keys: keys.add(okey) for k in keys: if k not in std_weights: std_weights[k] = [] #While we cannot deduce the order of activations of the neurons due to the fact that we allow for arbitrary connection #schemes, we certainly want the output neurons to activate last. conns = {} for k in keys: conns[k] = [i for i, w in std_weights[k]] sorted_keys = order_of_activation(conns, input_keys, output_keys) #Create the nodes of the network based on the weights dictionaries created above and the genome. for node_key in sorted_keys: if node_key not in keys: continue node = genome.nodes[node_key] if node.is_switch: #If the node doesn't have any connections then it is not needed if node_key not in std_weights.keys( ) and node_key not in mod_weights.keys(): continue #if the switch neuron only has modulatory weights then we copy those weights for the standard part as well. #this is not the desired behaviour but it is done to avoid errors during forward pass. if node_key not in std_weights.keys() and node_key in mod_weights: std_weights[node_key] = mod_weights[node_key] if node_key not in mod_weights.keys(): mod_weights[node_key] = [] nodes.append( SwitchNeuron(node_key, std_weights[node_key], mod_weights[node_key])) continue if node_key not in std_weights: std_weights[node_key] = [] #Create the standard part dictionary for the neuron params = { 'activation_function': genome_config.activation_defs.get(node.activation), 'integration_function': genome_config.aggregation_function_defs.get(node.aggregation), 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': std_weights[node_key] } nodes.append(Neuron(node_key, params)) return SwitchNeuronNetwork(input_keys, output_keys, nodes)
def create(genome, config): genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) input_keys = genome_config.input_keys output_keys = genome_config.output_keys #A dictionary where we keep the standard weights for every node std_weights = {} #Create a set with the keys of the nodes in the network keys = set() #Iterate over the connections for cg in itervalues(genome.connections): #if not cg.enabled: # continue i, o = cg.key #If neither of the nodes in the connection are required for output then skip this connection if o not in required and i not in required: continue if i not in input_keys: keys.add(i) keys.add(o) #Add the weight to the standard weight of the o node. if o not in std_weights.keys(): std_weights[o] = [(i,cg.weight)] else: std_weights[o].append((i,cg.weight)) #Create the array with the network's nodes nodes = [] #Sometimes the output neurons end up not having any connections during the evolutionary process. While we do not #desire such networks, we should still allow them to make predictions to avoid fatal errors. for okey in output_keys: if okey not in keys: keys.add(okey) std_weights[okey] = [] #Sometimes a neuron emerges which is only connected to the output neurons with an outgoing connection for k in keys: if k not in std_weights.keys(): std_weights[k] = [] #Deduce the order of activation of the neurons conns = {} for node in std_weights.keys(): conns[node] = [inp for inp, weight in std_weights[node]] sorted_keys = order_of_activation(conns, input_keys, output_keys) #Create the nodes of the network based on the weights dictionaries created above and the genome. for node_key in sorted_keys: if node_key not in keys: continue node = genome.nodes[node_key] #Create the standard part dictionary for the neuron params = { 'activation_function' : genome_config.activation_defs.get(node.activation), 'integration_function' : genome_config.aggregation_function_defs.get(node.aggregation), 'bias' : node.bias, 'activity' : 0, 'output' : 0, 'weights' : std_weights[node_key] } nodes.append(Neuron(node_key,params)) return RecurrentNetwork(input_keys,output_keys,nodes)
def create(genome, config, map_size): """ Receives a genome and returns its phenotype (a MapNetwork). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {} children = {} node_keys = list( genome.nodes.keys())[:] #+ list(genome_config.input_keys[:]) # for key in genome_config.input_keys + genome_config.output_keys: # children[key] = [] # for i in range(1,map_size): # if key < 0: # new_idx = min(node_keys) - 1 # else: # new_idx = max(node_keys) + 1 # children[key].append(new_idx) # node_keys.append(new_idx) for cg in itervalues(genome.connections): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue for n in [i, o]: if n in children.keys(): continue children[n] = [] if n in genome_config.input_keys or n in genome_config.output_keys: continue if not genome.nodes[n].is_isolated: for _ in range(1, map_size): new_idx = max(node_keys) + 1 children[n].append(new_idx) node_keys.append(new_idx) in_map = [i] + children[i] out_map = [o] + children[o] for n in out_map: if n not in node_inputs.keys(): node_inputs[n] = [] if len(in_map) == map_size and len(out_map) == map_size: #Map to map connectivity if cg.one_to_one: #1-to-1 mapping weight = 5 * cg.weight for i_n in range(map_size): node_inputs[out_map[i_n]].append((in_map[i_n], weight)) else: #1-to-all if cg.is_gaussian: #Gaussian for o_n in out_map: for i_n in in_map: node_inputs[o_n].append( (i_n, np.random.normal(cg.weight, cg.sigma))) else: #Uniform for o_n in out_map: for i_n in in_map: node_inputs[o_n].append((i_n, 5 * cg.weight)) else: #Map-to-isolated or isolated-to-isolated if cg.is_gaussian: # Gaussian for o_n in out_map: for i_n in in_map: node_inputs[o_n].append( (i_n, np.random.normal(cg.weight, cg.sigma))) else: # Uniform for o_n in out_map: for i_n in in_map: \ node_inputs[o_n].append((i_n, 5 * cg.weight)) input_keys = genome_config.input_keys output_keys = genome_config.output_keys conns = {} for k in genome.nodes.keys(): if k not in node_inputs: node_inputs[k] = [] if k in children: for c in children[k]: node_inputs[c] = [] conns[k] = [i for i, _ in node_inputs[k]] sorted_keys = order_of_activation(conns, input_keys, output_keys) nodes = [] for node_key in sorted_keys: if node_key not in genome.nodes.keys(): continue node = genome.nodes[node_key] activation_function = genome_config.activation_defs.get( node.activation) aggregation_function = genome_config.aggregation_function_defs.get( node.aggregation) nodes.append( Neuron( node_key, { 'activation_function': activation_function, 'integration_function': aggregation_function, 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': node_inputs[node_key] })) if node_key not in children: continue for n in children[node_key]: nodes.append( Neuron( n, { 'activation_function': activation_function, 'integration_function': aggregation_function, 'bias': node.bias, 'activity': 0, 'output': 0, 'weights': node_inputs[n] })) # for key in genome_config.input_keys: # input_keys.append(key) # if key in children.keys(): # for child in children[key]: # input_keys.append(child) # # for key in genome_config.output_keys: # output_keys.append(key) # if key in children.keys(): # for child in children[key]: # output_keys.append(child) return MapNetwork(input_keys, output_keys, nodes)
def create_cppn(genome, config, leaf_names, node_names, output_activation=None): """ Create cppn as described in HyperNEAT (linked above) :param genome: :param config: :param leaf_names: names of input nodes aka input keys :param node_names: names of output nodes aka output keys :param output_activation: """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # for HyperNEAT, there should be exactly one output node here # so it should look like: # node_inputs = {0:[]} # Gather inputs and expressed connections. # incoming connections of each node i node_inputs = {i: [] for i in genome_config.output_keys} for cg in genome.connections.values(): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if i in genome_config.output_keys: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) if i not in node_inputs: node_inputs[i] = [] # node inputs now contains for all nodes a list of tuples of incoming node, incoming weight # initialize set of all nodes with input nodes ("leaves") nodes = {i: Leaf() for i in genome_config.input_keys} assert len(leaf_names) == len( genome_config.input_keys), (leaf_names, genome_config.input_keys) leaves = { name: nodes[i] for name, i in zip(leaf_names, genome_config.input_keys) } def build_node(idx): # Depth first search if idx in nodes: # already built node because its a leaf or its been built on different build_node DFS return nodes[idx] node = genome.nodes[idx] conns = node_inputs[idx] children = [build_node(i) for i, w in conns] weights = [w for i, w in conns] if idx in genome_config.output_keys and output_activation is not None: activation = output_activation else: activation = str_to_activation[node.activation] aggregation = str_to_aggregation[node.aggregation] nodes[idx] = Node( children, weights, node.response, node.bias, activation, aggregation, leaves=leaves, ) return nodes[idx] for idx in genome_config.output_keys: # start DFS tree creation, starting at each output connection build_node(idx) outputs = [nodes[i] for i in genome_config.output_keys] # assign names to input nodes/input keys/leaves FIXME correct? for name in leaf_names: leaves[name].name = name # assign names to output nodes/output keys FIXME correct? for i, name in zip(genome_config.output_keys, node_names): nodes[i].name = name return outputs