20 #include "absl/strings/str_format.h"
32 class Deviation :
public Constraint {
34 Deviation(Solver*
const solver,
const std::vector<IntVar*>& vars,
35 IntVar*
const deviation_var,
int64 total_sum)
39 deviation_var_(deviation_var),
40 total_sum_(total_sum),
41 scaled_vars_assigned_value_(new
int64[size_]),
42 scaled_vars_min_(new
int64[size_]),
43 scaled_vars_max_(new
int64[size_]),
46 maximum_(new
int64[size_]),
47 overlaps_sup_(new
int64[size_]),
49 active_sum_rounded_down_(0),
50 active_sum_rounded_up_(0),
51 active_sum_nearest_(0) {
52 CHECK(deviation_var !=
nullptr);
55 ~Deviation()
override {}
57 void Post()
override {
58 Solver*
const s = solver();
59 Demon*
const demon = s->MakeConstraintInitialPropagateCallback(
this);
60 for (
int i = 0; i < size_; ++i) {
61 vars_[i]->WhenRange(demon);
63 deviation_var_->WhenRange(demon);
64 s->AddConstraint(s->MakeSumEquality(vars_, total_sum_));
67 void InitialPropagate()
override {
68 const int64 delta_min = BuildMinimalDeviationAssignment();
69 deviation_var_->SetMin(delta_min);
70 PropagateBounds(delta_min);
73 std::string DebugString()
const override {
74 return absl::StrFormat(
"Deviation([%s], deviation_var = %s, sum = %d)",
76 deviation_var_->DebugString(), total_sum_);
79 void Accept(ModelVisitor*
const visitor)
const override {
93 int64 BuildMinimalDeviationAssignment() {
94 RepairGreedySum(BuildGreedySum(
true));
95 int64 minimal_deviation = 0;
96 for (
int i = 0; i < size_; ++i) {
98 std::abs(scaled_vars_assigned_value_[i] - total_sum_);
100 return minimal_deviation;
108 void PropagateBounds(
int64 min_delta) {
109 PropagateBounds(min_delta,
true);
110 PropagateBounds(min_delta,
false);
118 void PropagateBounds(
int64 min_delta,
bool upper_bound) {
120 const int64 greedy_sum = BuildGreedySum(upper_bound);
122 RepairSumAndComputeInfo(greedy_sum);
124 PruneVars(min_delta, upper_bound);
128 void ComputeData(
bool upper_bound) {
131 for (
int i = 0; i < size_; ++i) {
132 scaled_vars_max_[i] =
133 size_ * (upper_bound ?
vars_[i]->Max() : -
vars_[i]->Min());
134 scaled_vars_min_[i] =
135 size_ * (upper_bound ?
vars_[i]->Min() : -
vars_[i]->Max());
136 scaled_sum_max_ += scaled_vars_max_[i];
137 scaled_sum_min_ += scaled_vars_min_[i];
139 active_sum_ = (!upper_bound ? -total_sum_ : total_sum_);
141 active_sum_rounded_down_ =
142 size_ * MathUtil::FloorOfRatio<int64>(active_sum_, size_);
144 active_sum_rounded_up_ = active_sum_rounded_down_ + size_;
145 active_sum_nearest_ = (active_sum_rounded_up_ - active_sum_ <=
146 active_sum_ - active_sum_rounded_down_)
147 ? active_sum_rounded_up_
148 : active_sum_rounded_down_;
152 int64 BuildGreedySum(
bool upper_bound) {
154 ComputeData(upper_bound);
157 DCHECK_GE(size_ * active_sum_, scaled_sum_min_);
158 DCHECK_LE(size_ * active_sum_, scaled_sum_max_);
163 for (
int i = 0; i < size_; ++i) {
164 if (scaled_vars_min_[i] >= active_sum_) {
165 scaled_vars_assigned_value_[i] = scaled_vars_min_[i];
166 }
else if (scaled_vars_max_[i] <= active_sum_) {
167 scaled_vars_assigned_value_[i] = scaled_vars_max_[i];
171 scaled_vars_assigned_value_[i] = active_sum_nearest_;
172 if (active_sum_ % size_ != 0) {
173 overlaps_.push_back(i);
176 sum += scaled_vars_assigned_value_[i];
178 DCHECK_EQ(0, active_sum_rounded_down_ % size_);
179 DCHECK_LE(active_sum_rounded_down_, active_sum_);
180 DCHECK_LT(active_sum_ - active_sum_rounded_down_, size_);
185 bool Overlap(
int var_index)
const {
186 return scaled_vars_min_[var_index] < active_sum_ &&
187 scaled_vars_max_[var_index] > active_sum_;
191 void RepairGreedySum(
int64 greedy_sum) {
193 const int64 scaled_total_sum = size_ * active_sum_;
195 const int64 delta = greedy_sum > scaled_total_sum ? -size_ : size_;
200 for (
int j = 0; j < overlaps_.size() && greedy_sum != scaled_total_sum;
202 scaled_vars_assigned_value_[overlaps_[j]] +=
delta;
206 for (
int i = 0; i < size_ && greedy_sum != scaled_total_sum; ++i) {
207 const int64 old_scaled_vars_i = scaled_vars_assigned_value_[i];
208 if (greedy_sum < scaled_total_sum) {
211 scaled_vars_assigned_value_[i] += scaled_total_sum - greedy_sum;
212 scaled_vars_assigned_value_[i] =
213 std::min(scaled_vars_assigned_value_[i], scaled_vars_max_[i]);
217 scaled_vars_assigned_value_[i] -= (greedy_sum - scaled_total_sum);
218 scaled_vars_assigned_value_[i] =
219 std::max(scaled_vars_assigned_value_[i], scaled_vars_min_[i]);
222 greedy_sum += scaled_vars_assigned_value_[i] - old_scaled_vars_i;
228 void ComputeMaxWhenNoRepair() {
229 int num_overlap_sum_rounded_up = 0;
230 if (active_sum_nearest_ == active_sum_rounded_up_) {
231 num_overlap_sum_rounded_up = overlaps_.size();
233 for (
int i = 0; i < size_; ++i) {
234 maximum_[i] = scaled_vars_assigned_value_[i];
235 if (Overlap(i) && active_sum_nearest_ == active_sum_rounded_up_ &&
236 active_sum_ % size_ != 0) {
237 overlaps_sup_[i] = num_overlap_sum_rounded_up - 1;
239 overlaps_sup_[i] = num_overlap_sum_rounded_up;
247 int ComputeNumOverlapsVariableRoundedUp() {
248 if (active_sum_ % size_ == 0) {
251 int num_overlap_sum_rounded_up = 0;
252 for (
int i = 0; i < size_; ++i) {
253 if (scaled_vars_assigned_value_[i] > scaled_vars_min_[i] &&
254 scaled_vars_assigned_value_[i] == active_sum_rounded_up_) {
255 num_overlap_sum_rounded_up++;
258 return num_overlap_sum_rounded_up;
264 bool CanPushSumAcrossMean(
int64 greedy_sum,
int64 scaled_total_sum)
const {
265 return (greedy_sum > scaled_total_sum &&
266 active_sum_nearest_ == active_sum_rounded_up_) ||
267 (greedy_sum < scaled_total_sum &&
268 active_sum_nearest_ == active_sum_rounded_down_);
273 void RepairSumAndComputeInfo(
int64 greedy_sum) {
274 const int64 scaled_total_sum = size_ * active_sum_;
278 if (greedy_sum == scaled_total_sum) {
279 ComputeMaxWhenNoRepair();
282 if (CanPushSumAcrossMean(greedy_sum, scaled_total_sum)) {
283 const int64 delta = greedy_sum > scaled_total_sum ? -size_ : size_;
284 for (
int j = 0; j < overlaps_.size() && greedy_sum != scaled_total_sum;
286 scaled_vars_assigned_value_[overlaps_[j]] +=
delta;
291 const int num_overlap_sum_rounded_up =
292 ComputeNumOverlapsVariableRoundedUp();
294 if (greedy_sum == scaled_total_sum) {
296 for (
int i = 0; i < size_; ++i) {
297 if (Overlap(i) && num_overlap_sum_rounded_up > 0) {
298 maximum_[i] = active_sum_rounded_up_;
299 overlaps_sup_[i] = num_overlap_sum_rounded_up - 1;
301 maximum_[i] = scaled_vars_assigned_value_[i];
302 overlaps_sup_[i] = num_overlap_sum_rounded_up;
305 }
else if (greedy_sum > scaled_total_sum) {
309 for (
int i = 0; i < size_; ++i) {
310 maximum_[i] = scaled_vars_assigned_value_[i];
311 overlaps_sup_[i] = 0;
314 for (
int i = 0; i < size_; ++i) {
315 if (Overlap(i) && num_overlap_sum_rounded_up > 0) {
316 overlaps_sup_[i] = num_overlap_sum_rounded_up - 1;
318 overlaps_sup_[i] = num_overlap_sum_rounded_up;
321 if (scaled_vars_assigned_value_[i] < scaled_vars_max_[i]) {
323 scaled_vars_assigned_value_[i] + scaled_total_sum - greedy_sum;
325 maximum_[i] = scaled_vars_assigned_value_[i];
333 void PruneVars(
int64 min_delta,
bool upper_bound) {
335 const int64 increase_down_up = (active_sum_rounded_up_ - active_sum_) -
336 (active_sum_ - active_sum_rounded_down_);
337 for (
int var_index = 0; var_index < size_; ++var_index) {
339 if (scaled_vars_max_[var_index] != scaled_vars_min_[var_index] &&
340 maximum_[var_index] < scaled_vars_max_[var_index]) {
341 const int64 new_max =
342 ComputeNewMax(var_index, min_delta, increase_down_up);
343 PruneBound(var_index, new_max, upper_bound);
349 int64 ComputeNewMax(
int var_index,
int64 min_delta,
int64 increase_down_up) {
350 int64 maximum_value = maximum_[var_index];
351 int64 current_min_delta = min_delta;
353 if (overlaps_sup_[var_index] > 0 &&
355 overlaps_sup_[var_index] * (size_ - increase_down_up) >=
356 deviation_var_->Max())) {
357 const int64 delta = deviation_var_->Max() - current_min_delta;
358 maximum_value += (size_ *
delta) / (size_ - increase_down_up);
359 return MathUtil::FloorOfRatio<int64>(maximum_value, size_);
361 if (maximum_value == active_sum_rounded_down_ &&
362 active_sum_rounded_down_ < active_sum_) {
364 current_min_delta += size_ + increase_down_up;
365 if (current_min_delta > deviation_var_->Max()) {
367 return maximum_value / size_;
369 maximum_value += size_;
372 overlaps_sup_[var_index] * (size_ - increase_down_up);
373 maximum_value += size_ * overlaps_sup_[var_index];
375 const int64 delta = deviation_var_->Max() - current_min_delta;
376 maximum_value +=
delta / 2;
377 return MathUtil::FloorOfRatio<int64>(maximum_value, size_);
382 void PruneBound(
int var_index,
int64 bound,
bool upper_bound) {
390 std::vector<IntVar*>
vars_;
392 IntVar*
const deviation_var_;
393 const int64 total_sum_;
394 std::unique_ptr<int64[]> scaled_vars_assigned_value_;
395 std::unique_ptr<int64[]> scaled_vars_min_;
396 std::unique_ptr<int64[]> scaled_vars_max_;
397 int64 scaled_sum_max_;
398 int64 scaled_sum_min_;
400 std::vector<int> overlaps_;
401 std::unique_ptr<int64[]> maximum_;
402 std::unique_ptr<int64[]> overlaps_sup_;
405 int64 active_sum_rounded_down_;
406 int64 active_sum_rounded_up_;
407 int64 active_sum_nearest_;
412 IntVar*
const deviation_var,
414 return RevAlloc(
new Deviation(
this, vars, deviation_var, total_sum));