Search code examples
optaplanner

OptaPlanner ConstraintProvider


I have created a small project resolve below usecase

Use Case:

  • We have DocYards – which can handle 80/40/20.. containers per hour. DocYards will place containers on Trucks

  • We have Trucks – which can Carry 10/20/6.. containers capacity.

Problem Solution : We need to plan which Truck goes to which DocYard at what time, based up on container capacities.

Example:

If DocYard having 40 Capacity We can send Two Trucks with Capacity 25, 15 (matched or less to DicYard Capacity) at 8:00 - 9:00 timeslot

If DocYard having 10 Capacity We can only send one Truck with Capacity >10 (matched or less to DicYard Capacity) at 9:00 - 10:00 timeslot

i have created below constraint to solve the problem

    Constraint requiredCapacityConstraint(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Truck.class)
            .groupBy(Truck::getDocYard, Truck::getTruckCapacity)
            .filter((docYard, reqCapacity) -> reqCapacity > docYard.getCapacity())
            .penalize("requiredCapacityTotal",
                    HardSoftScore.ONE_HARD,
                    (docYard, truckCapacity) -> truckCapacity - docYard.getCapacity());
}

i got below output , which is wrong - because it is assigning trucks greaterthan its capacity

|            |DocYard-A-40|DocYard-B-20|DocYard-C-10|
|------------|------------|------------|------------|
| 08:00      | T40        | T19        | T10        |
|------------|------------|------------|------------|
| 09:00      | T15,T30    | T11,T12    | T03        |
|------------|------------|------------|------------|
| 10:00      | T22        | T20        | T05        |
|------------|------------|------------|------------|
here number after alphabet is CAPACITY of that Truck / DocYard

Can someone help me what's wrong & how can i resolve this ?

attching github repo of above problem

Github Link


Solution

  • The main problem is groupBy(Truck::getDocYard, Truck::getTruckCapacity) only groups the trucks, not sum them. In addition, it does not take into account the Truck's timeslot. To calculate capacity used during a particular timeslot, a second groupBy key need to be used, along with ConstraintCollector.sum to calculate the sum:

    Constraint requiredCapacityConstraint(ConstraintFactory constraintFactory) {
        return constraintFactory.forEach(Truck.class)
            .groupBy(Truck::getDocYard, Truck::getTimeslot, ConstraintCollectors.sum(Truck::getTruckCapacity))
            .filter((docYard, timeslot, reqCapacity) -> reqCapacity > docYard.getCapacity())
            .penalize("requiredCapacityTotal",
                    HardSoftScore.ONE_HARD,
                    (docYard, timeslot, truckCapacity) -> truckCapacity - docYard.getCapacity());
    }