@@ -5363,6 +5363,114 @@ void RoutingDimension::InitializeTransits(
5363
5363
}
5364
5364
}
5365
5365
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
+
5366
5474
void RoutingDimension::CloseModel (bool use_light_propagation) {
5367
5475
Solver* const solver = model_->solver ();
5368
5476
const auto capacity_lambda = [this ](int64 vehicle) {
@@ -5780,6 +5888,19 @@ void RoutingDimension::SetupGlobalSpanCost(
5780
5888
}
5781
5889
}
5782
5890
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
+
5783
5904
void RoutingDimension::SetupSlackAndDependentTransitCosts (
5784
5905
std::vector<IntVar*>* cost_elements) const {
5785
5906
if (model_->vehicles () == 0 ) return ;
0 commit comments