Equalizers
In complex vehicle routing scenarios, optimization focused solely on minimizing total cost commonly produces unbalanced routes. One vehicle might be assigned 18 drops while another gets 4; one driver might run from early morning to late evening while another finishes before lunch. Each plan is cheap, but the shift pattern behind it is rarely something a depot manager can hand to drivers, and it leads to problems with driver fairness, overtime costs, and inconsistent vehicle utilization.
SWAT provides two independent equalizers for this. They balance different quantities and are configured in different places:
| Path Equalizer | Working Hours Equalizer | |
|---|---|---|
| Balances | The number of stops per vehicle | The fleet-wide time window (makespan) |
| Measure | Each vehicle's deviation from the average stop count, squared | Latest route end minus earliest route start |
| Configured in | calculation_parameters | model_parameters |
| Fields | use_path_equalizer, path_equalizer_weight | working_hours_equalizer |
Reach for the Path Equalizer when the complaint is about how many drops each driver gets, and the Working Hours Equalizer when it is about shift times. They are independent and can be combined.
Neither equalizer is a substitute for the other. An even split of stops can still produce uneven shift lengths when some stops are much further apart than others, and a tight common shift window can still hide very uneven drop counts.
Path Equalizer
The Path Equalizer promotes a more equitable distribution of workload — measured as the number of stops each vehicle serves — across all vehicles in the fleet. Its goal is to minimize the variance in stop counts, so that no driver carries a far heavier drop list than another.
How it works
The solver counts how many nodes each vehicle is assigned, compares that against the fleet average, and charges the square of the difference, multiplied by path_equalizer_weight.
Squaring matters: one badly overloaded vehicle costs far more than several slightly uneven ones, so the solver works hardest on the worst outlier.
This fairness cost is then minimized alongside all other costs (travel time, vehicle usage, penalties). To reduce it, the solver reassigns stops between vehicles — it might move a stop from a vehicle with many stops to one with few, even if that slightly increases total fleet travel, because the drop in the fairness penalty makes it a better overall solution.
Example. Two vehicles and 22 stops, initially split 18 / 4. The fleet average is 11, so one vehicle is 7 above it and the other 7 below — an expensive imbalance once each gap is squared. With the Path Equalizer enabled the solver might rebalance to 12 / 10: total travel distance may rise slightly, but the drop lists are far more even.
Configuration
"calculation_parameters": {
...
"use_path_equalizer": true,
"path_equalizer_weight": 1000,
...
}
calculation_parametersBoth fields belong to calculation_parameters, not model_parameters. The API ignores unknown fields instead of rejecting them, so a request that puts them in the wrong object is accepted and solved with the Path Equalizer silently switched off. If enabling it appears to change nothing, check this first.
| Parameter | Type | Default | What it does |
|---|---|---|---|
use_path_equalizer | boolean | false | Set to true to activate the Path Equalizer heuristic. |
path_equalizer_weight | number | 100 | Weight of the penalty for unbalanced stop counts. A higher weight tells the solver an even split is important and it should accept higher travel costs to get one. |
Finding the right weight usually requires experimentation to strike the balance between route equity and overall efficiency for your operation.
Working Hours Equalizer
The Working Hours Equalizer compresses the fleet's overall schedule window, which in practice pulls start and end-of-shift times closer together across the fleet.
What it optimises
working_hours_equalizer is a cost coefficient applied to the global span of the solver's time dimension:
global span = (latest route end across all vehicles) - (earliest route start across all vehicles)
The solver pays working_hours_equalizer × global span on top of every other cost in the model, so a higher value pushes it harder towards a tight common window, accepting more total time or distance to get there.
Because this is a makespan term, it is worth being precise about what it does and does not do:
- It penalises the overall spread of the working day across the fleet.
- It does not compare routes pairwise, and it does not directly penalise the gap between the longest and the shortest individual route.
The practical consequence: if order time windows already force one vehicle to start early and another to finish late, those two vehicles fix the global span, and the equalizer has little room left to improve anything.
The waiting-time caveat
The penalty applies to the fleet-wide window, not to each route. That leaves the solver a second, cheaper way to narrow the span: rather than moving work onto a short route, it can simply let that vehicle wait. Waiting time inside the window carries no cost of its own.
The result can be a plan whose shift window looks even while the work inside it is not.
cost_per_hourSet cost_per_hour — globally in model_parameters, or per vehicle — alongside working_hours_equalizer. cost_per_hour prices each vehicle's own route duration, so padding a short route with idle time stops being free and the solver has to redistribute real work instead.
Configuration
"model_parameters": {
...
"working_hours_equalizer": 1000,
...
}
| Parameter | Type | Default | What it does |
|---|---|---|---|
working_hours_equalizer | integer | null | Cost coefficient applied to the fleet-wide global span of the time dimension. Higher values weight a tight common window more heavily against travel cost. |
The parameter is ignored in two cases:
- the request contains a single vehicle — there is no spread to close; and
- the value is
0ornull, which disables the term entirely.
There is no universally correct weight. It is a relative cost coefficient competing against travel time, distance and vehicle costs in the same objective, so the value that works depends on the magnitude of those other terms in your own requests. Start low, then raise it until the shift window tightens as much as you need, and watch total travel cost as you go.
working_hours_equalizer is the Stateless API's own name for this parameter. The Integration API exposes it as a pair — logistics_api_settings.use_working_hours_equalizer to switch it on and working_hours_equalizer_weight to set the coefficient — which the pipeline collapses into the single working_hours_equalizer value the solver reads. The weight has no effect while the boolean is false. See Logistics API Settings.
Using both together
The two terms are added to the same objective function and do not conflict, so enabling both is reasonable when you want even drop lists and a tight shift window. Bear in mind that each one trades total efficiency for evenness, and their weights compete with each other as well as with travel cost — so raise them one at a time rather than together, and check total fleet travel time and distance after each change.
Like any balancing term, both equalizers trade total efficiency for evenness. Expect fleet travel time or distance to rise as the weights go up.
Playgrounds
Path Equalizer
Two vehicles and two clusters of jobs, with use_path_equalizer set to true at a high weight to encourage an even split of stops.
Try changing it to false to see how the solver would otherwise assign routes based purely on cost and distance.
Working Hours Equalizer
The same two vehicles and job clusters, with working_hours_equalizer set to a high weight to encourage a tight common shift window, and cost_per_hour set alongside it so that padding a short route with idle time is not free.
Try lowering working_hours_equalizer to 0 to see the schedule the solver would otherwise build on cost alone.