Source code for jinete.models.criterions

from __future__ import (
    annotations,
)

import logging
from abc import (
    ABC,
    abstractmethod,
)
from statistics import (
    mean,
)
from typing import (
    TYPE_CHECKING,
)

from .constants import (
    MAX_FLOAT,
    MIN_FLOAT,
    OptimizationDirection,
)

if TYPE_CHECKING:
    from typing import (
        Optional,
        List,
        Iterable,
    )
    from .routes import Route

logger = logging.getLogger(__name__)


[docs]class RouteCriterion(ABC):
[docs] def __init__(self, name: str, direction: OptimizationDirection, *args, **kwargs): self.name = name self.direction = direction
[docs] @abstractmethod def scoring(self, route: Route) -> float: pass
[docs] def best(self, *args: Optional[Route]) -> Route: return self.direction((arg for arg in args if arg is not None), key=self.scoring, default=None,)
[docs] def sorted(self, routes: Iterable[Route], inplace: bool = False) -> List[Route]: return self.direction.sorted(routes, key=self.scoring, inplace=inplace)
[docs] def nbest(self, n: int, routes: Iterable[Route], inplace: bool = False) -> List[Route]: return self.direction.nbest(n, routes, key=self.scoring, inplace=inplace)
[docs]class EarliestLastDepartureTimeRouteCriterion(RouteCriterion):
[docs] def __init__(self, *args, **kwargs): super().__init__( direction=OptimizationDirection.MINIMIZATION, name="Shortest-Time", *args, **kwargs, )
[docs] def scoring(self, route: Route) -> float: if not route.feasible: return MAX_FLOAT return route.last_departure_time
[docs]class ShortestAveragePlannerTripDurationCriterion(RouteCriterion):
[docs] def __init__(self, *args, **kwargs): super().__init__( direction=OptimizationDirection.MINIMIZATION, name="Shortest-Time", *args, **kwargs, )
[docs] def scoring(self, route: Route) -> float: if not route.feasible: return MAX_FLOAT if not any(route.planned_trips): return 0 return mean(planned_trip.duration for planned_trip in route.planned_trips)
[docs]class ShortestTimeRouteCriterion(RouteCriterion):
[docs] def __init__(self, *args, **kwargs): super().__init__( direction=OptimizationDirection.MINIMIZATION, name="Shortest-Time", *args, **kwargs, )
[docs] def scoring(self, route: Route) -> float: if not route.feasible: return MAX_FLOAT return route.duration
[docs]class LongestTimeRouteCriterion(RouteCriterion):
[docs] def __init__(self, *args, **kwargs): super().__init__( direction=OptimizationDirection.MAXIMIZATION, name="Longest-Time", *args, **kwargs, )
[docs] def scoring(self, route: Route) -> float: if not route.feasible: return MIN_FLOAT return route.duration
[docs]class LongestUtilTimeRouteCriterion(RouteCriterion):
[docs] def __init__(self, *args, **kwargs): super().__init__( direction=OptimizationDirection.MAXIMIZATION, name="Longest-Util-Time", *args, **kwargs, )
[docs] def scoring(self, route: Route) -> float: if not route.feasible: return MIN_FLOAT scoring = 0.0 for trip in route.trips: scoring += trip.distance return scoring
[docs]class HashCodeRouteCriterion(RouteCriterion):
[docs] def __init__(self, *args, **kwargs): super().__init__( direction=OptimizationDirection.MAXIMIZATION, name="Longest-Time", *args, **kwargs, )
[docs] def scoring(self, route: Route) -> float: if not route.feasible: return MIN_FLOAT scoring = 0.0 for planned_trip in route.planned_trips: scoring += planned_trip.distance if planned_trip.pickup_time == planned_trip.trip.origin_earliest: scoring += planned_trip.trip.on_time_bonus return scoring