Ejemplo n.º 1
0
    def add_node_mutation(self, node_classes, random_gen, innovator):
        if not node_classes:
            return

        active_connections = list(
            filter(lambda gene: gene[1].is_active,
                   self.connection_genes.items()))
        if not active_connections:
            return

        selected_connection = random_gen.choice(active_connections)[1]
        selected_connection.disable()

        new_node_id = len(self.node_genes)
        new_node = random_gen.choice(node_classes)(new_node_id,
                                                   NodeType.HIDDEN)

        in_to_new = (selected_connection.in_node, new_node_id)
        new_to_out = (new_node_id, selected_connection.out_node)
        in_to_new_innovation = innovator.next_innovation_number(in_to_new)
        new_to_out_innovation = innovator.next_innovation_number(new_to_out)

        in_to_new = ConnectionGene(*in_to_new, 1.0, True, in_to_new_innovation)
        new_to_out = ConnectionGene(*new_to_out, selected_connection.weight,
                                    True, new_to_out_innovation)

        self.node_genes[new_node_id] = new_node
        self.connection_genes[in_to_new_innovation] = in_to_new
        self.connection_genes[new_to_out_innovation] = new_to_out
Ejemplo n.º 2
0
	def mutate_add_node(self, inno_num):
		# Get a random index in range [0, len(connections))
		# Loop ensures we get an enabled one
		while(True):
			connection_index = np.random.randint(len(self.connections))
			if (self.connections[connection_index].enabled): 
				break

		con_to_split = self.connections[connection_index]
		con_to_split.enabled = False
		old_in_node = con_to_split.in_node
		old_out_node = con_to_split.out_node
		old_weight = con_to_split.weight
		
		new_node = HiddenNode(self.next_node_id)
		self.next_node_id += 1

		inno_num += 1
		new_in_con = ConnectionGene(old_in_node, new_node, 1, True, inno_num)
		inno_num += 1
		new_out_con = ConnectionGene(new_node, old_out_node, old_weight, True, inno_num)

		self.nodes.append(new_node)
		self.connections.append(new_in_con)
		self.connections.append(new_out_con)
		self.n_hidden_nodes += 1

		return inno_num
Ejemplo n.º 3
0
def test_add_node(supply_generation_one_basic_genome):
    generation = supply_generation_one_basic_genome
    genome = generation.genomes[0]

    inno_num = 0
    inno_num = genome.mutate_add_node(inno_num)
    active_cons = [con for con in genome.connections if con.enabled]
    inactive_cons = [con for con in genome.connections if not con.enabled]

    assert inno_num == 2
    assert len(active_cons) == 2
    assert len(genome.connections) == 3
    assert len(genome.nodes) == 3
    assert genome.nodes[2].id == 2
    assert genome.nodes[2].type == NodeGeneTypesEnum.HIDDEN.value

    assert genome.next_node_id == 3
    assert genome.n_input_nodes == 1
    assert genome.n_hidden_nodes == 1
    assert genome.n_output_nodes == 1

    assert active_cons[0] == ConnectionGene(NodeGene(0,
                                                     None), NodeGene(2, None),
                                            None, None, None)
    assert active_cons[1] == ConnectionGene(NodeGene(2,
                                                     None), NodeGene(1, None),
                                            None, None, None)
    assert inactive_cons[0] == ConnectionGene(NodeGene(0, None),
                                              NodeGene(1, None), None, None,
                                              None)
Ejemplo n.º 4
0
    def node_mutation(self):
        """ Tries to mutate the network to add a new node..
            returns a node gene and two connection genes without adding
            anything to the network..
        """
        # list of active connections
        # exclude the bias connections..
        active_connections = [
            conn for conn in self.connections.values()
            if (conn.disabled == False) and (conn.incoming != self.bias.id)
        ]

        # No active connections to add nodes between
        if (not active_connections): return False

        conn = random.choice(active_connections)

        node = NodeGene(Node.Hidden, -1, self.nodes[conn.incoming].layerid + 1,
                        str(conn))

        # try to add the node
        # ** node.id will chance if it's added to the network **
        is_in_new_layer = (node.layerid == self.nodes[conn.outgoing].layerid)
        node_added = self.add_node(node, new_layer=is_in_new_layer)

        if (node_added == False): return False
        # if the node was successfully added
        # disable the connection and put the new connections
        conn.disabled = True
        self.add_connection(ConnectionGene(conn.incoming, node.id))
        self.add_connection(ConnectionGene(node.id, conn.outgoing))

        return True
Ejemplo n.º 5
0
    def add_connection(self, conn: ConnectionGene, copy: bool = False):
        """
            Adds the given connection to the phenotype of the genome.
            Returns True if the connection was added successfully,
                    False if it's a duplicate connection.
        """
        if copy: conn = ConnectionGene.Copy(conn)
        if (str(conn) in self.connections): return False

        conn.innv = self.innv_tracker.get_innovation_number(conn)
        self.connections[str(conn)] = conn
        self.reverse_graph[conn.outgoing].append(conn)
        return True
Ejemplo n.º 6
0
    def add_connection_mutation(self, random_gen, innovator):
        #! Useless config variables
        PROB_MIN = -1.0
        PROB_MAX = 1.0
        #!
        in_nodes = self.node_genes.keys()
        out_nodes = [
            node for node in in_nodes
            if self.node_genes[node]._type != NodeType.INPUT
        ]

        selected_connection = None
        present_connections = [(node[1].in_node, node[1].out_node)
                               for node in self.connection_genes.items()]
        random_gen.shuffle(present_connections)
        for connection in product(in_nodes, out_nodes):
            if [
                    self.node_genes[connection[0]]._type,
                    self.node_genes[connection[1]]._type
            ] == [NodeType.OUTPUT, NodeType.OUTPUT]:
                continue
            if connection not in present_connections:
                selected_connection = connection
                break

        if not selected_connection:
            return
        weight = random_gen.uniform(PROB_MIN, PROB_MAX)
        innovation_number = innovator.next_innovation_number(
            selected_connection)
        connection = ConnectionGene(*selected_connection, weight, True,
                                    innovation_number)
        self.connection_genes[innovation_number] = connection
Ejemplo n.º 7
0
    def add_node(self,
                 node: NodeGene,
                 copy: bool = False,
                 new_layer: bool = False):
        """
            Adds the given node to the phenotype of the genome.
            if new_layer is set to true, inserts the node in a new layer.

            Returns True if the node was added successfully,
                    False if it's a duplicate node.
        """
        if copy: node = NodeGene.Copy(node)

        node.id = self.innv_tracker.get_node_id(node)

        if (node.id in self.nodes): return False

        if (new_layer):
            for nn_node in self.nodes.values():
                nn_node.layerid += (nn_node.layerid >= node.layerid)

        # don't count the bias node in it's layer
        self.nodes[node.id] = node
        self.shape[node.layerid] += (node.type != Node.Bias)

        # connect to the bias node
        if (node.type == Node.Hidden) or (node.type == Node.Output):
            self.add_connection(ConnectionGene(self.bias.id, node.id, 0.0))

        return True
Ejemplo n.º 8
0
    def initialize_population(self, population=None, size=None):
        if population is not None:
            self.previous_generation = population
            self.max_id = len(population.keys())
        elif size is not None:
            self.max_id = self.config.POPULATION_SIZE
            pop_size = self.config.POPULATION_SIZE
            input_size = size[0]
            output_size = size[1]

            for genome_id in range(pop_size):
                genome = Genome(genome_id)

                node_id = 0
                # Input Nodes
                for _ in range(input_size):
                    node_gene = self.random.choice(self.node_classes)(node_id, NodeType.INPUT)
                    genome.add_node_gene(node_gene)
                    node_id += 1
                
                # Output Nodes
                for _ in range(output_size):
                    node_gene = self.random.choice(self.node_classes)(node_id, NodeType.OUTPUT)
                    genome.add_node_gene(node_gene)
                    node_id += 1
                
                # Connect each input to every other output
                for in_id in range(input_size):
                    for out_id in range(output_size):
                        connection = ConnectionGene(in_id, out_id + input_size, 1.0, True, self.innovator.next_innovation_number((in_id, out_id)))
                        genome.add_connection_gene(connection)

                self.previous_generation[genome_id] = genome
        else:
            raise ValueError("Invalid Parameters")
Ejemplo n.º 9
0
def supply_generation_one_basic_genome():
    generation = Generation([])

    nodes = make_node_list(1, 0, 1)
    cons = [ConnectionGene(nodes[0], nodes[1], np.random.uniform(), 1, 0)]
    genome = Genome(nodes, cons, generation)
    generation.genomes.append(genome)
    return generation
Ejemplo n.º 10
0
def supply_generation_two_basic_genomes():
    generation = Generation([])

    nodes = make_node_list(1, 1, 2)
    cons1 = [
        ConnectionGene(nodes[0], nodes[1], np.random.uniform(), 1, 0),
        ConnectionGene(nodes[1], nodes[2], np.random.uniform(), 1, 1)
    ]
    genome1 = Genome(nodes, cons1, generation)

    cons2 = [
        ConnectionGene(nodes[0], nodes[1], np.random.uniform(), 1, 0),
        ConnectionGene(nodes[2], nodes[3], np.random.uniform(), 1, 2)
    ]
    genome2 = Genome(nodes.copy(), cons2, generation)

    generation.genomes.extend([genome1, genome2])
    return generation
Ejemplo n.º 11
0
	def mutate_add_connection(self, inno_num):
		# First check if we can even add a connection
		if (self.is_fully_connected()):
			print("THIS RAN")
			return inno_num

		while (True):

			node1 = self.nodes[np.random.randint(len(self.nodes))]
			node2 = self.nodes[np.random.randint(len(self.nodes))]
			(con_exists, is_active) = self.con_exists(node1, node2)
			
			# TODO: Check this/abbreviate it.
			# Keep it if its either a new connection or an old, disabled connection
			# it must also be a valid connection (inputs cant connect to inputs, etc)
			if ( (not con_exists or not is_active) and self.is_valid_con(node1, node2) ):
				break
		

		# So now we know which two nodes we are connecting/reconnecting
		# Make the new connection and figure out if its a new innovation
		temp_con = ConnectionGene(node1, node2, None, None, None)

		# If there is a matching connection, reenable it
		# Note that we don't change inno number
		if (con_exists):
			index = self.connections.index(temp_con)
			self.connections[index].enabled = True


		#Otherwise we have a brand new connection!
		else:
			inno_num += 1
			temp_con.inno_num = inno_num
			temp_con.enabled = True
			temp_con.weight = np.random.uniform()
			self.connections.append(temp_con)


		return inno_num
Ejemplo n.º 12
0
    def Crossover(parent1, parent2):
        if ((parent1.fitness_updated == False)
                or (parent2.fitness_updated == False)):
            raise RuntimeError("Calculate fitness before crossing over!")

        # set parent2 to always be the more fit parent
        if (parent1.fitness > parent2.fitness):
            parent1, parent2 = parent2, parent1

        # since we inherit disjoint & excess genes from the more fit (parent2)
        # the topology of the child network will be the same as parent2
        # we can safely copy all nodes from parent2
        # don't inherit the bias node, as the constructor will create a new one
        child_nodes = [
            NodeGene.Copy(node) for node in parent2.nodes.values()
            if (node.type != Node.Bias)
        ]
        child_connections = []

        # map innovation numbers to connections
        freq = {conn.innv: conn for conn in parent1.connections.values()}
        for conn in parent2.connections.values():
            gene = freq.get(conn.innv, None)

            if (gene != None):  # Matched
                gene = ConnectionGene.Copy(random.choice((gene, conn)))
                gene.disabled = (conn.disabled | gene.disabled)

                gene.disabled &= (random.uniform(0, 1) <
                                  NEATGenome.disable_inherited_chance)

            else:  # Excess or Disjoint
                # inherit from the more fit (paretn2)
                gene = ConnectionGene.Copy(conn)

            child_connections.append(gene)

        return NEATGenome(child_nodes, child_connections,
                          parent2.fitness_function, parent2.innv_tracker)
Ejemplo n.º 13
0
def generate_complete_genome(id, n, r, ir):
    g = Genome(id)

    for i in range(n):
        g.add_node_gene(TestNode(i, r.choice(list(NodeType))))

    for i in range(n):
        for j in range(i, n):
            w = r.random()
            a = r.random() < 0.7
            inr = ir.next_innovation_number((i, j))
            g.add_connection_gene(ConnectionGene(i, j, w, a, inr))

    return g
Ejemplo n.º 14
0
def test_add_connection_normal(supply_generation_one_basic_genome):
    generation = supply_generation_one_basic_genome
    genome = generation.genomes[0]

    # For all of the other tests, we'll use the connection in the genome...
    # in this one, we'll manually delete it, then add it back using the mutation
    genome.connections.clear()
    inno_num = 0
    inno_num = genome.mutate_add_connection(inno_num)

    assert inno_num == 1
    assert len(genome.connections) == 1
    assert genome.connections[0].enabled
    assert genome.connections[0] == ConnectionGene(NodeGene(0, None),
                                                   NodeGene(1, None), None,
                                                   None, None)
    assert genome.connections[0].inno_num == 1
Ejemplo n.º 15
0
def generate_genome(id, n, r, ir):
    g = Genome(id)

    max_c = int(n * (n - 1) / 2)
    c = r.randint(max_c - 1, max_c)

    for i in range(n):
        g.add_node_gene(TestNode(i, r.choice(list(NodeType))))

    for _ in range(c):
        i = r.randint(0, n - 1)
        o = r.randint(0, n - 1)
        w = r.random()
        a = r.random() < 0.7
        inr = ir.next_innovation_number((i, o))
        g.add_connection_gene(ConnectionGene(i, o, w, a, inr))

    return g
Ejemplo n.º 16
0
    def FullFeedForwardNetwork(layers: tuple,
                               fitness_function: Callable[..., float],
                               innv_tracker: InnovationTracker = None,
                               default_weight: float = None):
        """ Returns a full feedforward neural network with nodes and connections initialized..
            Connection weights are randomized..

            layers -> a tuple representing the structure of the neural network.
            default_weight -> resets the weights to the given value if not None

            ex.
                nn = NEATGenome.FullFeedForwardNetwork((2,4,3), 1.0)
                nn -> a fully connected neural network with:
                        an input layer of 2 input nodes and a bias node,
                        a hidden layer with 4 hidden nodes, and
                        an output layer with 3 output nodes.
                      with all weights set to 1.0.

            # Bias node connections are always set to 0.0
            # Bias node id is always 0
            # Bias layerid is always 0

            return a NEATGenome object
        """
        # connections = [ConnectionGene(0, outgoing, 0.0)
        #                for outgoing in range(layers[0]+1, sum(layers)+1)]
        connections = []
        id_shift = 1

        # don't process the last layer
        for index, layer_size in enumerate(layers[:-1]):
            for incoming in range(layer_size):
                for outgoing in range(layers[index + 1]):
                    connections.append(
                        ConnectionGene(incoming + id_shift,
                                       outgoing + id_shift + layer_size,
                                       default_weight))
                    # print(incoming+id_shift, end=", ") #DEBUG
                    # print(outgoing+id_shift+layer_size) #DEBUG
            id_shift += layer_size

        return NEATGenome.BasicNetwork(layers, fitness_function, connections,
                                       innv_tracker)
Ejemplo n.º 17
0
    def connection_mutation(self):
        """ Tries to mutate the network to add a new connection
            returns True if the network is successfully mutated...
        """
        layers = len(self.shape)
        layer1 = random.randrange(0, layers - 1)
        layer2 = random.randrange(layer1 + 1, layers)

        #DEBUG
        if (layer1 >= layer2):
            raise ValueError(f"Wrong choice of layers.. {layer1}, {layer2}")

        # now that we have two layers, let's get the nodes in each layer
        layer1 = [
            node for node in self.nodes.values() if node.layerid == layer1
        ]
        layer2 = [
            node for node in self.nodes.values() if node.layerid == layer2
        ]

        return self.add_connection(
            ConnectionGene(random.choice(layer1).id,
                           random.choice(layer2).id))
Ejemplo n.º 18
0
# # print(network.run([1,1,1,1]))
# network = Network(genome, debug=True)
# print("All 0")
# print(network.run([0,0,0,0]))
# print(network.run([0,0,0,0]))

# def run2():
inputValues = [1, 1]

input = [NodeGene(NodeType.INPUT) for i in range(1, 3)]
hidden = [NodeGene(NodeType.HIDDEN) for i in range(3, 5)]
output = [NodeGene(NodeType.OUTPUT) for i in range(5, 6)]

connections = []

connections.append(ConnectionGene(1, 3, True))
connections.append(ConnectionGene(2, 3, True))
connections.append(ConnectionGene(2, 4, True))
connections.append(ConnectionGene(3, 4, True))
connections.append(ConnectionGene(4, 5, True))
# Recurring
connections.append(ConnectionGene(3, 3, True))

nodes = input + hidden + output

genome = Genome(nodes, connections)
network = Network(genome, debug=True)
print("All 1")
print(network.run([1, 1]))
print(network.run([1, 1]))