Files
ortools-clone/ortools/constraint_solver/sched_search.cc

887 lines
28 KiB
C++
Raw Permalink Normal View History

2025-01-10 11:35:44 +01:00
// Copyright 2010-2025 Google LLC
2010-09-15 12:42:33 +00:00
// 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.
2022-05-18 16:36:05 +02:00
#include <algorithm>
2021-04-01 12:13:35 +02:00
#include <cstdint>
#include <cstring>
2021-04-01 12:13:35 +02:00
#include <limits>
2011-09-21 15:16:48 +00:00
#include <string>
#include <utility>
2011-09-21 15:16:48 +00:00
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "absl/log/check.h"
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
#include "absl/strings/str_format.h"
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/util/string_array.h"
2010-09-15 12:42:33 +00:00
namespace operations_research {
namespace {
2021-04-01 12:13:35 +02:00
int64_t ValueToIndex(int64_t value) { return value - 1; }
2021-04-01 12:13:35 +02:00
int64_t IndexToValue(int64_t index) { return index + 1; }
2020-10-22 23:36:58 +02:00
} // namespace
2010-09-15 12:42:33 +00:00
// ----- SequenceVar -----
// TODO(user): Add better class invariants, in particular checks
// that ranked_first, ranked_last, and unperformed are truly disjoint.
2020-10-29 14:25:39 +01:00
SequenceVar::SequenceVar(Solver* const s,
const std::vector<IntervalVar*>& intervals,
const std::vector<IntVar*>& nexts,
const std::string& name)
2020-10-22 23:36:58 +02:00
: PropagationBaseObject(s),
intervals_(intervals),
nexts_(nexts),
previous_(nexts.size() + 1, -1) {
set_name(name);
}
SequenceVar::~SequenceVar() {}
2020-10-29 14:25:39 +01:00
IntervalVar* SequenceVar::Interval(int index) const {
return intervals_[index];
}
2020-10-29 14:25:39 +01:00
IntVar* SequenceVar::Next(int index) const { return nexts_[index]; }
std::string SequenceVar::DebugString() const {
2021-04-01 12:13:35 +02:00
int64_t hmin, hmax, dmin, dmax;
HorizonRange(&hmin, &hmax);
DurationRange(&dmin, &dmax);
int unperformed = 0;
int ranked = 0;
int not_ranked = 0;
ComputeStatistics(&ranked, &not_ranked, &unperformed);
return absl::StrFormat(
"%s(horizon = %d..%d, duration = %d..%d, not ranked = %d, ranked = %d, "
"nexts = [%s])",
name(), hmin, hmax, dmin, dmax, not_ranked, ranked,
JoinDebugStringPtr(nexts_, ", "));
}
2020-10-29 14:25:39 +01:00
void SequenceVar::Accept(ModelVisitor* const visitor) const {
visitor->VisitSequenceVariable(this);
}
2021-04-01 12:13:35 +02:00
void SequenceVar::DurationRange(int64_t* const dmin,
int64_t* const dmax) const {
int64_t dur_min = 0;
int64_t dur_max = 0;
for (int i = 0; i < intervals_.size(); ++i) {
2020-10-29 14:25:39 +01:00
IntervalVar* const t = intervals_[i];
if (t->MayBePerformed()) {
if (t->MustBePerformed()) {
dur_min += t->DurationMin();
}
dur_max += t->DurationMax();
}
}
*dmin = dur_min;
*dmax = dur_max;
}
2021-04-01 12:13:35 +02:00
void SequenceVar::HorizonRange(int64_t* const hmin, int64_t* const hmax) const {
int64_t hor_min = std::numeric_limits<int64_t>::max();
int64_t hor_max = std::numeric_limits<int64_t>::min();
for (int i = 0; i < intervals_.size(); ++i) {
2020-10-29 14:25:39 +01:00
IntervalVar* const t = intervals_[i];
if (t->MayBePerformed()) {
2020-10-29 14:25:39 +01:00
IntervalVar* const t = intervals_[i];
hor_min = std::min(hor_min, t->StartMin());
2012-07-24 00:22:05 +00:00
hor_max = std::max(hor_max, t->EndMax());
}
}
*hmin = hor_min;
*hmax = hor_max;
}
2021-04-01 12:13:35 +02:00
void SequenceVar::ActiveHorizonRange(int64_t* const hmin,
int64_t* const hmax) const {
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
absl::flat_hash_set<int> decided;
for (int i = 0; i < intervals_.size(); ++i) {
if (intervals_[i]->CannotBePerformed()) {
decided.insert(i);
}
}
int first = 0;
while (nexts_[first]->Bound()) {
first = nexts_[first]->Min();
if (first < nexts_.size()) {
decided.insert(ValueToIndex(first));
} else {
break;
}
}
if (first != nexts_.size()) {
UpdatePrevious();
int last = nexts_.size();
while (previous_[last] != -1) {
last = previous_[last];
decided.insert(ValueToIndex(last));
}
}
2021-04-01 12:13:35 +02:00
int64_t hor_min = std::numeric_limits<int64_t>::max();
int64_t hor_max = std::numeric_limits<int64_t>::min();
for (int i = 0; i < intervals_.size(); ++i) {
if (!decided.contains(i)) {
2020-10-29 14:25:39 +01:00
IntervalVar* const t = intervals_[i];
hor_min = std::min(hor_min, t->StartMin());
2012-07-24 00:22:05 +00:00
hor_max = std::max(hor_max, t->EndMax());
}
}
*hmin = hor_min;
*hmax = hor_max;
}
2020-10-29 14:25:39 +01:00
void SequenceVar::ComputeStatistics(int* const ranked, int* const not_ranked,
int* const unperformed) const {
*unperformed = 0;
for (int i = 0; i < intervals_.size(); ++i) {
if (intervals_[i]->CannotBePerformed()) {
(*unperformed)++;
}
}
*ranked = 0;
int first = 0;
while (first < nexts_.size() && nexts_[first]->Bound()) {
first = nexts_[first]->Min();
(*ranked)++;
}
if (first != nexts_.size()) {
UpdatePrevious();
int last = nexts_.size();
while (previous_[last] != -1) {
last = previous_[last];
(*ranked)++;
}
2020-10-22 23:36:58 +02:00
} else { // We counted the sentinel.
(*ranked)--;
}
*not_ranked = intervals_.size() - *ranked - *unperformed;
}
int SequenceVar::ComputeForwardFrontier() {
int first = 0;
while (first != nexts_.size() && nexts_[first]->Bound()) {
first = nexts_[first]->Min();
}
return first;
}
int SequenceVar::ComputeBackwardFrontier() {
UpdatePrevious();
int last = nexts_.size();
while (previous_[last] != -1) {
last = previous_[last];
}
return last;
}
void SequenceVar::ComputePossibleFirstsAndLasts(
2020-10-29 14:25:39 +01:00
std::vector<int>* const possible_firsts,
std::vector<int>* const possible_lasts) {
possible_firsts->clear();
possible_lasts->clear();
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
absl::flat_hash_set<int> to_check;
for (int i = 0; i < intervals_.size(); ++i) {
2012-07-25 00:37:43 +00:00
if (intervals_[i]->MayBePerformed()) {
to_check.insert(i);
}
}
int first = 0;
while (nexts_[first]->Bound()) {
first = nexts_[first]->Min();
if (first == nexts_.size()) {
2012-07-25 00:37:43 +00:00
return;
}
to_check.erase(ValueToIndex(first));
2012-07-25 00:37:43 +00:00
}
2020-10-29 14:25:39 +01:00
IntVar* const forward_var = nexts_[first];
2012-07-24 00:22:05 +00:00
std::vector<int> candidates;
2021-04-01 12:13:35 +02:00
int64_t smallest_start_max = std::numeric_limits<int64_t>::max();
2012-07-24 00:22:05 +00:00
int ssm_support = -1;
2021-04-01 12:13:35 +02:00
for (int64_t i = forward_var->Min(); i <= forward_var->Max(); ++i) {
2012-09-11 15:57:55 +00:00
// TODO(user): use domain iterator.
if (i != 0 && i < IndexToValue(intervals_.size()) &&
intervals_[ValueToIndex(i)]->MayBePerformed() &&
forward_var->Contains(i)) {
const int candidate = ValueToIndex(i);
2012-07-24 00:22:05 +00:00
candidates.push_back(candidate);
if (intervals_[candidate]->MustBePerformed()) {
if (smallest_start_max > intervals_[candidate]->StartMax()) {
smallest_start_max = intervals_[candidate]->StartMax();
ssm_support = candidate;
}
}
}
}
2012-07-24 00:22:05 +00:00
for (int i = 0; i < candidates.size(); ++i) {
const int candidate = candidates[i];
if (candidate == ssm_support ||
intervals_[candidate]->EndMin() <= smallest_start_max) {
possible_firsts->push_back(candidate);
}
}
2012-07-25 00:37:43 +00:00
UpdatePrevious();
int last = nexts_.size();
2012-07-25 00:37:43 +00:00
while (previous_[last] != -1) {
last = previous_[last];
to_check.erase(ValueToIndex(last));
2012-07-25 00:37:43 +00:00
}
candidates.clear();
2021-04-01 12:13:35 +02:00
int64_t biggest_end_min = std::numeric_limits<int64_t>::min();
2012-07-25 00:37:43 +00:00
int bem_support = -1;
for (const int candidate : to_check) {
if (nexts_[IndexToValue(candidate)]->Contains(last)) {
2012-07-25 00:37:43 +00:00
candidates.push_back(candidate);
if (intervals_[candidate]->MustBePerformed()) {
if (biggest_end_min < intervals_[candidate]->EndMin()) {
biggest_end_min = intervals_[candidate]->EndMin();
bem_support = candidate;
}
}
}
}
for (int i = 0; i < candidates.size(); ++i) {
const int candidate = candidates[i];
if (candidate == bem_support ||
intervals_[candidate]->StartMax() >= biggest_end_min) {
possible_lasts->push_back(candidate);
}
}
}
2020-10-29 14:25:39 +01:00
void SequenceVar::RankSequence(const std::vector<int>& rank_first,
const std::vector<int>& rank_last,
const std::vector<int>& unperformed) {
2020-10-22 23:36:58 +02:00
solver()->GetPropagationMonitor()->RankSequence(this, rank_first, rank_last,
unperformed);
// Mark unperformed.
for (const int value : unperformed) {
intervals_[value]->SetPerformed(false);
}
// Forward.
int forward = 0;
for (int i = 0; i < rank_first.size(); ++i) {
const int next = 1 + rank_first[i];
nexts_[forward]->SetValue(next);
forward = next;
}
2012-07-24 23:06:40 +00:00
// Backward.
int backward = IndexToValue(intervals_.size());
2012-07-24 23:06:40 +00:00
for (int i = 0; i < rank_last.size(); ++i) {
const int next = 1 + rank_last[i];
nexts_[next]->SetValue(backward);
backward = next;
}
}
void SequenceVar::RankFirst(int index) {
solver()->GetPropagationMonitor()->RankFirst(this, index);
2012-07-24 22:32:50 +00:00
intervals_[index]->SetPerformed(true);
int forward_frontier = 0;
while (forward_frontier != nexts_.size() &&
nexts_[forward_frontier]->Bound()) {
forward_frontier = nexts_[forward_frontier]->Min();
if (forward_frontier == IndexToValue(index)) {
return;
}
}
DCHECK_LT(forward_frontier, nexts_.size());
nexts_[forward_frontier]->SetValue(IndexToValue(index));
}
void SequenceVar::RankNotFirst(int index) {
solver()->GetPropagationMonitor()->RankNotFirst(this, index);
const int forward_frontier = ComputeForwardFrontier();
if (forward_frontier < nexts_.size()) {
nexts_[forward_frontier]->RemoveValue(IndexToValue(index));
2012-07-24 00:22:05 +00:00
}
}
void SequenceVar::RankLast(int index) {
solver()->GetPropagationMonitor()->RankLast(this, index);
2012-07-24 22:32:50 +00:00
intervals_[index]->SetPerformed(true);
UpdatePrevious();
int backward_frontier = nexts_.size();
while (previous_[backward_frontier] != -1) {
backward_frontier = previous_[backward_frontier];
if (backward_frontier == IndexToValue(index)) {
return;
}
}
DCHECK_NE(backward_frontier, 0);
nexts_[IndexToValue(index)]->SetValue(backward_frontier);
}
void SequenceVar::RankNotLast(int index) {
solver()->GetPropagationMonitor()->RankNotLast(this, index);
const int backward_frontier = ComputeBackwardFrontier();
nexts_[IndexToValue(index)]->RemoveValue(backward_frontier);
}
void SequenceVar::UpdatePrevious() const {
for (int i = 0; i < intervals_.size() + 2; ++i) {
previous_[i] = -1;
}
for (int i = 0; i < nexts_.size(); ++i) {
if (nexts_[i]->Bound()) {
previous_[nexts_[i]->Min()] = i;
}
}
}
2020-10-29 14:25:39 +01:00
void SequenceVar::FillSequence(std::vector<int>* const rank_first,
std::vector<int>* const rank_last,
std::vector<int>* const unperformed) const {
CHECK(rank_first != nullptr);
CHECK(rank_last != nullptr);
CHECK(unperformed != nullptr);
rank_first->clear();
rank_last->clear();
unperformed->clear();
for (int i = 0; i < intervals_.size(); ++i) {
if (intervals_[i]->CannotBePerformed()) {
unperformed->push_back(i);
}
}
int first = 0;
while (nexts_[first]->Bound()) {
first = nexts_[first]->Min();
if (first < nexts_.size()) {
rank_first->push_back(ValueToIndex(first));
} else {
break;
}
}
if (first != nexts_.size()) {
UpdatePrevious();
int last = nexts_.size();
while (previous_[last] != -1) {
last = previous_[last];
rank_last->push_back(ValueToIndex(last));
}
}
}
2010-09-15 12:42:33 +00:00
// ----- Decisions and DecisionBuilders on interval vars -----
// TODO(user) : treat optional intervals
// TODO(user) : Call DecisionVisitor and pass name of variable
namespace {
//
// Forward scheduling.
//
2010-09-15 12:42:33 +00:00
class ScheduleOrPostpone : public Decision {
2020-10-22 23:36:58 +02:00
public:
2021-04-01 12:13:35 +02:00
ScheduleOrPostpone(IntervalVar* const var, int64_t est, int64_t* const marker)
2010-09-15 12:42:33 +00:00
: var_(var), est_(est), marker_(marker) {}
~ScheduleOrPostpone() override {}
2010-09-15 12:42:33 +00:00
2020-10-29 14:25:39 +01:00
void Apply(Solver* const s) override {
2010-09-15 12:42:33 +00:00
var_->SetPerformed(true);
2013-06-11 14:49:19 +00:00
if (est_.Value() < var_->StartMin()) {
est_.SetValue(s, var_->StartMin());
}
var_->SetStartRange(est_.Value(), est_.Value());
2010-09-15 12:42:33 +00:00
}
2020-10-29 14:25:39 +01:00
void Refute(Solver* const s) override {
2016-01-04 14:17:41 +01:00
s->SaveAndSetValue(marker_, est_.Value());
2010-09-15 12:42:33 +00:00
}
2020-10-29 14:25:39 +01:00
void Accept(DecisionVisitor* const visitor) const override {
CHECK(visitor != nullptr);
2013-06-11 14:49:19 +00:00
visitor->VisitScheduleOrPostpone(var_, est_.Value());
}
std::string DebugString() const override {
return absl::StrFormat("ScheduleOrPostpone(%s at %d)", var_->DebugString(),
est_.Value());
2010-09-15 12:42:33 +00:00
}
2020-10-22 23:36:58 +02:00
private:
2020-10-29 14:25:39 +01:00
IntervalVar* const var_;
2021-04-01 12:13:35 +02:00
NumericalRev<int64_t> est_;
int64_t* const marker_;
2010-09-15 12:42:33 +00:00
};
class SetTimesForward : public DecisionBuilder {
2020-10-22 23:36:58 +02:00
public:
2020-10-29 14:25:39 +01:00
explicit SetTimesForward(const std::vector<IntervalVar*>& vars)
2021-04-01 12:13:35 +02:00
: vars_(vars),
markers_(vars.size(), std::numeric_limits<int64_t>::min()) {}
2010-09-15 12:42:33 +00:00
~SetTimesForward() override {}
2010-09-15 12:42:33 +00:00
2020-10-29 14:25:39 +01:00
Decision* Next(Solver* const s) override {
2021-04-01 12:13:35 +02:00
int64_t best_est = std::numeric_limits<int64_t>::max();
int64_t best_lct = std::numeric_limits<int64_t>::max();
2010-09-15 12:42:33 +00:00
int support = -1;
2016-01-04 15:57:23 +01:00
// We are looking for the interval that has the smallest start min
// (tie break with smallest end max) and is not postponed. And
// you're going to schedule that interval at its start min.
for (int i = 0; i < vars_.size(); ++i) {
2020-10-29 14:25:39 +01:00
IntervalVar* const v = vars_[i];
2016-01-04 15:57:23 +01:00
if (v->MayBePerformed() && v->StartMax() != v->StartMin() &&
!IsPostponed(i) &&
(v->StartMin() < best_est ||
(v->StartMin() == best_est && v->EndMax() < best_lct))) {
best_est = v->StartMin();
best_lct = v->EndMax();
support = i;
2010-09-15 12:42:33 +00:00
}
}
// TODO(user) : remove this crude quadratic loop with
// reversibles range reduction.
2020-10-22 23:36:58 +02:00
if (support == -1) { // All intervals are either fixed or postponed.
2021-04-01 12:13:35 +02:00
UnperformPostponedTaskBefore(std::numeric_limits<int64_t>::max());
2016-01-04 14:17:41 +01:00
return nullptr;
}
UnperformPostponedTaskBefore(best_est);
return s->RevAlloc(
new ScheduleOrPostpone(vars_[support], best_est, &markers_[support]));
}
std::string DebugString() const override { return "SetTimesForward()"; }
2020-10-29 14:25:39 +01:00
void Accept(ModelVisitor* const visitor) const override {
visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
vars_);
visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
}
2020-10-22 23:36:58 +02:00
private:
2016-01-04 15:57:23 +01:00
bool IsPostponed(int index) {
DCHECK(vars_[index]->MayBePerformed());
return vars_[index]->StartMin() <= markers_[index];
}
2021-04-01 12:13:35 +02:00
void UnperformPostponedTaskBefore(int64_t date) {
2016-01-04 15:57:23 +01:00
for (int i = 0; i < vars_.size(); ++i) {
2020-10-29 14:25:39 +01:00
IntervalVar* const v = vars_[i];
2016-01-04 15:57:23 +01:00
if (v->MayBePerformed() && v->StartMin() != v->StartMax() &&
IsPostponed(i) &&
// There are two rules here:
// - v->StartMax() <= date: the interval should have been scheduled
// as it cannot be scheduled later (assignment is chronological).
// - v->EndMin() <= date: The interval can fit before the current
// start date. In that case, it 'should' always fit, and as it has
// not be scheduled, then we are missing it. So, as a dominance
// rule, it should be marked as unperformed.
(v->EndMin() <= date || v->StartMax() <= date)) {
v->SetPerformed(false);
}
}
}
2020-10-29 14:25:39 +01:00
const std::vector<IntervalVar*> vars_;
2021-04-01 12:13:35 +02:00
std::vector<int64_t> markers_;
2010-09-15 12:42:33 +00:00
};
//
// Backward scheduling.
//
class ScheduleOrExpedite : public Decision {
2020-10-22 23:36:58 +02:00
public:
2021-04-01 12:13:35 +02:00
ScheduleOrExpedite(IntervalVar* const var, int64_t est, int64_t* const marker)
: var_(var), est_(est), marker_(marker) {}
~ScheduleOrExpedite() override {}
2020-10-29 14:25:39 +01:00
void Apply(Solver* const s) override {
var_->SetPerformed(true);
if (est_.Value() > var_->EndMax()) {
est_.SetValue(s, var_->EndMax());
}
var_->SetEndRange(est_.Value(), est_.Value());
}
2020-10-29 14:25:39 +01:00
void Refute(Solver* const s) override {
s->SaveAndSetValue(marker_, est_.Value() - 1);
}
2020-10-29 14:25:39 +01:00
void Accept(DecisionVisitor* const visitor) const override {
CHECK(visitor != nullptr);
visitor->VisitScheduleOrExpedite(var_, est_.Value());
}
std::string DebugString() const override {
return absl::StrFormat("ScheduleOrExpedite(%s at %d)", var_->DebugString(),
est_.Value());
}
2020-10-22 23:36:58 +02:00
private:
2020-10-29 14:25:39 +01:00
IntervalVar* const var_;
2021-04-01 12:13:35 +02:00
NumericalRev<int64_t> est_;
int64_t* const marker_;
};
class SetTimesBackward : public DecisionBuilder {
2020-10-22 23:36:58 +02:00
public:
2020-10-29 14:25:39 +01:00
explicit SetTimesBackward(const std::vector<IntervalVar*>& vars)
2021-04-01 12:13:35 +02:00
: vars_(vars),
markers_(vars.size(), std::numeric_limits<int64_t>::max()) {}
~SetTimesBackward() override {}
2020-10-29 14:25:39 +01:00
Decision* Next(Solver* const s) override {
2021-04-01 12:13:35 +02:00
int64_t best_end = std::numeric_limits<int64_t>::min();
int64_t best_start = std::numeric_limits<int64_t>::min();
int support = -1;
int refuted = 0;
for (int i = 0; i < vars_.size(); ++i) {
2020-10-29 14:25:39 +01:00
IntervalVar* const v = vars_[i];
if (v->MayBePerformed() && v->EndMax() > v->EndMin()) {
if (v->EndMax() <= markers_[i] &&
(v->EndMax() > best_end ||
(v->EndMax() == best_end && v->StartMin() > best_start))) {
best_end = v->EndMax();
best_start = v->StartMin();
support = i;
} else {
refuted++;
}
}
}
// TODO(user) : remove this crude quadratic loop with
// reversibles range reduction.
if (support == -1) {
if (refuted == 0) {
return nullptr;
} else {
s->Fail();
}
}
return s->RevAlloc(new ScheduleOrExpedite(
vars_[support], vars_[support]->EndMax(), &markers_[support]));
}
std::string DebugString() const override { return "SetTimesBackward()"; }
2020-10-29 14:25:39 +01:00
void Accept(ModelVisitor* const visitor) const override {
visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
vars_);
visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
}
2020-10-22 23:36:58 +02:00
private:
2020-10-29 14:25:39 +01:00
const std::vector<IntervalVar*> vars_;
2021-04-01 12:13:35 +02:00
std::vector<int64_t> markers_;
};
2010-09-15 12:42:33 +00:00
// ----- Decisions and DecisionBuilders on sequences -----
class RankFirst : public Decision {
2020-10-22 23:36:58 +02:00
public:
2020-10-29 14:25:39 +01:00
RankFirst(SequenceVar* const seq, int index)
2010-09-15 12:42:33 +00:00
: sequence_(seq), index_(index) {}
~RankFirst() override {}
2010-09-15 12:42:33 +00:00
2020-10-29 14:25:39 +01:00
void Apply(Solver* const s) override { sequence_->RankFirst(index_); }
2010-09-15 12:42:33 +00:00
2020-10-29 14:25:39 +01:00
void Refute(Solver* const s) override { sequence_->RankNotFirst(index_); }
2010-09-15 12:42:33 +00:00
2020-10-29 14:25:39 +01:00
void Accept(DecisionVisitor* const visitor) const override {
CHECK(visitor != nullptr);
visitor->VisitRankFirstInterval(sequence_, index_);
}
std::string DebugString() const override {
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
return absl::StrFormat("RankFirst(%s, %d)", sequence_->DebugString(),
index_);
2010-09-15 12:42:33 +00:00
}
2020-10-22 23:36:58 +02:00
private:
2020-10-29 14:25:39 +01:00
SequenceVar* const sequence_;
2010-09-15 12:42:33 +00:00
const int index_;
};
class RankLast : public Decision {
2020-10-22 23:36:58 +02:00
public:
2020-10-29 14:25:39 +01:00
RankLast(SequenceVar* const seq, int index) : sequence_(seq), index_(index) {}
~RankLast() override {}
2020-10-29 14:25:39 +01:00
void Apply(Solver* const s) override { sequence_->RankLast(index_); }
2020-10-29 14:25:39 +01:00
void Refute(Solver* const s) override { sequence_->RankNotLast(index_); }
2020-10-29 14:25:39 +01:00
void Accept(DecisionVisitor* const visitor) const override {
CHECK(visitor != nullptr);
visitor->VisitRankLastInterval(sequence_, index_);
}
std::string DebugString() const override {
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
return absl::StrFormat("RankLast(%s, %d)", sequence_->DebugString(),
index_);
}
2020-10-22 23:36:58 +02:00
private:
2020-10-29 14:25:39 +01:00
SequenceVar* const sequence_;
const int index_;
};
class RankFirstIntervalVars : public DecisionBuilder {
2020-10-22 23:36:58 +02:00
public:
2020-10-29 14:25:39 +01:00
RankFirstIntervalVars(const std::vector<SequenceVar*>& sequences,
Solver::SequenceStrategy str)
: sequences_(sequences), strategy_(str) {}
~RankFirstIntervalVars() override {}
2010-09-15 12:42:33 +00:00
2020-10-29 14:25:39 +01:00
Decision* Next(Solver* const s) override {
SequenceVar* best_sequence = nullptr;
best_possible_firsts_.clear();
while (true) {
if (FindSequenceVar(s, &best_sequence)) {
// No not create a choice point if it is not needed.
DCHECK(best_sequence != nullptr);
if (best_possible_firsts_.size() == 1 &&
best_sequence->Interval(best_possible_firsts_.back())
->MustBePerformed()) {
best_sequence->RankFirst(best_possible_firsts_.back());
continue;
}
int best_interval = -1;
if (!FindIntervalVar(s, best_sequence, &best_interval)) {
s->Fail();
}
CHECK_NE(-1, best_interval);
return s->RevAlloc(new RankFirst(best_sequence, best_interval));
} else {
return nullptr;
}
}
}
2020-10-29 14:25:39 +01:00
void Accept(ModelVisitor* const visitor) const override {
visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
visitor->VisitSequenceArrayArgument(ModelVisitor::kSequencesArgument,
sequences_);
visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
}
2020-10-22 23:36:58 +02:00
private:
// Selects the interval var to rank.
2020-10-29 14:25:39 +01:00
bool FindIntervalVarOnStartMin(Solver* const s,
SequenceVar* const best_sequence,
int* const best_interval_index) {
int best_interval = -1;
2021-04-01 12:13:35 +02:00
int64_t best_start_min = std::numeric_limits<int64_t>::max();
for (int index = 0; index < best_possible_firsts_.size(); ++index) {
const int candidate = best_possible_firsts_[index];
2020-10-29 14:25:39 +01:00
IntervalVar* const interval = best_sequence->Interval(candidate);
if (interval->StartMin() < best_start_min) {
best_interval = candidate;
best_start_min = interval->StartMin();
}
}
if (best_interval == -1) {
return false;
} else {
*best_interval_index = best_interval;
return true;
}
}
2020-10-29 14:25:39 +01:00
bool FindIntervalVarRandomly(Solver* const s,
SequenceVar* const best_sequence,
int* const best_interval_index) {
DCHECK(!best_possible_firsts_.empty());
const int index = s->Rand32(best_possible_firsts_.size());
*best_interval_index = best_possible_firsts_[index];
return true;
}
2020-10-29 14:25:39 +01:00
bool FindIntervalVar(Solver* const s, SequenceVar* const best_sequence,
int* const best_interval_index) {
switch (strategy_) {
2020-10-22 23:36:58 +02:00
case Solver::SEQUENCE_DEFAULT:
case Solver::SEQUENCE_SIMPLE:
case Solver::CHOOSE_MIN_SLACK_RANK_FORWARD:
return FindIntervalVarOnStartMin(s, best_sequence, best_interval_index);
case Solver::CHOOSE_RANDOM_RANK_FORWARD:
return FindIntervalVarRandomly(s, best_sequence, best_interval_index);
default:
LOG(FATAL) << "Unknown strategy " << strategy_;
return false;
}
}
// Selects the sequence var to start ranking.
2020-10-29 14:25:39 +01:00
bool FindSequenceVarOnSlack(Solver* const s,
SequenceVar** const best_sequence) {
2021-04-01 12:13:35 +02:00
int64_t best_slack = std::numeric_limits<int64_t>::max();
int64_t best_ahmin = std::numeric_limits<int64_t>::max();
*best_sequence = nullptr;
best_possible_firsts_.clear();
for (int i = 0; i < sequences_.size(); ++i) {
2020-10-29 14:25:39 +01:00
SequenceVar* const candidate_sequence = sequences_[i];
int ranked = 0;
int not_ranked = 0;
int unperformed = 0;
candidate_sequence->ComputeStatistics(&ranked, &not_ranked, &unperformed);
if (not_ranked > 0) {
2013-06-11 14:49:19 +00:00
candidate_possible_firsts_.clear();
candidate_possible_lasts_.clear();
candidate_sequence->ComputePossibleFirstsAndLasts(
&candidate_possible_firsts_, &candidate_possible_lasts_);
// No possible first, failing.
2017-04-19 16:20:56 +02:00
if (candidate_possible_firsts_.empty()) {
s->Fail();
}
// Only 1 candidate, and non optional: ranking without branching.
2013-06-11 14:49:19 +00:00
if (candidate_possible_firsts_.size() == 1 &&
candidate_sequence->Interval(candidate_possible_firsts_.back())
->MustBePerformed()) {
*best_sequence = candidate_sequence;
2013-06-11 14:49:19 +00:00
best_possible_firsts_ = candidate_possible_firsts_;
return true;
}
// Evaluating the sequence.
2021-04-01 12:13:35 +02:00
int64_t hmin, hmax, dmin, dmax;
candidate_sequence->HorizonRange(&hmin, &hmax);
candidate_sequence->DurationRange(&dmin, &dmax);
2021-04-01 12:13:35 +02:00
int64_t ahmin, ahmax;
candidate_sequence->ActiveHorizonRange(&ahmin, &ahmax);
2021-04-01 12:13:35 +02:00
const int64_t current_slack = (hmax - hmin - dmax);
if (current_slack < best_slack ||
(current_slack == best_slack && ahmin < best_ahmin)) {
best_slack = current_slack;
*best_sequence = candidate_sequence;
2013-06-11 14:49:19 +00:00
best_possible_firsts_ = candidate_possible_firsts_;
best_ahmin = ahmin;
2010-09-15 12:42:33 +00:00
}
}
}
return *best_sequence != nullptr;
}
2020-10-29 14:25:39 +01:00
bool FindSequenceVarRandomly(Solver* const s,
SequenceVar** const best_sequence) {
std::vector<SequenceVar*> all_candidates;
std::vector<std::vector<int>> all_possible_firsts;
for (int i = 0; i < sequences_.size(); ++i) {
2020-10-29 14:25:39 +01:00
SequenceVar* const candidate_sequence = sequences_[i];
int ranked = 0;
int not_ranked = 0;
int unperformed = 0;
candidate_sequence->ComputeStatistics(&ranked, &not_ranked, &unperformed);
if (not_ranked > 0) {
2013-06-11 14:49:19 +00:00
candidate_possible_firsts_.clear();
candidate_possible_lasts_.clear();
candidate_sequence->ComputePossibleFirstsAndLasts(
&candidate_possible_firsts_, &candidate_possible_lasts_);
// No possible first, failing.
2017-04-19 16:20:56 +02:00
if (candidate_possible_firsts_.empty()) {
s->Fail();
2010-09-15 12:42:33 +00:00
}
// Only 1 candidate, and non optional: ranking without branching.
2013-06-11 14:49:19 +00:00
if (candidate_possible_firsts_.size() == 1 &&
candidate_sequence->Interval(candidate_possible_firsts_.back())
->MustBePerformed()) {
*best_sequence = candidate_sequence;
2013-06-11 14:49:19 +00:00
best_possible_firsts_ = candidate_possible_firsts_;
return true;
}
all_candidates.push_back(candidate_sequence);
2013-06-11 14:49:19 +00:00
all_possible_firsts.push_back(candidate_possible_firsts_);
2010-09-15 12:42:33 +00:00
}
}
if (all_candidates.empty()) {
return false;
}
const int chosen = s->Rand32(all_candidates.size());
*best_sequence = all_candidates[chosen];
best_possible_firsts_ = std::move(all_possible_firsts[chosen]);
return true;
2010-09-15 12:42:33 +00:00
}
2020-10-29 14:25:39 +01:00
bool FindSequenceVar(Solver* const s, SequenceVar** const best_sequence) {
switch (strategy_) {
2020-10-22 23:36:58 +02:00
case Solver::SEQUENCE_DEFAULT:
case Solver::SEQUENCE_SIMPLE:
case Solver::CHOOSE_MIN_SLACK_RANK_FORWARD:
return FindSequenceVarOnSlack(s, best_sequence);
case Solver::CHOOSE_RANDOM_RANK_FORWARD:
return FindSequenceVarRandomly(s, best_sequence);
default:
LOG(FATAL) << "Unknown strategy " << strategy_;
}
}
2020-10-29 14:25:39 +01:00
const std::vector<SequenceVar*> sequences_;
const Solver::SequenceStrategy strategy_;
std::vector<int> best_possible_firsts_;
2013-06-11 14:49:19 +00:00
std::vector<int> candidate_possible_firsts_;
std::vector<int> candidate_possible_lasts_;
2010-09-15 12:42:33 +00:00
};
2020-10-22 23:36:58 +02:00
} // namespace
2010-09-15 12:42:33 +00:00
2021-04-01 12:13:35 +02:00
Decision* Solver::MakeScheduleOrPostpone(IntervalVar* const var, int64_t est,
int64_t* const marker) {
CHECK(var != nullptr);
CHECK(marker != nullptr);
return RevAlloc(new ScheduleOrPostpone(var, est, marker));
}
2021-04-01 12:13:35 +02:00
Decision* Solver::MakeScheduleOrExpedite(IntervalVar* const var, int64_t est,
int64_t* const marker) {
CHECK(var != nullptr);
CHECK(marker != nullptr);
return RevAlloc(new ScheduleOrExpedite(var, est, marker));
}
2020-10-29 14:25:39 +01:00
DecisionBuilder* Solver::MakePhase(const std::vector<IntervalVar*>& intervals,
IntervalStrategy str) {
switch (str) {
2020-10-22 23:36:58 +02:00
case Solver::INTERVAL_DEFAULT:
case Solver::INTERVAL_SIMPLE:
case Solver::INTERVAL_SET_TIMES_FORWARD:
return RevAlloc(new SetTimesForward(intervals));
case Solver::INTERVAL_SET_TIMES_BACKWARD:
return RevAlloc(new SetTimesBackward(intervals));
default:
LOG(FATAL) << "Unknown strategy " << str;
}
}
2020-10-29 14:25:39 +01:00
Decision* Solver::MakeRankFirstInterval(SequenceVar* const sequence,
int index) {
CHECK(sequence != nullptr);
return RevAlloc(new RankFirst(sequence, index));
}
2020-10-29 14:25:39 +01:00
Decision* Solver::MakeRankLastInterval(SequenceVar* const sequence, int index) {
CHECK(sequence != nullptr);
return RevAlloc(new RankLast(sequence, index));
}
2020-10-29 14:25:39 +01:00
DecisionBuilder* Solver::MakePhase(const std::vector<SequenceVar*>& sequences,
2010-09-15 12:42:33 +00:00
SequenceStrategy str) {
return RevAlloc(new RankFirstIntervalVars(sequences, str));
2010-09-15 12:42:33 +00:00
}
2020-10-22 23:36:58 +02:00
} // namespace operations_research