예제 #1
0
    def _give_extra_brain_cell(self):
        """Add a new node to the genome via mutation.

        This process chooses a random enabled connection, and splits it into
        two new connections with a new node in the middle.
        """
        new_node = NodeGene(Hidden())
        new_node.node.bias = 0
        new_node.node.id = len(self.node_genes)
        self.add_gene(new_node)

        enabled_connections = list(
            filter(lambda cg: cg.is_enabled, self.connection_genes))
        connection_to_split = random.choice(enabled_connections)
        connection_to_split.connection.is_enabled = False

        first_connection = \
            ConnectionGene(connection_to_split.connection.input_id, new_node.node.id)
        first_connection.connection.weight = 1.0
        self.add_gene(first_connection)

        second_connection = \
            ConnectionGene(new_node.node.id, connection_to_split.connection.input_id)
        second_connection.connection.weight = \
            connection_to_split.connection.weight
        self.add_gene(second_connection)
예제 #2
0
    def test_connection_gene_json(self):
        """Test whether a connection gene can be saved to and loaded from JSON.
        """
        cg = ConnectionGene(0, 1)
        dump = json.dumps(cg.to_json())
        cg_load = ConnectionGene.from_json(json.loads(dump))

        self.assertEqual(cg, cg_load)
        self.assertEqual(cg.is_enabled, cg_load.is_enabled)
예제 #3
0
    def _initialize_connections(self):
        if self.genome_config.is_initial_fully_connected:
            # initialize fully connected network with no recurrent connections
            connections = self._compute_full_connections()
        else:
            # initialize network with only a few connections between input-output nodes
            connections = self._compute_random_connections(repetitions=min(
                self.initial_nodes_sample, len(self.input_nodes_keys)))

        for input_node, output_node in connections:
            key = (int(input_node), int(output_node))
            connection = ConnectionGene(key=key)
            connection.random_initialization()
            self.connection_genes[key] = connection
예제 #4
0
    def _connection_distance(self, connection_1: ConnectionGene,
                             connection_2: ConnectionGene):
        '''
        Connection distance is modified to account for both bias and standard deviation
        '''
        distance = l2_distance(
            v1=[connection_1.get_mean(),
                connection_1.get_std()],
            v2=[connection_2.get_mean(),
                connection_2.get_std()])

        # this is not being used
        if connection_1.enabled != connection_2.enabled:
            distance += 1.0

        return distance * self.compatibility_weight_coefficient
예제 #5
0
    def _build_bridges_not_walls(self):
        """Add a new connection to the genome via mutation."""
        nodes = [ng.node for ng in self.node_genes]
        target_node, input_node = random.choices(nodes, k=2)

        # Sensor nodes 'reject' incoming connections.
        # No recurrent connections from Output nodes.
        while isinstance(target_node, Sensor) or \
                isinstance(input_node, Output):
            target_node, input_node = random.choices(nodes, k=2)

        self.add_gene(ConnectionGene(target_node.id, input_node.id))
예제 #6
0
def generate_genome_given_graph(graph, connection_weights):
    genome = Genome(key='foo')

    unique_node_keys = []
    input_nodes = []
    for connection in graph:
        for node_key in connection:
            if node_key not in unique_node_keys:
                unique_node_keys.append(node_key)

            if node_key < 0:
                input_nodes.append(node_key)
    input_nodes = set(input_nodes)

    unique_node_keys = list(
        set(unique_node_keys + genome.get_output_nodes_keys()) - input_nodes)
    nodes = {}
    for node_key in unique_node_keys:
        node = NodeGene(key=node_key).random_initialization()
        node.set_mean(0)
        node.set_std(STD)
        nodes[node_key] = node

    connections = {}
    for connection_key, weight in zip(graph, connection_weights):
        connection = ConnectionGene(key=connection_key)
        connection.set_mean(weight)
        connection.set_std(STD)
        connections[connection_key] = connection

    genome.connection_genes = connections
    genome.node_genes = nodes
    return genome
예제 #7
0
    def _get_connection_crossover(self, connection_1: ConnectionGene,
                                  connection_2: ConnectionGene):
        assert connection_1.key == connection_2.key
        connection_key = connection_1.key

        new_connection = ConnectionGene(key=connection_key)
        for attribute in new_connection.crossover_attributes:
            if random.random() > 0.5:
                self.set_child_attribute(attribute=attribute,
                                         new_gene=new_connection,
                                         parent_gene=connection_1)
            else:
                self.set_child_attribute(attribute=attribute,
                                         new_gene=new_connection,
                                         parent_gene=connection_2)

        return new_connection
예제 #8
0
    def from_json(config):
        """Load a genome object from JSON.

        Arguments:
            config: the JSON dictionary loaded from file.

        Returns: a genome object.
        """
        genotype = Genome()
        genotype.add_genes([
            NodeGene.from_json(ng_config) for ng_config in config['node_genes']
        ])
        genotype.add_genes([
            ConnectionGene.from_json(cg_config)
            for cg_config in config['connection_genes']
        ])

        return genotype
예제 #9
0
    def mutate_add_connection(self, genome: Genome):
        possible_outputs = list(genome.node_genes.keys())
        k = min(len(possible_outputs), self.architecture_mutation_power)
        out_node_keys = random.sample(possible_outputs, k)
        for out_node_key in out_node_keys:
            possible_inputs = self._calculate_possible_inputs_when_adding_connection(
                genome, out_node_key=out_node_key, config=self.config)
            if len(possible_inputs) == 0:
                continue
            in_node = random.choice(possible_inputs)

            new_connection_key = (in_node, out_node_key)

            new_connection = ConnectionGene(
                key=new_connection_key).random_initialization()
            genome.connection_genes[new_connection_key] = new_connection
            logger.network(
                f'Genome {genome.key}. Mutation: Add a Connection: {new_connection_key}'
            )
        return genome
예제 #10
0
    def from_dict(genome_dict: dict):
        genome_config_dict = genome_dict['config']
        genome_config = jsons.load(genome_config_dict, BaseConfiguration)
        genome = Genome(key=genome_dict['key'],
                        id=genome_dict['id'],
                        genome_config=genome_config)

        # reconstruct nodes and connections
        connection_genes_dict = genome_dict['connection_genes']
        for key, connection_gene_dict in connection_genes_dict.items():
            connection_gene = ConnectionGene.from_dict(
                connection_gene_dict=connection_gene_dict)
            genome.connection_genes[connection_gene.key] = connection_gene

        node_genes_dict = genome_dict['node_genes']
        for key, node_gene_dict in node_genes_dict.items():
            node_gene = NodeGene.from_dict(node_gene_dict=node_gene_dict)
            genome.node_genes[node_gene.key] = node_gene

        genome.n_weight_parameters = genome_dict['n_weight_parameters']
        genome.n_bias_parameters = genome_dict['n_bias_parameters']
        genome.node_counter = count(max(list(genome.node_genes.keys())) + 1)
        return genome
예제 #11
0
    def create_from_julia_dict(genome_dict: dict):
        config = get_configuration()
        genome = Genome(key=genome_dict["key"], id=None, genome_config=config)

        # reconstruct nodes and connections
        connection_genes_dict = genome_dict['connections']
        for key_str, connection_gene_dict in connection_genes_dict.items():
            connection_key = Genome._get_connection_key_from_key_str(key_str)
            connection_gene = ConnectionGene(key=connection_key)
            connection_gene.set_mean(connection_gene_dict['mean_weight'])
            connection_gene.set_std(connection_gene_dict['std_weight'])
            genome.connection_genes[connection_gene.key] = connection_gene

        node_genes_dict = genome_dict['nodes']
        for key_str, node_gene_dict in node_genes_dict.items():
            node_key = int(key_str)
            node_gene = NodeGene(key=node_key)
            node_gene.set_mean(node_gene_dict['mean_bias'])
            node_gene.set_std(node_gene_dict['std_bias'])
            genome.node_genes[node_gene.key] = node_gene

        genome.calculate_number_of_parameters()
        return genome
예제 #12
0
 def add_connection(self, key, mean=None, std=None):
     connection = ConnectionGene(key=key)
     connection.set_mean(mean)
     connection.set_std(std)
     self.connection_genes[key] = connection
예제 #13
0
    def mutate_add_node(self, genome: Genome):
        # TODO: careful! this can add multihop-jumps to the network

        possible_connections_to_split = list(genome.connection_genes.keys())
        # Choose a random connection to split
        k = min(len(possible_connections_to_split),
                self.architecture_mutation_power)
        connection_to_split_keys = random.sample(possible_connections_to_split,
                                                 k)
        for connection_to_split_key in connection_to_split_keys:
            new_node_key = genome.get_new_node_key()
            new_node = NodeGene(key=new_node_key).random_initialization()
            genome.node_genes[new_node_key] = new_node

            connection_to_split = genome.connection_genes[
                connection_to_split_key]
            i, o = connection_to_split_key

            # add connection between input and the new node
            new_connection_key_i = (i, new_node_key)
            new_connection_i = ConnectionGene(key=new_connection_key_i)
            new_connection_i.set_mean(mean=1.0), new_connection_i.set_std(
                std=0.0000001)
            genome.connection_genes[new_connection_key_i] = new_connection_i

            # add connection between new node and output
            new_connection_key_o = (new_node_key, o)
            new_connection_o = ConnectionGene(
                key=new_connection_key_o).random_initialization()
            new_connection_o.set_mean(mean=connection_to_split.get_mean())
            new_connection_o.set_std(std=connection_to_split.get_std())
            genome.connection_genes[new_connection_key_o] = new_connection_o

            # delete connection
            # Careful: neat-python disable the connection instead of deleting
            # conn_to_split.enabled = False
            del genome.connection_genes[connection_to_split_key]
            logger.network(
                f'Genome {genome.key}. Mutation: Add a Node: {new_node_key} between {i}->{o}'
            )
        return genome
예제 #14
0
def add_connection(connection_genes, key):
    connection_i = ConnectionGene(key=key)
    connection_i.random_initialization()
    connection_genes[key] = connection_i
    return connection_genes