Source code for passengersim.rm.leg_value

from __future__ import annotations

from typing import TYPE_CHECKING, Literal

import numpy as np

from ._common import RmAction

if TYPE_CHECKING:
    from passengersim.config import Config
    from passengersim.driver import Simulation


[docs] class LegValue(RmAction): produces: set[str] = {"leg_value"} frequency = "begin_sample" def __init__( self, *, carrier: str = "", minimum_sample: int = 3, cfg: Config | None = None, algorithm: Literal["bottom_up", "top_down"] = "bottom_up", minimum_pct_separation: float = 0.05, frequency: Literal["dcp", "daily", "begin_sample"] = "dcp", ): super().__init__( carrier=carrier, minimum_sample=minimum_sample, cfg=cfg, ) self.algorithm: Literal["bottom_up", "top_down"] = algorithm """Algorithm to use for leg value correction. Corrections may be needed to address fare inversions. The `bottom_up` algorithm will start with the lowest bucket and work up, pushing fares upwards if needed to maintain ordering and ensure the indicated minimum percentage separation. The `top_down` algorithm will start with the highest bucket and work down, pushing fares downwards if needed. """ self.minimum_pct_separation: float = minimum_pct_separation """Minimum percentage separation between bucket values. If the computed values are inverted or too close to each other, many optimization algorithms will fail. This parameter ensures that the values are separated by at least this percentage at each step. """ self.frequency: Literal["dcp", "daily", "begin_sample"] = frequency """How often to run this step."""
[docs] def run(self, sim: Simulation, days_prior: int): if not self.should_run(sim, days_prior): return if self.algorithm == "bottom_up": for leg in sim.eng.legs.set_filters(carrier=self.carrier): previous_value = 0.0 # assume buckets are sorted by naive value from high to low for bkt in reversed(leg.buckets): bkt.fcst_revenue = max(bkt.prorated_value, previous_value * (1 + self.minimum_pct_separation)) previous_value = bkt.fcst_revenue elif self.algorithm == "top_down": for leg in sim.eng.legs.set_filters(carrier=self.carrier): previous_value = np.inf for bkt in leg.buckets: bkt.fcst_revenue = min(bkt.prorated_value, previous_value * (1 - self.minimum_pct_separation)) previous_value = bkt.fcst_revenue