Skip to content

Commit

Permalink
[CP-SAT] remove Domain deprecated API; work on diffn
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Dec 21, 2023
1 parent ef9986c commit 4b797d4
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 62 deletions.
2 changes: 1 addition & 1 deletion ortools/flatzinc/cp_model_fz_solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct,
}
FillDomainInProto(domain, proto.mutable_variables(var));

for (const ClosedInterval interval : domain.intervals()) {
for (const ClosedInterval interval : domain) {
for (int64_t value = interval.start; value <= interval.end; ++value) {
// Create one Boolean variable for this arc.
const int literal = proto.variables_size();
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/constraint_violation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ void LinearIncrementalEvaluator::PrecomputeCompactView(

bool LinearIncrementalEvaluator::ViolationChangeIsConvex(int var) const {
for (const int c : VarToConstraints(var)) {
if (domains_[c].intervals().size() > 2) return false;
if (domains_[c].NumIntervals() > 2) return false;
}
return true;
}
Expand Down
3 changes: 1 addition & 2 deletions ortools/sat/cp_model_search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,9 @@ void AddDualSchedulingHeuristics(SatParameters& new_params) {
new_params.set_use_overload_checker_in_cumulative(true);
new_params.set_use_strong_propagation_in_disjunctive(true);
new_params.set_use_timetable_edge_finding_in_cumulative(true);
new_params.set_use_pairwise_reasoning_in_no_overlap_2d(true);
new_params.set_max_size_pairwise_reasoning_in_no_overlap_2d(200);
new_params.set_use_timetabling_in_no_overlap_2d(true);
new_params.set_use_energetic_reasoning_in_no_overlap_2d(true);
new_params.set_use_area_energetic_reasoning_in_no_overlap_2d(true);
}

// We want a random tie breaking among variables with equivalent values.
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/diffn.cc
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ bool RectanglePairwisePropagator::Propagate() {
}

std::vector<PairwiseRestriction> restrictions;
if (full_pairwise_propagation_) {
if (non_zero_area_boxes_.size() <= full_pairwise_propagation_threshold_) {
RETURN_IF_FALSE(FindRestrictionsAndPropagateConflict(non_zero_area_boxes_,
&restrictions));
}
Expand Down
6 changes: 3 additions & 3 deletions ortools/sat/diffn.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ class RectanglePairwisePropagator : public PropagatorInterface {
: global_x_(*x),
global_y_(*y),
shared_stats_(model->GetOrCreate<SharedStatistics>()),
full_pairwise_propagation_(
full_pairwise_propagation_threshold_(
model->GetOrCreate<SatParameters>()
->use_pairwise_reasoning_in_no_overlap_2d()) {}
->max_size_pairwise_reasoning_in_no_overlap_2d()) {}

~RectanglePairwisePropagator() override;

Expand Down Expand Up @@ -174,7 +174,7 @@ class RectanglePairwisePropagator : public PropagatorInterface {
int64_t num_pairwise_conflicts_ = 0;
int64_t num_pairwise_propagations_ = 0;

const bool full_pairwise_propagation_;
const int full_pairwise_propagation_threshold_;

std::vector<ItemForPairwiseRestriction> non_zero_area_boxes_;
std::vector<ItemForPairwiseRestriction> horizontal_zero_area_boxes_;
Expand Down
156 changes: 113 additions & 43 deletions ortools/sat/diffn_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -810,16 +810,7 @@ ProbingRectangle::ProbingRectangle(
.subspan(idx_begin, i - idx_begin)});
}

indexes_[Edge::LEFT] = 0;
indexes_[Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 1;
indexes_[Edge::BOTTOM] = 0;
indexes_[Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 1;

// Remove the four bogus points we added.
Shrink(Edge::LEFT);
Shrink(Edge::BOTTOM);
Shrink(Edge::RIGHT);
Shrink(Edge::TOP);
Reset();
}

void ProbingRectangle::Reset() {
Expand All @@ -828,6 +819,11 @@ void ProbingRectangle::Reset() {
indexes_[Edge::BOTTOM] = 0;
indexes_[Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 1;

next_indexes_[Edge::LEFT] = 1;
next_indexes_[Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 2;
next_indexes_[Edge::BOTTOM] = 1;
next_indexes_[Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 2;

minimum_energy_ = full_energy_;
ranges_touching_both_boundaries_[0].clear();
ranges_touching_both_boundaries_[1].clear();
Expand Down Expand Up @@ -890,6 +886,10 @@ void ProbingRectangle::ValidateInvariants() const {
IntegerValue intersect_length[4] = {0, 0, 0, 0};
IntegerValue corner_count[4] = {0, 0, 0, 0};
IntegerValue energy = 0;
CHECK_LE(next_indexes_[Edge::LEFT], indexes_[Edge::RIGHT]);
CHECK_LE(next_indexes_[Edge::BOTTOM], indexes_[Edge::TOP]);
CHECK_GE(next_indexes_[Edge::TOP], indexes_[Edge::BOTTOM]);
CHECK_GE(next_indexes_[Edge::RIGHT], indexes_[Edge::LEFT]);

for (int interval_idx = 0; interval_idx < intervals_.size(); interval_idx++) {
const RectangleInRange& range = intervals_[interval_idx];
Expand All @@ -901,6 +901,9 @@ void ProbingRectangle::ValidateInvariants() const {
energy += min_intersect.Area();

std::array<bool, 4> touching_boundary = {false, false, false, false};
CHECK_EQ(CanConsumeEnergy(current_rectangle, range) &&
current_rectangle.Area() != 0,
range.GetMinimumIntersectionArea(current_rectangle) != 0);
if (CanConsumeEnergy(current_rectangle, range)) {
touching_boundary = GetPossibleEdgeIntersection(current_rectangle, range);
}
Expand Down Expand Up @@ -1056,39 +1059,53 @@ template <ProbingRectangle::Edge edge>
void ProbingRectangle::ShrinkImpl() {
constexpr EdgeInfo e = GetEdgeInfo(edge);

const Rectangle prev_rectangle = GetCurrentRectangle();
bool update_next_index[4] = {false, false, false, false};
update_next_index[edge] = true;

IntegerValue step_1d_size;
minimum_energy_ -= GetShrinkDeltaEnergy(edge);
const std::vector<PointsForCoordinate>& sorted_intervals =
e.shrink_direction == Direction::LEFT_AND_RIGHT
? grouped_intervals_sorted_by_x_
: grouped_intervals_sorted_by_y_;

const Rectangle prev_rectangle = GetCurrentRectangle();
indexes_[edge] = next_indexes_[edge];
const Rectangle current_rectangle = GetCurrentRectangle();

switch (edge) {
case Edge::LEFT:
step_1d_size = sorted_intervals[indexes_[edge] + 1].coordinate -
prev_rectangle.x_min;
indexes_[edge]++;
step_1d_size = current_rectangle.x_min - prev_rectangle.x_min;
next_indexes_[edge] =
std::min(indexes_[edge] + 1, indexes_[e.opposite_edge]);
next_indexes_[e.opposite_edge] =
std::max(indexes_[edge], next_indexes_[e.opposite_edge]);
break;
case Edge::BOTTOM:
step_1d_size = sorted_intervals[indexes_[edge] + 1].coordinate -
prev_rectangle.y_min;
indexes_[edge]++;
step_1d_size = current_rectangle.y_min - prev_rectangle.y_min;
next_indexes_[edge] =
std::min(indexes_[edge] + 1, indexes_[e.opposite_edge]);
next_indexes_[e.opposite_edge] =
std::max(indexes_[edge], next_indexes_[e.opposite_edge]);
break;
case Edge::RIGHT:
step_1d_size = prev_rectangle.x_max -
sorted_intervals[indexes_[edge] - 1].coordinate;
indexes_[edge]--;
step_1d_size = prev_rectangle.x_max - current_rectangle.x_max;
next_indexes_[edge] =
std::max(indexes_[edge] - 1, indexes_[e.opposite_edge]);
next_indexes_[e.opposite_edge] =
std::min(indexes_[edge], next_indexes_[e.opposite_edge]);
break;
case Edge::TOP:
step_1d_size = prev_rectangle.y_max -
sorted_intervals[indexes_[edge] - 1].coordinate;
indexes_[edge]--;
step_1d_size = prev_rectangle.y_max - current_rectangle.y_max;
next_indexes_[edge] =
std::max(indexes_[edge] - 1, indexes_[e.opposite_edge]);
next_indexes_[e.opposite_edge] =
std::min(indexes_[edge], next_indexes_[e.opposite_edge]);
break;
}

absl::Span<ProbingRectangle::IntervalPoint> items_touching_coordinate =
sorted_intervals[indexes_[edge]].items_touching_coordinate;
const Rectangle current_rectangle = GetCurrentRectangle();

IntegerValue delta_corner_count[4] = {0, 0, 0, 0};
for (const auto& item : items_touching_coordinate) {
Expand Down Expand Up @@ -1152,6 +1169,9 @@ void ProbingRectangle::ShrinkImpl() {
if (!remove_edge) {
continue;
}

update_next_index[edge_to_update] = true;

if (touching_boundary_before[info.opposite_edge]) {
ranges_touching_both_boundaries_[info.shrink_direction].erase(
item.index);
Expand Down Expand Up @@ -1200,6 +1220,59 @@ void ProbingRectangle::ShrinkImpl() {
for (int i = 0; i < 4; i++) {
corner_count_[i] += delta_corner_count[i];
}

auto points_consume_energy =
[this,
&current_rectangle](absl::Span<ProbingRectangle::IntervalPoint> points) {
for (const auto& item : points) {
const RectangleInRange& range = intervals_[item.index];
if (CanConsumeEnergy(current_rectangle, range)) {
return true;
}
}
return false;
};
if (update_next_index[Edge::LEFT]) {
for (; next_indexes_[Edge::LEFT] < indexes_[Edge::RIGHT];
++next_indexes_[Edge::LEFT]) {
if (points_consume_energy(
grouped_intervals_sorted_by_x_[next_indexes_[Edge::LEFT]]
.items_touching_coordinate)) {
break;
}
}
}
if (update_next_index[Edge::BOTTOM]) {
for (; next_indexes_[Edge::BOTTOM] < indexes_[Edge::TOP];
++next_indexes_[Edge::BOTTOM]) {
if (points_consume_energy(
grouped_intervals_sorted_by_y_[next_indexes_[Edge::BOTTOM]]
.items_touching_coordinate)) {
break;
}
}
}
if (update_next_index[Edge::RIGHT]) {
for (; next_indexes_[Edge::RIGHT] > indexes_[Edge::LEFT];
--next_indexes_[Edge::RIGHT]) {
if (points_consume_energy(
grouped_intervals_sorted_by_x_[next_indexes_[Edge::RIGHT]]
.items_touching_coordinate)) {
break;
}
}
}
if (update_next_index[Edge::TOP]) {
for (; next_indexes_[Edge::TOP] > indexes_[Edge::BOTTOM];
--next_indexes_[Edge::TOP]) {
if (points_consume_energy(
grouped_intervals_sorted_by_y_[next_indexes_[Edge::TOP]]
.items_touching_coordinate)) {
break;
}
}
}

probe_area_ = current_rectangle.Area();
CacheShrinkDeltaEnergy(0);
CacheShrinkDeltaEnergy(1);
Expand All @@ -1224,23 +1297,21 @@ void ProbingRectangle::Shrink(Edge edge) {

IntegerValue ProbingRectangle::GetShrinkDeltaArea(Edge edge) const {
const Rectangle current_rectangle = GetCurrentRectangle();
const std::vector<PointsForCoordinate>& sorted_intervals =
(edge == Edge::LEFT || edge == Edge::RIGHT)
? grouped_intervals_sorted_by_x_
: grouped_intervals_sorted_by_y_;
const IntegerValue coordinate =
sorted_intervals[next_indexes_[edge]].coordinate;
switch (edge) {
case Edge::LEFT:
return (grouped_intervals_sorted_by_x_[indexes_[edge] + 1].coordinate -
current_rectangle.x_min) *
current_rectangle.SizeY();
return (coordinate - current_rectangle.x_min) * current_rectangle.SizeY();
case Edge::BOTTOM:
return (grouped_intervals_sorted_by_y_[indexes_[edge] + 1].coordinate -
current_rectangle.y_min) *
current_rectangle.SizeX();
return (coordinate - current_rectangle.y_min) * current_rectangle.SizeX();
case Edge::RIGHT:
return (current_rectangle.x_max -
grouped_intervals_sorted_by_x_[indexes_[edge] - 1].coordinate) *
current_rectangle.SizeY();
return (current_rectangle.x_max - coordinate) * current_rectangle.SizeY();
case Edge::TOP:
return (current_rectangle.y_max -
grouped_intervals_sorted_by_y_[indexes_[edge] - 1].coordinate) *
current_rectangle.SizeX();
return (current_rectangle.y_max - coordinate) * current_rectangle.SizeX();
}
}

Expand All @@ -1262,9 +1333,9 @@ void ProbingRectangle::CacheShrinkDeltaEnergy(int dimension) {
}

next_rectangle_up.x_min =
grouped_intervals_sorted_by_x_[indexes_[Edge::LEFT] + 1].coordinate;
grouped_intervals_sorted_by_x_[next_indexes_[Edge::LEFT]].coordinate;
next_rectangle_down.x_max =
grouped_intervals_sorted_by_x_[indexes_[Edge::RIGHT] - 1].coordinate;
grouped_intervals_sorted_by_x_[next_indexes_[Edge::RIGHT]].coordinate;

step_1d_size_up = next_rectangle_up.x_min - current_rectangle.x_min;
step_1d_size_down = current_rectangle.x_max - next_rectangle_down.x_max;
Expand All @@ -1280,9 +1351,9 @@ void ProbingRectangle::CacheShrinkDeltaEnergy(int dimension) {
}

next_rectangle_up.y_min =
grouped_intervals_sorted_by_y_[indexes_[Edge::BOTTOM] + 1].coordinate;
grouped_intervals_sorted_by_y_[next_indexes_[Edge::BOTTOM]].coordinate;
next_rectangle_down.y_max =
grouped_intervals_sorted_by_y_[indexes_[Edge::TOP] - 1].coordinate;
grouped_intervals_sorted_by_y_[next_indexes_[Edge::TOP]].coordinate;

step_1d_size_up = next_rectangle_up.y_min - current_rectangle.y_min;
step_1d_size_down = current_rectangle.y_max - next_rectangle_down.y_max;
Expand Down Expand Up @@ -1339,10 +1410,10 @@ bool ProbingRectangle::CanShrink(Edge edge) const {
switch (edge) {
case Edge::LEFT:
case Edge::RIGHT:
return (indexes_[Edge::RIGHT] - indexes_[Edge::LEFT] > 1);
return (next_indexes_[Edge::RIGHT] != indexes_[Edge::LEFT]);
case Edge::BOTTOM:
case Edge::TOP:
return (indexes_[Edge::TOP] - indexes_[Edge::BOTTOM] > 1);
return (indexes_[Edge::TOP] != next_indexes_[Edge::BOTTOM]);
}
}

Expand Down Expand Up @@ -1419,7 +1490,6 @@ std::vector<Rectangle> FindRectanglesWithEnergyConflictMC(
// Pick a change with a probability proportional to exp(- delta_E / Temp)
ranges.Shrink(candidates[weighted_pick(weights, random)]);
}
CHECK_GT(ranges.GetCurrentRectangleArea(), 0);
if (ranges.GetMinimumEnergy() > ranges.GetCurrentRectangleArea()) {
result.push_back(ranges.GetCurrentRectangle());
}
Expand Down
7 changes: 7 additions & 0 deletions ortools/sat/diffn_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ struct RectangleInRange {

return result;
}

template <typename Sink>
friend void AbslStringify(Sink& sink, const RectangleInRange& r) {
absl::Format(&sink, "item(size=%vx%v, BB=%v)", r.x_size, r.y_size,
r.bounding_area);
}
};

// Cheaply test several increasingly smaller rectangles for energy conflict.
Expand Down Expand Up @@ -553,6 +559,7 @@ class ProbingRectangle {
IntegerValue minimum_energy_;
IntegerValue probe_area_;
int indexes_[4];
int next_indexes_[4];

absl::flat_hash_set<int> ranges_touching_both_boundaries_[2];
IntegerValue corner_count_[4] = {0, 0, 0, 0};
Expand Down
5 changes: 3 additions & 2 deletions ortools/sat/sat_parameters.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ option csharp_namespace = "Google.OrTools.Sat";
// Contains the definitions for all the sat algorithm parameters and their
// default values.
//
// NEXT TAG: 276
// NEXT TAG: 277
message SatParameters {
// In some context, like in a portfolio of search, it makes sense to name a
// given parameters set for logging purpose.
Expand Down Expand Up @@ -828,7 +828,8 @@ message SatParameters {

// Performs an extra step of propagation in the no_overlap_2d constraint by
// looking at all pairs of intervals.
optional bool use_pairwise_reasoning_in_no_overlap_2d = 251 [default = false];
optional int32 max_size_pairwise_reasoning_in_no_overlap_2d = 276
[default = 50];

// When set, it activates a few scheduling parameters to improve the lower
// bound of scheduling problems. This is only effective with multiple workers
Expand Down
2 changes: 1 addition & 1 deletion ortools/util/sorted_interval_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ Domain Domain::SquareSuperset() const {
if (abs_domain.Size() >= kDomainComplexityLimit) {
Domain result;
result.intervals_.reserve(abs_domain.NumIntervals());
for (const auto& interval : abs_domain.intervals()) {
for (const auto& interval : abs_domain) {
result.intervals_.push_back(
ClosedInterval(CapProd(interval.start, interval.start),
CapProd(interval.end, interval.end)));
Expand Down
8 changes: 0 additions & 8 deletions ortools/util/sorted_interval_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,14 +467,6 @@ class Domain {
return intervals_.end();
}

// Deprecated.
//
// TODO(user): remove, this makes a copy and is of a different type that our
// internal InlinedVector() anyway.
std::vector<ClosedInterval> intervals() const {
return {intervals_.begin(), intervals_.end()};
}

private:
// Same as Negation() but modify the current domain.
void NegateInPlace();
Expand Down

0 comments on commit 4b797d4

Please sign in to comment.