Source code for biogeme.specification

"""Model specification in a multiple expression context

:author: Michel Bierlaire
:date: Mon Apr 10 12:33:18 2023

Implements a model specification in a multiple expression context (using Catalogs)
"""
import logging
from biogeme_optimization.pareto import SetElement
import biogeme.biogeme as bio
from biogeme import tools
from biogeme.configuration import Configuration
import biogeme.exceptions as excep

logger = logging.getLogger(__name__)


[docs]class Specification: """Implements a specification""" database = None #: :class:`biogeme.database.Database` object all_results = {} #: dict(str: `biogeme.results.bioResults`) expression = None #: :class:`biogeme.expressions.Expression` object """ function that generates all the objectives: fct(bioResults) -> list[floatNone] """ validity = None """ function that checks the validity of the results """ generic_name = 'default_name' #: short name for file names
[docs] def __init__(self, configuration): """Creates a specification from a configuration :param configuration: configuration of the multiple expression :type configuration: biogeme.configuration.Configuration """ if not isinstance(configuration, Configuration): error_msg = 'Ctor needs an object of type Configuration' raise excep.BiogemeError(error_msg) self.configuration = configuration self.model_names = None self._estimate()
[docs] @classmethod def from_string_id(cls, configuration_id): """Constructor using a configuration""" return cls(Configuration.from_string(configuration_id))
[docs] def configure_expression(self): """Configure the expression to the current configuration""" self.expression.configure_catalogs(self.configuration)
[docs] @classmethod def default_specification(cls): """Alternative constructor for generate the default specification""" cls.expression.reset_expression_selection() the_config = cls.expression.current_configuration() return cls(the_config)
@property def config_id(self): """Defined config_id as a property""" return self.configuration.get_string_id() @config_id.setter def config_id(self, value): self.configuration = Configuration.from_string(value)
[docs] def get_results(self): """Obtain the estimation results of the specification""" the_results = self.all_results.get(self.config_id) if the_results is None: error_msg = f'No result is available for specification {self.config_id}' raise excep.BiogemeError(error_msg) return the_results
def __repr__(self): return str(self.config_id) def _estimate(self): """Estimate the parameter of the current specification, if not already done :param quick_estimate: if True, a "quick estimate" is performed, in the sense that the final statistics are not calculated :type quick_estimate: bool """ if self.expression is None: error_msg = 'No expression has been provided for the model.' raise excep.BiogemeError(error_msg) if self.database is None: error_msg = 'No database has been provided for the estimation.' raise excep.BiogemeError(error_msg) if self.model_names is None: self.model_names = tools.ModelNames(prefix=self.generic_name) if self.config_id in self.all_results: return logger.debug(f'****** Estimate {self.config_id}') the_biogeme = bio.BIOGEME.from_configuration( config_id=self.config_id, expression=self.expression, database=self.database ) the_biogeme.modelName = self.model_names(self.config_id) logger.info(f'*** Estimate {the_biogeme.modelName}') the_biogeme.generate_html = False the_biogeme.generate_pickle = False results = the_biogeme.quickEstimate() self.all_results[self.config_id] = results
[docs] def describe(self): """Short description of the solution. Used for reporting. :return: short description of the solution. :rtype: str """ the_results = self.get_results() return f'{the_results.short_summary()}'
[docs] def get_element(self, multi_objectives): """Obtains the element from the Pareto set corresponding to a specification :param multi_objectives: function calculating the objectives from the estimation results :type multi_objectives: fct(biogeme.results.bioResults) --> list[float] :return: element from the Pareto set :rtype: biogeme.pareto.SetElement """ the_id = self.config_id the_results = self.get_results() the_objectives = multi_objectives(the_results) element = SetElement(the_id, the_objectives) logger.debug(f'{element=}') return element