def add_successful_genome_for_test(self, current_gen, use_this_genome):
        """
        This function adds a pre programmed genome which is known to converge for the XOR dataset.
        :param current_gen:
        :param use_this_genome: Whether this genome should be added to the population or not
        :return:
        """
        # Wait for current_gen > 1 because if using backprop the first gen skips using backprop.
        if current_gen > 1 and use_this_genome:
            node_list = [
                NodeGene(node_id=0, node_type='source'),
                NodeGene(node_id=1, node_type='source'),
                NodeGene(node_id=2, node_type='output', bias=0.5),
                NodeGene(node_id=3, node_type='hidden', bias=1),
                NodeGene(node_id=4, node_type='hidden', bias=1),
                NodeGene(node_id=5, node_type='hidden', bias=1),
                NodeGene(node_id=6, node_type='hidden', bias=1),
            ]

            connection_list = [ConnectionGene(input_node=0, output_node=3, innovation_number=1, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=1, output_node=3, innovation_number=2, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=0, output_node=4, innovation_number=3, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=1, output_node=4, innovation_number=4, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=3, output_node=5, innovation_number=5, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=4, output_node=5, innovation_number=6, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=3, output_node=6, innovation_number=7, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=4, output_node=6, innovation_number=8, enabled=True,
                                              weight=np.random.randn()),
                               ConnectionGene(input_node=5, output_node=2, innovation_number=9, enabled=True,
                                              weight=np.random.rand()),
                               ConnectionGene(input_node=6, output_node=2, innovation_number=10, enabled=True,
                                              weight=np.random.randn())
                               ]

            test_genome = Genome(connections=connection_list, nodes=node_list, key=1)
            test_genome.fitness = -99999999999
            self.population[32131231] = test_genome
Example #2
0
    def create_new_population(self, population_size, num_features):
        population = {}

        node_list = []
        connection_list = []
        # Create the source nodes
        for node in range(num_features):
            node_list.append(NodeGene(node_id=node, node_type='source'))

        # Add the output node (There is only one in this case)
        node_list.append(
            NodeGene(node_id=num_features, node_type='output', bias=1))

        # Save the innovations for the first generation.
        for source_node_id in range(num_features):
            # Increment for the new innovation
            self.global_innovation_number += 1
            # The output node will always have the node_id equal to the number of features
            self.innovation_tracker[(
                source_node_id, num_features)] = self.global_innovation_number

        # For each feature there will be a connection to the output
        for i in range(num_features):
            connection = (i, num_features)
            # The connection was already saved, so this should return true
            assert (connection in self.innovation_tracker)
            connection_list.append(
                ConnectionGene(
                    input_node=i,
                    output_node=num_features,
                    innovation_number=self.innovation_tracker[connection],
                    enabled=True))

        # Create a population of size population_size
        for index in range(population_size):
            # Deep copies otherwise changing the connection weight change's it for every genome that has the same
            # reference to the class
            deep_copy_connections = copy.deepcopy(connection_list)
            deep_copy_nodes = copy.deepcopy(node_list)
            # Set all the connections to a random weight for each genome
            for connection in deep_copy_connections:
                connection.weight = np.random.randn()
            # Increment since the index value has been assigned
            self.genome_indexer += 1

            # Create the genome
            population[index] = Genome(connections=deep_copy_connections,
                                       nodes=deep_copy_nodes,
                                       key=self.genome_indexer)

        self.show_population_weight_distribution(population=population)

        return population
Example #3
0
def main():
    node_list = [
        NodeGene(node_id=0, node_type='source'),
        NodeGene(node_id=1, node_type='source'),
        NodeGene(node_id=2, node_type='output', bias=1),
        NodeGene(node_id=3, node_type='output', bias=1),
        NodeGene(node_id=4, node_type='output', bias=1)
    ]

    connection_list = [
        ConnectionGene(input_node=0,
                       output_node=2,
                       innovation_number=1,
                       weight=-0.351,
                       enabled=True),
        ConnectionGene(input_node=0,
                       output_node=3,
                       innovation_number=2,
                       weight=-0.351,
                       enabled=True),
        ConnectionGene(input_node=0,
                       output_node=4,
                       innovation_number=3,
                       weight=-0.351,
                       enabled=True),
        ConnectionGene(input_node=1,
                       output_node=2,
                       innovation_number=4,
                       weight=-0.351,
                       enabled=True),
        ConnectionGene(input_node=1,
                       output_node=3,
                       innovation_number=5,
                       weight=-0.351,
                       enabled=True),
        ConnectionGene(input_node=1,
                       output_node=4,
                       innovation_number=6,
                       weight=-0.351,
                       enabled=True)
    ]

    genome = GenomeMultiClass(connections=connection_list,
                              nodes=node_list,
                              key=3)
    x_data, y_data = create_data(n_generated=500)
    genome_nn = GenomeNeuralNetwork(genome=genome,
                                    create_weights_bias_from_genome=False,
                                    activation_type='sigmoid',
                                    learning_rate=0.1,
                                    x_train=x_data,
                                    y_train=y_data)
    print(genome.num_layers_including_input)
    print(genome.constant_weight_connections)
    print(genome.layer_connections_dict)
Example #4
0
def main():
    node_list = [
        NodeGene(node_id=1, node_type='source'),
        NodeGene(node_id=2, node_type='source'),
        NodeGene(node_id=3, node_type='hidden'),
        NodeGene(node_id=4, node_type='hidden'),
        NodeGene(node_id=5, node_type='output')
    ]

    # Note that one of the connections isn't enabled
    connection_list = [
        ConnectionGene(input_node=1,
                       output_node=5,
                       innovation_number=1,
                       enabled=True),
        ConnectionGene(input_node=1,
                       output_node=4,
                       innovation_number=2,
                       enabled=True),
        ConnectionGene(input_node=2,
                       output_node=3,
                       innovation_number=3,
                       enabled=True),
        ConnectionGene(input_node=2,
                       output_node=4,
                       innovation_number=4,
                       enabled=True),
        ConnectionGene(input_node=3,
                       output_node=4,
                       innovation_number=7,
                       enabled=True),
        ConnectionGene(input_node=3,
                       output_node=5,
                       innovation_number=8,
                       enabled=True),
        ConnectionGene(input_node=4,
                       output_node=5,
                       innovation_number=6,
                       enabled=True)
    ]

    genome = Genome(connections=connection_list, nodes=node_list, key=3)

    print(genome.num_layers_including_input)
    print(genome.constant_weight_connections)
    print(genome.layer_connections_dict)
Example #5
0
    def add_node(self, reproduction_instance, innovation_tracker):
        """
        Add a node between two existing nodes
        """

        # Create the new node
        new_node = NodeGene(node_id=(max(self.nodes.keys()) + 1),
                            node_type='hidden',
                            bias=1)

        # The connection where the node will be added
        connections_list = list(self.connections.values())
        # Remove any connections which aren't enabled as a candidate where a node can be added
        for connection in connections_list:
            if not connection.enabled:
                connections_list.remove(connection)

        connection_to_add_node = random.choice(connections_list)
        # Disable the connection since it will be replaced
        connection_to_add_node.enabled = False

        input_node = connection_to_add_node.input_node
        output_node = connection_to_add_node.output_node

        # Create new connection gene. Which has a weight of 1
        first_combination = (input_node, new_node.node_id)
        if first_combination in innovation_tracker:
            first_new_connection = ConnectionGene(
                input_node=input_node,
                output_node=new_node.node_id,
                innovation_number=innovation_tracker[first_combination],
                weight=1)
        else:
            # Increment since there is a new innovation
            reproduction_instance.global_innovation_number += 1
            first_new_connection = ConnectionGene(
                input_node=input_node,
                output_node=new_node.node_id,
                innovation_number=reproduction_instance.
                global_innovation_number,
                weight=1)
            # Save the innovation since it's new
            innovation_tracker[
                first_combination] = reproduction_instance.global_innovation_number
        second_combination = (new_node.node_id, output_node)

        # The second connection keeps the weight of the connection it replaced
        if second_combination in innovation_tracker:
            second_new_connection = ConnectionGene(
                input_node=new_node.node_id,
                output_node=output_node,
                innovation_number=innovation_tracker[second_combination],
                weight=connection_to_add_node.weight)
        else:
            # Increment since there is a new innovation
            reproduction_instance.global_innovation_number += 1
            second_new_connection = ConnectionGene(
                input_node=new_node.node_id,
                output_node=output_node,
                innovation_number=reproduction_instance.
                global_innovation_number,
                weight=connection_to_add_node.weight)
            # save the innovation if it doesn't already exist
            innovation_tracker[
                second_combination] = reproduction_instance.global_innovation_number

        # Add the new node and connections
        self.nodes[new_node.node_id] = new_node
        self.connections[
            first_new_connection.innovation_number] = first_new_connection
        self.connections[
            second_new_connection.innovation_number] = second_new_connection

        return first_new_connection, second_new_connection
Example #6
0
    def add_connection(self, reproduction_instance, innovation_tracker):
        """
        Add a random connection
        :param innovation_tracker: The innovations that have happened in the past
        :param reproduction_instance: An instance of the reproduction class so we can access the global innovation number

        """

        # Keeps a list of nodes which can't be chosen from to be the start node of the new connection
        viable_start_nodes = []

        # Any node that isn't the output node is a viable start_node
        for node_id, node in self.nodes.items():
            if node.node_type != 'output':
                viable_start_nodes.append(node_id)

        possible_combinations = itertools.combinations(self.get_active_nodes(),
                                                       2)
        cleaned_possible_combinations = self.clean_combinations(
            possible_combinations=possible_combinations)

        # Get all the existing combinations
        already_existing_connections = set()
        for connection in self.connections.values():
            already_existing_connections.add(
                (connection.input_node, connection.output_node))

        possible_new_connections = []
        for connection in cleaned_possible_combinations:
            # If it's not an existing combination then we can choose from it randomly
            if connection not in already_existing_connections:
                possible_new_connections.append(connection)

        if possible_new_connections:
            # Pick randomly from the possible new choices
            new_connection = random.choice(possible_new_connections)

            if new_connection in innovation_tracker:
                # If the connection was already made then use whatever the innovation number that was assigned to
                # that connection
                new_connection_gene = ConnectionGene(
                    input_node=new_connection[0],
                    output_node=new_connection[1],
                    innovation_number=innovation_tracker[new_connection],
                    weight=np.random.randn())

            else:
                reproduction_instance.global_innovation_number += 1
                new_connection_gene = ConnectionGene(
                    input_node=new_connection[0],
                    output_node=new_connection[1],
                    innovation_number=reproduction_instance.
                    global_innovation_number,
                    weight=np.random.randn())

                # Save the connection as a current gen innovation
                innovation_tracker[
                    new_connection] = reproduction_instance.global_innovation_number

            # Add the connection the the genome
            self.connections[
                new_connection_gene.innovation_number] = new_connection_gene

            return new_connection_gene
Example #7
0
    def create_new_population(self, population_size, num_features,
                              num_classes):
        population = {}

        source_node_list = []
        output_node_list = []

        # Create the source and output nodes
        for node_id in range(num_features + num_classes):
            if node_id < num_features:
                source_node_list.append(
                    NodeGene(node_id=node_id, node_type='source'))
            else:
                output_node_list.append(
                    NodeGene(node_id=node_id, node_type='output', bias=1))

        # Save innovations on population creation
        for source_node in source_node_list:
            for output_node in output_node_list:
                # Increment for the new innovation
                self.global_innovation_number += 1
                # The output node will always have the node_id equal to the number of features
                self.innovation_tracker[(
                    source_node.node_id,
                    output_node.node_id)] = self.global_innovation_number

        connection_list = []
        # For each feature there will be a connection to the output
        for source_node in source_node_list:
            for output_node in output_node_list:
                connection = (source_node.node_id, output_node.node_id)
                # The connection was already saved, so this should return true
                assert (connection in self.innovation_tracker)
                connection_list.append(
                    ConnectionGene(
                        input_node=source_node.node_id,
                        output_node=output_node.node_id,
                        innovation_number=self.innovation_tracker[connection],
                        enabled=True))
        all_nodes_list = source_node_list + output_node_list
        # Create a population of size population_size
        for index in range(population_size):
            # Deep copies otherwise changing the connection weight change's it for every genome that has the same
            # reference to the class
            deep_copy_connections = copy.deepcopy(connection_list)
            deep_copy_nodes = copy.deepcopy(all_nodes_list)
            # Set all the connections to a random weight for each genome
            for connection in deep_copy_connections:
                connection.weight = np.random.randn()
            # Increment since the index value has been assigned
            self.genome_indexer += 1

            # Create the genome
            population[index] = GenomeMultiClass(
                connections=deep_copy_connections,
                nodes=deep_copy_nodes,
                key=self.genome_indexer)

        self.show_population_weight_distribution(population=population)

        return population