Choice model specification used in the latent-variable tutorial.

This module defines the systematic utility functions of the mode-choice model. It illustrates how a choice model is specified independently of the latent-variable model and how latent variables can subsequently be injected into the utility functions.

The specification considers three transportation alternatives:

  • public transport,

  • car,

  • slow modes.

The utilities are generated by the function generate_utility_functions and returned as a dictionary indexed by alternative identifiers. The resulting dictionary can be passed directly to a Biogeme logit model.

The example is intentionally structured in two layers:

  1. A baseline specification based exclusively on observed attributes (travel time, travel cost, distance, and trip purpose).

  2. An extended specification in which latent variables may enter the utility functions through interaction terms.

The latent variables themselves are not defined in this module. They are constructed elsewhere from structural and measurement equations and are provided here as Biogeme expressions through the argument latent_expressions. This separation reflects the conceptual structure of hybrid choice models, where latent variables are first generated and then incorporated into the choice model.

In the current example, the latent variable car_centric_attitude may influence the utility of the car alternative. If no latent expressions are provided, the function reduces to the baseline mode-choice specification.

The cost coefficient is normalized to -1 and a positive scale parameter is estimated. This normalization defines the unit of utility while allowing the overall scale of the choice model to be estimated.

Michel Bierlaire Fri Jun 12 2026, 15:00:22

from optima import (
    CostCarCHF,
    MarginalCostPT,
    PurpHWH,
    TimeCar_hour,
    TimePT_hour,
    distance_km,
)

from biogeme.expressions import (
    Beta,
    Expression,
)


def generate_utility_functions(
    latent_expressions: dict[str, Expression] | None = None,
) -> dict[int, Expression]:
    """Generate the systematic utility functions of the choice model.

    The function creates the utility expressions associated with the three
    transportation alternatives considered in the tutorial:

    - alternative 0: public transport,
    - alternative 1: car,
    - alternative 2: slow modes.

    The utilities are expressed using Biogeme expressions and include the
    estimated parameters declared in this function.

    Baseline specification
    ----------------------

    Public transport:

    .. math::

       V_{\mathrm{PT}}
       = \beta_{\mathrm{asc,PT}}
       + \beta_{\mathrm{time,PT}}\,\mathrm{TimePT}
       + \beta_{\mathrm{cost}}\,\mathrm{MarginalCostPT}

    Car:

    .. math::

       V_{\mathrm{Car}}
       = \beta_{\mathrm{asc,Car}}
       + \beta_{\mathrm{time,Car}}\,\mathrm{TimeCar}
       + \beta_{\mathrm{cost}}\,\mathrm{CostCar}

    Slow modes:

    .. math::

       V_{\mathrm{Slow}}
       = \beta_{\mathrm{dist}}\,\mathrm{Distance}

    where the distance coefficient depends on trip purpose.

    Latent-variable extension
    -------------------------

    When ``latent_expressions`` is provided, the latent variable
    ``car_centric_attitude`` enters the utility of the car alternative
    through an additional coefficient:

    .. math::

       V_{\mathrm{Car}}
       \leftarrow
       V_{\mathrm{Car}}
       + \beta_{\mathrm{car\_centric}}
         x^*_{\mathrm{car\_centric}}

    This illustrates the standard mechanism used in hybrid choice models:
    latent variables are treated as additional explanatory variables in the
    utility specification.

    Utility scaling
    ---------------

    After the utilities are constructed, they are multiplied by a positive
    scale parameter. Because the cost coefficient is fixed to -1, the scale
    parameter can be estimated and captures the overall sensitivity of
    choice probabilities to utility differences.

    :param latent_expressions: Optional dictionary mapping latent-variable
        names to Biogeme expressions. If ``None``, the baseline choice model
        is generated. The current specification expects the key
        ``'car_centric_attitude'`` when latent variables are used.
    :return: Dictionary mapping alternative identifiers to systematic
        utility expressions.
    """
    work_trip = PurpHWH == 1
    other_trip_purposes = PurpHWH != 1

    # Choice model: parameters
    choice_beta_cost = Beta('choice_beta_cost', -1, None, 0, 1)

    choice_asc_car = Beta('choice_asc_car', 0.0, None, None, 0)

    choice_asc_pt = Beta('choice_asc_pt', 0, None, None, 0)

    choice_beta_dist_work = Beta('choice_beta_dist_work', 0, None, 0, 0)
    choice_beta_dist_other_purposes = Beta(
        'choice_beta_dist_other_purposes', 0, None, 0, 0
    )
    choice_beta_dist = (
        choice_beta_dist_work * work_trip
        + choice_beta_dist_other_purposes * other_trip_purposes
    )

    scale_parameter = Beta('choice_scale_parameter', 1, 0.0001, None, 0)

    # Time coefficients with optional LV interactions
    choice_beta_time_car = Beta('choice_beta_time_car', 0, None, 0, 0)
    choice_beta_time_pt = Beta('choice_beta_time_pt', 0, None, 0, 0)

    choice_beta_car_centric_attitude_car = Beta(
        'choice_beta_car_centric_attitude_car', 0, None, None, 0
    )
    v_public_transport_det = (
        choice_asc_pt
        + choice_beta_time_pt * TimePT_hour
        + choice_beta_cost * MarginalCostPT
    )
    v_public_transport = v_public_transport_det

    v_car_det = (
        choice_asc_car
        + choice_beta_time_car * TimeCar_hour
        + choice_beta_cost * CostCarCHF
    )
    v_car = (
        v_car_det
        if latent_expressions is None
        else v_car_det
        + choice_beta_car_centric_attitude_car
        * latent_expressions['car_centric_attitude']
    )

    v_slow_modes_det = choice_beta_dist * distance_km
    v_slow_modes = v_slow_modes_det

    v = {
        0: scale_parameter * v_public_transport,
        1: scale_parameter * v_car,
        2: scale_parameter * v_slow_modes,
    }
    return v

Gallery generated by Sphinx-Gallery