Ridehail Laboratory v0.16 (2023-01-28)
Frame
0

Loading the ridehail simulator...

Scale
Chart type
Mode
Start here

When the ridehail simulator has loaded, click the big orange button. The simulator runs entirely in your browser (not on a server), so you cannot break it and nobody tracks what you do.

The map shows a model city as a square of blocks. Ridehail vehicles (shown as triangles) drive the streets, waiting to be assigned a trip. When a passenger requests a trip (red diamond), it is assigned to a vehicle, which takes them to their destination (green circle).

Choose a community Scale of village, town, or city to set a starting point. Press z on your keyboard to zoom in on the map, and again to zoom back out. Press p to pause and to resume.

Choose a Chart type of "Statistics" to show wait times, how driver time is split between different activities, and driver income (before and after expenses).

With the Mode to "Simple Model" the stats are just calculated per-block. If you set the Mode to "Costs & Incomes" you can set fares and costs based on kilometers and minutes.

The simulation outcomes depend on many factors. Experiment with changing the size of the community, the number of vehicles, and the rate of requests. If you check "Free entry & exit" then drivers will become active if there is money to be made (over and above their 'reservation wage'), and leave it if there is not. Choose "Costs & Incomes" to set fares and driver costs.

How do wait times, driver "busy" times, and incomes change with the ridehail environment? Set up a simulation here, then go to the What If? tab to explore how policy or other changes affect the outcoms.

For more information, click Read in the coloured title bar (under construction!). If things go wrong, press the "reset" button at the top left.

Costs & incomes

You have chosen the "Costs & Incomes" mode, which lets you link the model to more real-world values. In this mode, each block represents a minute of travel time. At an average speed of 30 km/h (which is realistic, as it includes all stopping times and pickup times) each block is 0.5km. You can set the following monetary parameters:

  • "Per km" fare component. In Toronto this is roughly $0.80 for UberX.
  • "Per minute" fare component. In Toronto this is roughly $0.20 for UberX.

At an average speed of 30 km/h, these two values produce a fare of $0.60 per minute (per block, in this model). In addition to these components, UberX fares include a fixed "booking fee". As this is not passed on to the driver, it has no effect on driver income and is ignored here, for now.

  • "Vehicle operating cost per km". One estimate of this would be $0.50/km, which is the lower end of the Revenue Canada "reasonable allowance rates" for reimbursement of employee use of a vehicle for business purposes. However, the model does not currently allow for the fact that drivers drive more slowly (or park) while in P1 phase (waiting for a ride). A $0.30/km estimate may be more reasonable until the slower driving in P1 is included.
  • "Vehicle opportunity cost per hour". This is the money, after all expenses, that a driver would need to make in order to work.

Adding these together gives the "reservation wage".

Ridehail environment

Think of each block as the distance a vehicle drives in one minute.

When zero, trip requests are distributed evenly across the area. When set to 1, all requests are made in a central zone. Trips always terminate evenly across the area.

Trip destinations are randomly placed in a rectangle of at most this distance (in blocks) from the origin

Vehicles (supply)

The total number of active ridehail vehicles in the area. If you check "Free entry & exit" the actual number will change during the simulation.

The overall average traffic speed, including all stops and pickups.

The cost of running a vehicle, per kilometer.

Trip requests (demand)

The number of requests in the time it takes a vehicle to travel one block. If you check "Free entry & exit" and set an elasticity to something other than zero, the actual request rate will be different to your setting here.

How the request rate responds to fare changes. If set to 0 the rate of requests does not change. Larger values make the demand fall more as the fare rises.

Entry and exit

Vehicles enter when there is money to be made above the "reservation wage", and leave when they cannot make money.

Fares

The fare per block.

The distance component of the fare. The total fare is built from distance and time components.

The time component of the fare. The total fare is built from distance and time components.

Platform income

The fraction of the fare that the ridehailing platform takes. It passes the rest to the driver.

Driver income

The income per block above which it is worth driving.

If you choose "Free entry & exit" then vehicles will enter if they can make at least this amount of money (after all deductions), and leave if they cannot.

Display
Statistics chart smoothing 20 frames

Statistics charts display a rolling average over this number of frames.

Chart display maximum frame rate: 300 milliseconds

A small time speeds up the simulation.

Reset
Choose baseline
Baseline
Comparison
Frame
0
Fare / minute
Commission
Reservation wage /hr
Demand (trips/hr)

Click the orange button to run a baseline simulation. Then change one or more of the settings and run a Comparison to see how those changes affect outcomes for drivers, passengers, and platforms.

Choose a baseline of Experiment to copy the current settings from the Experiment tab to here, and explore how policy, platform, or environment changes affect the outcomes.

Choose a baseline of Preset to use a ridehail environment similar to the "Town" on the Experiment tab.

Whichever baseline you choose, you can see the detailed settings used in the table below once you start the simulation. The Measures table shows the simulation outcomes.

Setting Baseline Comparison
Measure Baseline Comparison

The ridehailing simulation model

Many current debates and discussions about Uber and other ridehail systems rely either on data provided by the ridehail companies themselves, or on broad qualitative claims. The goal of this simulation is to fit a gap in the middle. The simulation is quantitative, and can be calibrated against broad aggregate statistics, such as utilization rates for a city, which are more widely available than detailed statistical data. The simulation is intended to be as simple as possible while still capturing the essential elements of a ridehail system: vehicles and trips in a generic "city". It is a work in progress that, if it turns out well, may enable some of the following:

  • Some policy debates fall into talk at cross purposes, with different unstated assumptions underlying rival claims. I hope a computer simulation lets some of these assumptions be checked.
  • Driver income for gig economy companies such as Uber is related to passenger fares in a different way to “normal” pay for service. Simulation helps us to see how “minimum wage” proposals that do not cover time waiting for an assignment fail to address the problems that drivers face.
  • A simple model that is independent of a particular city geography and road layout is easier for comparisons of the ridehail experience among cities.
  • A computer simulation allows “what if?” experiments that may provide insights into the tradeoffs faced by platform companies, municipal governments, drivers and passengers.

It is a work in progress. It is written in python and source code is available from GitHub. It can be run either in the browser (here) or as a desktop application. Currently, this web site version is primarily for exploration -- there is no ability to save your work. For more extensive work, please use the desktop application.

The ridehail simulation has three components:

  • A geographical area. This is called a City, as most discussions about ridehail systems have focused on cities as the unit of comparison, but it need not be an actual “city”.
  • Vehicles. These drive around the city, respond to trip requests, and carry passengers from the trip origin to the trip destination.
  • Trips. These start as trip requests, and have an origin and destination within the city.

The city

A city is represented as simply as possible: it is a square of uniformly-sized blocks, with C blocks on each side of the square. The square is, however, “wrapped”, so that a vehicle going off the top of the city appears at the bottom, and a vehicle going off the right of the city appears on the left. This is not as unrealistic as it may first appear: in the real world, drivers are not bound by city boundaries: some trips will go outside the city and some will come from outside in, so the City can be thought of as an area with vehicles leaving and entering. In this uniform city, all locations are identical. A city with distinct zones is also allowed, and is described below.

A single block is the unit of travel, and may be thought of as a distance or a time of travel, or a combination of the two. In practice it is often taken as a minute of travel time, but this is not a requirement.

Vehicles

Vehicles all drive at the same speed, and are always in one of three “phases”, which are commonly used in discussions of ridehail systems.

In “P1” they are available to be assigned for trips, but do not have an assigned trip. In this phase they drive randomly around the city. Vehicles in P1 are also sometimes called “idle”.

In “P2” a vehicle is assigned to a trip, and drives towards the trip origin by the shortest route to pick up the passenger. This route may involve going “off the edge” of the city and appearing at the opposite side. P2 is sometimes called “en route” to picking up the passenger.

In “P3”, a vehicle has a passenger and is driving by the shortest route to the destination. Evaluated across the whole driver population, the proportion of time drivers spend in P3 is also the utilization rate for the system. Once the trip is complete, the vehicle returns to P1.

Trips

A trip has an origin and a destination. In the simplest simulation, each is chosen randomly from intersections in the city, except that the two locations cannot be the same.

Each vehicle can take only one trip at a time. This can be thought of as one passenger or a group of passengers, but it does not include “shared trips” such as the “Uber Pool” service where (groups of) passengers request trips separately but share a vehicle for some portion of the trip. RIdehail operators have repeatedly tried to introduce such systems but, even before Covid, such shared trips made up a small portion of the total trips, and even when a passenger requested an “Uber Pool” trip they often end up not sharing the vehicle. The simulation focuses entirely on “UberX-like” systems.

Trip time is in one of two phases: waiting or traveling. For randomly-selected trip origns and destinations, the average trip length in a city with C blocks along each side is C/2.

The “wait time” here, between placing a trip request and the arrival of the vehicle does not exactly match that commonly used by Uber. For Uber, the wait time is the time until the driver appears at the location, but that leaves an unassigned period between arrival and the start of the trip, which may include time for the passenger to come out onto the street, find the vehicle (or for the driver to pick out the passenger), and get in the vehicle.

The simulation

Each simulation is a sequence of moves, in each of which vehicles travel one block. A typical move involves the following events:

  1. Any new trip requests are generated and made.
  2. Available vehicles are assigned to trips. The trip is assigned to the nearest available vehicle in P1, and if there are multiple vehicles at the same distance from the trip origin, one is selected at random. Vehicles always accept trip assignments.
  3. Vehicles move from one intersection to the next. For each vehicle, this involves choosing a direction and moving a block to the next intersection. For vehicles in P1, the direction is random; for vehicles in P2 and P3, the direction is towards the trip origin or destination, as applicable.
  4. Any trip that reaches its destination terminates.

At the end of each move, the simulation records and optionally plots the state of each vehicle and statistics about the overall system (fraction of vehicles in each phase, for example).

The simulation is implemented in the Python programming language, and the code is available on GitHub. It can be run as a desktop application from a command line or at a web site.

The desktop application is controlled by a configuration file, which at its simplest specifies the size of the city (number of blocks on each side), the base demand rate (number of trip requests each move), and the number of vehicles.

The simulation is also available at a web site, where the configuration is set in the browser. The simulation runs in the browser (not at a server), using the Pyodide python distribution, and is the same code as the desktop. The web site is currently a “laboratory, which allows experimentation but does not provide a way to save results. All results in this report are taken from desktop simulations.

Entry and exit

The description so far could apply to any taxi-like system. An element of the gig-economy ridehail business model is that drivers can enter and exit at any time, by signing into the application or turning it off. The simulation captures entry and exit of drivers as follows.

Each trip is assigned a price per block p, with the ridehail platform taking a fraction of that price as a commission (m). Driver gross income before expenses is therefore

I = p3 . p . (1 – m)

Drivers also have costs. These include fixed costs, marginal cost of operations (fuel, insurance, maintenance, cleaning, etc.) and “opportunity costs” co, which is the value of other things they could be doing at the same time. The opportunity costs can also be considered as the reservation wage (cw): the after-expenses income a driver must make for driving to be worth their while.

Phrased another way, the “driver utility” U is

U = p3. p . (1 – m) – co - cw

Periodically, a simulation with equilibration enabled computes the average driver utility for the system as a whole based on the entered price, commission, and costs (which all stay constant for a simulation), and also on the observed utilization rate p3. If U is greater than zero (income after expenses is greater than the reservation wage) then there is money to be made by drivers, and more vehicles enter the system. If U is less than zero, then for some drivers it is not worth their time to be driving and they leave the system. Over time, the system equilibrates to a steady state.

Mapping to cities

While the simulation is carried out entirely in abstract units of blocks, and currency-free prices and costs, the configuration also allows these to be derived from less abstract quantities. If a “real-world” option is chosen, the configuration can separately enter prices per km and per minute; driver costs can be assigned per km (operational costs) and per minute (opportunity costs), and an average speed connects the relationship between space and time so that a “block” has an assigned time and distance. These values are translated into the abstract prices and costs per block.

Extensions

Several additional refinements have been implemented, but the spirit of the simulation is to be as simple as possible, so that it can be calibrated using the very limited high-level public data available and so that it can be used comparatively. However, two are necessary for some applications below.

Many cities have a central “downtown” zone where the demand for ridehail traffic is high, surrounded by suburban areas where demand is lower. This inhomogeneity is built into the model as two zones. A central zone has sides C/2 (so, one quarter of the city’s area) and has a higher rate of trip requests than the surrounding area. The trip destinations remain randomly distributed around the whole city. With an inhomogeneity of zero, the central zone is the same as the rest, with an inhomogeneity of one all trip requests take place in the central zone. In this way, and in the sprit of keeping the model as simple and parsimonious as possible, a single inhomogeneity parameter between zero and one captures the two zones.

The use of random locations for trip destinations implies an average trip length of C/2 (regardless of inhomogeneity). To model an area where the average trip length may differ from this, a maximum trip length can be set, with trips distributed randomly over intersections within that distance. This maximum trip length is mentioned below.

Contact

Email: tom@tomslee.net

Acknowledgements

The project was inspired in part by this 2017 New York Times interactive visualization (scroll down that page a bit).

The calculations run in the browser using the amazing Pyodide distribution, although you can also run it on a Linux or Windows desktop if you are OK with setting up and running python packages. It should run on a Mac too, but I have not verified that.

The site design is inspired by the impressive TensorFlow Playground, and, like that site, uses Material Design Lite for components and layout.

Thanks also to Thorben and JJ from The RideFairTO Coalition, for encouragement, conversations, guidance, and contacts.

Errors and other flaws are all mine.

Ridehailing

Start with a community, which is a square grid with a number of "blocks" on each side.

A vehicle drives randomly around the community. If it goes off one edge, it appears on the opposite edge. This turns out to be a useful simplification. You can also think of it as some vehicles leaving the area, and others entering.

In addition to vehicles, we need trips. Here is a vehicle driving around, and occasionally picking up a passenger for a trip.

The vehicle can be in three "phases".

  • P1: it does not have a trip assigned, and is available. Sometimes this is called 'idle'. It is shown in blue.
  • P2: 'en route' to pick up a passenger. It is shown in an 'amber' colour.
  • P3: busy. The vehicle has a passenger

The trip origin is marked by a red diamond, and the trip destination (which shows up once the vehicle has picked up the passenger) is shown as a green circle (think of it as a target).

When the red diamond is shown, the passenger is in a "waiting" state. When the red circle is shown, the passenger is in the vehicle.

A starting point

First, let's see if this rough model can simulate a city like Toronto. Toronto is an area of 630km^2, modelled here as a 60*60 square city, with an average speed of 30 km/h, and each block representing one minute of travel. From p25 of The Transportation Impacts of Vehicle-for-Hire in Toronto: Oct 2018-July 2021 (VFH2), on Feb 6 2020 the ridehail market in Toronto was like this:

  • 194,000 trips (average 135/min)
  • Average trip length of 8km (16 mins).
  • Vehicle phases: 40.5% P1, 11.2% P2, 48.3% P3.
  • Average wait time 2.1 minutes. This does not include the time between the driver "arriving" and the trip starting, so the time in this model should be a couple of minutes longer.
  • Estimated average number of active vehicles: 6000 (eyeballing a chart).

Using random trips, with a maximum length of 40mins (average trip length of 20 minutes), the chart shows that the simple model gets the P3 fraction right, but underestimates P2.

See this page for informal notes on data sources. The charts are generated by the desktop version of the model.

Many trips take place in downtown Toronto

One additional step is to introduce two "zones": a downtown zone of 30 blocks by 30 blocks, in which more trip requests are made. Here, 40% more trip requests per block come from this downtown zone than from the outside areas - all other aspects remain the same. This one additional parameter gives the right P2 values. This seems to be the simplest model of a city that reproduces the broad features of a ride hail market or system.

Supply and demand

A distinguishing feature of ridehail systems is that drivers are free to enter and leave at any time. Here, drivers will enter if they can earn more than a "reservation wage" and leave if the current pay is less. Driver earnings is the fare, minus the platform commission, multiplied by the fraction of time they are driving (P3).

The fare in Toronto in 2020 is about $0.18/min and $0.81/km ( source ), which equates to $0.58 per minute total. In addition there is a base fare (booking fee) that does not go to the driver (see this CBC article , and so is not included here. Uber's commission is often said to be 25%.

The chart at right shows that, to sustain 6000 drivers at P3=48%, drivers must have a reservation wage (before any expenses) of $0.22 per minute, which is equivalent to $13/hour. Out of this must come vehicle expenses: if those are $0.20/km (the CRA suggestion is higher, at $0.50, but drivers do drive less when waiting for a ride, so this is a guess), which is $6/hr, then drivers are ending up with an income of $7/hr.

Lowering the platform commission

If Uber decides to attract more drivers, it has several levers it can use. One is to lower its commission. What is the effect of a lower commission on driver income? It sounds like it should lead to higher incomes, but that's not what happens. Instead, the higher pay per ride attracts more drivers, so the fraction of time each driver is busy goes down until their income is the same reservation wage as before.

The lowered commission and consequent increase in vehicle numbers (from 6400 to 7200 in these runs) does lower wait times, but only marginally, reducing them by about 10%.

Raising the platform commission

Going the other way, if Uber decides it needs to make more money and raises the commission, the number of vehicles falls (to 5700), each vehicle is busier so that each driver makes the same hourly wage as before, and the wait time is increased, again only slightly.

The effect of fare and commission on demand for rides is not considered here.