Source code for biogeme.sampling_of_alternatives.generate_model

"""Generation of models estimated with samples of alternatives

:author: Michel Bierlaire
:date: Fri Sep 22 12:14:59 2023
"""

import copy
import logging

from biogeme.expressions import (
    Expression,
    LogSampleCrossNested,
    LogSampledLogit,
    LogSampledNested,
    Variable,
)
from biogeme.nests import NestsForNestedLogit

from ..expressions.add_prefix_suffix import add_prefix_suffix_to_all_variables
from .sampling_context import CNL_PREFIX, LOG_PROBA_COL, MEV_WEIGHT, SamplingContext

logger = logging.getLogger(__name__)


[docs] class GenerateModel: """Class in charge of generating the biogeme expression for the loglikelihood function """ def __init__(self, context: SamplingContext): """Constructor :param context: contains all the information that is needed to perform the sampling of alternatives. """ self.context = context self.utility_function = context.utility_function self.total_sample_size = context.total_sample_size self.total_mev_sample_size = context.total_mev_sample_size self.attributes = context.attributes self.mev_prefix = context.mev_prefix self.utilities = { alt_id: self.generate_utility(prefix='', suffix=f'_{alt_id}') for alt_id in range(self.total_sample_size) } if self.context.mev_partition is None: self.mev_utilities = { alt_id: self.utilities[alt_id] for alt_id in range(1, self.total_sample_size) } logger.debug( f'No specific MEV partition. {self.total_sample_size} are sampled for MEV terms.' ) else: self.mev_utilities = { alt_id: self.generate_utility( prefix=self.mev_prefix, suffix=f'_{alt_id}' ) for alt_id in range(self.context.total_mev_sample_size) } logger.debug( f'Specific MEV partition provided. {self.total_mev_sample_size} are sampled for MEV terms.' )
[docs] def generate_utility(self, prefix: str, suffix: str) -> Expression: """Generate the utility function for one alternative :param prefix: prefix to add to the attributes :param suffix: suffix to add to the attributes """ copy_utility = copy.deepcopy(self.utility_function) add_prefix_suffix_to_all_variables( expr=copy_utility, prefix=prefix, suffix=suffix ) return copy_utility
[docs] def get_logit(self) -> Expression: """Returns the expression for the log likelihood of the logit model""" log_probabilities = { i: Variable(f'{LOG_PROBA_COL}_{i}') for i in self.utilities } return LogSampledLogit( utilities=self.utilities, log_probabilities=log_probabilities, choice=0, )
[docs] def get_nested_logit(self, nests: NestsForNestedLogit) -> Expression: """Returns the expression for the log likelihood of the nested logit model :param nests: A tuple containing as many items as nests. Each item is also a tuple containing two items: - an object of type biogeme.expressions.expr.Expression representing the nest parameter, - a list containing the list of identifiers of the alternatives belonging to the nest. Example:: nesta = MUA ,[1, 2, 3] nestb = MUB ,[4, 5, 6] nests = nesta, nestb """ log_probabilities = { i: Variable(f'{LOG_PROBA_COL}_{i}') for i in self.utilities } alternative_ids = { i: Variable(f'{self.context.id_column}_{i}') for i in self.utilities } mev_alternative_ids = { i: Variable(f'{self.mev_prefix}{self.context.id_column}_{i}') for i in self.mev_utilities } mev_weights = { i: Variable(f'{self.mev_prefix}{MEV_WEIGHT}_{i}') for i in self.mev_utilities } return LogSampledNested( utilities=self.utilities, log_probabilities=log_probabilities, alternative_ids=alternative_ids, mev_utilities=self.mev_utilities, mev_weights=mev_weights, mev_alternative_ids=mev_alternative_ids, nests=nests, choice=0, )
[docs] def get_cross_nested_logit(self) -> Expression: """Returns the expression for the log likelihood of the nested logit model""" nests = self.context.cnl_nests log_probabilities = { i: Variable(f'{LOG_PROBA_COL}_{i}') for i in self.utilities } alphas = { nest.name: { i: Variable(f'{CNL_PREFIX}{nest.name}_{i}') for i in self.utilities } for nest in nests } mev_weights = { i: Variable(f'{self.mev_prefix}{MEV_WEIGHT}_{i}') for i in self.mev_utilities } mev_alphas = { nest.name: { i: Variable(f'{self.mev_prefix}{CNL_PREFIX}{nest.name}_{i}') for i in self.mev_utilities } for nest in nests } return LogSampleCrossNested( utilities=self.utilities, log_probabilities=log_probabilities, alphas=alphas, mev_utilities=self.mev_utilities, mev_weights=mev_weights, mev_alphas=mev_alphas, nests=nests, choice=0, )