def __init__(self, input_size=2, output_size=2, weight_low=-2, weight_high=2, depth=3): """ Builds empty genome. :param input_size: Number of input nodes :param output_size: Number of output nodes :param weight_low: Maximum weight on node and edges :param weight_high: Minimum weight on node and edges :param depth: Number of layers in network. """ self.depth = depth self.weight_low = weight_low self.weight_high = weight_high self.edge_innovs = set() self.inputs = [Node(0, i, 0, type='input') for i in range(input_size)] self.outputs = [ Node(1 + depth, j, 0, type='output') for j in range(output_size) ] self.layers = None self.nodes = [] self.edges = [] self.fitness = None
def test_node_copy(self): n = Node(0, 0, 0) m = Node.copy(n) self.assertNotEqual(n, m) self.assertEqual(n.innov, m.innov) self.assertEqual(n.layer_num, m.layer_num) self.assertEqual(n.layer_ind, m.layer_ind)
def test_node_init(self): n1 = Node(0, 0, 0) n2 = Node(0, 1, 0) n3 = Node(1, 0, 0) n4 = Node(1, 1, 0) # Check correct nodes self.assertEqual(n1.innov, 0) self.assertEqual(n2.innov, 1) self.assertEqual(n3.innov, 2) self.assertEqual(n4.innov, 3)
def test_edge_init(self): n = Node(0, 0, 0) m = Node(0, 1, 0) with self.assertRaises(Exception) as context: Edge(n, m, 0) err_msg = 'Cannot connect edge to lower or same layer' self.assertTrue(err_msg == str(context.exception)) k = Node(1, 0, 0) e = Edge(n, k, 0) self.assertEqual(e.from_node, n) self.assertEqual(e.to_node, k)
def copy(genome): """Deep copy of genome instance. :param genome: Instance of Genome class :return: Copied instance of Genome glass """ new_genome = Genome( input_size=len(genome.inputs), output_size=len(genome.outputs), weight_low=genome.weight_low, weight_high=genome.weight_high, depth=genome.depth) layers = [[Node.copy(node) for node in layer] for layer in genome.layers[1:-1]] new_genome.layers = [ new_genome.inputs, *layers, new_genome.outputs ] nodes = [new_genome.layers[node.layer_num][node.layer_ind] for node in genome.nodes] new_genome.nodes = nodes for edge in genome.edges: new_edge = Edge.copy(edge, new_genome) new_genome.edge_innovs.add(( new_edge.from_node.innov, new_edge.to_node.innov)) return new_genome
def genome_pair_factory(weight_gen=default_gen(), bias_gen=default_gen()): Node.innov_iter = itertools.count() Edge.innov_iter = itertools.count() np.random.seed(1) g1 = minimal(input_size=2, output_size=3, depth=5) n1 = g1.add_node(4) g1.add_edge(g1.layers[0][0], n1) g1.add_edge(n1, g1.outputs[0]) n2 = g1.add_node(3) g1.add_edge(g1.layers[0][1], n2) g1.add_edge(n2, g1.outputs[2]) g2 = copy(g1) n4 = g2.add_node(2) g2.add_edge(g2.layers[0][1], n4) g2.add_edge(n4, g2.layers[3][0]) n5 = g1.add_node(4) g1.add_edge(g1.layers[3][0], n5) g1.add_edge(n5, g1.outputs[0]) n3 = g1.add_node(3) e1 = g1.add_edge(g1.layers[0][0], n3) e2 = g1.add_edge(n3, g1.outputs[2]) g2.layers[3].append(Node.copy(n3)) g2.nodes.append(Node.copy(n3)) n6 = g1.add_node(3) g1.add_edge(g1.layers[0][0], n6) g1.add_edge(n6, g1.outputs[1]) g2.add_edge(g2.layers[0][0], n3) e2 = g2.add_edge(n3, g2.outputs[2]) for w, edge in zip(weight_gen, [*g1.edges, *g2.edges]): edge.weight = w for w, node in zip(bias_gen, [*g1.nodes, *g2.nodes]): node.weight = w return g1, g2
def add_node(self, layer_num): """Add node to genome layer. :param layer_num: layer to add node too. Must be less than depth. :raises ValueError: If attempt to add node to input or output layers. :return: The new node. """ if layer_num == 0 or layer_num == len(self.layers) - 1: raise ValueError('Cannot add node to input or output layer') new_node = Node(layer_num, len(self.layers[layer_num]), sample_weight(self.weight_low, self.weight_high)) self.layers[layer_num].append(new_node) if self.nodes and new_node.innov > self.nodes[-1].innov: self.nodes.append(new_node) else: self.nodes = [n for n in self.nodes if n.innov < new_node.innov] + [new_node] \ + [n for n in self.nodes if n.innov > new_node.innov] return new_node
def from_genes( nodes_genes, edges, input_size=2, output_size=2, weight_low=-2, weight_high=2, depth=3): """Reconstructs a Genome class from the return value to_reduced_repr called on an instance of a Genome class. :param nodes_genes: First member of genome.to_reduced_repr, list of node properties and indices tuples. Takes form: [(layer_num, layer_ind, innov, weight, type), ...] where layer number is the genome layer index, layer_ind is the index in the layer, innov is the innovation number. weight is the node bias and type is one of 'input', 'hidden' or 'output'. :param edges: Second member of genome.to_reduced_repr, tuple of edge properties and indices. Takes form: [( from_node.to_reduced_repr, to_node.to_reduced_repr, weight, innov, active ), ...] where from_node.to_reduced_repr and to_node.to_reduced_repr take the same form as an individual node in node_genes. weight is the edge weight, innov it's innovation number and active is True or False. :param input_size: Number of input nodes :param output_size: Number of output nodes :param weight_low: Maximum weight on node and edges :param weight_high: Minimum weight on node and edges :param depth: Number of layers in network. :return: Constructed genome. """ new_genome = Genome( input_size=input_size, output_size=output_size, weight_low=weight_low, weight_high=weight_high, depth=depth) layer_maxes = [0 for _ in range(depth)] for node_gene in nodes_genes: layer_num, layer_ind, _, weight, _ = node_gene if layer_maxes[layer_num - 1] < layer_ind + 1: layer_maxes[layer_num - 1] = layer_ind + 1 layers = [] for layer_max in layer_maxes: layers.append([None for _ in range(layer_max)]) nodes = [] for node_gene in nodes_genes: layer_num, layer_ind, innov, weight, node_type = node_gene node = Node(layer_num, layer_ind, weight, type=node_type) nodes.append(node) layers[layer_num - 1][layer_ind] = node new_genome.layers = [new_genome.inputs, *layers, new_genome.outputs] new_genome.nodes = nodes for from_node_reduced, to_node_reduced, weight, innov, active in edges: from_layer_num, from_layer_ind, _, _, _ = from_node_reduced from_node = new_genome.layers[from_layer_num][from_layer_ind] to_layer_num, to_layer_ind, _, _, _ = to_node_reduced to_node = new_genome.layers[to_layer_num][to_layer_ind] edge = Edge(from_node, to_node, weight, active) new_genome.edges.append(edge) new_genome.edge_innovs.add((from_node.innov, to_node.innov)) return new_genome