Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ repos:
rev: 24.8.0
hooks:
- id: black
language_version: python3.12
language_version: python3
- repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions src/ridepy/util/dispatchers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ridepy.util.dispatchers.taxicab import TaxicabDispatcherDriveFirst
from ridepy.util.dispatchers.ridepooling import (
BruteForceTotalTravelTimeMinimizingDispatcher,
MinimalPassengerTravelTimeDispatcher,
)

"""
Expand Down
208 changes: 207 additions & 1 deletion src/ridepy/util/dispatchers/ridepooling.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from copy import deepcopy

from itertools import tee
import numpy as np

from ridepy.data_structures import (
Expand Down Expand Up @@ -209,6 +209,27 @@ def is_timewindow_violated_or_violation_worsened_due_to_insertion(
return False


def pairwise(iterable):
# A pairwise iterator.
a, b = tee(iterable)
next(b, None)
return zip(a, b)


def is_between(network, a, u, v):
"""
checks if a is on a shortest path between u and v
"""
dist_to = network.t(u, a)
dist_from = network.t(a, v)
dist_direct = network.t(u, v)
is_inbetween = dist_to + dist_from == dist_direct
if is_inbetween:
return True, dist_to, dist_from, dist_direct
else:
return False, dist_to, dist_from, dist_direct


@dispatcherclass
def BruteForceTotalTravelTimeMinimizingDispatcher(
request: TransportationRequest,
Expand Down Expand Up @@ -386,3 +407,188 @@ def BruteForceTotalTravelTimeMinimizingDispatcher(
return min_cost, new_stoplist, (EAST_pu, LAST_pu, EAST_do, LAST_do)
else:
return min_cost, None, (np.nan, np.nan, np.nan, np.nan)


@dispatcherclass
def MinimalPassengerTravelTimeDispatcher(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is possibly not optimal. As far as I understand, the dispatcher only accepts requests for which both pick-up and delivery locations lie on existing vehicle routes, except if the request can be appended? Or did I confuse it? I didn't fully read the code, yet.

request: TransportationRequest,
stoplist: Stoplist,
space: TransportSpace,
seat_capacity: int,
) -> DispatcherSolution:
"""
The dispatcher optimizes travel time for each passenger. Once a passenger is assigned, their travel time cannot be increased, meaning detours are not permitted.
"""
min_cost = np.inf
bool_insert_end = True
best_pickup_idx = len(stoplist) - 1
best_dropoff_idx = len(stoplist) - 1

for counter in range(0, len(stoplist) - 1):
stop_before_pickup = stoplist[counter]
stop_after_pickup = stoplist[counter + 1]
list_result_in_between_test = is_between(
space,
request.origin,
stop_before_pickup.location,
stop_after_pickup.location,
)
if (
list_result_in_between_test[0] == True
and list_result_in_between_test[2] != 0
):
if stop_before_pickup.occupancy_after_servicing == seat_capacity:
continue
time_to_pickup = space.t(stop_before_pickup.location, request.origin)
CPAT_pu = cpat_of_inserted_stop(stop_before_pickup, time_to_pickup)
EAST_pu = request.pickup_timewindow_min
if CPAT_pu > request.pickup_timewindow_max:
continue
CPAT_do = max(EAST_pu, CPAT_pu) + space.t(
request.origin, request.destination
)
if CPAT_do > request.delivery_timewindow_max:
continue

best_pickup_idx = counter
bool_drop_off_enroute = False
bool_continue_pick_up_loop = False
bool_break_pick_up_loop = False

list_result_in_between_testFollowingPickUp = is_between(
space, request.destination, request.origin, stop_after_pickup.location
)
if (
list_result_in_between_testFollowingPickUp[0] == True
and list_result_in_between_testFollowingPickUp[2] != 0
):
best_dropoff_idx = counter
min_cost = CPAT_pu
bool_drop_off_enroute = True
bool_insert_end = False
break

for counter_drop_off, (
stop_before_dropoff,
stop_after_dropoff,
) in enumerate(pairwise(stoplist[best_pickup_idx:])):
if counter_drop_off == best_pickup_idx:
continue
if counter_drop_off < best_pickup_idx:
continue
list_result_in_between_testDropOff = is_between(
space,
request.destination,
stop_before_dropoff.location,
stop_after_dropoff.location,
)
if (
list_result_in_between_testDropOff[0] == True
and list_result_in_between_testDropOff[1] != 0
):
best_dropoff_idx = counter + counter_drop_off
time_to_dropoff = space.t(
stop_before_dropoff.location, request.destination
)
CPAT_do = cpat_of_inserted_stop(
stop_before_dropoff, time_to_dropoff, delta_cpat=0
)
stoplist_request_in_vehicle = stoplist[
best_pickup_idx : best_dropoff_idx + 1
]

list_occupancies_after_servicing = []
for x in stoplist_request_in_vehicle:
list_occupancies_after_servicing.append(
x.occupancy_after_servicing
)

if seat_capacity in list_occupancies_after_servicing:
bool_continue_pick_up_loop = True
break
if CPAT_do > request.delivery_timewindow_max:
bool_continue_pick_up_loop = True
break
bool_insert_end = False
min_cost = CPAT_do
bool_drop_off_enroute = True
bool_break_pick_up_loop = True
break

if bool_break_pick_up_loop:
break

if bool_continue_pick_up_loop:
best_pickup_idx = len(stoplist) - 1
best_dropoff_idx = len(stoplist) - 1
continue

if not bool_drop_off_enroute:
best_dropoff_idx = len(stoplist) - 1
stop_before_dropoff = stoplist[-1]
time_to_dropoff = space.t(
stop_before_dropoff.location, request.destination
)
CPAT_do = cpat_of_inserted_stop(
stop_before_dropoff, time_to_dropoff, delta_cpat=0
)
stoplist_request_in_vehicle = stoplist[
best_pickup_idx : best_dropoff_idx + 1
]

list_occupancies_after_servicing = []
for x in stoplist_request_in_vehicle:
list_occupancies_after_servicing.append(x.occupancy_after_servicing)

if seat_capacity in list_occupancies_after_servicing:
best_pickup_idx = len(stoplist) - 1
best_dropoff_idx = len(stoplist) - 1
continue
if CPAT_do > request.delivery_timewindow_max:
best_pickup_idx = len(stoplist) - 1
best_dropoff_idx = len(stoplist) - 1
continue
bool_insert_end = False
min_cost = CPAT_do
break
else:
continue

if bool_insert_end:
best_pickup_idx = len(stoplist) - 1
best_dropoff_idx = len(stoplist) - 1
time_to_pickup = space.t(stoplist[best_pickup_idx].location, request.origin)
CPAT_pu = cpat_of_inserted_stop(stoplist[best_pickup_idx], time_to_pickup)
EAST_pu = request.pickup_timewindow_min
CPAT_do = max(EAST_pu, CPAT_pu) + space.t(request.origin, request.destination)
if CPAT_pu > request.pickup_timewindow_max:
min_cost = np.inf
elif CPAT_do > request.delivery_timewindow_max:
min_cost = np.inf
else:
min_cost = CPAT_do

if min_cost < np.inf:
new_stoplist = insert_request_to_stoplist_drive_first(
stoplist=stoplist,
request=request,
pickup_idx=best_pickup_idx,
dropoff_idx=best_dropoff_idx,
space=space,
)
EAST_pu, LAST_pu = (
new_stoplist[best_pickup_idx + 1].time_window_min,
new_stoplist[best_pickup_idx + 1].time_window_max,
)
EAST_do, LAST_do = (
new_stoplist[best_dropoff_idx + 2].time_window_min,
new_stoplist[best_dropoff_idx + 2].time_window_max,
)

listOccupanciesNewStopList = list(
map(lambda x: x.occupancy_after_servicing, new_stoplist)
)

return min_cost, new_stoplist, (EAST_pu, LAST_pu, EAST_do, LAST_do)
else:
return min_cost, None, (np.nan, np.nan, np.nan, np.nan)
1 change: 1 addition & 0 deletions src/ridepy/util/dispatchers_cython/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .dispatchers import (
BruteForceTotalTravelTimeMinimizingDispatcher,
MinimalPassengerTravelTimeDispatcher,
SimpleEllipseDispatcher,
)
Loading