Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cumulative_energy.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <iterator>
19#include <limits>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include "absl/container/inlined_vector.h"
25#include "absl/log/check.h"
26#include "absl/types/span.h"
31#include "ortools/sat/integer.h"
33#include "ortools/sat/model.h"
36#include "ortools/sat/util.h"
38
39namespace operations_research {
40namespace sat {
41
45 Model* model) {
46 auto* watcher = model->GetOrCreate<GenericLiteralWatcher>();
47 CumulativeEnergyConstraint* constraint =
48 new CumulativeEnergyConstraint(capacity, helper, demands, model);
49 constraint->RegisterWith(watcher);
50 model->TakeOwnership(constraint);
51}
52
56 Model* model) {
57 auto* watcher = model->GetOrCreate<GenericLiteralWatcher>();
58
60 new CumulativeDualFeasibleEnergyConstraint(capacity, helper, demands,
61 model);
62 constraint_dff->RegisterWith(watcher);
63 model->TakeOwnership(constraint_dff);
64}
65
69 : capacity_(capacity),
70 integer_trail_(model->GetOrCreate<IntegerTrail>()),
71 helper_(helper),
72 demands_(demands) {
73 const int num_tasks = helper_->NumTasks();
74 task_to_start_event_.resize(num_tasks);
75}
76
78 const int id = watcher->Register(this);
79 helper_->WatchAllTasks(id, watcher);
80 watcher->SetPropagatorPriority(id, 2);
82}
83
85 // This only uses one time direction, but the helper might be used elsewhere.
86 // TODO(user): just keep the current direction?
87 if (!helper_->SynchronizeAndSetTimeDirection(true)) return false;
88 demands_->CacheAllEnergyValues();
89
90 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
91 // TODO(user): force capacity_max >= 0, fail/remove optionals when 0.
92 if (capacity_max <= 0) return true;
93
94 // Set up theta tree.
95 start_event_task_time_.clear();
96 int num_events = 0;
97 for (const auto task_time : helper_->TaskByIncreasingStartMin()) {
98 const int task = task_time.task_index;
99 if (helper_->IsAbsent(task) || demands_->EnergyMax(task) == 0) {
100 task_to_start_event_[task] = -1;
101 continue;
102 }
103 start_event_task_time_.emplace_back(task_time);
104 task_to_start_event_[task] = num_events;
105 num_events++;
106 }
107 start_event_is_present_.assign(num_events, false);
108 theta_tree_.Reset(num_events);
109
110 bool tree_has_mandatory_intervals = false;
111
112 // Main loop: insert tasks by increasing end_max, check for overloads.
113 const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
114 for (const auto [current_task, current_end] :
115 ::gtl::reversed_view(by_decreasing_end_max)) {
116 if (task_to_start_event_[current_task] == -1) continue;
117
118 // Add the current task to the tree.
119 {
120 const int current_event = task_to_start_event_[current_task];
121 const IntegerValue start_min = start_event_task_time_[current_event].time;
122 const bool is_present = helper_->IsPresent(current_task);
123 start_event_is_present_[current_event] = is_present;
124 if (is_present) {
125 tree_has_mandatory_intervals = true;
126 theta_tree_.AddOrUpdateEvent(current_event, start_min * capacity_max,
127 demands_->EnergyMin(current_task),
128 demands_->EnergyMax(current_task));
129 } else {
130 theta_tree_.AddOrUpdateOptionalEvent(current_event,
131 start_min * capacity_max,
132 demands_->EnergyMax(current_task));
133 }
134 }
135
136 if (tree_has_mandatory_intervals) {
137 // Find the critical interval.
138 const IntegerValue envelope = theta_tree_.GetEnvelope();
139 const int critical_event =
140 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(envelope - 1);
141 const IntegerValue window_start =
142 start_event_task_time_[critical_event].time;
143 const IntegerValue window_end = current_end;
144 const IntegerValue window_size = window_end - window_start;
145 if (window_size == 0) continue;
146 const IntegerValue new_capacity_min =
147 CeilRatio(envelope - window_start * capacity_max, window_size);
148
149 // Push the new capacity min, note that this can fail if it go above the
150 // maximum capacity.
151 //
152 // TODO(user): We do not need the capacity max in the reason, but by using
153 // a lower one, we could maybe have propagated more the minimum capacity.
154 // investigate.
155 if (new_capacity_min > integer_trail_->LowerBound(capacity_)) {
156 helper_->ClearReason();
157 for (int event = critical_event; event < num_events; event++) {
158 if (start_event_is_present_[event]) {
159 const int task = start_event_task_time_[event].task_index;
160 helper_->AddPresenceReason(task);
161 demands_->AddEnergyMinReason(task);
162 helper_->AddStartMinReason(task, window_start);
163 helper_->AddEndMaxReason(task, window_end);
164 }
165 }
166 if (capacity_.var == kNoIntegerVariable) {
167 return helper_->ReportConflict();
168 } else {
169 if (!helper_->PushIntegerLiteral(
170 capacity_.GreaterOrEqual(new_capacity_min))) {
171 return false;
172 }
173 }
174 }
175 }
176
177 // Reduce energy of all tasks whose max energy would exceed an interval
178 // ending at current_end.
179 while (theta_tree_.GetOptionalEnvelope() > current_end * capacity_max) {
180 // Some task's max energy is too high, reduce its maximal energy.
181 // Explain with tasks present in the critical interval.
182 // If it is optional, it might get excluded, in that case,
183 // remove it from the tree.
184 // TODO(user): This could be done lazily.
185 // TODO(user): the same required task can have its energy pruned
186 // several times, making this algorithm O(n^2 log n). Is there a way
187 // to get the best pruning in one go? This looks like edge-finding not
188 // being able to converge in one pass, so it might not be easy.
189 helper_->ClearReason();
190 int critical_event;
191 int event_with_new_energy_max;
192 IntegerValue new_energy_max;
194 current_end * capacity_max, &critical_event,
195 &event_with_new_energy_max, &new_energy_max);
196
197 const IntegerValue window_start =
198 start_event_task_time_[critical_event].time;
199
200 // TODO(user): Improve window_end using envelope of critical event.
201 const IntegerValue window_end = current_end;
202 for (int event = critical_event; event < num_events; event++) {
203 if (start_event_is_present_[event]) {
204 if (event == event_with_new_energy_max) continue;
205 const int task = start_event_task_time_[event].task_index;
206 helper_->AddPresenceReason(task);
207 helper_->AddStartMinReason(task, window_start);
208 helper_->AddEndMaxReason(task, window_end);
209 demands_->AddEnergyMinReason(task);
210 }
211 }
212 if (capacity_.var != kNoIntegerVariable) {
213 helper_->MutableIntegerReason()->push_back(
214 integer_trail_->UpperBoundAsLiteral(capacity_.var));
215 }
216
217 const int task_with_new_energy_max =
218 start_event_task_time_[event_with_new_energy_max].task_index;
219 helper_->AddStartMinReason(task_with_new_energy_max, window_start);
220 helper_->AddEndMaxReason(task_with_new_energy_max, window_end);
221 if (!demands_->DecreaseEnergyMax(task_with_new_energy_max,
222 new_energy_max)) {
223 return false;
224 }
225
226 if (helper_->IsPresent(task_with_new_energy_max)) {
227 theta_tree_.AddOrUpdateEvent(
228 task_to_start_event_[task_with_new_energy_max],
229 start_event_task_time_[event_with_new_energy_max].time *
230 capacity_max,
231 demands_->EnergyMin(task_with_new_energy_max), new_energy_max);
232 } else {
233 theta_tree_.RemoveEvent(event_with_new_energy_max);
234 }
235 }
236 }
237 return true;
238}
239
241 IntegerVariable var, AffineExpression capacity,
242 const std::vector<int>& subtasks, absl::Span<const IntegerValue> offsets,
244 Model* model)
245 : var_to_push_(var),
246 capacity_(capacity),
247 subtasks_(subtasks),
248 integer_trail_(model->GetOrCreate<IntegerTrail>()),
249 helper_(helper),
250 demands_(demands) {
251 is_in_subtasks_.assign(helper->NumTasks(), false);
252 task_offsets_.assign(helper->NumTasks(), 0);
253 for (int i = 0; i < subtasks.size(); ++i) {
254 is_in_subtasks_[subtasks[i]] = true;
255 task_offsets_[subtasks[i]] = offsets[i];
256 }
257}
258
260 if (!helper_->SynchronizeAndSetTimeDirection(true)) return false;
261
262 IntegerValue best_time = kMaxIntegerValue;
263 IntegerValue best_bound = kMinIntegerValue;
264
265 IntegerValue previous_time = kMaxIntegerValue;
266 IntegerValue energy_after_time(0);
267 IntegerValue profile_height(0);
268
269 // If the capacity_max is low enough, we compute the exact possible subset
270 // of reachable "sum of demands" of all tasks used in the energy. We will use
271 // the highest reachable as the capacity max.
272 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
273 dp_.Reset(capacity_max.value());
274
275 // We consider the energy after a given time.
276 // From that we derive a bound on the end_min of the subtasks.
277 const auto& profile = helper_->GetEnergyProfile();
278 IntegerValue min_offset = kMaxIntegerValue;
279 for (int i = profile.size() - 1; i >= 0;) {
280 // Skip tasks not relevant for this propagator.
281 {
282 const int t = profile[i].task;
283 if (!helper_->IsPresent(t) || !is_in_subtasks_[t]) {
284 --i;
285 continue;
286 }
287 }
288
289 const IntegerValue time = profile[i].time;
290 if (profile_height > 0) {
291 energy_after_time += profile_height * (previous_time - time);
292 }
293 previous_time = time;
294
295 // Any newly introduced tasks will only change the reachable capa max or
296 // the min_offset on the next time point.
297 const IntegerValue saved_capa_max = dp_.CurrentMax();
298 const IntegerValue saved_min_offset = min_offset;
299
300 for (; i >= 0 && profile[i].time == time; --i) {
301 // Skip tasks not relevant for this propagator.
302 const int t = profile[i].task;
303 if (!helper_->IsPresent(t) || !is_in_subtasks_[t]) continue;
304
305 min_offset = std::min(min_offset, task_offsets_[t]);
306 const IntegerValue demand_min = demands_->DemandMin(t);
307 if (profile[i].is_first) {
308 profile_height -= demand_min;
309 } else {
310 profile_height += demand_min;
311 if (demands_->Demands()[t].IsConstant()) {
312 dp_.Add(demand_min.value());
313 } else {
314 dp_.Add(capacity_max.value()); // Abort DP.
315 }
316 }
317 }
318
319 // We prefer higher time in case of ties since that should reduce the
320 // explanation size.
321 //
322 // Note that if the energy is zero, we don't push anything. Other propagator
323 // will make sure that the end_min is greater than the end_min of any of
324 // the task considered here. TODO(user): actually, we will push using the
325 // last task, and the reason will be non-optimal, fix.
326 if (energy_after_time == 0) continue;
327 DCHECK_GT(saved_capa_max, 0);
328 DCHECK_LT(saved_min_offset, kMaxIntegerValue);
329 const IntegerValue end_min_with_offset =
330 time + CeilRatio(energy_after_time, saved_capa_max) + saved_min_offset;
331 if (end_min_with_offset > best_bound) {
332 best_time = time;
333 best_bound = end_min_with_offset;
334 }
335 }
336 DCHECK_EQ(profile_height, 0);
337
338 if (best_bound == kMinIntegerValue) return true;
339 if (best_bound > integer_trail_->LowerBound(var_to_push_)) {
340 // Compute the reason.
341 // It is just the reason for the energy after time.
342 helper_->ClearReason();
343 for (int t = 0; t < helper_->NumTasks(); ++t) {
344 if (!is_in_subtasks_[t]) continue;
345 if (!helper_->IsPresent(t)) continue;
346
347 const IntegerValue size_min = helper_->SizeMin(t);
348 if (size_min == 0) continue;
349
350 const IntegerValue demand_min = demands_->DemandMin(t);
351 if (demand_min == 0) continue;
352
353 const IntegerValue end_min = helper_->EndMin(t);
354 if (end_min <= best_time) continue;
355
356 helper_->AddEndMinReason(t, std::min(best_time + size_min, end_min));
357 helper_->AddSizeMinReason(t);
358 helper_->AddPresenceReason(t);
359 demands_->AddDemandMinReason(t);
360 }
361 if (capacity_.var != kNoIntegerVariable) {
362 helper_->MutableIntegerReason()->push_back(
363 integer_trail_->UpperBoundAsLiteral(capacity_.var));
364 }
365
366 // Propagate.
367 if (!helper_->PushIntegerLiteral(
368 IntegerLiteral::GreaterOrEqual(var_to_push_, best_bound))) {
369 return false;
370 }
371 }
372
373 return true;
374}
375
377 GenericLiteralWatcher* watcher) {
378 helper_->SetTimeDirection(true);
379 const int id = watcher->Register(this);
380 watcher->SetPropagatorPriority(id, 2);
381 watcher->WatchUpperBound(capacity_, id);
382 for (const int t : subtasks_) {
383 watcher->WatchLowerBound(helper_->Starts()[t], id);
384 watcher->WatchLowerBound(helper_->Ends()[t], id);
385 watcher->WatchLowerBound(helper_->Sizes()[t], id);
386 watcher->WatchLowerBound(demands_->Demands()[t], id);
387 if (!helper_->IsPresent(t) && !helper_->IsAbsent(t)) {
388 watcher->WatchLiteral(helper_->PresenceLiteral(t), id);
389 }
390 }
391}
392
396 : random_(model->GetOrCreate<ModelRandomGenerator>()),
397 shared_stats_(model->GetOrCreate<SharedStatistics>()),
398 opp_infeasibility_detector_(*random_, shared_stats_),
399 capacity_(capacity),
400 integer_trail_(model->GetOrCreate<IntegerTrail>()),
401 helper_(helper),
402 demands_(demands) {
403 const int num_tasks = helper_->NumTasks();
404 task_to_start_event_.resize(num_tasks);
405}
406
409 if (!VLOG_IS_ON(1)) return;
410 std::vector<std::pair<std::string, int64_t>> stats;
411 stats.push_back(
412 {"CumulativeDualFeasibleEnergyConstraint/called", num_calls_});
413 stats.push_back(
414 {"CumulativeDualFeasibleEnergyConstraint/conflicts", num_conflicts_});
415 stats.push_back({"CumulativeDualFeasibleEnergyConstraint/no_potential_window",
416 num_no_potential_window_});
417
418 shared_stats_->AddStats(stats);
419}
420
422 GenericLiteralWatcher* watcher) {
423 const int id = watcher->Register(this);
424 helper_->WatchAllTasks(id, watcher);
425 watcher->SetPropagatorPriority(id, 3);
427}
428
429bool CumulativeDualFeasibleEnergyConstraint::FindAndPropagateConflict(
430 IntegerValue window_start, IntegerValue window_end) {
431 const int num_tasks = helper_->NumTasks();
432 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
433 std::vector<IntegerValue> sizes;
434 std::vector<IntegerValue> demands;
435 std::vector<int> index_to_task;
436 sizes.reserve(num_tasks);
437 demands.reserve(num_tasks);
438 index_to_task.reserve(num_tasks);
439 for (int task = 0; task < num_tasks; ++task) {
440 if (!helper_->IsPresent(task) || demands_->DemandMin(task) == 0) {
441 continue;
442 }
443 const IntegerValue size = Smallest1DIntersection(
444 helper_->StartMin(task), helper_->EndMax(task), helper_->SizeMin(task),
445 window_start, window_end);
446 if (size == 0) continue;
447
448 sizes.push_back(size);
449 demands.push_back(demands_->DemandMin(task));
450 index_to_task.push_back(task);
451 }
452 auto result = opp_infeasibility_detector_.TestFeasibility(
453 sizes, demands, {window_end - window_start, capacity_max},
454 OrthogonalPackingOptions{
455 .use_pairwise = true,
456 .use_dff_f0 = true,
457 .use_dff_f2 = true,
458 // Disable brute force which is correct only for bin packing.
459 .brute_force_threshold = 0,
460 .dff2_max_number_of_parameters_to_check = 100});
461
462 if (result.GetResult() != OrthogonalPackingResult::Status::INFEASIBLE) {
463 return true;
464 }
465 VLOG_EVERY_N_SEC(2, 3) << "Found a conflict on the sub-problem of window ["
466 << window_start << ", " << window_end << "] (with "
467 << sizes.size() << "/" << num_tasks << " tasks)"
468 << " with "
469 << result.GetItemsParticipatingOnConflict().size()
470 << " tasks participating on the conflict.";
471
472 const auto& items = result.GetItemsParticipatingOnConflict();
473 for (int i = 0; i < items.size(); ++i) {
474 const int task = index_to_task[items[i].index];
475 const IntegerValue size_zero_level = Smallest1DIntersection(
476 helper_->LevelZeroStartMin(task), helper_->LevelZeroEndMax(task),
477 helper_->SizeMin(task), window_start, window_end);
478
479 result.TryUseSlackToReduceItemSize(
481 result.TryUseSlackToReduceItemSize(i,
483 demands_->LevelZeroDemandMin(task));
484 }
485 helper_->ClearReason();
486 for (const auto& item : result.GetItemsParticipatingOnConflict()) {
487 const int task = index_to_task[item.index];
488
489 const IntegerValue full_x_size = helper_->SizeMin(task);
490 const IntegerValue size_slack = full_x_size - item.size_x;
491
492 helper_->AddStartMinReason(task, window_start - size_slack);
493 helper_->AddEndMaxReason(task, window_end + size_slack);
494
495 helper_->AddSizeMinReason(task);
496 helper_->AddPresenceReason(task);
497
498 demands_->AddDemandMinReason(task, item.size_y);
499 }
500 if (capacity_.var != kNoIntegerVariable) {
501 helper_->MutableIntegerReason()->push_back(
502 integer_trail_->UpperBoundAsLiteral(capacity_.var));
503 }
504 return helper_->ReportConflict();
505}
506
508 if (!helper_->SynchronizeAndSetTimeDirection(true)) return false;
509 demands_->CacheAllEnergyValues();
510
511 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
512 if (capacity_max <= 0) return true;
513
514 // Set up theta tree.
515 start_event_task_time_.clear();
516 int num_events = 0;
517 for (const auto task_time : helper_->TaskByIncreasingStartMin()) {
518 const int task = task_time.task_index;
519 if (!helper_->IsPresent(task) || demands_->DemandMin(task) == 0) {
520 task_to_start_event_[task] = -1;
521 continue;
522 }
523 start_event_task_time_.emplace_back(task_time);
524 task_to_start_event_[task] = num_events;
525 num_events++;
526 }
527
528 if (num_events == 0) return true;
529 ++num_calls_;
530
531 const IntegerValue largest_window =
532 helper_->EndMax(helper_->TaskByDecreasingEndMax().front().task_index) -
533 helper_->TaskByIncreasingStartMin().front().time;
534 const IntegerValue max_for_fixpoint_inverse =
535 std::numeric_limits<IntegerValue>::max() /
536 (num_events * capacity_max * largest_window);
537
538 theta_tree_.Reset(num_events);
539
540 // Since checking all possible dual-feasible functions is expensive, we only
541 // look for energy conflicts on time windows where a conflict with a DFF is
542 // possible. To rule out time windows where DFF conflicts are impossible, we
543 // use the following nice property stated in [1]:
544 //
545 // If f is a DFF, then for all possible sizes h_i of a problem of height H:
546 // f(h_i)/f(H) <= 1 / floor(H / h_i).
547 //
548 // This follows from the fact that floor(H / h_i) copies of h_i can fit
549 // sideways on the original problem and that those copies must still fit after
550 // any arbitrary DFF is applied.
551 //
552 // So, in practice, for a cumulative constraint with maximum capacity C and
553 // demands d_i, we look for time windows with energy conflicts for the
554 // modified problem:
555 // Capacity: L
556 // Demand for item i: L / (C / d_i)
557 // where L is any sufficiently large integer used to compute inverses without
558 // losing too much precision.
559 //
560 // [1] Carlier, Jacques, François Clautiaux, and Aziz Moukrim. "New reduction
561 // procedures and lower bounds for the two-dimensional bin packing problem
562 // with fixed orientation." Computers & Operations Research 34.8 (2007):
563 // 2223-2250.
564 std::vector<std::pair<IntegerValue, IntegerValue>> candidates_for_conflict;
565 const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
566 for (const auto [current_task, current_end] :
567 ::gtl::reversed_view(by_decreasing_end_max)) {
568 if (task_to_start_event_[current_task] == -1) continue;
569 if (!helper_->IsPresent(current_task)) continue;
570
571 // Add the current task to the tree.
572 {
573 const IntegerValue current_pseudo_energy =
574 helper_->SizeMin(current_task) *
575 (max_for_fixpoint_inverse /
576 (capacity_max / demands_->DemandMin(current_task)));
577 const int current_event = task_to_start_event_[current_task];
578 const IntegerValue start_min = start_event_task_time_[current_event].time;
579 theta_tree_.AddOrUpdateEvent(
580 current_event, start_min * max_for_fixpoint_inverse,
581 current_pseudo_energy, current_pseudo_energy);
582 }
583
584 {
585 // Find the critical interval.
586 const IntegerValue envelope = theta_tree_.GetEnvelope();
587 const int critical_event =
588 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(envelope - 1);
589 const IntegerValue window_start =
590 start_event_task_time_[critical_event].time;
591 const IntegerValue window_end = current_end;
592 const IntegerValue window_size = window_end - window_start;
593 if (window_size == 0) continue;
594
595 if (envelope > window_end * max_for_fixpoint_inverse) {
596 candidates_for_conflict.push_back({window_start, window_end});
597 }
598 }
599 }
600 VLOG_EVERY_N_SEC(2, 3) << "Found " << candidates_for_conflict.size()
601 << " intervals with potential energy conflict using a "
602 "DFF on a problem of size "
603 << num_events << ".";
604
605 if (candidates_for_conflict.empty()) {
606 ++num_no_potential_window_;
607 return true;
608 }
609 // The code above is efficient for pruning the initial problem to a set of
610 // windows with potential conflict, but it might produce some "overly large"
611 // windows: ie., a window that has no conflict but would show one if narrowed.
612 //
613 // TODO(user): explore with using Theta-trees with a multi-valued energy
614 // value.
615 absl::InlinedVector<std::pair<IntegerValue, IntegerValue>, 3>
616 sampled_candidates;
617 std::sample(candidates_for_conflict.begin(), candidates_for_conflict.end(),
618 std::back_inserter(sampled_candidates), 3, *random_);
619 for (const auto& [window_start, window_end] : sampled_candidates) {
620 if (!FindAndPropagateConflict(window_start, window_end)) {
621 ++num_conflicts_;
622 return false;
623 }
624 }
625
626 return true;
627}
628
629} // namespace sat
630} // namespace operations_research
IntegerValue size
Implementation of AddCumulativeOverloadCheckerDff().
CumulativeDualFeasibleEnergyConstraint(AffineExpression capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
Implementation of AddCumulativeOverloadChecker().
void RegisterWith(GenericLiteralWatcher *watcher)
CumulativeEnergyConstraint(AffineExpression capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
CumulativeIsAfterSubsetConstraint(IntegerVariable var, AffineExpression capacity, const std::vector< int > &subtasks, absl::Span< const IntegerValue > offsets, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition integer.h:1844
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
Definition integer.h:1870
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
Definition integer.h:1852
void SetPropagatorPriority(int id, int priority)
Definition integer.cc:2358
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
Definition integer.cc:2332
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
Definition integer.h:1717
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
Definition integer.h:1765
IntegerValue UpperBound(IntegerVariable i) const
Definition integer.h:1721
void Add(int64_t value)
Add a value to the base set for which subset sums will be taken.
Definition util.cc:501
OrthogonalPackingResult TestFeasibility(absl::Span< const IntegerValue > sizes_x, absl::Span< const IntegerValue > sizes_y, std::pair< IntegerValue, IntegerValue > bounding_box_size, const OrthogonalPackingOptions &options=OrthogonalPackingOptions())
absl::Span< const TaskTime > TaskByIncreasingStartMin()
Definition intervals.cc:569
void WatchAllTasks(int id, bool watch_max_side=true)
Definition intervals.cc:802
std::vector< IntegerLiteral > * MutableIntegerReason()
Definition intervals.h:451
int NumTasks() const
Returns the number of task.
Definition intervals.h:293
absl::Span< const AffineExpression > Sizes() const
Definition intervals.h:481
const std::vector< ProfileEvent > & GetEnergyProfile()
Definition intervals.cc:632
absl::Span< const AffineExpression > Starts() const
Definition intervals.h:479
void AddEndMaxReason(int t, IntegerValue upper_bound)
Definition intervals.h:895
absl::Span< const AffineExpression > Ends() const
Definition intervals.h:480
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition intervals.h:887
void ClearReason()
Functions to clear and then set the current reason.
Definition intervals.h:801
absl::Span< const TaskTime > TaskByDecreasingEndMax()
Definition intervals.cc:603
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
Definition intervals.cc:483
ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral lit)
Definition intervals.cc:709
void AddStartMinReason(int t, IntegerValue lower_bound)
Definition intervals.h:873
const std::vector< AffineExpression > & Demands() const
Definition intervals.h:650
ABSL_MUST_USE_RESULT bool DecreaseEnergyMax(int t, IntegerValue value)
Simple class to add statistics by name and print them at the end.
void AddStats(absl::Span< const std::pair< std::string, int64_t > > stats)
Adds a bunch of stats, adding count for the same key together.
void RemoveEvent(int event)
Makes event absent, compute the new envelope in O(log n).
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
void GetEventsWithOptionalEnvelopeGreaterThan(IntegerType target_envelope, int *critical_event, int *optional_event, IntegerType *available_energy) const
int GetMaxEventWithEnvelopeGreaterThan(IntegerType target_envelope) const
void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
IntVar * var
GRBmodel * model
ReverseView< Container > reversed_view(const Container &c)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition integer.h:85
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
void AddCumulativeOverloadCheckerDff(AffineExpression capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
IntegerValue Smallest1DIntersection(IntegerValue range_min, IntegerValue range_max, IntegerValue size, IntegerValue interval_min, IntegerValue interval_max)
void AddCumulativeOverloadChecker(AffineExpression capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t time
Definition resource.cc:1708
Rev< int64_t > start_min
Rev< int64_t > end_min
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
var * coeff + constant >= bound.
Definition integer.h:1696
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition integer.h:1667