Source code for clinamen.evpd.core.population

# -*- coding: utf-8 -*-
""" Copyright 2020 Marco Arrigoni

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from collections.abc import MutableSequence
import numpy as np
import pandas as pd

from clinamen.evpd import ROOT_LOGGER as logger
from clinamen.evpd.core.individual import Individual


[docs]class BadPopulationMember(Exception): """ Raise the exception when one tries to add to a ``Population`` instance an object which is not an ``Individual`` instance """ pass
[docs]class Population(MutableSequence): """ A population is a group of individual of a given size. Parameters ---------- individuals : a list or tuple of individuals. the container of individuals forming the population. It can also be a single individual or empty. """ def __init__(self, *individuals): self._individuals = [] # passing variable number of arguments if hasattr(individuals, '__iter__') and len(individuals) > 1: for ind in individuals: self._check_type(ind) self._individuals.append(ind) # empty elif hasattr(individuals, '__iter__') and len(individuals) == 0: pass # passing a list or tuple elif ((isinstance(individuals[0], tuple) or isinstance(individuals[0], list)) and len(individuals) == 1): for ind in individuals[0]: self._check_type(ind) self._individuals.append(ind) # single element elif len(individuals) == 1: self._check_type(individuals[0]) self._individuals.append(individuals[0]) else: self._check_type(individuals) self._individuals.append(individuals) def _check_type(self, obj): if not isinstance(obj, Individual): raise BadPopulationMember('Only instances of type Individual can ' 'be part of a Population instance') def __len__(self): return len(self._individuals) def __getitem__(self, key): """ Return another Population instance if key is a slice. Note that the individuals are not copied, similar behavior of a ``list``""" if isinstance(key, slice): return self.__class__(self._individuals[key]) else: return self._individuals[key] def __setitem__(self, key, value): isiter = hasattr(value, '__iter__') if isinstance(key, int) and isinstance(value, Individual): self._individuals[key] = value logger.info('Individual number "{}" has been replaced'.format(key)) elif isinstance(key, slice) and isiter: for i, individual in enumerate(value): self._check_type(individual) step = key.step if key.step else 1 self._individuals[key.start + i * step] = individual logger.info('Individuals "{}" have been ' 'replaced'.format(':'.join([str(key.start), str(key.stop), str(key.step)]))) else: self._check_type(value) def __delitem__(self, key): if isinstance(key, (int, slice)): del self._individuals[key] else: raise TypeError('Indices must be integers or slices')
[docs] def insert(self, index, value): isseq = hasattr(value, '__len__') and hasattr(value, '__getitem__') if isseq and isinstance(value, Individual): # Individuals are iterables pass elif isseq: # another iterable for individual in value: self._check_type(individual) else: self._check_type(value) if isinstance(index, int): self._individuals.insert(index, value) else: raise TypeError('The index must be an integer')
@property def individuals_fitness(self): """ Return a list with the fitness of each individual """ self._individuals_fitness = [x.fitness for x in self] return self._individuals_fitness @property def dataframe(self): data = [x.data for x in self] df = pd.DataFrame(data=data) df.rename_axis('Name', inplace=True) df.reset_index(inplace=True) df.index = df.index.map(int) return df def sort(self): fitnesses = self.individuals_fitness indices = np.argsort(fitnesses) old_ind = self._individuals[:] for i, index in enumerate(indices): self._individuals[i] = old_ind[index] def get_equal_individuals(self): keys = [x.my_name for x in self] equals = {} for i, ind in enumerate(self): if ind.my_name in keys: equals[ind.my_name] = [] keys.remove(ind.my_name) for ind2 in self[i+1:-1]: if ind == ind2: equals[ind.my_name].append(ind2.my_name) keys.remove(ind2.my_name) return equals