-
Notifications
You must be signed in to change notification settings - Fork 160
-
Hi Timefold team,
For my bachelor thesis, I want to build a machine learning pipeline in the cloud that predicts and schedules refills for fuel tanks at gas stations. I recently met someone from your team and thought this might be a relevant use case to share here.
I’ve received a historical dataset with sensor data from the tanks, primarily the measured volume of the tanks over time, and have started exploring it. However, I’m not sure how to get started with creating a predictive model that determines when a tank needs refilling. On top of that, I’d like to integrate scheduling logic to determine how and when to actually refill the tanks in an optimized way.
As an example of the scheduling challenge: currently, a tank is refilled when it drops below a certain threshold. But you can imagine that refilling on a Sunday or public holiday may be much more expensive. If the refill could be delayed by just a day to avoid that, it might result in significant cost savings, so smarter scheduling could really help here.
The problem spans multiple tanks distributed across the Benelux region, so there’s also a routing component involved. Ideally, multiple tanks could be refilled in a single delivery trip, introducing a traveling salesman-type problem. To make it more complex, the fuel truck itself must be loaded in a specific way, it can’t be filled completely, and needs to maintain certain fill percentages to avoid rollover risks. So scheduling and routing must also take into account these safety constraints.
Can I get started with just the volume data, or are there other data points I should definitely look into? Do you have any tips, ideas, or inspiration that could help me move forward with this project?
Thanks in advance!
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 3 comments 9 replies
-
Hello @93TV, and welcome to the community!
Timefold Solver won't be able to help you with predictions, it's not designed to do that. But the rest of your problem seems like a run-off-the-mill CVRP. Have you seen our Vehicle Routing quickstart?
IMO you should start with your predictive part - figure out how much you'll need to deliver, where to, and how many trucks you got. That'll be the input to the solver.
If deliveries on the weekends are more expensive, penalize them in your constraints. You can go further, and specify an expected time window for each delivery - that turns the problem from CVRP to CVRPTW, and we can do that too.
I wouldn't worry too much about fill percentages. The tank has a certain maximum capacity to which it can legally/realistically be filled, so use that as your vehicle capacity. (The solver doesn't need to know that, physically, you can fit even more fuel in the tank.) There may be a minimum required fill, meaning that a tank must always contain at least X units of fuel - have another constraint penalize going under this threshold.
Overall, assuming your description is the entire problem and you've included every important consideration, I expect Timefold Solver will do just fine.
Beta Was this translation helpful? Give feedback.
All reactions
-
@triceo thank you for the quick reply! Aha those are some good tips! I'll start with looking into the predictions and see if I can get my hands on the information about the fleet and the location of the stations. I'll also give the Vehicle Routing Quickstart a go. If more questions rise, I'll drop them here! :D
Big Thanks, this was the inspiration I needed to get started.
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
Hi again @triceo & the Timefold community,
I've been exploring the vehicle-routing quickstart and found it straightforward to adapt to my own data, truly an impressive tool!
I've successfully containerized the application and deployed it on GCP Cloud Run.
As I look to integrate this into a broader ML pipeline, I'm curious about best practices. Specifically:
Is deploying the solver within a Docker container the recommended approach for production environments?
How do you suggest managing solver models and constraints in a scalable ML pipeline?
Thanks for the inspiration and support!
Beta Was this translation helpful? Give feedback.
All reactions
-
Hello @93TV.
Is deploying the solver within a Docker container the recommended approach for production environments?
There is nothing wrong with that. It's how things are done these days.
How do you suggest managing solver models and constraints in a scalable ML pipeline?
I have no experience in this area, and therefore no advice.
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
Thank you @triceo ! I'll dive deeper!
Beta Was this translation helpful? Give feedback.
All reactions
-
After succesfully deploying I dived into playing with the constraints. If I add this constraint will it take in mind the capacity of the vehicles between the stops as well?
protected Constraint vehicleCapacity2(ConstraintFactory factory) { return factory.forEach(Vehicle.class) .filter(vehicle -> vehicle.getTotalDemand() < vehicle.getCapacity() * 0.20 || vehicle.getTotalDemand() > vehicle.getCapacity() * 0.80) .penalizeLong(HardSoftLongScore.ONE_HARD,vehicle -> vehicle.getTotalDemand() - vehicle.getCapacity()) .asConstraint(VEHICLE_CAPACITY); }
For this context trucks (filled with fluids) can only drive when filled less than 20% or more than 80% of their capacity. Would this rule be applied with the hard constraint noted above?
Thanks again! <3
Beta Was this translation helpful? Give feedback.
All reactions
-
@triceo
It took me some time to get back to it, but I gave it a first try yesterday , I added a new hard constraint and a shadow variable on the Visit class. However, the solver keeps using the minimum number of vehicles, and doesn’t activate more vehicles to satisfy the constraint, so it ends up violating it.
Could you maybe point me in the right direction to start debugging this? (some code snippits and screenshot attached below)
Big thanks!
- Constraint:
protected Constraint unsafeDrivingBetween20And80Percent(ConstraintFactory factory) { return factory.forEach(Visit.class) .filter(Visit::isLoadInUnsafeRangeBeforeDriving) .penalize(HardSoftLongScore.ONE_HARD) .asConstraint("Unsafe load between 20% and 80%"); }
- Shadow Variable + extras:
@CascadingUpdateShadowVariable(targetMethodName = "updateVehicleLoad")
private Integer vehicleLoad;
@SuppressWarnings("unused")
private void updateVehicleLoad() {
if (vehicle == null) {
return;
}
int previousLoad = (previousVisit != null && previousVisit.getVehicleLoad() != null)
? previousVisit.getVehicleLoad()
: vehicle.getTotalDemand();
vehicleLoad = previousLoad - demand;
}
public Integer getVehicleLoad() {
return vehicleLoad;
}
public void setVehicleLoad(Integer vehicleLoad) {
this.vehicleLoad = vehicleLoad;
}
@CascadingUpdateShadowVariable(targetMethodName = "updateVehicleLoad")
private Integer vehicleLoad;
@SuppressWarnings("unused")
private void updateVehicleLoad() {
if (vehicle == null) {
return;
}
int previousLoad = (previousVisit != null && previousVisit.getVehicleLoad() != null)
? previousVisit.getVehicleLoad()
: vehicle.getTotalDemand();
vehicleLoad = previousLoad - demand;
}
public Integer getVehicleLoad() {
return vehicleLoad;
}
public void setVehicleLoad(Integer vehicleLoad) {
this.vehicleLoad = vehicleLoad;
}
@JsonIgnore
public boolean isLoadInUnsafeRangeBeforeDriving() {
if (vehicle == null) {
return false;
}
Integer loadBeforeDrive = previousVisit != null
? previousVisit.getVehicleLoad()
: (vehicle.getVisits() == null ? null
: vehicle.getTotalDemand());
if (loadBeforeDrive == null) {
return false;
}
double ratio = (double) loadBeforeDrive / vehicle.getCapacity();
return ratio > 0.2 && ratio < 0.8;
}
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi @93TV , could you please run the solver in FULL_ASSERT mode? The output states there is a corruption in your output above. The output would be helpful.
Beta Was this translation helpful? Give feedback.
All reactions
-
Besides what Tom is saying (score corruptions are bad and they indicate a bug somewhere), you should not expect the solver to activate any more vehicles. The solver will play with the vehicles you give it in your solution - it has no way of inventing more.
If it's absolutely necessary to have this functionality, typically you'd start with more vehicles than you think you need, and penalize every used vehicle enough so that it discourages the solver from using it if it can find a better solution.
Beta Was this translation helpful? Give feedback.
All reactions
-
@TomCools I've run the quickstart application with default configuration so far, I'll have a look at using the VehicleRoutingSolverConfig.xml right now, thanks. Keep you posted.
@triceo , I don't think I understand. The issue here is that I actually have 8 vehicles available and the solver only uses 3 even though there are 5 hard constraints that could be solved by using the other 5 vehicles. But maybe this has also something to do with the bug. Let me look into that!
Thank you both! :D
Beta Was this translation helpful? Give feedback.
All reactions
-
@TomCools , I feel a bit stupid asking this, but I can't find the xml config file the docs are talking about in the quickstart solution that I'm playing with. I've manually added one but my build keeps failing. Could I add this configuration (FULL_ASSERT) in the application.properties file somehow? Or set the quarkus log.category on level=DEBUG for the same result?
Beta Was this translation helpful? Give feedback.