Skip to content

Feature proposal: alternative capacity configurations per vehicle (capacity_profiles) #1346

@vlohayk

Description

@vlohayk

Many real fleets have vehicles that can be physically reconfigured between trips: a van whose seats fold to trade passenger slots for cargo space, a truck with removable refrigerated sections, a vehicle with an optional trailer. Each configuration is a different capacity vector, and a vehicle operates a whole route in exactly one of them.

This isn't expressible today. Multi-dimensional capacity tracks several metrics at once, but the limits are fixed — there's no way to say "10 pallets or 6 pallets + 2 fridge slots, your choice per route". Modeling each configuration as a duplicate vehicle doesn't work either, since nothing stops the solver from using two clones of the same physical vehicle simultaneously.

Proposal

A capacity_profiles key on vehicles, mutually exclusive with capacity:

"vehicles": [{
  "id": 1,
  "capacity_profiles": [
    { "name": "standard", "capacity": [10, 0] },
    { "name": "mixed",    "capacity": [6, 2] }
  ]
}]

Semantics: a set of tasks is feasible for a vehicle iff at least one profile fits the resulting load at every route step — a single profile applies to the whole route. The output reports the profile in use via a capacity_profile route key (first fitting profile in input order, so users order by preference).

Note this is strictly stronger than checking each step against the component-wise max of the profiles: a peak load of [8, 1] fits the max envelope of the example above but fits neither profile, and must be rejected.

Implementation

I have a working implementation on a fork branch: https://github.com/Desert-Acacia/vroom/tree/feature/capacity-profiles

Design in brief:

  • Vehicle::capacity becomes the component-wise max over profiles — still a sound necessary condition, so all existing pre-filters (margins, quick load checks in local search) are untouched.
  • The exact feasibility checks in RawRoute::is_valid_addition_for_capacity* require a single profile to satisfy all conditions (std::ranges::any_of over profiles). Same for the direct checks in RouteExchange::is_valid, route split, and initial-route validation.
  • With no capacity_profiles, the profile vector holds the single plain capacity, so the default path is behaviorally identical to today. Existing inputs are unaffected.
  • In plan mode, load violations are reported against the profile minimizing the number of violated steps.

Open questions I'd appreciate input on before opening a PR:

  1. Naming — happy to adjust capacity_profiles / capacity_profile if you prefer something that avoids overloading "profile" (which already means routing profile).
  2. Plan-mode profile selection (minimize violated steps) — is that the semantics you'd want, or would e.g. minimizing total overload be preferable?
  3. Performance — the single-profile hot path is a one-element any_of over the same comparisons as today, but I'm glad to run the benchmark instances and post numbers with the PR.

If this fits the project's scope, I'll clean the branch up against current master and submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions