Beispiel #1
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
Beispiel #2
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
def make_node_list(n_in, n_hid, n_out):
    INPUT = NodeGeneTypesEnum.INPUT.value
    HIDDEN = NodeGeneTypesEnum.HIDDEN.value
    OUTPUT = NodeGeneTypesEnum.OUTPUT.value

    # List comp for input nodes
    inputs = [NodeGene(id, INPUT) for id in range(n_in)]
    hiddens = [NodeGene(id, HIDDEN) for id in range(n_in, n_in + n_hid)]
    outputs = [
        NodeGene(id, OUTPUT)
        for id in range(n_in + n_hid, n_in + n_hid + n_out)
    ]

    # Now accumulate them all
    res = inputs + hiddens + outputs
    return res
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
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)
Beispiel #6
0
    def __init__(self,
                 nodes: list,
                 connections: list,
                 fitness_function: Callable[..., float],
                 innv_tracker: InnovationTracker,
                 fitness: float = 0,
                 adjusted_fitness: float = 0,
                 fitness_updated: bool = False):

        self.nodes = dict()
        self.connections = dict()

        self.innv_tracker = innv_tracker

        # node id :[list of incoming nodes]
        self.reverse_graph = collections.defaultdict(list)

        self.fitness = round(fitness, NEATGenome.precision)
        self.adjusted_fitness = round(adjusted_fitness, NEATGenome.precision)
        self.fitness_updated = fitness_updated

        # length of each layer basically
        # bias node doesn't count in the first layer..
        self.shape = collections.defaultdict(int)
        self.bias = NodeGene(Node.Bias, 0, 0, '0')
        self.add_node(self.bias)

        # you should always add connections before adding nodes
        # whenever you add a hidden node or an output node,
        # the add_node method tries to connect it to the bias node
        # and add_connection method ignores duplicate connections
        # so if the node is already connected to the bias,
        # you will lose the connection weight
        for conn in connections:
            self.add_connection(conn, copy=True)

        for node in nodes:
            self.add_node(node, copy=True)

        self.fitness_function = fitness_function
Beispiel #7
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)
Beispiel #8
0
    def BasicNetwork(layers: tuple,
                     fitness_function: Callable[..., float],
                     connections: list = [],
                     innv_tracker: InnovationTracker = None):
        """ Constructs a basic neural network with the given connections.
            node ids are numbered from 0..

            layers   -> a tuple representing the structure of the neural network.

            ex.
                nn = NEATGenome.BasicNetwork((2,4,3))
                nn -> a basic 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.
                adds the given connections to the network..
            raises TypeError if len(layers) < 2
        """
        if (len(layers) < 2):
            raise TypeError("Can't create a neural network with one layer!!")

        # add the bias node...
        nodes = []
        next_id = 1

        for layerid, layer_size in enumerate(layers):
            node_type = Node.Sensor if layerid == 0 else \
                        Node.Hidden if layerid < len(layers)-1 else \
                        Node.Output

            for _ in range(layer_size):
                nodes.append(
                    NodeGene(node_type, next_id, layerid, str(next_id)))
                next_id += 1

        return NEATGenome(nodes, list(connections), fitness_function,
                          innv_tracker)
Beispiel #9
0
# nodes = input + hidden + output
#
# genome = Genome(nodes, connections)
#
# # network = Network(genome, debug=False)
# # print("All 1")
# # 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