| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- """
- Parse genome from config file.
- Only int and float genes are supported currently.
- Example config file:
- [x1]
- type = float
- randMin = -100.0
- randMax = 100.0
- mutProb = 0.1
- mutAmt = 0.1
- [x2]
- type = int
- randMin = -50
- randMax = 50
- mutProb = 0.2
- mutAmt = 1
- One section per gene.
- 'type' is necessary - other fields depends on the selected type
- """
- import ConfigParser
- from ConfigParser import NoOptionError
- from gene import ComplexGeneFactory
- from gene import IntGeneFactory, IntGeneExchangeFactory
- from gene import FloatGeneFactory, FloatGeneRandomFactory, FloatGeneMaxFactory
- from gene import FloatGeneExchangeFactory
- class LoaderError(Exception):
- pass
- # Casts to int and float (TODO: Add other)
- def _intcast(section, key, value):
- "Parse string into int or None with correct exceptions"
- if not value: # '' and None are coerced to None
- return None
- try:
- return int(value)
- except ValueError:
- raise LoaderError("Invalid integer value '%s' in position %s / %s." % (
- value, section, key))
- def _floatcast(section, key, value):
- "Parse string into float or None with correct exceptions"
- if not value:
- return None
- try:
- return float(value)
- except ValueError:
- raise LoaderError("Invalid floating-point value '%s' in position %s / %s." % (
- value, section, key))
- class ConfigLoader(object):
- def __init__(self, filename, require_genes=[]):
- """
- Genome loader.
- Filename - path to configuration
- If require_genes are passed after the loading we ensure that
- they exist.
- """
- # Dictionary of supported types into casts and factories
- self.types = {
- 'int': (_intcast, IntGeneFactory),
- 'int_exchange': (_intcast, IntGeneExchangeFactory),
- 'float': (_floatcast, FloatGeneFactory),
- 'float_exchange': (_floatcast, FloatGeneExchangeFactory),
- 'float_random': (_floatcast, FloatGeneRandomFactory),
- 'float_max': (_floatcast, FloatGeneMaxFactory),
- 'complex': (_floatcast, ComplexGeneFactory),
- # 'char': (str, CharGeneFactory),
- # ... TODO: add other
- }
- self.genome = {}
- self.require_genes = require_genes
- self.config = ConfigParser.RawConfigParser()
- self.config.optionxform = str # Don't lower() names
- self.config.read(filename)
- # Do we have a population definition also?
- self._pre_parse_population()
- def register_type(self, typename, cast, factory):
- """
- User can register his types using this method
- """
- self.types[typename] = (cast, factory)
- def _pre_parse_population(self):
- self.has_population = self.config.has_section('population')
- try:
- genes = self.config.get('population', 'genes')
- genes = [gene.strip() for gene in genes.split(" ")]
- self.genes = genes if genes else None
- except NoOptionError:
- self.genes = None
- def load_population(self, name, species):
- """
- Parse population options and return a population
- """
- import new
- from population import Population
- if not self.has_population:
- raise LoaderError("No population is defined in the config file")
- args = {
- 'species': species
- }
- def parse(fun, name):
- if self.config.has_option('population', name):
- try:
- val = fun('population', name)
- args[name] = val
- except ValueError:
- raise LoaderError("Invalid value for population option " + name)
- parse(self.config.getint, 'initPopulation')
- parse(self.config.getint, 'childCull')
- parse(self.config.getint, 'childCount')
- parse(self.config.getint, 'incest')
- parse(self.config.getint, 'numNewOrganisms')
- parse(self.config.getboolean, 'mutateAfterMating')
- parse(self.config.getfloat, 'mutants')
- return new.classobj(name, (Population,), args)
- def _parse_gene(self, section):
- """
- parse_gene is called by parse_genome to parse
- a single gene from a config.
- config - ConfigParser instance
- section - gene name / config section name
- """
- # This check won't work because of configparser:
- if section in self.genome:
- raise LoaderError("Gene %s was already defined" % section_)
- try:
- typename = self.config.get(section, 'type')
- except NoOptionError:
- raise LoaderError(("Required field 'type' was not "
- "found in section/gene '%s'") % section)
- try:
- cast, factory = self.types[typename]
- except KeyError:
- raise LoaderError("Unhandled type in config file: " + typename)
- args = {}
- for key, value in self.config.items(section):
- if key == "type":
- continue
- if key == "mutProb": # Always float
- converted = _floatcast(section, key, value)
- else:
- converted = cast(section, key, value)
- args[key] = converted
- gene = factory(typename + "_" + section, **args)
- return gene
- def load_genome(self):
- """
- Load genome from config file
- """
- sections = self.genes if self.genes else self.config.sections()
- for section in sections:
- if section.lower() == 'population':
- continue
- elif self.config.has_option(section, 'alias'):
- alias = self.config.get(section, 'alias')
- if alias not in self.genome:
- raise LoaderError(("Gene %s is an alias for non-existing gene %s. "
- "Order matters!") % (section, alias))
- genome[section] = self.genome[alias]
- continue
- else:
- gene = self._parse_gene(section)
- self.genome[section] = gene
- for gene in self.require_genes:
- if gene not in self.genome:
- raise LoaderError("Required gene '%s' was not found in the config" % gene)
- #for gene in self.genome.itervalues():
- # print gene.__dict__
- return self.genome
|