Files
ortools-clone/ortools/sat/intervals.cc

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