speed up sat scheduling code
This commit is contained in:
@@ -87,10 +87,9 @@ bool CumulativeEnergyConstraint::Propagate() {
|
||||
bool tree_has_mandatory_intervals = false;
|
||||
|
||||
// Main loop: insert tasks by increasing end_max, check for overloads.
|
||||
for (const auto task_time :
|
||||
::gtl::reversed_view(helper_->TaskByDecreasingEndMax())) {
|
||||
const int current_task = task_time.task_index;
|
||||
const IntegerValue current_end = task_time.time;
|
||||
const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
|
||||
for (const auto [current_task, current_end] :
|
||||
::gtl::reversed_view(by_decreasing_end_max)) {
|
||||
if (task_to_start_event_[current_task] == -1) continue;
|
||||
|
||||
// Add the current task to the tree.
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "ortools/sat/intervals.h"
|
||||
#include "ortools/sat/linear_constraint.h"
|
||||
#include "ortools/sat/model.h"
|
||||
#include "ortools/sat/precedences.h"
|
||||
#include "ortools/sat/sat_base.h"
|
||||
#include "ortools/sat/sat_parameters.pb.h"
|
||||
#include "ortools/sat/timetable.h"
|
||||
@@ -47,8 +46,7 @@ namespace {
|
||||
// TODO(user): Use the faster variable only version if all expressions reduce
|
||||
// to a single variable?
|
||||
void AddIsEqualToMinOf(IntegerVariable min_var,
|
||||
const std::vector<AffineExpression>& exprs,
|
||||
Model* model) {
|
||||
absl::Span<const AffineExpression> exprs, Model* model) {
|
||||
std::vector<LinearExpression> converted;
|
||||
for (const AffineExpression& affine : exprs) {
|
||||
LinearExpression e;
|
||||
@@ -66,8 +64,7 @@ void AddIsEqualToMinOf(IntegerVariable min_var,
|
||||
}
|
||||
|
||||
void AddIsEqualToMaxOf(IntegerVariable max_var,
|
||||
const std::vector<AffineExpression>& exprs,
|
||||
Model* model) {
|
||||
absl::Span<const AffineExpression> exprs, Model* model) {
|
||||
std::vector<LinearExpression> converted;
|
||||
for (const AffineExpression& affine : exprs) {
|
||||
LinearExpression e;
|
||||
@@ -331,7 +328,7 @@ bool NonOverlappingRectanglesDisjunctivePropagator::
|
||||
// Compute relevant boxes, the one with a mandatory part of y. Because we will
|
||||
// need to sort it this way, we consider them by increasing start max.
|
||||
indexed_boxes_.clear();
|
||||
const std::vector<TaskTime>& temp = y->TaskByDecreasingStartMax();
|
||||
const auto temp = y->TaskByDecreasingStartMax();
|
||||
for (int i = temp.size(); --i >= 0;) {
|
||||
const int box = temp[i].task_index;
|
||||
// Ignore absent boxes.
|
||||
|
||||
@@ -139,9 +139,9 @@ class LinearConstraintPropagator : public PropagatorInterface {
|
||||
// (resp. coefficients) contained in the range [0, rev_num_fixed_vars_) of
|
||||
// vars_ (resp. coeffs_) are fixed (resp. belong to fixed variables).
|
||||
const int size_;
|
||||
std::unique_ptr<IntegerVariable[]> vars_;
|
||||
std::unique_ptr<IntegerValue[]> coeffs_;
|
||||
std::unique_ptr<IntegerValue[]> max_variations_;
|
||||
const std::unique_ptr<IntegerVariable[]> vars_;
|
||||
const std::unique_ptr<IntegerValue[]> coeffs_;
|
||||
const std::unique_ptr<IntegerValue[]> max_variations_;
|
||||
|
||||
// This is just the negation of the enforcement literal and it never changes.
|
||||
std::vector<Literal> literal_reason_;
|
||||
|
||||
@@ -169,9 +169,10 @@ SchedulingConstraintHelper* IntervalsRepository::GetOrCreateHelper(
|
||||
|
||||
SchedulingDemandHelper* IntervalsRepository::GetOrCreateDemandHelper(
|
||||
SchedulingConstraintHelper* helper,
|
||||
const std::vector<AffineExpression>& demands) {
|
||||
absl::Span<const AffineExpression> demands) {
|
||||
const std::pair<SchedulingConstraintHelper*, std::vector<AffineExpression>>
|
||||
key = {helper, demands};
|
||||
key = {helper,
|
||||
std::vector<AffineExpression>(demands.begin(), demands.end())};
|
||||
const auto it = demand_helper_repository_.find(key);
|
||||
if (it != demand_helper_repository_.end()) return it->second;
|
||||
|
||||
@@ -194,7 +195,15 @@ SchedulingConstraintHelper::SchedulingConstraintHelper(
|
||||
integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
||||
precedence_relations_(model->GetOrCreate<PrecedenceRelations>()),
|
||||
precedences_(model->GetOrCreate<PrecedencesPropagator>()),
|
||||
interval_variables_(tasks) {
|
||||
interval_variables_(tasks),
|
||||
capacity_(tasks.size()),
|
||||
cached_size_min_(new IntegerValue[capacity_]),
|
||||
cached_start_min_(new IntegerValue[capacity_]),
|
||||
cached_end_min_(new IntegerValue[capacity_]),
|
||||
cached_negated_start_max_(new IntegerValue[capacity_]),
|
||||
cached_negated_end_max_(new IntegerValue[capacity_]),
|
||||
cached_shifted_start_min_(new IntegerValue[capacity_]),
|
||||
cached_negated_shifted_end_max_(new IntegerValue[capacity_]) {
|
||||
starts_.clear();
|
||||
ends_.clear();
|
||||
minus_ends_.clear();
|
||||
@@ -228,7 +237,15 @@ SchedulingConstraintHelper::SchedulingConstraintHelper(int num_tasks,
|
||||
: trail_(model->GetOrCreate<Trail>()),
|
||||
integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
||||
precedence_relations_(model->GetOrCreate<PrecedenceRelations>()),
|
||||
precedences_(model->GetOrCreate<PrecedencesPropagator>()) {
|
||||
precedences_(model->GetOrCreate<PrecedencesPropagator>()),
|
||||
capacity_(num_tasks),
|
||||
cached_size_min_(new IntegerValue[capacity_]),
|
||||
cached_start_min_(new IntegerValue[capacity_]),
|
||||
cached_end_min_(new IntegerValue[capacity_]),
|
||||
cached_negated_start_max_(new IntegerValue[capacity_]),
|
||||
cached_negated_end_max_(new IntegerValue[capacity_]),
|
||||
cached_shifted_start_min_(new IntegerValue[capacity_]),
|
||||
cached_negated_shifted_end_max_(new IntegerValue[capacity_]) {
|
||||
starts_.resize(num_tasks);
|
||||
CHECK_EQ(NumTasks(), num_tasks);
|
||||
}
|
||||
@@ -380,13 +397,8 @@ void SchedulingConstraintHelper::InitSortedVectors() {
|
||||
recompute_all_cache_ = true;
|
||||
recompute_cache_.resize(num_tasks, true);
|
||||
|
||||
cached_shifted_start_min_.resize(num_tasks);
|
||||
cached_negated_shifted_end_max_.resize(num_tasks);
|
||||
cached_size_min_.resize(num_tasks);
|
||||
cached_start_min_.resize(num_tasks);
|
||||
cached_end_min_.resize(num_tasks);
|
||||
cached_negated_start_max_.resize(num_tasks);
|
||||
cached_negated_end_max_.resize(num_tasks);
|
||||
// Make sure all the cached_* arrays can hold enough data.
|
||||
CHECK_LE(num_tasks, capacity_);
|
||||
|
||||
task_by_increasing_start_min_.resize(num_tasks);
|
||||
task_by_increasing_end_min_.resize(num_tasks);
|
||||
@@ -491,7 +503,7 @@ void SchedulingConstraintHelper::AddLevelZeroPrecedence(int a, int b) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<TaskTime>&
|
||||
absl::Span<const TaskTime>
|
||||
SchedulingConstraintHelper::TaskByIncreasingStartMin() {
|
||||
const int num_tasks = NumTasks();
|
||||
for (int i = 0; i < num_tasks; ++i) {
|
||||
@@ -503,7 +515,7 @@ SchedulingConstraintHelper::TaskByIncreasingStartMin() {
|
||||
return task_by_increasing_start_min_;
|
||||
}
|
||||
|
||||
const std::vector<TaskTime>&
|
||||
absl::Span<const TaskTime>
|
||||
SchedulingConstraintHelper::TaskByIncreasingEndMin() {
|
||||
const int num_tasks = NumTasks();
|
||||
for (int i = 0; i < num_tasks; ++i) {
|
||||
@@ -515,7 +527,7 @@ SchedulingConstraintHelper::TaskByIncreasingEndMin() {
|
||||
return task_by_increasing_end_min_;
|
||||
}
|
||||
|
||||
const std::vector<TaskTime>&
|
||||
absl::Span<const TaskTime>
|
||||
SchedulingConstraintHelper::TaskByDecreasingStartMax() {
|
||||
const int num_tasks = NumTasks();
|
||||
for (int i = 0; i < num_tasks; ++i) {
|
||||
@@ -528,7 +540,7 @@ SchedulingConstraintHelper::TaskByDecreasingStartMax() {
|
||||
return task_by_decreasing_start_max_;
|
||||
}
|
||||
|
||||
const std::vector<TaskTime>&
|
||||
absl::Span<const TaskTime>
|
||||
SchedulingConstraintHelper::TaskByDecreasingEndMax() {
|
||||
const int num_tasks = NumTasks();
|
||||
for (int i = 0; i < num_tasks; ++i) {
|
||||
@@ -540,7 +552,7 @@ SchedulingConstraintHelper::TaskByDecreasingEndMax() {
|
||||
return task_by_decreasing_end_max_;
|
||||
}
|
||||
|
||||
const std::vector<TaskTime>&
|
||||
absl::Span<const TaskTime>
|
||||
SchedulingConstraintHelper::TaskByIncreasingShiftedStartMin() {
|
||||
if (recompute_shifted_start_min_) {
|
||||
recompute_shifted_start_min_ = false;
|
||||
@@ -822,13 +834,13 @@ IntegerValue ComputeEnergyMinInWindow(
|
||||
}
|
||||
|
||||
SchedulingDemandHelper::SchedulingDemandHelper(
|
||||
std::vector<AffineExpression> demands, SchedulingConstraintHelper* helper,
|
||||
Model* model)
|
||||
absl::Span<const AffineExpression> demands,
|
||||
SchedulingConstraintHelper* helper, Model* model)
|
||||
: integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
||||
product_decomposer_(model->GetOrCreate<ProductDecomposer>()),
|
||||
sat_solver_(model->GetOrCreate<SatSolver>()),
|
||||
assignment_(model->GetOrCreate<SatSolver>()->Assignment()),
|
||||
demands_(std::move(demands)),
|
||||
demands_(demands.begin(), demands.end()),
|
||||
helper_(helper) {
|
||||
const int num_tasks = helper->NumTasks();
|
||||
linearized_energies_.resize(num_tasks);
|
||||
|
||||
@@ -167,7 +167,7 @@ class IntervalsRepository {
|
||||
// demands must be the compatible.
|
||||
SchedulingDemandHelper* GetOrCreateDemandHelper(
|
||||
SchedulingConstraintHelper* helper,
|
||||
const std::vector<AffineExpression>& demands);
|
||||
absl::Span<const AffineExpression> demands);
|
||||
|
||||
// Calls InitDecomposedEnergies on all SchedulingDemandHelper created.
|
||||
void InitAllDecomposedEnergies();
|
||||
@@ -362,11 +362,11 @@ class SchedulingConstraintHelper : public PropagatorInterface,
|
||||
//
|
||||
// TODO(user): we could merge the first loop of IncrementalSort() with the
|
||||
// loop that fill TaskTime.time at each call.
|
||||
const std::vector<TaskTime>& TaskByIncreasingStartMin();
|
||||
const std::vector<TaskTime>& TaskByIncreasingEndMin();
|
||||
const std::vector<TaskTime>& TaskByDecreasingStartMax();
|
||||
const std::vector<TaskTime>& TaskByDecreasingEndMax();
|
||||
const std::vector<TaskTime>& TaskByIncreasingShiftedStartMin();
|
||||
absl::Span<const TaskTime> TaskByIncreasingStartMin();
|
||||
absl::Span<const TaskTime> TaskByIncreasingEndMin();
|
||||
absl::Span<const TaskTime> TaskByDecreasingStartMax();
|
||||
absl::Span<const TaskTime> TaskByDecreasingEndMax();
|
||||
absl::Span<const TaskTime> TaskByIncreasingShiftedStartMin();
|
||||
|
||||
// Returns a sorted vector where each task appear twice, the first occurrence
|
||||
// is at size (end_min - size_min) and the second one at (end_min).
|
||||
@@ -434,12 +434,13 @@ class SchedulingConstraintHelper : public PropagatorInterface,
|
||||
IntegerLiteral lit);
|
||||
|
||||
// Returns the underlying affine expressions.
|
||||
const std::vector<IntervalVariable>& IntervalVariables() const {
|
||||
absl::Span<const IntervalVariable> IntervalVariables() const {
|
||||
return interval_variables_;
|
||||
}
|
||||
const std::vector<AffineExpression>& Starts() const { return starts_; }
|
||||
const std::vector<AffineExpression>& Ends() const { return ends_; }
|
||||
const std::vector<AffineExpression>& Sizes() const { return sizes_; }
|
||||
absl::Span<const AffineExpression> Starts() const { return starts_; }
|
||||
absl::Span<const AffineExpression> Ends() const { return ends_; }
|
||||
absl::Span<const AffineExpression> Sizes() const { return sizes_; }
|
||||
|
||||
Literal PresenceLiteral(int index) const {
|
||||
DCHECK(IsOptional(index));
|
||||
return Literal(reason_for_presence_[index]);
|
||||
@@ -528,13 +529,19 @@ class SchedulingConstraintHelper : public PropagatorInterface,
|
||||
int previous_level_ = 0;
|
||||
|
||||
// The caches of all relevant interval values.
|
||||
std::vector<IntegerValue> cached_size_min_;
|
||||
std::vector<IntegerValue> cached_start_min_;
|
||||
std::vector<IntegerValue> cached_end_min_;
|
||||
std::vector<IntegerValue> cached_negated_start_max_;
|
||||
std::vector<IntegerValue> cached_negated_end_max_;
|
||||
std::vector<IntegerValue> cached_shifted_start_min_;
|
||||
std::vector<IntegerValue> cached_negated_shifted_end_max_;
|
||||
// These are initially of size capacity and never resized.
|
||||
//
|
||||
// TODO(user): Because of std::swap() in SetTimeDirection, we cannot mark
|
||||
// most of them as "const" and as a result we loose some performance since
|
||||
// the address need to be re-fetched on most access.
|
||||
const int capacity_;
|
||||
const std::unique_ptr<IntegerValue[]> cached_size_min_;
|
||||
std::unique_ptr<IntegerValue[]> cached_start_min_;
|
||||
std::unique_ptr<IntegerValue[]> cached_end_min_;
|
||||
std::unique_ptr<IntegerValue[]> cached_negated_start_max_;
|
||||
std::unique_ptr<IntegerValue[]> cached_negated_end_max_;
|
||||
std::unique_ptr<IntegerValue[]> cached_shifted_start_min_;
|
||||
std::unique_ptr<IntegerValue[]> cached_negated_shifted_end_max_;
|
||||
|
||||
// Sorted vectors returned by the TasksBy*() functions.
|
||||
std::vector<TaskTime> task_by_increasing_start_min_;
|
||||
@@ -581,7 +588,7 @@ class SchedulingDemandHelper {
|
||||
public:
|
||||
// Hack: this can be called with and empty demand vector as long as
|
||||
// OverrideEnergies() is called to define the energies.
|
||||
SchedulingDemandHelper(std::vector<AffineExpression> demands,
|
||||
SchedulingDemandHelper(absl::Span<const AffineExpression> demands,
|
||||
SchedulingConstraintHelper* helper, Model* model);
|
||||
|
||||
// When defined, the interval will consume this much demand during its whole
|
||||
|
||||
@@ -688,7 +688,7 @@ std::optional<AffineExpression> DetectMakespanFromPrecedences(
|
||||
const SchedulingConstraintHelper& helper, Model* model) {
|
||||
if (helper.NumTasks() == 0) return {};
|
||||
|
||||
const std::vector<AffineExpression>& ends = helper.Ends();
|
||||
const absl::Span<const AffineExpression> ends = helper.Ends();
|
||||
std::vector<IntegerVariable> end_vars;
|
||||
for (const AffineExpression& end : ends) {
|
||||
// TODO(user): Deal with constant end.
|
||||
|
||||
@@ -68,8 +68,8 @@ void TimeTableEdgeFinding::BuildTimeTable() {
|
||||
ecp_.clear();
|
||||
|
||||
// Build start of compulsory part events.
|
||||
for (const auto task_time :
|
||||
::gtl::reversed_view(helper_->TaskByDecreasingStartMax())) {
|
||||
const auto by_decreasing_start_max = helper_->TaskByDecreasingStartMax();
|
||||
for (const auto task_time : ::gtl::reversed_view(by_decreasing_start_max)) {
|
||||
const int t = task_time.task_index;
|
||||
if (!helper_->IsPresent(t)) continue;
|
||||
if (task_time.time < helper_->EndMin(t)) {
|
||||
@@ -88,10 +88,8 @@ void TimeTableEdgeFinding::BuildTimeTable() {
|
||||
|
||||
DCHECK_EQ(scp_.size(), ecp_.size());
|
||||
|
||||
const std::vector<TaskTime>& by_decreasing_end_max =
|
||||
helper_->TaskByDecreasingEndMax();
|
||||
const std::vector<TaskTime>& by_start_min =
|
||||
helper_->TaskByIncreasingStartMin();
|
||||
const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
|
||||
const auto by_start_min = helper_->TaskByIncreasingStartMin();
|
||||
|
||||
IntegerValue height = IntegerValue(0);
|
||||
IntegerValue energy = IntegerValue(0);
|
||||
|
||||
Reference in New Issue
Block a user