diff --git a/src/biogeme/mdcev.py b/src/biogeme/mdcev.py deleted file mode 100644 index d0e7ec70..00000000 --- a/src/biogeme/mdcev.py +++ /dev/null @@ -1,320 +0,0 @@ -"""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 - -""" -from biogeme.expressions import exp, Elem, log, bioMultSum -from biogeme.mdcev_models import translated, generalized, gamma_profile, non_monotonic - - -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 - - -def mdcev_translated( - number_of_chosen_alternatives, - consumed_quantities, - baseline_utilities, - alpha_parameters, - gamma_parameters, - scale_parameter=None, -): - """Generate the Biogeme formula for the log probability of the - MDCEV model using the translated utility function. - - :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 baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` - :type alpha_parameters: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: 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" - - """ - - specific_model = translated( - baseline_utilities, - consumed_quantities, - alpha_parameters, - gamma_parameters, - ) - - return mdcev( - number_of_chosen_alternatives=number_of_chosen_alternatives, - consumed_quantities=consumed_quantities, - specific_model=specific_model, - scale_parameter=scale_parameter, - ) - - -def mdcev_generalized( - number_of_chosen_alternatives, - consumed_quantities, - baseline_utilities, - alpha_parameters, - gamma_parameters, - prices=None, - scale_parameter=None, -): - """Generate the Biogeme formula for the log probability of the - MDCEV model using the generalized translated utility function - - :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 baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` - :type alpha_parameters: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - :param prices: see the module documentation :mod:`biogeme.mdcev` - :type prices: biogeme.expressions.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" - - """ - - specific_model = generalized( - baseline_utilities, - consumed_quantities, - alpha_parameters, - gamma_parameters, - prices, - ) - - return mdcev( - number_of_chosen_alternatives=number_of_chosen_alternatives, - consumed_quantities=consumed_quantities, - specific_model=specific_model, - scale_parameter=scale_parameter, - ) - - -def mdcev_gamma( - number_of_chosen_alternatives, - consumed_quantities, - baseline_utilities, - gamma_parameters, - prices=None, - scale_parameter=None, -): - """Generate the Biogeme formula for the log probability of the - MDCEV model using the linear expenditure system. - - :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 baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - :param prices: see the module documentation :mod:`biogeme.mdcev` - :type prices: biogeme.expressions.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" - - """ - - specific_model = gamma_profile( - baseline_utilities, - consumed_quantities, - gamma_parameters, - prices, - ) - - return mdcev( - number_of_chosen_alternatives=number_of_chosen_alternatives, - consumed_quantities=consumed_quantities, - specific_model=specific_model, - scale_parameter=scale_parameter, - ) - - -def mdcev_non_monotonic( - number_of_chosen_alternatives, - consumed_quantities, - baseline_utilities, - mu_utilities, - alpha_parameters, - gamma_parameters, - prices=None, -): - """Generate the Biogeme formula for the log probability of the - MDCEV model using the linear expenditure system. - - :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 baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` - :type alpha_parameters: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - :param prices: see the module documentation :mod:`biogeme.mdcev` - :type prices: biogeme.expressions.Expression - - A detailed explanation is provided in the technical report - "Estimating the MDCEV model with Biogeme" - - """ - - specific_model = non_monotonic( - baseline_utilities, - mu_utilities, - consumed_quantities, - alpha_parameters, - gamma_parameters, - prices, - ) - - return mdcev( - number_of_chosen_alternatives=number_of_chosen_alternatives, - consumed_quantities=consumed_quantities, - specific_model=specific_model, - scale_parameter=None, - ) diff --git a/src/biogeme/mdcev/__init__.py b/src/biogeme/mdcev/__init__.py new file mode 100644 index 00000000..9b0d251b --- /dev/null +++ b/src/biogeme/mdcev/__init__.py @@ -0,0 +1,4 @@ +from .translated import mdcev_translated +from .generalized import mdcev_generalized +from .gamma_profile import mdcev_gamma +from .non_monotonic import mdcev_non_monotonic diff --git a/src/biogeme/mdcev/gamma_profile.py b/src/biogeme/mdcev/gamma_profile.py new file mode 100644 index 00000000..c9907878 --- /dev/null +++ b/src/biogeme/mdcev/gamma_profile.py @@ -0,0 +1,159 @@ +"""Implementation of the "gamma profile" MDCEV model. See section 3.1 in +the technical report. + +:author: Michel Bierlaire +:date: Sun Nov 5 15:56:36 2023 + +""" +from typing import Optional +from biogeme.expressions import Expression, log +from .mdcev import mdcev, info_gamma_parameters, SpecificModel + + +def gamma_profile( + baseline_utilities: dict[int, Expression], + consumed_quantities: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], + prices: Optional[dict[int, Expression]] = None, +): + """Calculates the determinant entries for the linear expenditure system + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + :param prices: see the module documentation + :mod:`biogeme.mdcev`. If None, assumed to be 1. + + """ + info_gamma_parameters(gamma_parameters) + + def calculate_utility( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the utility. The formula is different is it is an + outside good, characterized by the absence of a gamma + parameter. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return baseline_utilities[the_id] - log(consumption) + if price is None: + return baseline_utilities[the_id] + log(gamma) - log(consumption + gamma) + return ( + baseline_utilities[the_id] + log(gamma) - log(consumption + price * gamma) + ) + + def calculate_log_determinant( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the log of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return -log(consumption) + if price is None: + return -log(consumption + gamma) + return -log(consumption + price * gamma) + + def calculate_inverse_determinant( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the inverse of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return consumption + if price is None: + return consumption + gamma + return consumption + price * gamma + + if prices: + utilities = { + the_id: (calculate_utility(the_id, consumption, prices[the_id])) + for the_id, consumption in consumed_quantities.items() + } + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + inverse_of_determinant_entries = { + the_id: calculate_inverse_determinant(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + + utilities = { + the_id: calculate_utility(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + inverse_of_determinant_entries = { + the_id: calculate_inverse_determinant(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + + +def mdcev_gamma( + number_of_chosen_alternatives: Expression, + consumed_quantities: dict[int, Expression], + baseline_utilities: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], + prices: Optional[dict[int, Expression]] = None, + scale_parameter: Optional[Expression] = None, +): + """Generate the Biogeme formula for the log probability of the + MDCEV model using the linear expenditure system. + + :param number_of_chosen_alternatives: see the module documentation + :mod:`biogeme.mdcev` + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + :param gamma_parameters: see the module documentation + :mod:`biogeme.mdcev` + :param prices: see the module documentation :mod:`biogeme.mdcev` + :param scale_parameter: see the module documentation :mod:`biogeme.mdcev` + + A detailed explanation is provided in the technical report + "Estimating the MDCEV model with Biogeme" + + """ + + specific_model = gamma_profile( + baseline_utilities, + consumed_quantities, + gamma_parameters, + prices, + ) + + return mdcev( + number_of_chosen_alternatives=number_of_chosen_alternatives, + consumed_quantities=consumed_quantities, + specific_model=specific_model, + scale_parameter=scale_parameter, + ) diff --git a/src/biogeme/mdcev/generalized.py b/src/biogeme/mdcev/generalized.py new file mode 100644 index 00000000..a6137987 --- /dev/null +++ b/src/biogeme/mdcev/generalized.py @@ -0,0 +1,192 @@ +"""Implementation of the "generalized" MDCEV model. See section 3 in +the technical report. + +:author: Michel Bierlaire +:date: Sun Nov 5 15:43:18 2023 + +""" +from typing import Optional +from biogeme.expressions import Expression, log +from .mdcev import mdcev, info_gamma_parameters, SpecificModel + + +def generalized( + baseline_utilities: dict[int, Expression], + consumed_quantities: dict[int, Expression], + alpha_parameters: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], + prices: Optional[dict[int, Expression]] = None, +): + """Calculates the determinant entries for the Bhat 2008 specification Eq (18) + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + + :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param prices: see the module documentation :mod:`biogeme.mdcev`. + If None, assumed to be 1. + + """ + info_gamma_parameters(gamma_parameters) + + def calculate_utility( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the utility. The formula is different is it is an + outside good, characterized by the absence of a gamma + parameter. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + + gamma = gamma_parameters[the_id] + if gamma is None: + if price is None: + # gamma is None. price is None + return baseline_utilities[the_id] + ( + alpha_parameters[the_id] - 1 + ) * log(consumption) + # gamma is None. price is not None + return ( + baseline_utilities[the_id] + + (alpha_parameters[the_id] - 1) * log(consumption) + - alpha_parameters[the_id] * log(price) + ) + # gamma is not None. price is None + if price is None: + return baseline_utilities[the_id] + (alpha_parameters[the_id] - 1) * log( + 1.0 + consumption / gamma + ) + # gamma is not None. price is not None + return ( + baseline_utilities[the_id] + - log(price) + + (alpha_parameters[the_id] - 1) * log(1.0 + consumption / (price * gamma)) + ) + + def calculate_log_determinant( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the log of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return log(Numeric(1) - alpha_parameters[the_id]) - log(consumption) + if price is None: + return log(Numeric(1) - alpha_parameters[the_id]) - log(consumption + gamma) + return log(Numeric(1) - alpha_parameters[the_id]) - log( + consumption + price * gamma + ) + + def calculate_inverse_determinant( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the inverse of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return consumption / (Numeric(1) - alpha_parameters[the_id]) + if price is None: + return (consumption + gamma) / (Numeric(1) - alpha_parameters[the_id]) + return (consumption + price * gamma) / (Numeric(1) - alpha_parameters[the_id]) + + if prices: + utilities = { + the_id: (calculate_utility(the_id, consumption, prices[the_id])) + for the_id, consumption in consumed_quantities.items() + } + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + inverse_of_determinant_entries = { + the_id: calculate_inverse_determinant(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + # Use unit prices if prices is set to None + utilities = { + the_id: (calculate_utility(the_id, consumption, None)) + for the_id, consumption in consumed_quantities.items() + } + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + inverse_of_determinant_entries = { + the_id: calculate_inverse_determinant(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + + +def mdcev_generalized( + number_of_chosen_alternatives: Expression, + consumed_quantities: dict[int, Expression], + baseline_utilities: dict[int, Expression], + alpha_parameters: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], + prices: Optional[dict[int, Expression]] = None, + scale_parameter: Optional[Expression] = None, +): + """Generate the Biogeme formula for the log probability of the + MDCEV model using the generalized translated utility function + + :param number_of_chosen_alternatives: see the module documentation + :mod:`biogeme.mdcev` + + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + + :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param prices: see the module documentation :mod:`biogeme.mdcev` + + :param scale_parameter: see the module documentation :mod:`biogeme.mdcev` + + A detailed explanation is provided in the technical report + "Estimating the MDCEV model with Biogeme" + + """ + + specific_model = generalized( + baseline_utilities, + consumed_quantities, + alpha_parameters, + gamma_parameters, + prices, + ) + + return mdcev( + number_of_chosen_alternatives=number_of_chosen_alternatives, + consumed_quantities=consumed_quantities, + specific_model=specific_model, + scale_parameter=scale_parameter, + ) diff --git a/src/biogeme/mdcev/mdcev.py b/src/biogeme/mdcev/mdcev.py new file mode 100644 index 00000000..a0512020 --- /dev/null +++ b/src/biogeme/mdcev/mdcev.py @@ -0,0 +1,139 @@ +"""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__) + + +class SpecificModel(NamedTuple): + utilities: dict[int, Expression] + log_determinant_entries: dict[int, Expression] + inverse_of_determinant_entries: dict[int, Expression] + + +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.' + ) + + +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 diff --git a/src/biogeme/mdcev/non_monotonic.py b/src/biogeme/mdcev/non_monotonic.py new file mode 100644 index 00000000..d40ceb33 --- /dev/null +++ b/src/biogeme/mdcev/non_monotonic.py @@ -0,0 +1,260 @@ +"""Implementation of the "non monotonic" MDCEV model. See section 3.2 in +the technical report. + +:author: Michel Bierlaire +:date: Sun Nov 5 15:58:46 2023 + +""" +from typing import Optional +from biogeme.expressions import Expression, log, exp, Numeric +from .mdcev import mdcev, info_gamma_parameters, SpecificModel + + +def non_monotonic( + baseline_utilities, + mu_utilities, + consumed_quantities, + alpha_parameters, + gamma_parameters, + prices=None, +): + """Calculates the determinant entries for the linear expenditure system + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + :type baseline_utilities: dict[int: biogeme.expression.Expression] + + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + :type consumed_quantities: dict[int: biogeme.expression.Expression] + + :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` + :type alpha_parameters: dict[int: biogeme.expression.Expression] + + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + :type gamma_parameters: dict[int: biogeme.expression.Expression] + + :param prices: see the module documentation + :mod:`biogeme.mdcev`. If None, assumed to be 1. + :type prices: biogeme.expressions.Expression + + + """ + info_gamma_parameters() + + def calculate_utility( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the utility. The formula is different is it is an + outside good, characterized by the absence of a gamma + parameter. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + + gamma = gamma_parameters[the_id] + if gamma is None: + if price is None: + # gamma is None. price is None. + return mu_utilities[the_id] + exp( + baseline_utilities[the_id] + ) * consumption ** (alpha_parameters[the_id] - 1) + # gamma is None. price is not None. + return ( + mu_utilities[the_id] + + exp(baseline_utilities[the_id]) + * (consumption / price) ** (alpha_parameters[the_id] - 1) + / price + ) + if price is None: + # gamma is not None. price is None. + return mu_utilities[the_id] + exp(baseline_utilities[the_id]) * ( + 1 + consumption / gamma + ) ** (alpha_parameters[the_id] - 1) + # gamma is not None. price is not None. + return ( + mu_utilities[the_id] + + exp(baseline_utilities[the_id]) + * (1 + consumption / (price * gamma)) ** (alpha_parameters[the_id] - 1) + / price + ) + + def calculate_log_determinant( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the log of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + if price is None: + # gamma is None. price is None. + return ( + baseline_utilities[the_id] + + log(1 - alpha_parameters[the_id]) + + (alpha_parameters[the_id] - 2) * log(consumption) + ) + # gamma is None. price is not None. + return ( + baseline_utilities[the_id] + + log(1 - alpha_parameters[the_id]) + + (alpha_parameters[the_id] - 2) * log(consumption) + - alpha_parameters[the_id] * log(price) + ) + if price is None: + # gamma is not None. price is None + return ( + baseline_utilities[the_id] + + log(1 - alpha_parameters[the_id]) + - log(gamma) + + (alpha_parameters[the_id] - 2) * log(1 + consumption / gamma) + ) + # gamma is not None. price is not None + return ( + Numeric(-2) * log(price) + + baseline_utilities[the_id] + + log(Numeric(1) - alpha_parameters[the_id]) + - log(gamma) + + (alpha_parameters[the_id] - Numeric(2)) + * log(Numeric(1) + consumption / (price * gamma)) + ) + + def calculate_inverse_determinant( + the_id: int, consumption: Expression, price: Optional[Expression] + ) -> Expression: + """Calculate the inverse of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + if price is None: + # gamma is None. price is None. + return ( + exp(-baseline_utilities[the_id]) + * consumption ** (2 - alpha_parameters[the_id]) + / (1 - alpha_parameters[the_id]) + ) + # gamma is None. price is not None. + return ( + price + * price + * exp(-baseline_utilities[the_id]) + * (consumption / price) ** (2 - alpha_parameters[the_id]) + / (1 - alpha_parameters[the_id]) + ) + if price is None: + # gamma is not None. price is None. + return ( + exp(-baseline_utilities[the_id]) + * gamma + * (1 + consumption / gamma) ** (2 - alpha_parameters[the_id]) + / (1 - alpha_parameters[the_id]) + ) + # gamma is not None. price is not None. + return ( + price + * price + * exp(-baseline_utilities[the_id]) + * gamma + * (1 + consumption / (price * gamma)) ** (2 - alpha_parameters[the_id]) + / (1 - alpha_parameters[the_id]) + ) + + if prices: + utilities = { + the_id: calculate_utility(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + + inverse_of_determinant_entries = { + the_id: calculate_inverse_determinant(the_id, consumption, prices[the_id]) + for the_id, consumption in consumed_quantities.items() + } + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + + utilities = { + the_id: calculate_utility(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + + inverse_of_determinant_entries = { + id: calculate_inverse_determinant(the_id, consumption, None) + for the_id, consumption in consumed_quantities.items() + } + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + + +def mdcev_non_monotonic( + number_of_chosen_alternatives: Expression, + consumed_quantities: dict[int, Expression], + baseline_utilities: dict[int, Expression], + mu_utilities: dict[int, Expression], + alpha_parameters: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], + prices: Optional[dict[int, Expression]] = None, +): + """Generate the Biogeme formula for the log probability of the + MDCEV model using the linear expenditure system. + + :param number_of_chosen_alternatives: see the module documentation + :mod:`biogeme.mdcev` + + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + + :param mu_utilities: additional utility for the non-monotonic + part. see the module documentation :mod:`biogeme.mdcev` + + :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param prices: see the module documentation :mod:`biogeme.mdcev` + + A detailed explanation is provided in the technical report + "Estimating the MDCEV model with Biogeme" + + """ + + specific_model = non_monotonic( + baseline_utilities, + mu_utilities, + consumed_quantities, + alpha_parameters, + gamma_parameters, + prices, + ) + + return mdcev( + number_of_chosen_alternatives=number_of_chosen_alternatives, + consumed_quantities=consumed_quantities, + specific_model=specific_model, + scale_parameter=None, + ) diff --git a/src/biogeme/mdcev/translated.py b/src/biogeme/mdcev/translated.py new file mode 100644 index 00000000..da7e2e8a --- /dev/null +++ b/src/biogeme/mdcev/translated.py @@ -0,0 +1,147 @@ +"""Implementation of the "translated" MDCEV model. See section 2.1 in +the technical report. + +:author: Michel Bierlaire +:date: Sun Nov 5 15:43:18 2023 + +""" +from typing import Optional +from biogeme.expressions import Expression, log, Numeric + +from .mdcev import mdcev, info_gamma_parameters, SpecificModel + + +def translated( + baseline_utilities: dict[int, Expression], + consumed_quantities: dict[int, Expression], + alpha_parameters: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], +): + """Calculates + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + + :param consumed_quantities: see the module documentation + :mod:`biogeme.mdcev` + + :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + + """ + info_gamma_parameters(gamma_parameters) + + def calculate_utility(the_id: int, consumption: Expression) -> Expression: + """Calculate the utility. The formula is different is it is an + outside good, characterized by the absence of a gamma + parameter. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return ( + baseline_utilities[the_id] + + log(alpha_parameters[the_id]) + + (alpha_parameters[the_id] - Numeric(1)) * log(consumption) + ) + return ( + baseline_utilities[the_id] + + log(alpha_parameters[the_id]) + + (alpha_parameters[the_id] - Numeric(1)) * log(consumption + gamma) + ) + + def calculate_log_determinant(the_id: int, consumption: Expression) -> Expression: + """Calculate the log of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return log(Numeric(1) - alpha_parameters[the_id]) - log(consumption) + return log(Numeric(1) - alpha_parameters[the_id]) - log(consumption + gamma) + + def calculate_inverse_determinant( + the_id: int, consumption: Expression + ) -> Expression: + """Calculate the inverse of the entries for the determinant. For + the outside good, gamma is equal to 0. + + :param the_id: identifier of the good. + :param consumption: expression for the consumption. + :param price: expression for the price, or None if prices are not considered. + """ + gamma = gamma_parameters[the_id] + if gamma is None: + return consumption / (Numeric(1) - alpha_parameters[the_id]) + + return (consumption + gamma) / (Numeric(1) - alpha_parameters[the_id]) + + utilities = { + the_id: calculate_utility(the_id, consumption) + for the_id, consumption in consumed_quantities.items() + } + + log_determinant_entries = { + the_id: calculate_log_determinant(the_id, consumption) + for the_id, consumption in consumed_quantities.items() + } + + inverse_of_determinant_entries = { + the_id: calculate_inverse_determinant(the_id, consumption) + for the_id, consumption in consumed_quantities.items() + } + + return SpecificModel( + utilities=utilities, + log_determinant_entries=log_determinant_entries, + inverse_of_determinant_entries=inverse_of_determinant_entries, + ) + + +def mdcev_translated( + number_of_chosen_alternatives: Expression, + consumed_quantities: dict[int, Expression], + baseline_utilities: dict[int, Expression], + alpha_parameters: dict[int, Expression], + gamma_parameters: dict[int, Optional[Expression]], + scale_parameter: Optional[Expression] = None, +): + """Generate the Biogeme formula for the log probability of the + MDCEV model using the translated utility function. + + :param number_of_chosen_alternatives: see the module documentation :mod:`biogeme.mdcev` + + :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` + + :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` + + :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` + + :param scale_parameter: see the module documentation :mod:`biogeme.mdcev` + + A detailed explanation is provided in the technical report + "Estimating the MDCEV model with Biogeme" + + """ + + specific_model = translated( + baseline_utilities, + consumed_quantities, + alpha_parameters, + gamma_parameters, + ) + + return mdcev( + number_of_chosen_alternatives=number_of_chosen_alternatives, + consumed_quantities=consumed_quantities, + specific_model=specific_model, + scale_parameter=scale_parameter, + ) diff --git a/src/biogeme/mdcev_models.py b/src/biogeme/mdcev_models.py deleted file mode 100644 index 54bb858e..00000000 --- a/src/biogeme/mdcev_models.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Implementation of the various versions of the MDCEV model - -Refer to the techical report for the mathematical expressions implemented in this file. -The report mentions three models: - - - translated utility function: labeled "translated" in this - implementation. - - generalized translated utility function: labeled "geneneralized" in - this implementation - - linear expenditure system: labeled "gamma_profile" in this implementation - -:author: Michel Bierlaire -:date: Thu Aug 24 09:14:36 2023 - -""" -from typing import NamedTuple -from biogeme.expressions import Expression, log, exp - - -class SpecificModel(NamedTuple): - utilities: dict[int, Expression] - log_determinant_entries: dict[int, Expression] - inverse_of_determinant_entries: dict[int, Expression] - - -def translated( - baseline_utilities, - consumed_quantities, - alpha_parameters, - gamma_parameters, -): - """Calculates - - :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` - :type consumed_quantities: dict[int: biogeme.expression.Expression] - - :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` - :type alpha_parameters: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - """ - utilities = { - id: ( - baseline_utilities[id] - + log(alpha_parameters[id]) - + (alpha_parameters[id] - 1) * log(consumption + gamma_parameters[id]) - ) - for id, consumption in consumed_quantities.items() - } - - log_determinant_entries = { - id: log(1 - alpha_parameters[id]) - log(consumption + gamma_parameters[id]) - for id, consumption in consumed_quantities.items() - } - - inverse_of_determinant_entries = { - id: (consumption + gamma_parameters[id]) / (1 - alpha_parameters[id]) - for id, consumption in consumed_quantities.items() - } - - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - ) - - -def generalized( - baseline_utilities, - consumed_quantities, - alpha_parameters, - gamma_parameters, - prices=None, -): - """Calculates the determinant entries for the Bhat 2008 specification Eq (18) - - :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` - :type consumed_quantities: dict[int: biogeme.expression.Expression] - - :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` - :type alpha_parameters: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - :param prices: see the module documentation :mod:`biogeme.mdcev`. - If None, assumed to be 1. - :type prices: biogeme.expressions.Expression - - """ - if prices: - utilities = { - id: ( - baseline_utilities[id] - - log(prices[id]) - + +(alpha_parameters[id] - 1) - * log(1.0 + consumption / (prices[id] * gamma_parameters[id])) - ) - for id, consumption in consumed_quantities.items() - } - log_determinant_entries = { - id: log(1 - alpha_parameters[id]) - - log(consumption + prices[id] * gamma_parameters[id]) - for id, consumption in consumed_quantities.items() - } - inverse_of_determinant_entries = { - id: (consumption + prices[id] * gamma_parameters[id]) - / (1 - alpha_parameters[id]) - for id, consumption in consumed_quantities.items() - } - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - ) - # Use unit prices if prices is set to None - utilities = { - id: ( - baseline_utilities[id] - + (alpha_parameters[id] - 1) * log(1.0 + consumption / gamma_parameters[id]) - ) - for id, consumption in consumed_quantities.items() - } - log_determinant_entries = { - id: log(1 - alpha_parameters[id]) - log(consumption + gamma_parameters[id]) - for id, consumption in consumed_quantities.items() - } - inverse_of_determinant_entries = { - id: (consumption + gamma_parameters[id]) / (1 - alpha_parameters[id]) - for id, consumption in consumed_quantities.items() - } - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - ) - - -def gamma_profile( - baseline_utilities, - consumed_quantities, - gamma_parameters, - prices=None, -): - """Calculates the determinant entries for the linear expenditure system - - :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` - :type consumed_quantities: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - :param prices: see the module documentation - :mod:`biogeme.mdcev`. If None, assumed to be 1. - :type prices: biogeme.expressions.Expression - - - """ - if prices: - utilities = { - id: ( - baseline_utilities[id] - + log(gamma_parameters[id]) - - log(consumption + prices[id] * gamma_parameters[id]) - ) - for id, consumption in consumed_quantities.items() - } - log_determinant_entries = { - id: -log(consumption + prices[id] * gamma_parameters[id]) - for id, consumption in consumed_quantities.items() - } - inverse_of_determinant_entries = { - id: consumption + prices[id] * gamma_parameters[id] - for id, consumption in consumed_quantities.items() - } - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - ) - - utilities = { - id: ( - baseline_utilities[id] - + log(gamma_parameters[id]) - - log(consumption + gamma_parameters[id]) - ) - for id, consumption in consumed_quantities.items() - } - log_determinant_entries = { - id: -log(consumption + gamma_parameters[id]) - for id, consumption in consumed_quantities.items() - } - inverse_of_determinant_entries = { - id: consumption + gamma_parameters[id] - for id, consumption in consumed_quantities.items() - } - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - ) - - -def non_monotonic( - baseline_utilities, - mu_utilities, - consumed_quantities, - alpha_parameters, - gamma_parameters, - prices=None, -): - """Calculates the determinant entries for the linear expenditure system - - :param baseline_utilities: see the module documentation :mod:`biogeme.mdcev` - :type baseline_utilities: dict[int: biogeme.expression.Expression] - - :param consumed_quantities: see the module documentation :mod:`biogeme.mdcev` - :type consumed_quantities: dict[int: biogeme.expression.Expression] - - :param alpha_parameters: see the module documentation :mod:`biogeme.mdcev` - :type alpha_parameters: dict[int: biogeme.expression.Expression] - - :param gamma_parameters: see the module documentation :mod:`biogeme.mdcev` - :type gamma_parameters: dict[int: biogeme.expression.Expression] - - :param prices: see the module documentation - :mod:`biogeme.mdcev`. If None, assumed to be 1. - :type prices: biogeme.expressions.Expression - - - """ - if prices: - utilities = { - id: ( - mu_utilities[id] - + exp(baseline_utilities[id]) - * (1 + consumption / (prices[id] * gamma_parameters[id])) - ** (alpha_parameters[id] - 1) - / prices[id] - ) - for id, consumption in consumed_quantities.items() - } - - log_determinant_entries = { - id: -2 * log(prices[id]) - + baseline_utilities[id] - + log(1 - alpha_parameters[id]) - - log(gamma_parameters[id]) - + (alpha_parameters[id] - 2) - * log(1 + consumption / (prices[id] * gamma_parameters[id])) - for id, consumption in consumed_quantities.items() - } - - inverse_of_determinant_entries = { - id: prices[id] - * prices[id] - * exp(-baseline_utilities[id]) - * gamma_parameters[id] - * (1 + consumption / (prices[id] * gamma_parameters[id])) - ** (2 - alpha_parameters[id]) - / (1 - alpha_parameters[id]) - for id, consumption in consumed_quantities.items() - } - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - ) - - utilities = { - id: ( - mu_utilities[id] - + exp(baseline_utilities[id]) - * (1 + consumption / gamma_parameters[id]) ** (alpha_parameters[id] - 1) - ) - for id, consumption in consumed_quantities.items() - } - - log_determinant_entries = { - id: baseline_utilities[id] - + log(1 - alpha_parameters[id]) - - log(gamma_parameters[id]) - + (alpha_parameters[id] - 2) * log(1 + consumption / gamma_parameters[id]) - for id, consumption in consumed_quantities.items() - } - - inverse_of_determinant_entries = { - id: exp(-baseline_utilities[id]) - * gamma_parameters[id] - * (1 + consumption / gamma_parameters[id]) ** (2 - alpha_parameters[id]) - / (1 - alpha_parameters[id]) - for id, consumption in consumed_quantities.items() - } - return SpecificModel( - utilities=utilities, - log_determinant_entries=log_determinant_entries, - inverse_of_determinant_entries=inverse_of_determinant_entries, - )