Paper · 2025-08-06 · cs.LG, cs.SI
HCRide: Harmonizing Passenger Fairness and Driver Preference for Human-Centered Ride-Hailing
Frames ride-hailing dispatch as a constrained MDP: short waits and passenger fairness, subject to a driver-preference cost limit. Habic, a multi-agent actor with reward and cost critics and a Lagrange multiplier, picks one of the nearest drivers per order. Author-reported on NYC: 2.02% efficiency, 5.39% intra-region fairness, 10.21% preference gains.
Constrained reinforcement learningMulti-agent reinforcement learningProximal policy optimization (PPO)Algorithmic fairnessride-hailingorder-dispatchconstrained-rlmulti-agent-rlfairness
arXivPDFCodeMarkdown15 min read
AI-compiled by claude-haiku-5-5 on 2026-11-05, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
When a ride-hailing app sends a driver to pick you up, it has to choose which driver. Most systems aim only at the company's revenue or at the shortest average wait. That can leave some passengers waiting much longer than others in the same area, and it can send drivers to places they do not like working.
This paper treats dispatch as a game played over many trips. The dispatcher tries to keep waits short, to keep waits similar for passengers in the same place and time, and to avoid sending drivers outside the areas they usually work in. The last two goals can pull against the first one, so the method adds a limit on how often drivers are sent to places they dislike.
The learner is a single shared network that scores a short list of nearby drivers for each order, then picks one. It learns from two scorers: one for the wait and fairness reward, and one for the driver-preference cost. A weight that grows when the cost is too high keeps the cost in check. The authors report better results than the baselines they compared against, on two city datasets, in their own simulator.
TL;DR
- Dispatch is a constrained Markov decision process: maximise a reward that combines waiting time and a passenger fairness term, subject to a limit on driver-preference cost §3.1 §3.2 eq. 9.
- Habic, the method, scores a candidate set of drivers with one actor and chooses one per order; it has a reward critic and a cost critic, and a Lagrange multiplier for the cost limit §3.3 eq. 14 eq. 16.
- Author-reported on NYC against baselines: efficiency +2.02%, intra-region fairness +5.39%, driver preference +10.21% §1 tab. 1.
- The code is public at
LinJiang18/HCRide, has no LICENSE file, and differs from the paper in the reward, the cost, the Lagrange update and the actor loss (see Paper vs code). - Treat the numbers as the authors' results on their simulator. Several paper parameters are not stated; the code sets them.
Problem
Order dispatch systems usually maximise platform revenue or system efficiency. The authors say this can harm both passengers and drivers §1. Prior fairness work mostly uses absolute fairness, which asks for equal waits everywhere regardless of local supply and demand §1. Driver preferences are often ignored, so drivers end up in unfamiliar or undesired areas §1 §2.1.
The authors observe in their Shenzhen data that waits differ between regions and between periods of the same day §2.1. They also observe that drivers differ in how widely they operate §2.1. Their design needs two definitions that do not yet exist: a spatio-temporal fairness notion and a per-driver preference notion §2.2.
The main difficulty is conflict. Raising passenger fairness can send drivers into high-demand areas away from their preferred regions §1.
Method
Problem as a CMDP. The authors model dispatch as a constrained Markov decision process with eight components: states, actions, transitions, reward, a set of costs, an initial state distribution, and two discount factors γ_r and γ_c §3.1. Each driver is an agent. An agent is inactive while it serves an order, so the number of active agents changes over time §3.1. Training is centralised and execution is decentralised §3.1.
- State. For driver k at slot t the state has three parts: spatio-temporal (region, period, coordinates), driver (preference), and context (supply, demand, weather, traffic) §3.1.
- Action. Accept an order, move to complete an order, or cruise §3.1.
- Reward. The order-level reward combines a waiting-time term and a fairness regulariser, weighted by α §3.1 eq. 6. Only the agent that serves the order receives the reward §3.1.
- Cost. Each driver has three region sets: positive, neutral and negative, built from its visit history §2.2.2 eq. 3–eq. 5. A cost is incurred when a dispatch ends in a region of the negative set. The paper says the cost's magnitude depends on the distance to the nearest positive region §3.1.
- Objective. Maximise the expected discounted reward J_r subject to J_c ≤ ξ eq. 7 eq. 8 eq. 9. The Lagrangian is L(θ, λ) = J_r − λ(J_c − ξ) eq. 10. The authors alternate updates of θ and λ until both change less than a tolerance §3.2 eq. 11–eq. 13.
Fairness and preference definitions.
- Intra-region fairness requires equal waits for passengers in the same region and one-hour period §2.2.1 eq. 1. The period length is set to one hour in the study, but the authors say it can be adjusted §2.2.1.
- Inter-region fairness compares each region's benchmark wait, WT_c(u, v), scaled by a ratio that depends on historical passenger and driver counts, with |C_i| = β_i · N_passenger / N_driver §2.2.1 eq. 2. The benchmark therefore falls when driver supply exceeds passenger demand §2.2.1.
- Preference sets. V_k(u) is driver k's historical visit frequency to region u. Region u is positive if V_k(u) > d eq. 3. It is neutral if it lies within κ·V_k(u_1) of a positive region u_1 eq. 4. It is negative otherwise eq. 5 §2.2.2.
Habic (Harmonization-oriented Actor-Bi-Critic). Habic has three parts §3.3.
- Multi-agent competition. For an order i, the drivers near it compete. Each candidate k gives a matching feature m_i^k made of its own state and the order's state. The order state includes pickup and drop-off locations, the regions, and the period §3.3.1.
- Dynamic actor. A matching-degree network Q scores each candidate, and a softmax over the candidates gives the probability of choosing each one eq. 14 §3.3.2. The actor is updated with a PPO-clip objective, and the update direction for θ is the reward gradient minus λ times the cost gradient eq. 15 §3.3.2. The multiplier λ is updated by a projected step eq. 16 §3.3.2.
- Bi-Critic. A reward critic V^R and a cost critic V^C estimate the two values §3.3.3. The advantage is estimated with GAE, parameter ψ eq. 18, under a KL trust-region bound δ eq. 19 and the PPO-clip form eq. 20. Both critics are fitted to one-step bootstrapped targets eq. 21 eq. 22.
The paper gives no algorithm box. The pseudocode below is written from the equations and sections cited.
# Dispatch one order from its candidate drivers [§3.3.1] [§3.3.2] [eq. 14]
# Candidates: nearest active drivers; the paper's "range" is not stated [§3.3.1]
def dispatch(order, active_drivers, matching_net):
# Pick the candidate set [§3.3.1]
candidates = nearest_k(active_drivers, order.pickup)
# One matching feature per candidate: driver state + order state [§3.3.1]
matching = concat(driver_state(candidates), order_state(order, candidates))
# Matching degree for each candidate [eq. 14]
degrees = matching_net(matching) # (n,)
# Probability of choosing each candidate [eq. 14]
probs = softmax(degrees) # (n,)
# Sample the chosen driver [§3.3.2]
chosen = sample(probs)
return candidates[chosen], matching# Order-level reward [eq. 6]
# wait: pickup wait of this order; k_waits: waits of passengers in region u
# benchmark: fairness benchmark WT_c(u, v) [eq. 2]; alpha: trade-off weight
def order_reward(wait, k_waits, benchmark, alpha):
# Efficiency term: a shorter wait gives a higher reward [eq. 6]
efficiency = -(1 - alpha) * wait
# Fairness term: mean squared gap to the benchmark [eq. 6]
fairness = -alpha * mean((k_waits - benchmark) ** 2)
return efficiency + fairness# Clipped surrogate for one sample [eq. 20]
# eps: clip range; not stated in paper [§3.3.3]
def clipped_surrogate(new_prob, old_prob, advantage, eps):
# Probability ratio between the new and old policy [eq. 20]
ratio = new_prob / old_prob
# Clipped ratio times the advantage [eq. 20]
clipped = clip(ratio, 1 - eps, 1 + eps)
return min(ratio * advantage, clipped * advantage)# Dual step on the Lagrange multiplier [eq. 16] [§3.2]
# cost_estimate: estimate of J_c; limit: xi in eq. 9
def update_multiplier(lam, cost_estimate, limit, lr):
# Raise lambda when the cost estimate exceeds the limit, keep it >= 0
# Eq. 16 is written in two forms; this is the first one [eq. 16]
return max(0.0, lam + lr * (cost_estimate - limit))Hyperparameters
Values from the paper are marked as such. Code values come from the repository at code_commit. Items marked "not stated" appear nowhere in the paper text.
| Name | Value | Where | Notes |
|---|---|---|---|
| Driver-preference limit (ξ) | 15% violation rate | §4.2.3 | Paper: expected to fall below this during training. Code: limit = 0.15 run.py |
| Efficiency-fairness trade-off (α) | not stated in paper | §3.1 eq. 6 | Code: alpha = 1.5 envs.py |
| Discount factors (γ_r, γ_c) | not stated in paper | §3.1 | Code: one gamma = 0.95 for both critics run.py Habic.py Habic.py |
| GAE parameter (ψ) | not stated in paper | §3.3.3 eq. 18 | Code does not use GAE; advantage is the one-step TD error Habic.py |
| KL bound (δ) | not stated in paper | §3.3.3 eq. 19 | Not used in code |
| Clip range (ε) | not stated in paper | §3.3.3 eq. 20 | Code: eps = 0.2 run.py |
| Update-round tolerances (ε1, ε2) | not stated in paper | §3.2 eq. 13 | Code runs a fixed schedule instead |
| Lagrange multiplier, initial value | not stated in paper | §3.2 | Code: lagrange = 1 run.py |
| Actor learning rate | not stated in paper | §3.3.2 | Code: 1e-3, halved every 50 update rounds run.py Habic.py |
| Critic learning rate | not stated in paper | §3.3.3 | Code: 1e-2, halved every 50 update rounds run.py Habic.py |
| Multiplier learning rate | not stated in paper | §3.3.2 | Code: 1e-3 run.py |
| PPO epochs per update | not stated in paper | §3.3.3 | Code: epochs = 5 run.py |
| Replay memory / batch size | not stated in paper | §3.3 | Code: 10,000 transitions, batch 1,000 run.py |
| Update frequency | not stated in paper | §3.3 | Code: one update every 20 slots, starting on day index 2 run.py |
| Candidate drivers per order | not stated in paper | §3.3.1 | Code: 20 nearest envs.py envs.py |
| Slots per episode | not stated in paper | §3.1 | Code: 180 run.py |
| Vehicle speed (for wait and trip times) | not stated in paper | §4.1 | Code: 30 (units not stated) envs.py |
| Training and test days | not stated in paper | §4.1 | Code: train on days 0–49, test on days 50–80 run.py execute.py |
| Driver count (Shenzhen, NYC) | 1,200 and 800 vehicles | §4.1 | Code: 150 drivers in both scripts run.py execute.py |
| Orders (Shenzhen, NYC) | 1.07 M (03/2021–06/2021); 214 k (01/2024–02/2024) | §4.1 | Only Shenzhen-range coordinates appear in code run.py |
| Grid | 491 regions in Shenzhen (motivating figure) | §2.1 | Code: 10 × 10 = 100 regions run.py |
| Time period for fairness | one hour | §2.2.1 | Code's benchmark is a cumulative regional mean with no period (see Paper vs code, item 2) |
| Positive-region threshold (d), influence factor (κ), ratio (β_i) | not stated in paper | §2.2.1 §2.2.2 | Not in code |
| Actor network | input 6 + 10 features; hidden layers 4, 6, 4; output 1 | Habic.py | Paper does not give the architecture of Q |
| Critic networks | 6 → 16 → 8 → 4 → 1 for both critics | Habic.py | Not stated in paper |
Results
Author-reported, on a simulator the authors built from two ride-hailing datasets. Shenzhen: 1.07 M orders, 1,200 vehicles, March to June 2021. NYC: 214 k orders, 800 vehicles, January and February 2024 §4.1. The benchmark is myopic dispatching MD [zhang2017taxi], and every value is a decrease relative to MD tab. 1. The eight baselines are DQND, AC-bgm, IPPO, MAPPO, CPO, Lag-TRPO and two HCRide variants with other fairness definitions, HCRide-AF and HCRide-MMF §4.1.
| Method | SZ DAPWT (efficiency) | SZ DPF inter | SZ DPF intra | SZ DPVR (preference) | NYC DAPWT | NYC DPF inter | NYC DPF intra | NYC DPVR |
|---|---|---|---|---|---|---|---|---|
| DQND | 2.51 ± 0.34 | 5.26 ± 1.75 | 6.05 ± 1.57 | 3.33 ± 3.56 | 3.43 ± 0.87 | 7.81 ± 2.23 | 9.92 ± 2.41 | 4.25 ± 3.29 |
| AC-bgm | 4.03 ± 0.85 | 6.92 ± 1.17 | 7.73 ± 1.34 | −2.25 ± 3.31 | 4.77 ± 1.52 | 10.21 ± 2.37 | 9.54 ± 2.11 | 2.53 ± 3.11 |
| IPPO | −0.99 ± 0.78 | 2.34 ± 1.03 | 2.55 ± 1.49 | 3.51 ± 3.25 | −3.01 ± 1.94 | 4.41 ± 3.05 | 5.87 ± 2.08 | −0.68 ± 2.34 |
| MAPPO | 1.11 ± 0.94 | 3.47 ± 1.94 | 4.12 ± 0.98 | 3.11 ± 3.37 | 2.26 ± 1.99 | 8.28 ± 2.03 | 7.92 ± 2.56 | −1.15 ± 3.48 |
| CPO | 4.51 ± 1.12 | 7.73 ± 1.26 | 8.52 ± 1.94 | 7.75 ± 2.95 | 4.72 ± 1.03 | 9.88 ± 1.88 | 12.73 ± 2.47 | 12.19 ± 5.38 |
| Lag-TRPO | 3.78 ± 1.32 | 8.55 ± 1.46 | 9.77 ± 1.54 | 12.74 ± 3.29 | 5.24 ± 2.17 | 10.38 ± 2.69 | 15.42 ± 3.02 | 15.81 ± 4.42 |
| HCRide-AF | 5.31 ± 1.45 | 7.00 ± 1.82 | 7.25 ± 1.69 | 19.23 ± 3.68 | 7.01 ± 1.98 | 10.01 ± 2.77 | 10.86 ± 2.98 | 22.45 ± 4.01 |
| HCRide-MMF | 5.42 ± 1.17 | 9.98 ± 1.68 | 11.36 ± 1.92 | 19.79 ± 3.03 | 7.12 ± 2.08 | 13.25 ± 3.01 | 14.34 ± 3.09 | 23.85 ± 4.21 |
| HCRide | 5.48 ± 1.13 | 13.39 ± 1.71 | 16.67 ± 1.46 | 19.52 ± 3.87 | 7.15 ± 2.03 | 15.11 ± 2.88 | 19.98 ± 3.03 | 24.41 ± 3.92 |
Source: tab. 1. Bold is as printed in the paper. Caption: "Lower values indicate better performance", which conflicts with the bold marks and with the text. See Pitfalls.
Headline numbers, as stated in the text: Shenzhen efficiency 5.48% against MD, and 1.77% over Lag-TRPO §4.2.1. Shenzhen driver-preference violation under MD is 18.21%, and the authors target 15% §4.2.3. The intro gives per-city gains over the baselines: Shenzhen 1.77 / 5.29 / 7.65 / 7.77 and NYC 2.02 / 5.28 / 5.39 / 10.21, for efficiency, inter-region fairness, intra-region fairness and preference §1. Each one equals (HCRide − Lag-TRPO) / (100 − Lag-TRPO) computed from the table, which is the formula the paper states for efficiency §4.2.1. Lag-TRPO is therefore the comparison point for these figures.
Figures: the distribution of average waiting time across regions narrows during training and approaches the fairness benchmark §4.2.2 fig. 4. Average waiting times by region and period in Shenzhen are shown in fig. 1. Driver preference patterns are shown in fig. 2. The framework is fig. 3.
Paper vs code
Official repository: https://github.com/LinJiang18/HCRide, linked in the paper §1. The owner is the first author. Commit e4f4effe6079d73b9d185d08c9b3f69846764a52 was cloned on 2026-11-05. The repository has no LICENSE file and no license statement in its README, so code is cited by path and line only, with no verbatim snippets. The repository contains only the Habic learner and the simulator. Baselines are not in the repo. algorithm/AC.py is imported by run/run.py but not used there.
Differences, each with both citations.
- Reward. Paper: efficiency weight (1 − α) on the wait, and α times the mean squared gap to WT_c over K passengers §3.1 eq. 6. Code: weight 1 on the wait, and α times the absolute gap to the region mean, divided by 3, with α = 1.5 envs.py. The training loop calls this function run.py.
- Fairness benchmark. Paper: WT_c from the supply-demand ratio with β_i §2.2.1 eq. 2. Code: the region's running mean wait,
accwt / accOrder, updated once per slot regions.py. Those totals are accumulated over the whole run and never reset per day regions.py regions.py. The reward uses that mean run.py. - Variants. Paper: HCRide-AF and HCRide-MMF are separate methods §4.1. Code:
cal_absolute_rewardandcal_maxmin_rewardexist envs.py but no script calls them. The variants in Table 1 cannot be reproduced from the repo as shipped. - Cost. Paper: cost when the destination lies in the negative set, with magnitude given by distance to the nearest positive region §3.1 §2.2.2. Code: binary; cost 1 if the destination region is in the driver's
preRegionlist envs.py. The repo does not say whether that list is the positive or the negative set, and the data pickles were not opened. - Actor loss. Paper: the θ update includes − λ ∇J^C eq. 15. Code: the actor loss is only the reward surrogate, L = J^R Habic.py Habic.py. λ is used only in the multiplier update Habic.py. So in the code, the cost constraint cannot change the policy.
- Constraint value. Paper: compares J^C, the cumulative cost estimate, with ξ eq. 9 eq. 16. Code: compares a batch mean of clipped cost advantages (JC) with the limit 0.15 Habic.py. That quantity is a surrogate for an advantage, not the cost estimate in eq. 9.
- Multiplier gradient. Paper: a projected gradient step on λ eq. 16. Code:
lagrange.gradis never cleared, so each step adds to gradients from all earlier calls Habic.py. The optimizerlagOptimizeris created but never stepped Habic.py. - Advantage. Paper: GAE with ψ eq. 18. Code: one-step TD error,
rewardTarget − VHabic.py. - Discounts. Paper: separate γ_r and γ_c §3.1. Code: one value 0.95 for both run.py Habic.py. The environment also defines
gamma = 0.98, which is unused envs.py. - State. Paper: the state includes driver preferences and a context vector §3.1. Code: six features: region index, region mean wait, normalised longitude and latitude, distance to the nearest order, and time envs.py. Preferences and context are computed and then dropped envs.py envs.py.
- Units in the state. The distance to the nearest order is in kilometres when the decision is made envs.py, but in minutes in the next state stored in replay envs.py.
- Actions. Paper: accept, move to complete, or cruise §3.1. Code: choose one candidate driver only; there is no cruise action run.py.
- Candidate range. Paper: drivers "within a certain range" §3.3.1. Code: the search starts at neighbour level 3, which is every region in the city. The wider fallback only runs when no idle driver exists anywhere envs.py regions.py. The candidates are then the 20 nearest envs.py.
- Scale. Paper: 1,200 and 800 vehicles §4.1. Code: 150 drivers in both scripts run.py execute.py. The grid is 10 × 10 run.py, while the motivating analysis uses 491 Shenzhen regions §2.1. The NYC data and NYC coordinates are not in the repo run.py.
- Metrics. Paper: DPVR is the share of orders sent to non-preferred regions §4.1. Code: per-driver cost is accumulated drivers.py, but neither
run/run.pynortest/execute.pyreports it run.py execute.py. The DPVR column of Table 1 cannot be reproduced from the shipped scripts. - Transitions. Code stores a transition when the trip ends, with the drop-off state as the next state envs.py. The paper does not say when transitions are formed.
- Trained model.
test/execute.pyloadsresult/Test1/agent.pthand acts greedily execute.py execute.py. Training samples actions Habic.py.
Searches: the GitHub link was taken from the paper itself, so no search was needed.
Pitfalls
- Unstated parameters. The paper gives no value for α, β_i, κ, d, δ, ψ, ε, the multiplier's initial value, learning rates, the number of candidates, the number of training episodes, or the batch and memory sizes. The code's choices are in the Hyperparameters table and differ from the paper's stated design in several places.
- Table 1 caption conflicts with its values. The caption says lower is better. The bold marks and the text treat higher as better, since the values are decreases relative to MD. Also, in Shenzhen the bolded DPVR is HCRide-MMF at 19.79, while HCRide at 19.52 is not bold. The text says HCRide is best on driver preference §4.2.3. Read each column by its own numbers.
- Spread not defined. The "±" values have no definition in the caption or text, and the number of runs or seeds is not stated.
- Simulator. The paper's text does not describe the simulator. The only description of driver movement, trip durations and order arrivals is in
simulator/. Its outputs are the paper's results. - Benchmark leakage across days. The fairness benchmark is a running mean that continues across training and test days regions.py regions.py. Results on test days depend on the training history.
- Constraint sign. Eq. 16 is written in two forms with differing sign conventions. The code's direction is λ + η(J^C − limit), which raises λ when the cost estimate exceeds the limit Habic.py.
- Cost of the method. Each update evaluates the actor on every stored candidate set, one at a time, in Python loops Habic.py. The paper does not report runtime or hardware.
- Pinned environment.
requirement.txtpins torch 1.10.2, pandas 1.3.5, numpy 1.21.6 and scipy 1.7.3 requirement.txt. The README asks for Python 3.7 README.md. Newer Python builds may not install these pins. - No algorithm box. Algorithm steps must be assembled from equations and prose §3.3. The pseudocode above is the card's own reading.
- Claims for variants. HCRide-AF and HCRide-MMF are in Table 1 but not reproducible from the shipped repo (see Paper vs code, item 3).
Lineage
- Builds on: PPO-clip, GAE and the KL-constrained policy update, all cited in §3.3.3, and the Lagrangian constrained-MDP form in §3.1–§3.2. None of these are cards here.
- Compared against: myopic dispatching MD [zhang2017taxi]; DQND [mnih2013playing]; AC-bgm [wang2023time]; IPPO [de2020independent]; MAPPO [yu2022surprising]; CPO [achiam2017constrained]; Lag-TRPO [ray2019benchmarking]; HCRide-AF [suhr2019two]; HCRide-MMF [sun2022optimizing] §4.1.
- Superseded by: none known.
Source and license
Adapted from “HCRide: Harmonizing Passenger Fairness and Driver Preference for Human-Centered Ride-Hailing” by Lin Jiang, Yu Yang, Guang Wang (arXiv:2508.04811), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/LinJiang18/HCRide at commit e4f4effe6079 (none (no LICENSE file in repository)). Any quoted code is shown under that license.