Exemple #1
0
    def compute_full_connections(self, config, direct):
        """
        Compute connections for a fully-connected feed-forward genome--each
        input connected to all hidden nodes
        (and output nodes if ``direct`` is set or there are no hidden nodes),
        each hidden node connected to all output nodes.
        (Recurrent genomes will also include node self-connections.)
        """
        hidden = [i for i in iterkeys(self.nodes) if i not in config.output_keys]
        output = [i for i in iterkeys(self.nodes) if i in config.output_keys]
        connections = []
        if hidden:
            for input_id in config.input_keys:
                for h in hidden:
                    connections.append((input_id, h))
            for h in hidden:
                for output_id in output:
                    connections.append((h, output_id))
        if direct or (not hidden):
            for input_id in config.input_keys:
                for output_id in output:
                    connections.append((input_id, output_id))

        # For recurrent genomes, include node self-connections.
        if not config.feed_forward:
            for i in iterkeys(self.nodes):
                connections.append((i, i))

        return connections
Exemple #2
0
    def mutate_add_connection(self, config):
        """
        Attempt to add a new connection, the only restriction being that the output
        node cannot be one of the network input pins.
        """
        possible_outputs = list(iterkeys(self.nodes))
        out_node = choice(possible_outputs)

        possible_inputs = possible_outputs + config.input_keys
        in_node = choice(possible_inputs)

        # Don't duplicate connections.
        key = (in_node, out_node)
        if key in self.connections:
            # TODO: Should this be using mutation to/from rates? Hairy to configure...
            if config.check_structural_mutation_surer():
                self.connections[key].enabled = True
            return

        # Don't allow connections between two output nodes
        if in_node in config.output_keys and out_node in config.output_keys:
            return

        # No need to check for connections between input nodes:
        # they cannot be the output end of a connection (see above).

        # For feed-forward networks, avoid creating cycles.
        if config.feed_forward and creates_cycle(list(iterkeys(self.connections)), key):
            return

        cg = self.create_connection(config, in_node, out_node)
        self.connections[cg.key] = cg
    def distance(self, other, config):
        """
        Returns the genetic distance between this genome and the other. This distance value
        is used to compute genome compatibility for speciation.
        """

        # Compute node gene distance component.
        node_distance = 0.0
        if self.nodes or other.nodes:
            disjoint_nodes = 0
            for k2 in iterkeys(other.nodes):
                if k2 not in self.nodes:
                    disjoint_nodes += 1

            for k1, n1 in iteritems(self.nodes):
                n2 = other.nodes.get(k1)
                if n2 is None:
                    disjoint_nodes += 1
                else:
                    # Homologous genes compute their own distance value.
                    node_distance += n1.distance(n2, config)

            max_nodes = max(len(self.nodes), len(other.nodes))
            node_distance = (node_distance +
                             config.compatibility_disjoint_coefficient *
                             disjoint_nodes) / max_nodes

        # Compute connection gene differences.
        connection_distance = 0.0
        if self.connections or other.connections:
            disjoint_connections = 0
            for k2 in iterkeys(other.connections):
                if k2 not in self.connections:
                    disjoint_connections += 1

            for k1, c1 in iteritems(self.connections):
                c2 = other.connections.get(k1)
                if c2 is None:
                    disjoint_connections += 1
                else:
                    # Homologous genes compute their own distance value.
                    connection_distance += c1.distance(c2, config)

            max_conn = max(len(self.connections), len(other.connections))
            connection_distance = (connection_distance +
                                   config.compatibility_disjoint_coefficient *
                                   disjoint_connections) / max_conn

        distance = node_distance + connection_distance

        return distance
    def end_generation(self, config, population, species_set):
        ng = len(population)
        ns = len(species_set.species)
        if self.show_species_detail:
            print('Population of {0:d} members in {1:d} species:'.format(
                ng, ns))
            sids = list(iterkeys(species_set.species))
            sids.sort()
            print("   ID   age  size  fitness  adj fit  stag")
            print("  ====  ===  ====  =======  =======  ====")
            for sid in sids:
                s = species_set.species[sid]
                a = self.generation - s.created
                n = len(s.members)
                f = "--" if s.fitness is None else "{:.1f}".format(s.fitness)
                af = "--" if s.adjusted_fitness is None else "{:.3f}".format(
                    s.adjusted_fitness)
                st = self.generation - s.last_improved
                print(
                    "  {: >4}  {: >3}  {: >4}  {: >7}  {: >7}  {: >4}".format(
                        sid, a, n, f, af, st))
        else:
            print('Population of {0:d} members in {1:d} species'.format(
                ng, ns))

        elapsed = time.time() - self.generation_start_time
        self.generation_times.append(elapsed)
        self.generation_times = self.generation_times[-10:]
        average = sum(self.generation_times) / len(self.generation_times)
        print('Total extinctions: {0:d}'.format(self.num_extinctions))
        if len(self.generation_times) > 1:
            print("Generation time: {0:.3f} sec ({1:.3f} average)".format(
                elapsed, average))
        else:
            print("Generation time: {0:.3f} sec".format(elapsed))
    def test_fully_connected_hidden_direct(self):
        """full with direct input-output connections (and also via hidden hodes)."""
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'full_direct'
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        self.assertEqual(len(g.connections), 8)

        # Check that each input is connected to each hidden node.
        for i in config.input_keys:
            for h in (1, 2):
                assert ((i, h) in g.connections)

        # Check that each hidden node is connected to the output.
        for h in (1, 2):
            assert ((h, 0) in g.connections)

        # Check that inputs are directly connected to the output
        for i in config.input_keys:
            assert ((i, 0) in g.connections)
    def test_fully_connected_hidden_nodirect_old(self):
        """
        full (no specification re direct/nodirect) with hidden nodes;
        should output warning re docs/code conflict.
        """
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'full'
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        print("\nThis should output a warning:", file=sys.stderr)
        g.configure_new(config)  # TODO: Test for emitted warning

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        self.assertEqual(len(g.connections), 6)

        # Check that each input is connected to each hidden node.
        for i in config.input_keys:
            for h in (1, 2):
                assert ((i, h) in g.connections)

        # Check that each hidden node is connected to the output.
        for h in (1, 2):
            assert ((h, 0) in g.connections)

        # Check that inputs are not directly connected to the output
        for i in config.input_keys:
            assert ((i, 0) not in g.connections)
Exemple #7
0
    def get_new_node_key(self, node_dict):
        if self.node_indexer is None:
            self.node_indexer = count(max(list(iterkeys(node_dict))) + 1)

        new_id = next(self.node_indexer)

        assert new_id not in node_dict

        return new_id
    def configure_new(self, config):
        # Create node genes for the output pins.
        for node_key in config.output_keys:
            self.nodes[node_key] = self.create_node(config, node_key)

        for input_id in config.input_keys:
            for node_id in iterkeys(self.nodes):
                connection = self.create_connection(config, input_id, node_id)
                self.connections[connection.key] = connection
Exemple #9
0
 def connect_fs_neat_hidden(self, config):
     """
     Randomly connect one input to all hidden and output nodes
     (FS-NEAT with connections to hidden, if any).
     """
     input_id = choice(config.input_keys)
     others = [i for i in iterkeys(self.nodes) if i not in config.input_keys]
     for output_id in others:
         connection = self.create_connection(config, input_id, output_id)
         self.connections[connection.key] = connection
    def test_unconnected_hidden(self):
        """Unconnected network with hidden nodes."""
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'unconnected'
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(self.config.genome_config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        assert (not g.connections)
    def test_fs_neat_nohidden(self):
        """fs_neat not connecting hidden nodes."""
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'fs_neat_nohidden'
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        self.assertEqual(len(g.connections), 1)
 def __init__(self, param_dict, param_list):
     self._params = param_list
     param_list_names = []
     for p in param_list:
         setattr(self, p.name, p.interpret(param_dict))
         param_list_names.append(p.name)
     unknown_list = [
         x for x in iterkeys(param_dict) if x not in param_list_names
     ]
     if unknown_list:
         if len(unknown_list) > 1:
             raise UnknownConfigItemError("Unknown configuration items:\n" +
                                          "\n\t".join(unknown_list))
         raise UnknownConfigItemError(
             "Unknown configuration item {!s}".format(unknown_list[0]))
    def test_partially_connected_hidden_nodirect(self):
        """partial with no direct input-output connections, only via hidden nodes."""
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'partial_nodirect'
        config.connection_fraction = 0.5
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        self.assertLess(len(g.connections), 6)
    def test_fs_neat_no_hidden(self):
        """
        fs_neat with no hidden nodes
        (equivalent to fs_neat_hidden and fs_neat_nohidden with no hidden nodes).
        """
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'fs_neat'
        config.num_hidden = 0

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0})
        self.assertEqual(len(g.connections), 1)
    def test_fs_neat_hidden_old(self):
        """
        fs_neat (without hidden/nohidden specification) with hidden;
        should output warning about doc/code conflict.
        """
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'fs_neat'
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        print("\nThis should output a warning:", file=sys.stderr)
        g.configure_new(config)  # TODO: Test for emitted warning

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        self.assertEqual(len(g.connections), 1)
    def test_partially_connected_no_hidden(self):
        """
        partial with no hidden nodes
        (equivalent to partial_nodirect and partial_direct with no hidden nodes)
        """
        gid = 42
        config = self.config2.genome_config
        config.initial_connection = 'partial'
        config.connection_fraction = 0.5
        config.num_hidden = 0

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0})
        self.assertLess(len(g.connections), 2)
    def test_partially_connected_hidden_nodirect_old(self):
        """
        partial (no specification re direct/nodirect) with hidden nodes;
        should output warning re docs/code conflict.
        """
        gid = 42
        config = self.config2.genome_config
        config.initial_connection = 'partial'
        config.connection_fraction = 0.5
        config.num_hidden = 2

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        print("\nThis should output a warning:", file=sys.stderr)
        g.configure_new(config)  # TODO: Test for emitted warning

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0, 1, 2})
        self.assertLess(len(g.connections), 6)
Exemple #18
0
    def mutate_delete_node(self, config):
        # Do nothing if there are no non-output nodes.
        available_nodes = [k for k in iterkeys(self.nodes) if k not in config.output_keys]
        if not available_nodes:
            return -1

        del_key = choice(available_nodes)

        connections_to_delete = set()
        for k, v in iteritems(self.connections):
            if del_key in v.key:
                connections_to_delete.add(v.key)

        for key in connections_to_delete:
            del self.connections[key]

        del self.nodes[del_key]

        return del_key
    def mutate_add_connection(self, config):
        '''
        Attempt to add a new connection, the only restriction being that the output
        node cannot be one of the network input pins.
        '''
        possible_outputs = list(iterkeys(self.nodes))
        out_node = choice(possible_outputs)

        possible_inputs = possible_outputs + config.input_keys
        in_node = choice(possible_inputs)

        if in_node == out_node:
            return

        # # Don't duplicate connections.
        # key = (in_node, out_node)
        # if key in self.connections:
        #     return

        cg = self.create_connection(config, in_node, out_node)
        self.connections[cg.key] = cg
    def test_fully_connected_no_hidden(self):
        """
        full with no hidden nodes
        (equivalent to full_nodirect and full_direct with no hidden nodes)
        """
        gid = 42
        config = self.config.genome_config
        config.initial_connection = 'full'
        config.num_hidden = 0

        g = neatfast.DefaultGenome(gid)
        self.assertEqual(gid, g.key)
        g.configure_new(config)

        print(g)
        self.assertEqual(set(iterkeys(g.nodes)), {0})
        self.assertEqual(len(g.connections), 2)

        # Check that each input is connected to the output node
        for i in config.input_keys:
            assert ((i, 0) in g.connections)
 def __init__(self, name, **default_dict):
     self.name = name
     for n, default in iteritems(default_dict):
         self._config_items[n] = [self._config_items[n][0], default]
     for n in iterkeys(self._config_items):
         setattr(self, n + "_name", self.config_item_name(n))
Exemple #22
0
    def speciate(self, config, population, generation):
        """
        Place genomes into species by genetic similarity.

        Note that this method assumes the current representatives of the species are from the old
        generation, and that after speciation has been performed, the old representatives should be
        dropped and replaced with representatives from the new generation.  If you violate this
        assumption, you should make sure other necessary parts of the code are updated to reflect
        the new behavior.
        """
        assert isinstance(population, dict)

        compatibility_threshold = self.species_set_config.compatibility_threshold

        # Find the best representatives for each existing species.
        unspeciated = set(iterkeys(population))
        distances = GenomeDistanceCache(config.genome_config)
        new_representatives = {}
        new_members = {}
        for sid, s in iteritems(self.species):
            candidates = []
            for gid in unspeciated:
                g = population[gid]
                d = distances(s.representative, g)
                candidates.append((d, g))

            # The new representative is the genome closest to the current representative.
            ignored_rdist, new_rep = min(candidates, key=lambda x: x[0])
            new_rid = new_rep.key
            new_representatives[sid] = new_rid
            new_members[sid] = [new_rid]
            unspeciated.remove(new_rid)

        # Partition population into species based on genetic similarity.
        while unspeciated:
            gid = unspeciated.pop()
            g = population[gid]

            # Find the species with the most similar representative.
            candidates = []
            for sid, rid in iteritems(new_representatives):
                rep = population[rid]
                d = distances(rep, g)
                if d < compatibility_threshold:
                    candidates.append((d, sid))

            if candidates:
                ignored_sdist, sid = min(candidates, key=lambda x: x[0])
                new_members[sid].append(gid)
            else:
                # No species is similar enough, create a new species, using
                # this genome as its representative.
                sid = next(self.indexer)
                new_representatives[sid] = gid
                new_members[sid] = [gid]

        # Update species collection based on new speciation.
        self.genome_to_species = {}
        for sid, rid in iteritems(new_representatives):
            s = self.species.get(sid)
            if s is None:
                s = Species(sid, generation)
                self.species[sid] = s

            members = new_members[sid]
            for gid in members:
                self.genome_to_species[gid] = sid

            member_dict = dict((gid, population[gid]) for gid in members)
            s.update(population[rid], member_dict)

        gdmean = mean(itervalues(distances.distances))
        gdstdev = stdev(itervalues(distances.distances))
        self.reporters.info(
            'Mean genetic distance {0:.3f}, standard deviation {1:.3f}'.format(
                gdmean, gdstdev))
 def get_config_params(self):
     return [ConfigParameter(self.config_item_name(n),
                             self._config_items[n][0],
                             self._config_items[n][1])
             for n in iterkeys(self._config_items)]
    def __init__(self, genome_type, reproduction_type, species_set_type,
                 stagnation_type, filename):
        # Check that the provided types have the required methods.
        assert hasattr(genome_type, 'parse_config')
        assert hasattr(reproduction_type, 'parse_config')
        assert hasattr(species_set_type, 'parse_config')
        assert hasattr(stagnation_type, 'parse_config')

        self.genome_type = genome_type
        self.reproduction_type = reproduction_type
        self.species_set_type = species_set_type
        self.stagnation_type = stagnation_type

        if not os.path.isfile(filename):
            raise Exception('No such config file: ' +
                            os.path.abspath(filename))

        parameters = ConfigParser()
        with open(filename) as f:
            if hasattr(parameters, 'read_file'):
                parameters.read_file(f)
            else:
                parameters.readfp(f)

        # NEAT configuration
        if not parameters.has_section('NEAT'):
            raise RuntimeError(
                "'NEAT' section not found in NEAT configuration file.")

        param_list_names = []
        for p in self.__params:
            if p.default is None:
                setattr(self, p.name, p.parse('NEAT', parameters))
            else:
                try:
                    setattr(self, p.name, p.parse('NEAT', parameters))
                except Exception:
                    setattr(self, p.name, p.default)
                    warnings.warn(
                        "Using default {!r} for '{!s}'".format(
                            p.default, p.name), DeprecationWarning)
            param_list_names.append(p.name)
        param_dict = dict(parameters.items('NEAT'))
        unknown_list = [
            x for x in iterkeys(param_dict) if x not in param_list_names
        ]
        if unknown_list:
            if len(unknown_list) > 1:
                raise UnknownConfigItemError(
                    "Unknown (section 'NEAT') configuration items:\n" +
                    "\n\t".join(unknown_list))
            raise UnknownConfigItemError(
                "Unknown (section 'NEAT') configuration item {!s}".format(
                    unknown_list[0]))

        # Parse type sections.
        genome_dict = dict(parameters.items(genome_type.__name__))
        self.genome_config = genome_type.parse_config(genome_dict)

        species_set_dict = dict(parameters.items(species_set_type.__name__))
        self.species_set_config = species_set_type.parse_config(
            species_set_dict)

        stagnation_dict = dict(parameters.items(stagnation_type.__name__))
        self.stagnation_config = stagnation_type.parse_config(stagnation_dict)

        reproduction_dict = dict(parameters.items(reproduction_type.__name__))
        self.reproduction_config = reproduction_type.parse_config(
            reproduction_dict)