Source code for galactic.context.memory

# This Python file uses the following encoding: utf-8

"""
The :mod:`galactic.context.memory` module give the ability to define
:class:`Context <galactic.context.Context>` that resides in memory.
"""

from typing import Mapping, Iterable, Union

from galactic.context import Context, Model, Population, Attribute, Individual
from galactic.context.mixins import ContextHolder, PopulationHolder, ModelHolder, \
    ConcreteAttribute, ConcreteIndividual, AttributesHolder, IndividualsHolder, ValuesHolder


# pylint: disable=too-few-public-methods,abstract-method,super-init-not-called
[docs]class MemoryContext( PopulationHolder['MemoryPopulation'], ModelHolder['MemoryModel'], Context['MemoryPopulation', 'MemoryModel', 'MemoryIndividual', 'MemoryAttribute'] ): """ The :class:`MemoryContext` class is designed to define contexts in memory. It inherits of all the behavior from the :class:`Context <galactic.context.Context>` class and allows direct creation and modification of a context. It's possible to create a context without nothing. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext() >>> print(context) {'population': [], 'model': {}} It's possible to create a context specifying the model definition. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext(definition={'mybool': bool, 'myint': int}) >>> print(context) {'population': [], 'model': {'mybool': <class 'bool'>, 'myint': <class 'int'>}} It's possible to create a context specifying the model definition and the list of individual identifiers. Example ------- >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> print(context) {'population': ['0', '1'], 'model': {'mybool': <class 'bool'>, 'myint': <class 'int'>}} It's possible to create a context specifying the model definition and the individual values. Example ------- >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals={'0': {'mybool': True}, '1':{'myint': 1}} ... ) >>> {ident: str(context.population[ident]) for ident in context.population} {'0': "{'mybool': True, 'myint': 0}", '1': "{'mybool': False, 'myint': 1}"} .. versionadded:: 0.0.1 """
[docs] def __init__(self, **kwargs): """ Initialise a context in memory. Keyword Arguments ----------------- definition : :class:`Mapping[str, type] <python:collections.abc.Mapping>` definition of the context by a mapping from name of attributes to their type individuals : :class:`Union[Iterable[str], Mapping[str, Mapping[str, object]]]` initial iterable of individual identifiers or a mapping from individual identifiers to individual values Raises ------ KeyError if an attribute is not in the definition ValueError if a value does not correspond to an attribute type TypeError if the definition or if the individuals parameter are not of the correct type .. versionadded:: 0.0.1 """ definition = MemoryContext._definition(**kwargs) individuals = MemoryContext._individuals(**kwargs) super().__init__( model=MemoryModel(self, {} if definition is None else definition), population=MemoryPopulation(self, []) ) if isinstance(individuals, Mapping): for identifier, values in individuals.items(): self.population[identifier] = values else: if individuals is not None: for identifier in individuals: self.population[identifier] = {}
@staticmethod def _definition(**kwargs) -> Mapping[str, type]: """ Get the definition from the keyword arguments. Keyword arguments ----------------- definition : :class:`Mapping[str, type] <python:collections.abc.Mapping>` definition of the context by a mapping from name of attributes to their type Raises ------ TypeError if the definition parameter is not of the correct type .. versionadded:: 0.0.2 """ if 'definition' in kwargs: definition = kwargs['definition'] if not isinstance(definition, Mapping): raise TypeError else: definition = None return definition @staticmethod def _individuals(**kwargs) -> Union[Iterable[str], Mapping[str, Mapping[str, object]]]: """ Get the individuals from the keyword arguments. Keyword arguments ----------------- individuals : :class:`Union[Iterable[str], Mapping[str, Mapping[str, object]]]` initial iterable of individual identifiers or a mapping from individual identifiers to individual values Raises ------ TypeError if the definition parameter is not of the correct type .. versionadded:: 0.0.2 """ if 'individuals' in kwargs: individuals = kwargs['individuals'] if not isinstance(individuals, (Iterable, Mapping)): raise TypeError else: individuals = None return individuals
# pylint: disable=too-few-public-methods,abstract-method,super-init-not-called
[docs]class MemoryModel( ContextHolder[MemoryContext], AttributesHolder['MemoryAttribute'], Model[MemoryContext, 'MemoryPopulation', 'MemoryAttribute'] ): """ The :class:`MemoryModel` class is designed to define models that resides in memory. It inherits of all the behavior from the :class:`Model <galactic.context.Model>` class. It's possible to change or to set attribute values. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> model = context.model >>> model['mybool'] = int >>> model['myint2'] = int >>> print(context.population['0']) {'mybool': 0, 'myint': 0, 'myint2': 0} It's possible to delete an attribute using its name. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> model = context.model >>> del model['mybool'] >>> {ident: str(context.population[ident]) for ident in context.population} {'0': "{'myint': 0}", '1': "{'myint': 0}"} .. versionadded:: 0.0.1 """
[docs] def __init__( self, context: MemoryContext, definition: Mapping[str, 'type'] ): """ Initialise a model in memory. Parameters ---------- context : :class:`MemoryContext` the underlying context definition : :class:`Mapping[str, type] <python:collections.abc.Mapping>` the attributes definition .. versionadded:: 0.0.1 """ super().__init__( context=context, attributes=[MemoryAttribute(self, name, cls) for name, cls in definition.items()] )
def __delitem__(self, name: str): """ Delete an attribute from a :class:`MemoryModel`. Parameters ---------- name : :class:`str` attribute name Raises ------ KeyError if the attribute is not in the model .. versionadded:: 0.0.1 """ super().__delitem__(name) for identifier in self.population: del self.population[identifier][name] def __setitem__(self, name: str, cls: type): """ Set an attribute for a :class:`MemoryModel`. Parameters ---------- name : :class:`str` the attribute name cls : :class:`python:type` the attribute type .. versionadded:: 0.0.1 """ if name in self: if self[name].type != cls: super().__setitem__(name, MemoryAttribute(self, name, cls)) for identifier in self.population: try: self.population[identifier][name] = cls(self.population[identifier][name]) except (ValueError, TypeError): self.population[identifier][name] = cls() else: super().__setitem__(name, MemoryAttribute(self, name, cls)) for identifier in self.population: self.population[identifier][name] = cls()
# pylint: disable=too-few-public-methods,abstract-method,super-init-not-called
[docs]class MemoryPopulation( ContextHolder[MemoryContext], IndividualsHolder['MemoryIndividual'], Population[MemoryContext, MemoryModel, 'MemoryIndividual'] ): """ The :class:`MemoryPopulation` class is designed to define populations that resides in memory. It inherits of all the behavior from the :class:`Population <galactic.context.Population>` class. It's possible to change or to set individual values. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> population = context.population >>> population['0'] = {'mybool': True} >>> population['2'] = {'myint': 1} >>> print(population['0']) {'mybool': True, 'myint': 0} >>> print(population['2']) {'mybool': False, 'myint': 1} It's possible to delete an individual using its identifier. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> population = context.population >>> del population['0'] >>> {ident: str(context.population[ident]) for ident in context.population} {'1': "{'mybool': False, 'myint': 0}"} .. versionadded:: 0.0.1 """
[docs] def __init__( self, context: MemoryContext, identifiers: Iterable[str] ): """ Initialise a population in memory. Parameters ---------- context : :class:`MemoryContext` the underlying context identifiers : :class:`Iterable[str] <python:collections.abc.Iterable>` an iterable of identifiers .. versionadded:: 0.0.1 """ super().__init__( context=context, individuals=[MemoryIndividual(self, identifier) for identifier in identifiers] )
def __setitem__(self, identifier: str, values: Mapping[str, object]): """ Set an individual for a :class:`MemoryPopulation`. Parameters ---------- identifier : :class:`str` the individual identifier values : :class:`Mapping[str, object] <<python:collections.abc.mapping>>` the initial values for some attributes Raises ------ KeyError if an attribute name does not belong to the underlying model. ValueError if an attribute value does not correspond to its type .. versionadded:: 0.0.1 """ try: individual = self[identifier] except KeyError: individual = MemoryIndividual(self, identifier) super().__setitem__(identifier, individual) for name, value in values.items(): if name not in self.model: raise KeyError individual[name] = value
# pylint: disable=too-few-public-methods,abstract-method,super-init-not-called
[docs]class MemoryIndividual( PopulationHolder[MemoryPopulation], ValuesHolder, ConcreteIndividual, Individual[ MemoryContext, MemoryPopulation, MemoryModel, 'MemoryIndividual', 'MemoryAttribute' ] ): """ The :class:`MemoryIndividual` is designed to define individuals that resides in memory. It inherits of all the behavior from the :class:`Individual <galactic.context.Individual>` class. It's possible to modify a value for an individual using an attribute name. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> individual = context.population['0'] >>> individual['mybool'] = True >>> individual['myint'] = 1 >>> {ident: str(context.population[ident]) for ident in context.population} {'0': "{'mybool': True, 'myint': 1}", '1': "{'mybool': False, 'myint': 0}"} .. versionadded:: 0.0.1 """
[docs] def __init__( self, population: MemoryPopulation, identifier: str ): """ Initialise an individual. Parameters ---------- population : :class:`MemoryPopulation` the population identifier : :class:`str` the individual identifier .. versionadded:: 0.0.1 """ super().__init__( population=population, identifier=identifier, values={name: attribute.type() for name, attribute in population.model.items()} )
def __setitem__(self, name: str, value): """ Set an individual's attribute value using either the attribute or its name. Parameters ---------- name: :class:`str` the attribute name value : :class:`object` the value Raises ------ KeyError if the attribute does not belong to the underlying context ValueError if the value passed in argument has its type different of the attribute type .. versionadded:: 0.0.1 """ attribute = self.model[name] if not isinstance(value, attribute.type): value = attribute.type(value) super().__setitem__(name, value) def __delitem__(self, name: str): """ Delete an individual value. Parameters ---------- name: :class:`str` the attribute name Raises ------ ValueError if the attribute is in the model (which should be always the case) .. versionadded:: 0.0.1 """ if name in self.model: raise ValueError else: super().__delitem__(name)
# pylint: disable=too-few-public-methods,abstract-method,super-init-not-called
[docs]class MemoryAttribute( ModelHolder[MemoryModel], ConcreteAttribute, Attribute[ MemoryContext, MemoryPopulation, MemoryModel, MemoryIndividual, 'MemoryAttribute' ] ): """ The :class:`MemoryAttribute` is designed to define attributes that resides in memory. It inherits of all the behavior from the :class:`Attribute <galactic.context.Attribute>` class. It's possible to modify a value for an attribute using an individual identifier. Example ------- >>> from galactic.context.memory import MemoryContext >>> context = MemoryContext( ... definition={'mybool': bool, 'myint': int}, ... individuals=['0', '1'] ... ) >>> attribute = context.model['myint'] >>> attribute['0'] = 3 >>> attribute['1'] = 4 >>> {ident: str(context.population[ident]) for ident in context.population} {'0': "{'mybool': False, 'myint': 3}", '1': "{'mybool': False, 'myint': 4}"} .. versionadded:: 0.0.1 """
[docs] def __init__( self, model: MemoryModel, name: str, cls: 'type' ): """ Initialise an attribute. Parameters ---------- model : :class:`MemoryModel` the underlying model name : :class:`str` the attribute name cls : :class:`type <python:type>` the attribute type .. versionadded:: 0.0.1 """ super().__init__( model=model, name=name, type=cls )
def __setitem__(self, identifier: str, value): """ Set an individual's attribute value using either the individual or its identifier. This method raises a :class:`KeyError` exception if the attribute does not belong to the underlying context and a :class:`ValueError` exception if the value passed in argument has its type different of the attribute type. Parameters ---------- identifier : :class:`str` the individual identifier value : :class:`object` the value Raises ------ KeyError if the individual does not exists ValueError if the value is not of the attribute type .. versionadded:: 0.0.1 """ self.population[identifier][self.name] = value