Files
ortools-clone/ortools/sat/no_overlap_2d_helper.h
Corentin Le Molgat b4b226801b update include guards
2025-11-05 11:54:02 +01:00

267 lines
9.7 KiB
C++

// Copyright 2010-2025 Google LLC
// 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.
#ifndef ORTOOLS_SAT_NO_OVERLAP_2D_HELPER_H_
#define ORTOOLS_SAT_NO_OVERLAP_2D_HELPER_H_
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include "absl/types/span.h"
#include "ortools/sat/diffn_util.h"
#include "ortools/sat/enforcement.h"
#include "ortools/sat/enforcement_helper.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/integer_base.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_base.h"
#include "ortools/sat/scheduling_helpers.h"
#include "ortools/sat/util.h"
namespace operations_research {
namespace sat {
// Helper class shared by the propagators that handle no_overlap_2d constraints.
//
// Having a helper class like this one makes much easier to do in-processing and
// to share pre-computed data between the two propagators.
class NoOverlap2DConstraintHelper : public PropagatorInterface {
public:
NoOverlap2DConstraintHelper(std::vector<AffineExpression> x_starts,
std::vector<AffineExpression> x_ends,
std::vector<AffineExpression> x_sizes,
std::vector<LiteralIndex> x_reason_for_presence,
std::vector<AffineExpression> y_starts,
std::vector<AffineExpression> y_ends,
std::vector<AffineExpression> y_sizes,
std::vector<LiteralIndex> y_reason_for_presence,
Model* model)
: axes_are_swapped_(false),
x_helper_(std::make_unique<SchedulingConstraintHelper>(
x_starts, x_ends, x_sizes, x_reason_for_presence, model)),
y_helper_(std::make_unique<SchedulingConstraintHelper>(
y_starts, y_ends, y_sizes, y_reason_for_presence, model)),
enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()),
enforcement_id_(-1),
model_(model),
watcher_(model->GetOrCreate<GenericLiteralWatcher>()) {
const int num_boxes = x_helper_->NumTasks();
connected_components_.reserve(1, num_boxes);
connected_components_.Add({});
for (int i = 0; i < x_helper_->NumTasks(); ++i) {
if (!x_helper_->IsAbsent(i) && !y_helper_->IsAbsent(i)) {
connected_components_.AppendToLastVector(i);
}
}
}
void RegisterWith(GenericLiteralWatcher* watcher,
absl::Span<const Literal> enforcement_literals);
bool SynchronizeAndSetDirection(bool x_is_forward_after_swap = true,
bool y_is_forward_after_swap = true,
bool swap_x_and_y = false);
bool IsOptional(int index) const {
return x_helper_->IsOptional(index) || y_helper_->IsOptional(index);
}
bool IsPresent(int index) const {
return x_helper_->IsPresent(index) && y_helper_->IsPresent(index);
}
bool IsAbsent(int index) const {
return x_helper_->IsAbsent(index) || y_helper_->IsAbsent(index);
}
Rectangle GetBoundingRectangle(int index) const {
return {.x_min = x_helper_->StartMin(index),
.x_max = x_helper_->EndMax(index),
.y_min = y_helper_->StartMin(index),
.y_max = y_helper_->EndMax(index)};
}
Rectangle GetLevelZeroBoundingRectangle(int index) const {
return {.x_min = x_helper_->LevelZeroStartMin(index),
.x_max = x_helper_->LevelZeroEndMax(index),
.y_min = y_helper_->LevelZeroStartMin(index),
.y_max = y_helper_->LevelZeroEndMax(index)};
}
bool IsFixed(int index) const {
return x_helper_->StartIsFixed(index) && x_helper_->EndIsFixed(index) &&
y_helper_->StartIsFixed(index) && y_helper_->EndIsFixed(index);
}
std::pair<IntegerValue, IntegerValue> GetBoxSizesMax(int index) const {
return {x_helper_->SizeMax(index), y_helper_->SizeMax(index)};
}
std::pair<IntegerValue, IntegerValue> GetLevelZeroBoxSizesMin(
int index) const {
return {x_helper_->LevelZeroSizeMin(index),
y_helper_->LevelZeroSizeMin(index)};
}
bool IsEnforced() const;
void ResetReason() {
x_helper_->ResetReason();
y_helper_->ResetReason();
}
void WatchAllBoxes(int id) { propagators_watching_.push_back(id); }
// Propagate a relationship between two boxes (ie., first must be to the left
// of the second, etc).
bool PropagateRelativePosition(
int first, int second, PairwiseRestriction::PairwiseRestrictionType type);
// Returns a "fixed size projection" of the item of the item `index`. More
// precisely, returns item of index `index` with its sizes fixed to their
// minimum value alongside a bounding box that contains the item.
RectangleInRange GetItemRangeForSizeMin(int index) const;
// Returns a {start_min, start_max, end_min, end_max} view of the item of
// the index `index`.
ItemWithVariableSize GetItemWithVariableSize(int index) const;
// If there is no possible placement for the two mandatory boxes (they will
// necessarily overlap), call this function to report this as a conflict.
// Returns true.
bool ReportConflictFromTwoBoxes(int box1, int box2);
// Reports a conflict due to a (potentially relaxed) infeasible subproblem of
// the no_overlap_2d constraint. More concretely, this function takes a list
// of fixed-size rectangles and their placement domains in `ranges` that
// satisfy:
// - the problem of placing all the rectangles in their domain is
// infeasible;
// - the x and y sizes of each box in `ranges` are smaller or equal than
// the corresponding current minimum sizes of the boxes;
// - for each range in `ranges`, range.box_index.bounding_box is fully
// contained inside GetItemRangeForSizeMin(range.box_index).bounding_box.
// In other terms, each element is infeasible in a domain at least as
// large as the current one.
bool ReportConflictFromInfeasibleBoxRanges(
absl::Span<const RectangleInRange> ranges);
void AddXSizeMinReason(int index) { x_helper_->AddSizeMinReason(index); }
void AddYSizeMinReason(int index) { y_helper_->AddSizeMinReason(index); }
void AddSizeMinReason(int index) {
AddXSizeMinReason(index);
AddYSizeMinReason(index);
}
// Push the explanation that the left edge of this box is to the right of the
// vertical line x=lower_bound.
//
// | => |
// | => \/
// | => +---+
// | => | |
// | => +---+
// |
void AddLeftMinReason(int index, IntegerValue lower_bound) {
x_helper_->AddStartMinReason(index, lower_bound);
}
// Push the explanation that the left edge of this box is to the left of the
// vertical line x=upper_bound.
//
// | <= |
// \/ <= |
// +---------|---+
// | <= | |
// | <= | |
// +---------|---|
// |
void AddLeftMaxReason(int index, IntegerValue upper_bound) {
x_helper_->AddStartMaxReason(index, upper_bound);
}
// Push the explanation that the bottom edge of this box is to the top of the
// horizontal line y=lower_bound.
void AddBottomMinReason(int index, IntegerValue lower_bound) {
y_helper_->AddStartMinReason(index, lower_bound);
}
// Push the explanation that the bottom edge of this box is to the bottom of
// the vertical line y=upper_bound.
void AddBottomMaxReason(int index, IntegerValue upper_bound) {
y_helper_->AddStartMaxReason(index, upper_bound);
}
void AddPresenceReason(int index) {
x_helper_->AddPresenceReason(index);
y_helper_->AddPresenceReason(index);
}
bool IncreaseLeftMin(int index, IntegerValue new_lower_bound) {
x_helper_->ImportReasonsFromOther(*y_helper_);
return x_helper_->IncreaseStartMin(index, new_lower_bound);
}
bool ReportConflict() {
x_helper_->ImportReasonsFromOther(*y_helper_);
return x_helper_->ReportConflict();
}
int NumBoxes() const { return x_helper_->NumTasks(); }
bool Propagate() override;
// Note that the helpers are only valid until the next call to
// Propagate().
SchedulingConstraintHelper& x_helper() const { return *x_helper_; }
SchedulingConstraintHelper& y_helper() const { return *y_helper_; }
SchedulingDemandHelper& x_demands_helper();
SchedulingDemandHelper& y_demands_helper();
const CompactVectorVector<int>& connected_components() const {
return connected_components_;
}
int64_t InProcessingCount() const { return inprocessing_count_; }
int64_t LastLevelZeroChangeIdx() const {
return level_zero_bound_change_idx_;
}
private:
void Reset(absl::Span<const Rectangle> fixed_boxes,
absl::Span<const int> non_fixed_box_indexes);
CompactVectorVector<int> connected_components_;
bool axes_are_swapped_;
std::unique_ptr<SchedulingConstraintHelper> x_helper_;
std::unique_ptr<SchedulingConstraintHelper> y_helper_;
std::unique_ptr<SchedulingDemandHelper> x_demands_helper_;
std::unique_ptr<SchedulingDemandHelper> y_demands_helper_;
EnforcementHelper& enforcement_helper_;
EnforcementId enforcement_id_;
Model* model_;
GenericLiteralWatcher* watcher_;
std::vector<int> propagators_watching_;
int64_t inprocessing_count_ = 0;
int64_t level_zero_bound_change_idx_ = 0;
};
} // namespace sat
} // namespace operations_research
#endif // ORTOOLS_SAT_NO_OVERLAP_2D_HELPER_H_