Skip to content

Commit a549c1e

Browse files
committed
add break API on the routing library
1 parent 8bf8471 commit a549c1e

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

src/constraint_solver/routing.cc

+121
Original file line numberDiff line numberDiff line change
@@ -5363,6 +5363,114 @@ void RoutingDimension::InitializeTransits(
53635363
}
53645364
}
53655365

5366+
namespace {
5367+
5368+
// Break constraint ensures break intervals fit on the route of a vehicle.
5369+
// It posts a disjunction constraint on break intervals + intervals
5370+
// corresponding to route nodes. For each node, |break_intervals|+1 intervals
5371+
// are created representing the fixed transit after the node; the transit
5372+
// can therefore be interrupted at most |break_intervals|+1 times. The
5373+
// constraint ensures that the sum of the duration of the "node" intervals
5374+
// is at least the value of the fixed transit after the node.
5375+
class BreakConstraint : public Constraint {
5376+
public:
5377+
BreakConstraint(const RoutingDimension* dimension, int vehicle,
5378+
std::vector<IntervalVar*> break_intervals)
5379+
: Constraint(dimension->model()->solver()),
5380+
dimension_(dimension),
5381+
vehicle_(vehicle),
5382+
break_intervals_(std::move(break_intervals)),
5383+
status_(solver()->MakeBoolVar(StrCat("status", vehicle))) {}
5384+
void Post() override {
5385+
RoutingModel* const model = dimension_->model();
5386+
solver()->AddConstraint(
5387+
solver()->MakePathConnected(model->Nexts(), {model->Start(vehicle_)},
5388+
{model->End(vehicle_)}, {status_}));
5389+
status_->WhenBound(MakeDelayedConstraintDemon0(
5390+
solver(), this, &BreakConstraint::PathClosed, "PathClosed"));
5391+
}
5392+
void InitialPropagate() override {
5393+
if (status_->Bound()) {
5394+
PathClosed();
5395+
}
5396+
}
5397+
5398+
private:
5399+
void PathClosed() {
5400+
// TODO(user): Make sure that 0 duration intervals are pushed at the end
5401+
// of the list.
5402+
if (status_->Max() == 0) {
5403+
for (IntervalVar* const break_interval : break_intervals_) {
5404+
break_interval->SetPerformed(false);
5405+
}
5406+
} else {
5407+
RoutingModel* const model = dimension_->model();
5408+
int64 current = model->Start(vehicle_);
5409+
std::vector<IntervalVar*> vehicle_intervals = break_intervals_;
5410+
while (!model->IsEnd(current)) {
5411+
const int next = model->NextVar(current)->Min();
5412+
std::vector<IntervalVar*> transit_intervals;
5413+
IntervalVar* last = nullptr;
5414+
for (int i = 0; i <= break_intervals_.size(); ++i) {
5415+
IntervalVar* const interval = solver()->MakeIntervalVar(
5416+
dimension_->CumulVar(current)->Min(),
5417+
dimension_->CumulVar(next)->Max(), 0,
5418+
dimension_->FixedTransitVar(current)->Value(), 0, kint64max,
5419+
false, StrCat(current, "-", i));
5420+
transit_intervals.push_back(interval);
5421+
vehicle_intervals.push_back(interval);
5422+
// Order transit intervals to cut symmetries.
5423+
if (last != nullptr) {
5424+
solver()->AddConstraint(solver()->MakeIntervalVarRelation(
5425+
interval, Solver::STARTS_AFTER_END, last));
5426+
last = interval;
5427+
}
5428+
}
5429+
std::vector<IntVar*> durations(transit_intervals.size());
5430+
for (int i = 0; i < transit_intervals.size(); ++i) {
5431+
durations[i] = transit_intervals[i]->DurationExpr()->Var();
5432+
if (i == 0) {
5433+
solver()->AddConstraint(
5434+
solver()->MakeEquality(transit_intervals[i]->StartExpr(),
5435+
dimension_->CumulVar(current)));
5436+
} else {
5437+
solver()->AddConstraint(
5438+
solver()->MakeGreaterOrEqual(transit_intervals[i]->StartExpr(),
5439+
dimension_->CumulVar(current)));
5440+
}
5441+
if (i == break_intervals_.size()) {
5442+
solver()->AddConstraint(solver()->MakeEquality(
5443+
dimension_->CumulVar(next), transit_intervals[i]->EndExpr()));
5444+
} else {
5445+
solver()->AddConstraint(solver()->MakeGreaterOrEqual(
5446+
dimension_->CumulVar(next), transit_intervals[i]->EndExpr()));
5447+
}
5448+
}
5449+
solver()->AddConstraint(solver()->MakeGreaterOrEqual(
5450+
solver()->MakeSum(durations),
5451+
dimension_->FixedTransitVar(current)->Value()));
5452+
current = next;
5453+
}
5454+
solver()->AddConstraint(solver()->MakeStrictDisjunctiveConstraint(
5455+
vehicle_intervals, StrCat("Vehicle breaks ", vehicle_)));
5456+
}
5457+
}
5458+
5459+
const RoutingDimension* const dimension_;
5460+
const int vehicle_;
5461+
const std::vector<IntervalVar*> break_intervals_;
5462+
IntVar* const status_;
5463+
};
5464+
5465+
Constraint* MakeBreakConstraint(const RoutingDimension* dimension, int vehicle,
5466+
std::vector<IntervalVar*> break_intervals) {
5467+
Solver* const solver = dimension->model()->solver();
5468+
return solver->RevAlloc(
5469+
new BreakConstraint(dimension, vehicle, std::move(break_intervals)));
5470+
}
5471+
5472+
} // namespace
5473+
53665474
void RoutingDimension::CloseModel(bool use_light_propagation) {
53675475
Solver* const solver = model_->solver();
53685476
const auto capacity_lambda = [this](int64 vehicle) {
@@ -5780,6 +5888,19 @@ void RoutingDimension::SetupGlobalSpanCost(
57805888
}
57815889
}
57825890

5891+
void RoutingDimension::SetBreakIntervalsOfVehicle(
5892+
std::vector<IntervalVar*> breaks, int vehicle) {
5893+
if (!breaks.empty()) {
5894+
for (IntervalVar* const interval : breaks) {
5895+
model_->AddIntervalToAssignment(interval);
5896+
model_->AddVariableMinimizedByFinalizer(
5897+
interval->SafeStartExpr(0)->Var());
5898+
}
5899+
model_->solver()->AddConstraint(
5900+
MakeBreakConstraint(this, vehicle, std::move(breaks)));
5901+
}
5902+
}
5903+
57835904
void RoutingDimension::SetupSlackAndDependentTransitCosts(
57845905
std::vector<IntVar*>* cost_elements) const {
57855906
if (model_->vehicles() == 0) return;

src/constraint_solver/routing.h

+7
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,13 @@ class RoutingDimension {
15231523
// Returns the cost coefficient of the soft lower bound of a cumul variable
15241524
// for a variable index. If no soft lower bound has been set, 0 is returned.
15251525
int64 GetCumulVarSoftLowerBoundCoefficientFromIndex(int64 index) const;
1526+
// Sets the breaks for a given vehicle. Breaks are represented by
1527+
// IntervalVars. They may interrupt transits between nodes and increase
1528+
// the value of corresponding slack variables. However an interval cannot
1529+
// overlap the cumul variable of a node (the interval must either be before
1530+
// or after the node).
1531+
void SetBreakIntervalsOfVehicle(std::vector<IntervalVar*> breaks,
1532+
int vehicle);
15261533
// Returns the parent in the dependency tree if any or nullptr otherwise.
15271534
const RoutingDimension* base_dimension() const { return base_dimension_; }
15281535
// It makes sense to use the function only for self-dependent dimension.

0 commit comments

Comments
 (0)