Two trucks with different equipment (crane, hazmat) and different per-dimension capacities (waste, recyclable). Jobs combine required_skills with multi-dimension demands; a skill-mismatched job is forced onto the correctly-equipped truck regardless of routing cost.
Skills & Capacities
required_skills on a job and skills on a resource form a hard eligibility filter, evaluated independently of and before the routing cost. demands and capacities can carry multiple independently-tracked named dimensions (e.g. separate waste and recyclable compartments) — a vehicle can serve a job only if every demand dimension fits within its remaining capacity for that dimension.
Setup
Two trucks at a shared depot, each with a different equipment profile and capacity split:
- truck-crane-1: has
skills: ["crane", "hazmat"],capacities: {"waste": 40, "recyclable": 20}. - truck-basic-2: has no special skills,
capacities: {"waste": 60, "recyclable": 30}— more raw capacity, but cannot serve equipment-restricted jobs.
Five jobs:
- job-crane-lift: requires
required_skills: ["crane"](a heavy item needing crane assistance). Demand{"waste": 8}. - job-hazmat-cleanup: requires
required_skills: ["hazmat"]. Demand{"waste": 5}. - job-crane-hazmat: requires both
required_skills: ["crane", "hazmat"]. Demand{"waste": 6, "recyclable": 4}. - job-routine-pickup: no skill requirement. Demand
{"recyclable": 12}. - job-bulk-recycling: no skill requirement, but a large demand
{"recyclable": 22}— onlytruck-basic-2has enough remainingrecyclablecapacity once routing is accounted for.
Only truck-crane-1 has the crane and hazmat skills, so the first three jobs must all go to it regardless of routing cost — even though truck-basic-2 might be geographically closer to some of them. truck-basic-2 picks up the two unrestricted jobs.
Request
{
"metadata": { "problem_id": "skills-capacities-example" },
"travel": {
"distance_matrix": [
[0, 3100, 4800, 2600, 5200, 6100],
[3100, 0, 2900, 4400, 3800, 5300],
[4800, 2900, 0, 5600, 2100, 3400],
[2600, 4400, 5600, 0, 6300, 7200],
[5200, 3800, 2100, 6300, 0, 2700],
[6100, 5300, 3400, 7200, 2700, 0]
]
},
"resources": [
{
"id": "truck-crane-1",
"depot_index": 0,
"capacities": { "waste": 40, "recyclable": 20 },
"skills": ["crane", "hazmat"]
},
{
"id": "truck-basic-2",
"depot_index": 0,
"capacities": { "waste": 60, "recyclable": 30 }
}
],
"jobs": [
{
"id": "job-crane-lift",
"location_index": 1,
"service_time": 1200,
"demands": { "waste": 8 },
"required_skills": ["crane"]
},
{
"id": "job-hazmat-cleanup",
"location_index": 2,
"service_time": 900,
"demands": { "waste": 5 },
"required_skills": ["hazmat"]
},
{
"id": "job-crane-hazmat",
"location_index": 3,
"service_time": 1500,
"demands": { "waste": 6, "recyclable": 4 },
"required_skills": ["crane", "hazmat"]
},
{
"id": "job-routine-pickup",
"location_index": 4,
"service_time": 600,
"demands": { "recyclable": 12 }
},
{
"id": "job-bulk-recycling",
"location_index": 5,
"service_time": 900,
"demands": { "recyclable": 22 }
}
],
"options": { "time_limit_seconds": 5 }
}
Response
{
"status": "optimal",
"problem_id": "skills-capacities-example",
"solve_time_ms": 38,
"routes": [
{
"resource_id": "truck-crane-1",
"day": null,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-crane-lift", "location_index": 1, "arrival_time": 3100, "departure_time": 4300, "service_time": 1200, "load_on_departure": { "total": 8, "by_dimension": { "waste": 8, "recyclable": 0 } }, "distance_from_prev": 3100, "time_from_prev": 3100 },
{ "type": "service", "job_id": "job-crane-hazmat", "location_index": 3, "arrival_time": 8700, "departure_time": 10200, "service_time": 1500, "load_on_departure": { "total": 18, "by_dimension": { "waste": 14, "recyclable": 4 } }, "distance_from_prev": 4400, "time_from_prev": 4400 },
{ "type": "service", "job_id": "job-hazmat-cleanup", "location_index": 2, "arrival_time": 15800, "departure_time": 16700, "service_time": 900, "load_on_departure": { "total": 23, "by_dimension": { "waste": 19, "recyclable": 4 } }, "distance_from_prev": 5600, "time_from_prev": 5600 },
{ "type": "end", "location_index": 0, "arrival_time": 21500, "distance_from_prev": 4800, "time_from_prev": 4800 }
],
"summary": { "total_distance": 17900, "total_travel_time": 17900, "total_service_time": 3600, "total_waiting_time": 0, "total_time": 21500, "num_stops": 3, "num_jobs": 3, "max_loads": { "waste": 19, "recyclable": 4 }, "cost": 17900.0 },
"violations": []
},
{
"resource_id": "truck-basic-2",
"day": null,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-routine-pickup", "location_index": 4, "arrival_time": 5200, "departure_time": 5800, "service_time": 600, "load_on_departure": { "total": 12, "by_dimension": { "waste": 0, "recyclable": 12 } }, "distance_from_prev": 5200, "time_from_prev": 5200 },
{ "type": "service", "job_id": "job-bulk-recycling", "location_index": 5, "arrival_time": 8500, "departure_time": 9400, "service_time": 900, "load_on_departure": { "total": 34, "by_dimension": { "waste": 0, "recyclable": 34 } }, "distance_from_prev": 2700, "time_from_prev": 2700 },
{ "type": "end", "location_index": 0, "arrival_time": 15500, "distance_from_prev": 6100, "time_from_prev": 6100 }
],
"summary": { "total_distance": 14000, "total_travel_time": 14000, "total_service_time": 1500, "total_waiting_time": 0, "total_time": 15500, "num_stops": 2, "num_jobs": 2, "max_loads": { "waste": 0, "recyclable": 34 }, "cost": 14000.0 },
"violations": []
}
],
"unserved": [],
"score": { "total": 31900.0, "travel_distance": 31900.0, "travel_time": 31900.0, "fixed_vehicle_cost": 0.0, "penalty_dropped": 0.0, "fairness_cost": 0.0, "num_vehicles_used": 2 },
"warnings": []
}
Why the assignment is forced, not chosen
All three equipment-restricted jobs (job-crane-lift, job-hazmat-cleanup, job-crane-hazmat) land on truck-crane-1 because truck-basic-2 has no skills at all — it fails the required_skills filter on every one of them and is never an eligible candidate. This is a hard pre-filter applied before the solver evaluates routing cost at all, not a cost-driven choice; the solver never even considers sending job-hazmat-cleanup to truck-basic-2, even though geographically it could be cheaper.
load_on_departure.by_dimension shows each capacity dimension tracked independently: truck-crane-1's recyclable load only changes at job-crane-hazmat (the only job in its route with a recyclable demand), while its waste load accumulates across all three stops. truck-basic-2 never carries any waste at all — both of its jobs only have a recyclable demand, and its higher recyclable capacity (30 vs. truck-crane-1's 20) is exactly why job-bulk-recycling's demand of 22 fits on it but would have exceeded truck-crane-1's remaining room.
job-crane-hazmat's required_skills: ["crane", "hazmat"] means a resource must have every listed skill to be eligible — a resource with only crane or only hazmat would still be filtered out.
- OptimizationRequest — Resource — full
skillsandcapacitiesfield reference. - OptimizationRequest — Job — full
required_skillsanddemandsfield reference. - Pickup & Delivery — another worked example combining skills with a different constraint shape.