Source code for biogeme.mdcev.mdcev

"""Implementation of the contribution to the log likelihood of the
MDCEV model.

See the technical report for a description of the various versions.

The functions involved in this module have a similar list of arguments. We document them here once for all:

- number_of_chosen_alternatives: number of alternative with non zero
    consumption. Note that it would be possible to calculate it from
    the consumption_quantities, but it would be too expensive in terms
    of calculation. Ideally should be stored as a variable in the
    database. Typically of type Variable.

- consumed_quantities: a dictionary associating the id of the
    alternatives with the observed number of times they are
    chosen. Typically, the values are of type Variable.

- baseline_utilities: a dictionary of baseline utility functions for
    each alternative. Typically, they are linear in the parameters. In
    the document, it is expressed as the inner product of beta and x.

- alpha_parameters: a dictionary of expressions for the alpha
    parameters appearing the specification of some the utility
    functions. See the technical report for details.

- gamma_parameters: a dictionary of expressions for the gamma
    parameters appearing the specification of the utility
    function. See the technical report for details.

- prices: a dictionary of expressions for the prices of each
      alternative.
    
- scale_parameter: expression for the scale parameter. Usually a
    numeric constant or a parameter. If None, the scale parameter is
    assumed to be fixed to 1, and is not included in the formulation.

:author: Michel Bierlaire
:date: Wed Aug 23 16:29:10 2023

"""
import logging
from typing import NamedTuple, Optional
from biogeme.expressions import Expression, exp, Elem, log, bioMultSum

logger = logging.getLogger(__name__)


[docs] class SpecificModel(NamedTuple): utilities: dict[int, Expression] log_determinant_entries: dict[int, Expression] inverse_of_determinant_entries: dict[int, Expression]
[docs] def info_gamma_parameters(gamma_parameters: dict[int, Optional[Expression]]) -> None: """Provides logging information about the outside good :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` """ none_count = sum(1 for value in gamma_parameters.values() if value is None) if none_count == 0: logger.info('No outside good is included in the model.') elif none_count == 1: logger.info('One outside good is included in the model.') else: logger.warning( 'Several outside goods are included in the model. If it is ' 'intentional, ignore this warning.' )
[docs] def mdcev( number_of_chosen_alternatives, consumed_quantities, specific_model, scale_parameter=None, ): """Generate the Biogeme formula for the log probability of the MDCEV model :param number_of_chosen_alternatives: see the module documentation :mod:`biogeme.mdcev` :type number_of_chosen_alternatives: biogeme.expression.Expression :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` :type consumed_quantities: dict[int: biogeme.expression.Expression] :param specific_model: a tuple containing dictionaries of expressions calculating - the utility functions. - the log of the entries of the determinant, - the inverse of the sames entries, :type specific_model: dict[int: biogeme.expression.Expression] :param scale_parameter: see the module documentation :mod:`biogeme.mdcev` :type scale_parameter: biogeme.expressions.Expression A detailed explanation is provided in the technical report "Estimating the MDCEV model with Biogeme" """ # utility of chosen goods terms = [ Elem({0: 0.0, 1: util}, consumed_quantities[i] > 0) for i, util in specific_model.utilities.items() ] if scale_parameter is None: baseline_term = bioMultSum(terms) else: baseline_term = scale_parameter * bioMultSum(terms) # Determinant: first term terms = [ Elem({0: 0.0, 1: z}, consumed_quantities[i] > 0) for i, z in specific_model.log_determinant_entries.items() ] first_determinant = bioMultSum(terms) # Determinant: second term terms = [ Elem({0: 0.0, 1: z}, consumed_quantities[i] > 0) for i, z in specific_model.inverse_of_determinant_entries.items() ] second_determinant = log(bioMultSum(terms)) # Logsum if scale_parameter is None: terms = [exp(util) for util in specific_model.utilities.values()] else: terms = [ scale_parameter * exp(util) for util in specific_model.utilities.values() ] logsum_term = number_of_chosen_alternatives * log(bioMultSum(terms)) log_prob = baseline_term + first_determinant + second_determinant - logsum_term # Scale parameter if scale_parameter is not None: log_prob += (number_of_chosen_alternatives - 1) * scale_parameter return log_prob