Source code for config

"""Does general configuration parsing; used by other classes for their configuration."""
from __future__ import print_function

import os
#import sys
import warnings

try:
    from configparser import ConfigParser, Error
except ImportError:
    from ConfigParser import Error, SafeConfigParser as ConfigParser

from neat.repr_util import check_if_file_object
from neat.six_util import iterkeys

[docs]class ConfigParameter(object): """Contains information about one configuration item.""" def __init__(self, name, value_type, default=None, default_ok=False): self.name = name self.value_type = value_type self.default = default self.default_ok = default_ok self.used_default = False
[docs] def __repr__(self): if self.default is None: return "ConfigParameter({0!r}, {1!r})".format(self.name, self.value_type) if self.default_ok: return "ConfigParameter({0!r}, {1!r}, {2!r}, default_ok=True)".format(self.name, self.value_type, self.default) return "ConfigParameter({0!r}, {1!r}, {2!r})".format(self.name, self.value_type, self.default)
[docs] def parse(self, section, config_parser): if int == self.value_type: return config_parser.getint(section, self.name) if bool == self.value_type: return config_parser.getboolean(section, self.name) if float == self.value_type: return config_parser.getfloat(section, self.name) if list == self.value_type: v = config_parser.get(section, self.name) return v.split(" ") if str == self.value_type: return config_parser.get(section, self.name) raise RuntimeError("Unexpected configuration type: " + repr(self.value_type))
[docs] def interpret(self, config_dict): """ Converts the config_parser output into the proper type, supplies defaults if available and needed, and checks for some errors. """ value = config_dict.get(self.name) if value is None: if self.default is None: raise RuntimeError('Missing configuration item: ' + self.name) else: if not self.default_ok: warnings.warn("Using default {0!r} for '{1}'".format( self.default, self.name), DeprecationWarning) self.used_default = True if (str != self.value_type) and isinstance(self.default, self.value_type): return self.default else: value = self.default try: if str == self.value_type: return str(value) if int == self.value_type: return int(value) if bool == self.value_type: if value.lower() == "true": return True elif value.lower() == "false": return False else: raise RuntimeError(self.name + " must be True or False") if float == self.value_type: return float(value) if list == self.value_type: return value.split(" ") except Exception: raise RuntimeError( "Error interpreting config item '{0}' with value {1!r} and type {2}".format( self.name, value, self.value_type)) raise RuntimeError("Unexpected configuration type: " + repr(self.value_type))
[docs] def format(self, value): if list == self.value_type: return " ".join(value) return str(value)
[docs]def write_pretty_params(f, config, params): """ Writes out current config parameters to file object f in a format suitable for to be read back in as a configuration file. """ param_names = [p.name for p in params] longest_name_len = max(len(name) for name in param_names) param_names.sort() params = dict((p.name, p) for p in params) param_names_used_default = [name for name in param_names if (params[name].used_default and not params[name].default_ok)] param_names_default_ok = [name for name in param_names if (params[name].used_default and params[name].default_ok)] param_names_not_defaulted = [name for name in param_names if not params[name].used_default] if param_names_not_defaulted: for name in param_names_not_defaulted: p = params[name] f.write('{} = {}\n'.format(p.name.ljust(longest_name_len), p.format(getattr(config, p.name)))) if param_names_default_ok: f.write('\n# Used expected default:\n') for name in param_names_default_ok: p = params[name] f.write('{} = {}\n'.format(p.name.ljust(longest_name_len), p.format(getattr(config, p.name)))) if param_names_used_default: f.write('\n# Used possibly-unexpected default:\n') for name in param_names_used_default: p = params[name] f.write('{} = {}\n'.format(p.name.ljust(longest_name_len), p.format(getattr(config, p.name))))
[docs]class UnknownConfigItemError(NameError): """Error for unknown configuration option - partially to catch typos.""" pass
[docs]class DefaultClassConfig(object): """ Replaces at least some boilerplate configuration code for reproduction, species_set, and stagnation classes. """ 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]))
[docs] @classmethod def write_config(cls, f, config): # pylint: disable=protected-access write_pretty_params(f, config, config._params)
[docs]class Config(object): """A simple container for user-configurable parameters of NEAT.""" __params = [ConfigParameter('pop_size', int), ConfigParameter('fitness_criterion', str), ConfigParameter('fitness_threshold', float), ConfigParameter('reset_on_extinction', bool), ConfigParameter('no_fitness_termination', bool, False)] 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 parameters = ConfigParser() if check_if_file_object(filename): if hasattr(parameters, 'read_file'): parameters.read_file(filename) else: parameters.readfp(filename) else: if not os.path.isfile(filename): raise Exception('No such config file: ' + os.path.abspath(filename)) 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)) if getattr(self, p.name) is None: setattr(self, p.name, p.default) p.used_default=True if not p.default_ok: warnings.warn("Using default {!r} for '{!s}'".format( p.default, p.name), DeprecationWarning) except (Error, RuntimeError): setattr(self, p.name, p.default) p.used_default = True if not p.default_ok: warnings.warn("Using default {!r} for '{!s}'".format(p.default, p.name), DeprecationWarning) if getattr(self, p.name) != p.default: p.used_default = False # Why needed??? 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)
[docs] def save(self, filename): if check_if_file_object(filename): self._save(filename) else: with open(filename, 'w') as f: self._save(f)
def _save(self, f): f.write('# The `NEAT` section specifies parameters particular to the NEAT algorithm\n') f.write('# or the experiment itself. This is the only required section.\n') f.write('[NEAT]\n') write_pretty_params(f, self, self.__params) f.write('\n[{0}]\n'.format(self.genome_type.__name__)) self.genome_type.write_config(f, self.genome_config) f.write('\n[{0}]\n'.format(self.species_set_type.__name__)) self.species_set_type.write_config(f, self.species_set_config) f.write('\n[{0}]\n'.format(self.stagnation_type.__name__)) self.stagnation_type.write_config(f, self.stagnation_config) f.write('\n[{0}]\n'.format(self.reproduction_type.__name__)) self.reproduction_type.write_config(f, self.reproduction_config)