250 lines
9.4 KiB
C++
250 lines
9.4 KiB
C++
// Copyright 2010-2017 Google
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "ortools/sat/intervals.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "ortools/util/sort.h"
|
|
|
|
namespace operations_research {
|
|
namespace sat {
|
|
|
|
IntervalVariable IntervalsRepository::CreateInterval(IntegerVariable start,
|
|
IntegerVariable end,
|
|
IntegerVariable size,
|
|
IntegerValue fixed_size,
|
|
LiteralIndex is_present) {
|
|
// Create the interval.
|
|
const IntervalVariable i(start_vars_.size());
|
|
start_vars_.push_back(start);
|
|
end_vars_.push_back(end);
|
|
size_vars_.push_back(size);
|
|
fixed_sizes_.push_back(fixed_size);
|
|
is_present_.push_back(is_present);
|
|
|
|
std::vector<Literal> enforcement_literals;
|
|
if (is_present != kNoLiteralIndex) {
|
|
enforcement_literals.push_back(Literal(is_present));
|
|
}
|
|
|
|
// Link properly all its components.
|
|
precedences_->AddPrecedenceWithAllOptions(StartVar(i), EndVar(i), fixed_size,
|
|
SizeVar(i), enforcement_literals);
|
|
precedences_->AddPrecedenceWithAllOptions(EndVar(i), StartVar(i), -fixed_size,
|
|
SizeVar(i) == kNoIntegerVariable
|
|
? kNoIntegerVariable
|
|
: NegationOf(SizeVar(i)),
|
|
enforcement_literals);
|
|
return i;
|
|
}
|
|
|
|
SchedulingConstraintHelper::SchedulingConstraintHelper(
|
|
const std::vector<IntervalVariable>& tasks, Model* model)
|
|
: trail_(model->GetOrCreate<Trail>()),
|
|
integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
|
precedences_(model->GetOrCreate<PrecedencesPropagator>()),
|
|
current_time_direction_(true) {
|
|
auto* repository = model->GetOrCreate<IntervalsRepository>();
|
|
start_vars_.clear();
|
|
end_vars_.clear();
|
|
minus_end_vars_.clear();
|
|
minus_start_vars_.clear();
|
|
duration_vars_.clear();
|
|
fixed_durations_.clear();
|
|
reason_for_presence_.clear();
|
|
for (const IntervalVariable i : tasks) {
|
|
if (repository->IsOptional(i)) {
|
|
reason_for_presence_.push_back(repository->IsPresentLiteral(i).Index());
|
|
} else {
|
|
reason_for_presence_.push_back(kNoLiteralIndex);
|
|
}
|
|
if (repository->SizeVar(i) == kNoIntegerVariable) {
|
|
duration_vars_.push_back(kNoIntegerVariable);
|
|
fixed_durations_.push_back(repository->MinSize(i));
|
|
} else {
|
|
duration_vars_.push_back(repository->SizeVar(i));
|
|
fixed_durations_.push_back(IntegerValue(0));
|
|
}
|
|
start_vars_.push_back(repository->StartVar(i));
|
|
end_vars_.push_back(repository->EndVar(i));
|
|
minus_start_vars_.push_back(NegationOf(repository->StartVar(i)));
|
|
minus_end_vars_.push_back(NegationOf(repository->EndVar(i)));
|
|
}
|
|
|
|
const int num_tasks = start_vars_.size();
|
|
task_by_increasing_min_start_.resize(num_tasks);
|
|
task_by_increasing_min_end_.resize(num_tasks);
|
|
task_by_decreasing_max_start_.resize(num_tasks);
|
|
task_by_decreasing_max_end_.resize(num_tasks);
|
|
for (int t = 0; t < num_tasks; ++t) {
|
|
task_by_increasing_min_start_[t].task_index = t;
|
|
task_by_increasing_min_end_[t].task_index = t;
|
|
task_by_decreasing_max_start_[t].task_index = t;
|
|
task_by_decreasing_max_end_[t].task_index = t;
|
|
}
|
|
}
|
|
|
|
void SchedulingConstraintHelper::SetTimeDirection(bool is_forward) {
|
|
if (current_time_direction_ == is_forward) return;
|
|
current_time_direction_ = is_forward;
|
|
|
|
std::swap(start_vars_, minus_end_vars_);
|
|
std::swap(end_vars_, minus_start_vars_);
|
|
std::swap(task_by_increasing_min_start_, task_by_decreasing_max_end_);
|
|
std::swap(task_by_increasing_min_end_, task_by_decreasing_max_start_);
|
|
}
|
|
|
|
const std::vector<SchedulingConstraintHelper::TaskTime>&
|
|
SchedulingConstraintHelper::TaskByIncreasingStartMin() {
|
|
const int num_tasks = NumTasks();
|
|
for (int i = 0; i < num_tasks; ++i) {
|
|
TaskTime& ref = task_by_increasing_min_start_[i];
|
|
ref.time = StartMin(ref.task_index);
|
|
}
|
|
IncrementalSort(task_by_increasing_min_start_.begin(),
|
|
task_by_increasing_min_start_.end());
|
|
return task_by_increasing_min_start_;
|
|
}
|
|
|
|
const std::vector<SchedulingConstraintHelper::TaskTime>&
|
|
SchedulingConstraintHelper::TaskByIncreasingEndMin() {
|
|
const int num_tasks = NumTasks();
|
|
for (int i = 0; i < num_tasks; ++i) {
|
|
TaskTime& ref = task_by_increasing_min_end_[i];
|
|
ref.time = EndMin(ref.task_index);
|
|
}
|
|
IncrementalSort(task_by_increasing_min_end_.begin(),
|
|
task_by_increasing_min_end_.end());
|
|
return task_by_increasing_min_end_;
|
|
}
|
|
|
|
const std::vector<SchedulingConstraintHelper::TaskTime>&
|
|
SchedulingConstraintHelper::TaskByDecreasingStartMax() {
|
|
const int num_tasks = NumTasks();
|
|
for (int i = 0; i < num_tasks; ++i) {
|
|
TaskTime& ref = task_by_decreasing_max_start_[i];
|
|
ref.time = StartMax(ref.task_index);
|
|
}
|
|
IncrementalSort(task_by_decreasing_max_start_.begin(),
|
|
task_by_decreasing_max_start_.end(),
|
|
std::greater<TaskTime>());
|
|
return task_by_decreasing_max_start_;
|
|
}
|
|
|
|
const std::vector<SchedulingConstraintHelper::TaskTime>&
|
|
SchedulingConstraintHelper::TaskByDecreasingEndMax() {
|
|
const int num_tasks = NumTasks();
|
|
for (int i = 0; i < num_tasks; ++i) {
|
|
TaskTime& ref = task_by_decreasing_max_end_[i];
|
|
ref.time = EndMax(ref.task_index);
|
|
}
|
|
IncrementalSort(task_by_decreasing_max_end_.begin(),
|
|
task_by_decreasing_max_end_.end(), std::greater<TaskTime>());
|
|
return task_by_decreasing_max_end_;
|
|
}
|
|
|
|
// Produces a relaxed reason for StartMax(before) < EndMin(after).
|
|
void SchedulingConstraintHelper::AddReasonForBeingBefore(int before,
|
|
int after) {
|
|
DCHECK_LT(StartMax(before), EndMin(after));
|
|
const IntegerValue slack = EndMin(after) - StartMax(before) - 1;
|
|
std::vector<IntegerLiteral> temp;
|
|
temp.push_back(integer_trail_->LowerBoundAsLiteral(end_vars_[after]));
|
|
temp.push_back(
|
|
integer_trail_->LowerBoundAsLiteral(NegationOf(start_vars_[before])));
|
|
integer_trail_->RelaxLinearReason(slack, {IntegerValue(1), IntegerValue(1)},
|
|
&temp);
|
|
integer_reason_.insert(integer_reason_.end(), temp.begin(), temp.end());
|
|
}
|
|
|
|
bool SchedulingConstraintHelper::PushIntegerLiteral(IntegerLiteral bound) {
|
|
return integer_trail_->Enqueue(bound, literal_reason_, integer_reason_);
|
|
}
|
|
|
|
bool SchedulingConstraintHelper::PushIntervalBound(int t, IntegerLiteral lit) {
|
|
if (IsAbsent(t)) return true;
|
|
|
|
// TODO(user): we can also push lit.var if its presence implies the interval
|
|
// presence.
|
|
if (IsOptional(t) && integer_trail_->OptionalLiteralIndex(lit.var) !=
|
|
reason_for_presence_[t]) {
|
|
if (IsPresent(t)) {
|
|
// We can still push, but we do need the presence reason.
|
|
AddPresenceReason(t);
|
|
} else {
|
|
// In this case we cannot push lit.var, but we may detect the interval
|
|
// absence.
|
|
if (lit.bound > integer_trail_->UpperBound(lit.var)) {
|
|
integer_reason_.push_back(
|
|
IntegerLiteral::LowerOrEqual(lit.var, lit.bound - 1));
|
|
if (!PushTaskAbsence(t)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!integer_trail_->Enqueue(lit, literal_reason_, integer_reason_)) {
|
|
return false;
|
|
}
|
|
|
|
if (IsAbsent(t)) return true;
|
|
return precedences_->PropagateOutgoingArcs(lit.var);
|
|
}
|
|
|
|
bool SchedulingConstraintHelper::IncreaseStartMin(int t,
|
|
IntegerValue new_min_start) {
|
|
return PushIntervalBound(
|
|
t, IntegerLiteral::GreaterOrEqual(start_vars_[t], new_min_start));
|
|
}
|
|
|
|
bool SchedulingConstraintHelper::DecreaseEndMax(int t,
|
|
IntegerValue new_max_end) {
|
|
return PushIntervalBound(
|
|
t, IntegerLiteral::LowerOrEqual(end_vars_[t], new_max_end));
|
|
}
|
|
|
|
bool SchedulingConstraintHelper::PushTaskAbsence(int t) {
|
|
DCHECK_NE(reason_for_presence_[t], kNoLiteralIndex);
|
|
DCHECK(!IsAbsent(t));
|
|
if (IsPresent(t)) {
|
|
literal_reason_.push_back(Literal(reason_for_presence_[t]).Negated());
|
|
return ReportConflict();
|
|
}
|
|
integer_trail_->EnqueueLiteral(Literal(reason_for_presence_[t]).Negated(),
|
|
literal_reason_, integer_reason_);
|
|
return true;
|
|
}
|
|
|
|
bool SchedulingConstraintHelper::ReportConflict() {
|
|
return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
|
|
}
|
|
|
|
void SchedulingConstraintHelper::WatchAllTasks(
|
|
int id, GenericLiteralWatcher* watcher) const {
|
|
const int num_tasks = start_vars_.size();
|
|
for (int t = 0; t < num_tasks; ++t) {
|
|
watcher->WatchIntegerVariable(start_vars_[t], id);
|
|
watcher->WatchIntegerVariable(end_vars_[t], id);
|
|
if (duration_vars_[t] != kNoIntegerVariable) {
|
|
watcher->WatchLowerBound(duration_vars_[t], id);
|
|
}
|
|
if (!IsPresent(t) && !IsAbsent(t)) {
|
|
watcher->WatchLiteral(Literal(reason_for_presence_[t]), id);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace sat
|
|
} // namespace operations_research
|