speed up sat scheduling code

This commit is contained in:
Laurent Perron
2023-10-25 17:05:22 +02:00
parent fb7aa1b853
commit 044bc5ebce
7 changed files with 70 additions and 57 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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_;

View File

@@ -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);

View File

@@ -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

View File

@@ -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.

View File

@@ -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);