Skip to main content

Solver features

The Stateless API exposes SWAT's VRP engine directly. Everything the engine does is switched on by a field in the request payload — nothing is configured or stored on the SWAT side.

This page is a map: what the engine can do, and which field controls it. The API reference has the complete field list with types and defaults, and the linked concept pages explain individual features in depth.

You don't need most of this to get started

Nodes, vehicles, a routing engine and a scheduling mode are already enough to produce a valid plan. Add features one at a time and compare the returned cost and rejected_bookings between runs — that is far more reliable than switching on several constraints at once and trying to work out which one changed the result.

How a request is organised

All engine configuration lives under engine_settings, split into four blocks:

BlockControlsExamples
model_parametersWhat the model is — the objective and the rules that apply fleet-wideoptimize_quantity, booking_penalty, mutually_exclusive_groups, compound_zones
calculation_parametersHow this particular run behavesscheduling_mode, calculations_mode, allow_vehicle_late, use_path_equalizer
solver_parametersHow hard and how long the solver searchesfirst_solution_strategy, time_limit_ms, use_local_search_metaheuristic
routing_engineHow travel times and distances are obtainedrouting_engine_name, road_network, time_factor

Anything that varies per vehicle or per stop is set on the vehicles and nodes objects themselves, and usually overrides the fleet-wide value.

Unknown fields are ignored, not rejected

The API drops fields it does not recognise instead of returning an error. A parameter placed in the wrong block is accepted and then silently does nothing. If a feature appears to have no effect, check which block it belongs to before investigating anything else.

Choosing a model

Two scheduling modes are supported, set with calculation_parameters.scheduling_mode:

  • prebook — pickup and delivery with time windows. Each booking has its own pickup and dropoff node, linked by booking_uid.
  • prebook_cvrptw — capacitated routing from a single warehouse or depot. Pickup nodes are merged, which greatly reduces problem size, at the cost of one trip per vehicle.

See CVRPTW vs. PDP scheduling modes for how to choose, and the Introduction for complete example payloads of both.

Between them these two modes cover a superset of the common VRP variants, so most textbook problems reduce to one of them — a plain TSP or a distance-constrained CVRP is a CVRPTW with constraints left unset.

Where CVRPTW and VRPPDTW sit in the VRP family

Objective value and penalties

The solver minimises a single number — the objective value. Every cost and every soft-constraint violation contributes to it, which is what allows the engine to trade a small detour against a broken preference and pick the better plan overall.

Cost termFields
Travel time or distancemodel_parameters.optimize_quantity
Using a vehicle at allmodel_parameters.vehicle_costs, vehicle.vehicle_cost, vehicle.amortized_linear_cost_factor, vehicle.amortized_quadratic_cost_factor
Distance, time and stop ratesvehicle.cost_per_km; cost_per_hour, cost_per_stop, overtime_cost_per_hour, overtime_threshold_duration (fleet-wide in model_parameters, overridable per vehicle)
Leaving a booking unservedmodel_parameters.booking_penalty, node.penalty
Duration of each pickup-to-dropoff tripnode.trip_cost
Idle waitingmodel_parameters.slack_cost_factor, calculation_parameters.waiting_time_cost_factor
Importance of an individual stopcalculation_parameters.use_node_weights_cost with node.weight
Soft-constraint violationsgroup_crossing_penalty, node.lifo_order_penalty, vehicle_late_penalty_coefficient, the penalties inside vehicle.efficiency
Workload fairnesspath_equalizer_weight, working_hours_equalizer
Route compactnessmodel_parameters.travel_matrix_operator, model_parameters.cluster_cost

Most constraints in the engine can be hard or soft. A hard constraint is never violated — the solver would rather return no solution at all. A soft constraint is violated at a price: a fixed penalty, a penalty proportional to the size of the violation, or both. Soft is usually the safer choice, because a hard constraint that cannot be met turns into rejected bookings rather than a slightly worse plan.

Penalties only work at the right scale

Penalties compete with travel cost, and travel cost is measured in seconds or metres. A penalty of 100 is invisible next to a route of 20,000 seconds; the default booking_penalty is 10000 for that reason. When a soft constraint is being ignored, the penalty is usually too small rather than broken.

Bookings the solver could not serve come back in rejected_bookings with a reason code, and the response's cost is the objective value it settled on — start with those two when a plan is not what you expected.

Constraints

Vehicle

CapabilityFieldsDetails
Multi-dimensional capacity and demandvehicle.capacity, node.demand — dictionaries keyed by dimension, e.g. {"weight": 2000, "volume": 10}Vehicle capacity and node demand
Required equipment, numeric rangesvehicle.characteristics, node.vehicle_characteristicsVehicle characteristics
Boolean label logic (AND / OR / NOT)vehicle.labels, node.vehicle_labelsVehicle and order labels
Minimum load during a trip, maximum load on returnvehicle.efficiencyVehicle efficiency
Trips back to the depot, physical stop countvehicle.number_of_trips with node.end_of_trip, vehicle.max_physical_stopsMaximum trips per vehicle
Consecutive pickups before a dropoff, and the reversevehicle.max_pickup_locations, vehicle.max_dropoff_locationsMaximum pickup and dropoff locations
Route length and duration ceilingsvehicle.max_trip_length, vehicle.max_trip_duration, node.max_trip_durationMaximum trip duration
Route directnesssolver_parameters.total_to_furthest_distance_ratioDistance ratio constraint
Start and end locations, fixed route prefixesvehicle.lat/lon, vehicle.partial_route, vehicle.partial_route_endVehicle start and end locations
Depot handlingnode_type: depot, node.end_of_tripVehicle depot

Time

CapabilityFieldsDetails
Time windowsnode.open_time_ts, node.close_time_ts, node.close_time_ts_dynamicTime windows in CVRPTW problems
Several windows on one stopnode.time_windowsMultiple time windows
Allow lateness at a pricecalculation_parameters.allow_vehicle_late, max_possible_lateness, vehicle_late_penalty_coefficient, node.allow_lateLateness configuration
Driver breaks, single or recurringvehicle.dynamic_break_duration and the other dynamic_break_* fieldsDriver breaks
Working hoursvehicle.start_time, vehicle.end_time, calculation_parameters.start_time_end_time_limitationsVehicle working hours
Limit idle waitingmodel_parameters.max_slack, node.max_slack, model_parameters.path_constraints_modeVehicle slack
Gate, queue and docking time on entry or exitmodel_parameters.compound_zonesCompound zones
Vehicles served at once at a depotmodel_parameters.cumulative_limitationsLimiting warehouse capacity
Earliest vs. latest scheduled arrivalnode.finalization_typeFinalization type

Orders and sequence

CapabilityFieldsDetails
Orders that must not share a vehiclemodel_parameters.mutually_exclusive_groups, strictly_exclusive_groups, group_crossing_penalty, node.groupsMutually exclusive groups
Visit order between groups — prebook_cvrptw onlymodel_parameters.groups_orderPrioritising a dropoff or pickup
Last-in-first-out unloadingmodel_parameters.use_lifo_order_check, lifo_order_check_on_all_vehicles, node.lifo_order_check, node.lifo_order_penaltyAPI reference
Cost of dropping an ordermodel_parameters.booking_penalty, node.penaltyBooking penalty
Protect a route already in progressvehicle.assigned_nodes, vehicle.assigned_nodes_protection_interval, calculation_parameters.allow_jumpRoute sequence locking

Geofences

Assign geofences to both sides of the match — vehicle.geofence_ids and node.geofence_ids — and a vehicle will only serve nodes whose geofences it covers. An empty list on a vehicle means no restriction.

Both fields are lists, and that is what makes cross-border work: give a node near a boundary the ids of both adjacent zones and either side's vehicles may serve it. Without this, strict zone borders force long detours for stops that happen to sit just on the wrong side.

Route quality and workload balance

Cheapest is not always operationally acceptable. These features shape the plan a dispatcher actually receives.

CapabilityFieldsDetails
Even out the number of stops per vehiclecalculation_parameters.use_path_equalizer, path_equalizer_weightEqualizers
Even out shift times across the fleetmodel_parameters.working_hours_equalizerEqualizers
Penalise long arcs so routes stay compactmodel_parameters.travel_matrix_operatorRoute compactness
Keep each vehicle's stops geographically clusteredmodel_parameters.cluster_cost, vehicle.cluster_cost_factorRoute compactness
Charge for how long each booking spends on the vehiclenode.trip_costTrip cost
Squeeze idle time out of the schedulemodel_parameters.slack_cost_factor, vehicle.logistics_optimize_slack (both require path_constraints_mode: logistics)Vehicle slack

Travel times and the road network

Travel times and distances come from the routing engine configured in engine_settings.routing_engine — SWAT's self-hosted engine, with the road profile chosen by road_network. A secondary_routing_engine can be configured alongside it, which is how straight-line distance is mixed into a time-based objective for compactness.

Mixed fleets. Set model_parameters.mixed_fleet to true when vehicles differ in routing profile or capacity, then give each vehicle its own vehicle.routing_engine. Small vans and heavy trucks then get different accessible roads and different speeds within a single request. Per-vehicle routing_engine is only read when mixed_fleet is true.

Driver-level speed. routing_engine.time_factor scales the travel times the engine returns, so a cautious driver can be modelled with a factor above 1 and a fast one below. Setting use_speed_in_routing with speed replaces map speeds with a fixed value instead.

Time-dependent travel. Per-node matrix_timestamp lets each stop's transits be computed for the time of day it is actually served, so peak-hour congestion is reflected in the plan rather than averaged away. model_parameters.time_dependent_transit accompanies this; note that it forces the objective to total_distance regardless of optimize_quantity.

Pre-computed matrices. Supply your own travel matrices in matrices and reference them per vehicle with vehicle.matrix_id to skip routing engine calls entirely — useful for repeated solves over a stable location set.

Search behaviour

The solver runs in two phases. First it builds an initial feasible solution using the strategy named in solver_parameters.first_solution_strategy; then, if use_local_search_metaheuristic is enabled, it improves that solution until time_limit_ms runs out. The time limit is therefore a quality dial, not just a timeout: more time means more improvement, and the right value depends on problem size, constraint count and how good the first solution was.

First solution strategy

first_solution_strategy takes a numeric code. You do not need to understand the algorithms behind them — these are the ones worth choosing between:

CodeStrategyReach for it when
0AUTOMATICDefault. Start here and only change it if the result or the runtime is unsatisfactory.
3PATH_CHEAPEST_ARCA dependable general-purpose alternative when AUTOMATIC underperforms.
8PARALLEL_CHEAPEST_INSERTIONMany stops are optional or penalised, and you want each placed where it costs least overall.
9LOCAL_CHEAPEST_INSERTIONFaster than 8 on large payloads, where building the first solution is itself the bottleneck.
14, 16SEQUENTIAL_ / ADJUSTABLE_PARALLEL_CHEAPEST_INSERTIONIncremental re-solves. Both build on the previous run's result, so keep it in vehicle.assigned_nodes.
17LOGISTICS_PARALLEL_CHEAPEST_INSERTIONSingle-warehouse logistics in prebook_cvrptw mode.
5EVALUATOR_STRATEGYYou want to supply the starting routes yourself.
18, 19CHOOSE_FIRST_VALID / CHOOSE_BESTNo single strategy is reliable across your payloads. See below.

The remaining codes are classical academic heuristics — Savings, Sweep, Christofides and the arc-based variants. They are accepted and occasionally useful for comparison, but rarely the best choice for a real fleet. The API reference lists every code with its description.

11 (SWEEP) is not supported

The solver build currently in use does not implement it.

Cascading from a known solution. With code 5, the initial solution is the union of every vehicle's first_solution list — useful for chaining runs, where one solve seeds the next, and for reproducing an exact starting configuration in testing. Nodes you leave out, and nodes that violate another constraint, start in rejected_bookings, though the solver may still bring them back in. Unlike partial_route, a first_solution list is a starting point rather than a fixture: the solver is free to change it. Populating first_solution without setting first_solution_strategy to 5 raises an exception.

Falling through several strategies. Set first_solution_strategy to 18 or 19 and list candidates in first_solution_strategies. 18 tries them in the given order and keeps the first that produces a valid solution — [2, 7, 1, 8] falls through to 1 if 2 and 7 fail. 19 runs them all and keeps the best result. first_solution_strategies_time_limit_ms bounds each attempt, while time_limit_ms still bounds the whole run.

Improvement operators

During the second phase the solver improves the plan by repeatedly trying small rearrangements. solver_parameters.use_all_local_search_operators enables the full set; use_local_search_operators switches on individual extras by name:

  • logistics_relocate_pair — moves a pickup and its dropoff together, and treats stops that share a physical location as one cluster that can be moved as a unit. Worth enabling for PDP logistics where many pickups or dropoffs sit at the same address: without it the solver spends time on reorderings within a single location that cannot change the cost, and can leave a vehicle looping back to an address it has already visited.
  • extended_swap_active — improves solutions when waypoints are used.

Robustness at scale

Unroutable stops. By default, when the map cannot connect a node the engine falls back to a straight-line approximation, so the run still completes. Set calculation_parameters.exclude_unroutable_bookings to true if you would rather have those bookings returned as unserved than routed on an estimate. Routability depends on the map, the vehicle profile and any temporary restrictions in force.

Large payloads. calculation_parameters.partition_nodes splits a payload into consecutive time partitions, solves them in sequence and stitches the results together. It requires partitioning to be enabled server-side; when it is not, the flag is ignored and the payload is solved whole.

Understanding a result. Every response carries the final cost, the per-vehicle plan, and a rejected_bookings list in which each entry names the reason it could not be served. Between those, most "why did it do that?" questions can be answered without changing the payload — check them before adjusting parameters.