Source code for biogeme.catalog.generic_alt_specific_catalog

"""Defines  a catalog containing generic and alternative specific specifications

Michel Bierlaire
Sun Apr 27 2025, 15:49:25
"""

from biogeme.exceptions import BiogemeError
from biogeme.expressions import Beta
from biogeme.segmentation import DiscreteSegmentationTuple
from .catalog import Catalog
from .controller import Controller
from .segmentation_catalog import SegmentedParameters, segmentation_catalogs


[docs] def generic_alt_specific_catalogs( generic_name: str, beta_parameters: list[Beta], alternatives: tuple[str, ...], potential_segmentations: tuple[DiscreteSegmentationTuple, ...] | None = None, maximum_number=5, ): """Generate catalogs selecting generic or alternative specific coefficients :param generic_name: name associated with all the parameters in the catalog :type generic_name: str :param beta_parameters: coefficients of interest :type beta_parameters: list(biogeme.expressions.Beta) :param alternatives: names of the alternatives :type alternatives: tuple(str) :param potential_segmentations: tuple of potential segmentations, or None :type potential_segmentations: tuple(biogeme.segmentation.DiscreteSegmentationTuple) :param maximum_number: maximum number of segmentations to consider :type maximum_number: int :return: a list of catalogs for each alternative :rtype: list(dict(str: biogeme.catalog.Catalog)) """ if len(alternatives) < 2: error_msg = ( f'An alternative specific specification requires at least 2 ' f'alternatives, and not {len(alternatives)}' ) raise BiogemeError(error_msg) if not isinstance(beta_parameters, list): error_msg = ( f'Argument "beta_parameters" of function ' f'"{generic_alt_specific_catalogs.__name__}" must be a list.' ) raise BiogemeError(error_msg) wrong_indices = [] for index, beta in enumerate(beta_parameters): if not isinstance(beta, Beta): wrong_indices.append(index) if wrong_indices: error_msg = ( f'The entries at the following indices are not Beta expressions: ' f'{wrong_indices}' ) raise BiogemeError(error_msg) # We first generate the alternative specific versions of the parameters generic_parameters = beta_parameters the_segmented_parameters = SegmentedParameters( beta_parameters=generic_parameters, alternatives=alternatives, ) # If applicable, we apply the potential segmentations if potential_segmentations: segmented_catalogs = segmentation_catalogs( generic_name=generic_name, beta_parameters=the_segmented_parameters.all_parameters, potential_segmentations=potential_segmentations, maximum_number=maximum_number, ) def get_expression(param_index: int, alternative: str | None): """Returns either the parameter, or the segmented version if applicable""" if potential_segmentations: the_index = the_segmented_parameters.get_index(param_index, alternative) return segmented_catalogs[the_index] return the_segmented_parameters.get_beta(param_index, alternative) # We now control for generic or alternative specific with a single # controller for all catalogs the_controller = Controller( controller_name=f'{generic_name}_gen_altspec', specification_names=('generic', 'altspec'), ) # We organize the catalogs as a list of dict results = [] for index, beta in enumerate(beta_parameters): the_dict = { alternative: Catalog.from_dict( catalog_name=f'{beta.name}_{alternative}_gen_altspec', dict_of_expressions={ 'generic': get_expression(index, None), 'altspec': get_expression(index, alternative), }, controlled_by=the_controller, ) for alternative in alternatives } results.append(the_dict) return results