Introduction
RidePy lets a user specify a FleetState
of \(n\) vehicles with desired
passenger capacities, as well as an iterator
of TransportationRequest
objects, each containing:
request_id
creation_timestamp
origin
destination
pickup_timewindow_min
pickup_timewindow_max
delivery_timewindow_min
delivery_timewindow_max
The request iterator must yield requests in increasing order of creation_timestamp
.
Calling FleetState.simulate()
then executes the simulation, which outputs a
sequence of Event
objects (add link here), from which the user can compute various metrics,
optionally using the util.analytics
module.
The stoplist
FleetState
contains a dictionary fleet
, which maps a vehicle_id
to a
VehicleState
. A VehicleState
, in turn holds the future trajectory of the
vehicle in the form of a Stoplist
: a list of Stop
objects containing:
location
,request
to be serviced,action
: one ofpickup
,dropoff
orinternal
,estimated_arrival_time
,occupancy_after_servicing
,time_window_min
,time_window_max
.
Therefore, a Stoplist
contains all the information on where the vehicle will
go and what it will do in the future (if the stoplist does not change in the
meantime, by e.g. a dispatcher
inserting a new TransportationRequest
s
pickup and dropoff in it).
The CPE Stop
The first element of any vehicle’s Stoplist
is a Stop
object that is
called the “CPE stop” (current position element), signifying the immediate
position of the vehicle. It is not associated with any TransportationRequest
.
The estimated_arrival_time
of this stop defines the time at which the
vehicle is at the CPEs location. This is always roughly the current location
and time, though it might be immediate past (before it has been updated to the
current state) or immediate future (when the vehicle is in between two adjacent nodes
on a graph, see TransportSpace.interp_time
).
The Simulation run: advancing the internal clock
When a FleetState
is created, an internal “clock” is started at time \(t= 0\).
When the simulation is run,
The next request
r
is fetched from the request iterator.The simulator’s internal clock is advanced to the
creation_timestamp
ofr
. For each vehicleAll the stops whose
estimated_arrival_time
is less than the current timestamp is removed. For each of these stops an appropriatePickupEvent
orDeliveryEvent
is emitted.Based on its
Stoplist
, its “current” position is inferred, and thelocation
andestimated_arrival_time
of its CPE stop are modified accordingly.
The vehicle that is “best suited” for transporting
r
is determined by thedispatcher
, and two new stops are inserted into itsStoplist
: the pickup and dropoff ofr
. We describe below how this choice is made. ARequestAcceptanceEvent
is emitted. If no such vehicle is found, aRequestRejectionEvent
is emitted instead.The previous steps are repeated until either the request iterator is exhausted or a time cutoff is reached.
The dispatcher
The dispatcher determines which vehicle should service a certain request, and in which order it should service the requests assigned to it. ridepy defines a clean interface enabling the users to create their own dispatchers. A number of predefined ones are available in`.util.dispatchers`.
When a new TransportationRequest
needs to be processed:
The
dispatcher
is called for each vehicle, which can be thought of as the mapping(new_request, stoplist) ⇒ (cost, new_stoplist)
. It
Checks if the pickup and dropoff of
r
can be inserted to the vehicle’sStoplist
without violating
The new request’s
pickup_timewindow_min|max
anddelivery_timewindow_min|max
.The
time_window_min|max
of every existing stop in the stoplist of the vehicle.The capacity contraints of the vehicle.
The implicit constraint of inserting Pickup, with regards to order, before Dropoff
There can be multiple possible insertions that do not violate any of these constraints. The dispatcher computes a numerical cost for each possible insertion and chooses the insertion with the least cost. One such cost could be the total detour caused by an insertion, for example.
If no insertion is found for a vehicle, the dispatcher returns a cost of \(\infty\).
If more than one vehicle can service the request, then the one with minimum cost is chosen. That is, its
Stoplist
is changed to thenew_stoplist
returned by the dispatcher.
The dispatcher is solely responsible for keeping the stoplist in a internally valid state, as the one with minimal cost substitutes the original one for the respective vehicle. Constraints are not checked again elsewhere. This incorporates in particular estimated arrival times which are constrained by the travel time on the transport space.
The Transport Space
The transport space defines the space that stops and requests live on. This has primarily three implications:
It defines the coordinates used for specifying locations. This can e.g. be an integer number for graph vertices, or a pair of floats for a continuous two-dimensional space.
The first key function that the
TransportSpace
provides is a metric \(T\times T\rightarrow\mathbb R, (u,v)\mapsto d(u,v)`\) which yields the distance between locations. In addition, the same is provided for the travel time, which takes the velocity at which the vehicle moves into account.The second function is
interp_dist(u,v, time_to_dest)
, and respectivelyinterp_time
. This interpolates a location on the space on the route between two pointsu
andv
, with the know remaining timetime_to_dest
. This is used to determine the current location of the vehicle when it is known that the vehicle is on the way tov
, having started atu
, and with remainingtime_to_dest = next_stop.estimated_arrival_time - current_time
.