Food delivery simulator

Last code update on Feb. 4, 2023

Author: K.Z.

Agent-based simulation. CPU multiprocessing for current demos.

Demonstration animation

N_v represents the number of idle riders (drivers), N_b represents the number of accumulated order batches, p is the customer matching probability, pp is the rider matching probability. The merchant node size represents the number of accumulated orders in this merchant.

In this demo, the max matching radius r=1 km, max delivery radius R=2 km, batch size (bundling ratio) k=3, and matching interval t=0.005 hour. The order arrival rate $\overline{q}=800$ orders/hour and the total number of riders is $N=200$. 5 merchants are spread in the city of Singapore.

FD simulator demo

The Singapore demo (real road network) is placed on a Chinese video stream platform Bilibili: link

Github repository here

Features

How do we match the riders (aka drivers) to customers?

Batch matching (see this paper for an introduction). The customers (of a merchant) are not eligible for matching until the platform has accumulated k orders for this merchant. These k orders are bundled into a batch and matched to one rider. The batch matching process happens every ceiling(t/t_resolution) iteration, where t means the matching interval and t_resolution is the time duration for each iteration.

How do we compute the optimal routes of riders?

Dijkstra algorithm.

How do we consider the riders’ decisions on which customer to serve next?

Follow the order of the customer_nodes, which is pre-defined when updating the customer information on matching. The order is, (2nd closest, 3rd closest, …, farthest, closest), to make sure the rider will go back (at least be close to) this merchant.

How do we consider riders’ decisions on where to go when they are idle?

Random walk (randomly select the next node and the next next node) and wait for being matched.

What do we consider riders’ decisions when they’ve completed delivering a bundle of orders?

Random walk and wait for being matched again.

How do we generate customer demands?

Randomly generate $n_q$ (in codes, it is called num_generated_order) customers on every iteration. $n_q$ is the number of generated customers in this iteration, determined by q_bar, R, and Delta. The platform will randomly choose 1 node from all the nodes in the network as the customer node ID (assuming the probability that the new customer will be located at each node is the same for all nodes) and 1 node from the merchant node set as merchant node ID. Then, the platform will check whether the distance from this customer to her corresponding merchant is less than the maximum delivery distance, R. If yes, keep it, and remove it otherwise. Then repeat the process until the number of newly generated customers reached $n_q$.

Rider, platform, and customer attributes

Rider attributes

AttributesTypeNoteValue
IDintspecified on generationunchanged
position1D arrayx y coordinate of this riderupdated on every move (every iteration)
statestringthe state of this rider‘idle’, ‘working’ or ‘stop’
speedintspeed in km/hrmaxspeed when working, half maxspeed when idle
maxspeedintspecified on generationunchanged
stop_timefloataccumulated stop time0 when moving, increased on every iteration when stop, back to 0 when restarting
total_timefloataccumulated (total) time spent on the current batch (pickup time + total delivery time)initially 0, increasing from matched (start working), back to 0 when complete
total_time_reclistrecord of each total_timea list that stores every value of total_time
prev_nodeintthe previous node that this rider traveled throughequals the next node when travel pass the next node
next_nodeintthe next node that this rider may travel bydetermined by the next node on path or randomly chosen
nextnext_nodeintthe next node that this rider may travel bydetermined by the next node on path or randomly chosen
destinationintthe destination of this current routingcan be merchant_node or customer_node, is None when idle
path_to_destlista list of nodes by orderobtained by Dijkstra method, is None when idle
linkdicta dictionary records the information of the current link this driver is traveling on 
closest_merchant_nodeintthe closest merchant node ID (by distance)closet merchant node ID, changed according to distance to each merchant
merchant_nodeintthe merchant node ID that this rider is currently servinggiven by the platform after being matched, is None when idle
merchant_node_setintset of all merchant nodesunchanged
customer_nodeslistthe unserved customers (the customer that this rider is currently heading for is not included), order:(2nd closest, 3rd closest, …, farthest, closest)updated on every arrival of destinations
newly_finished_destinationintthe new finished destination, merchant node ID or customer node IDupdated on every completion of stop, is None when working or idle
if_matchedbooleanif the rider is matchedis True after being matched, if False after complete serving the batch
if_matchablebooleanif the rider lies in the matching area, it is matchableupdated on every move, is True if the tider state is idle and the distance to the closest merchant is less than the maximum matching radius, False otherwise
dec_vardictdecision variables, contains r cR k t N q_barspeficied by user
rd_stobjectGlobal random staten/a

Platform attributes

AttributesTypeNoteValue
customer_dfpd.DataFramea table of unmatched customers, columns: [‘node_ID’, ‘merchant_node’, ‘waiting_time’, ‘position_x’, ‘position_y’]add unmatched customer(s) on each iteration (order generation), remove (to matched_customer_df) on each matching
matched_customer_dfpd.DataFramea table of matched customers, columns: [‘node_ID’, ‘merchant_node’, ‘waiting_time’, ‘position_x’, ‘position_y’]add matched customers on each matching, update (reduce) customer(s) on each iteration (if there is anyone delivered)
num_accumulated_orderfloatnumber of accumulated orders, continuous numberupdated on every iteration (order generation)
rfloatone of the decision variablesassigned by user
cRfloatone of the decision variablesassigned by user
kfloatone of the decision variablesassigned by user
tfloatone of the decision variablesassigned by user

Customer attributes

Customers are defined as pd.DataFrame and are stored in class “platform”

AttributesTypeNoteValue
node_IDintID of this customerassigned on generation
merchant_IDintthe merchant where the customer ordered meal fromassigned on generation
waiting_timefloatthe total waiting time (for matching and meal delivery) for this customerinitially 0, increase over time
position_xfloatthe x coordinate of this customerassigned on generation
position_yfloatthe y coordinate of this customerassigned on generation

Rider, platform behaviors

Rider behaviors

Behavior nameDescriptionWhen excecutesup behavior(s)sub behavior(s)
initInitialize the rider as a idle rider, define attributesOn the generation of this ridern/a 
update_customer_orderUpdate the order of the customersAfter being matched, after updating merchant node ID and customer nodesupdate_customer_nodesn/a
update_customer_nodesUpdate the attributes according to the customers that this rider is going to serve (customer nodes are given in function “move_rider”, which is not in this class)After being matchedmoven/a
check_distance_2_closest_merchantCheck the distance from this rider to the closest merchant. If it is less than the maximum matching radius, if_matchable is True, otherwise, if_matchable is Falseafter move (position change)moven/a
moveMove the rider to the next position, and update the corresponding attributesevery iteration (after order generation and matching)n/aupdate_customer_nodes, stop, check_distance_2_closest_merchant
stopStop at the current position, and update the corresponding attributesWhen the stop time is below the threshold or when the rider arrives at the destination (can be merchant or customer)move, update_next_desinationupdate_next_desination
completeComplete serving the batch, and update the corresponding attributesWhen arrived the last destination (the next destination is None)update_next_desinationn/a
update_next_desinationUpdate the next destination and corresponding attributesAfter completed stopstopcomplete
update_idle_destinationUpdate the next destination and corresponding attributesAfter completed stopinit, complete, move/

Platform behaviors

Behavior nameDescriptionWhen excecutesup behavior(s)sub behavior(s)
update_cust_df_with_new_custUpdate the attribute customer_df with newly generated customersWhen (quickly after) order generationacquire_ordern/a
acquire_orderGenerate new orders, only those who lie in the delivery area will be keptAt the beginning of each iterationn/aupdate_cust_df_with_new_cust
update_matched_orderUpdate attribute matched_customer_df with matched orders (by node ID, if multiple customers locate on one node, label the one with the longest waiting time as matched), and remove the matched customers from the attributes customer_dfAfter complete matchingmatchn/a
matchMatch the idle riders to customers (and corresponding merchant, only when the closest merchant of this rider is the corresponding merchant can it be matched, and of course, its if_matchable attribute should be True), batch matchingEvery matching periodn/aupdate_matched_order