Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
constraint_violation.h
Go to the documentation of this file.
1// Copyright 2010-2025 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
14#ifndef ORTOOLS_SAT_CONSTRAINT_VIOLATION_H_
15#define ORTOOLS_SAT_CONSTRAINT_VIOLATION_H_
16
17#include <cstddef>
18#include <cstdint>
19#include <memory>
20#include <optional>
21#include <utility>
22#include <vector>
23
24#include "absl/container/flat_hash_map.h"
25#include "absl/log/check.h"
26#include "absl/types/span.h"
30#include "ortools/sat/util.h"
31#include "ortools/util/bitset.h"
35
36namespace operations_research {
37namespace sat {
38
40 public:
42
43 // Returns the index of the new constraint.
44 int NewConstraint(Domain domain);
45
46 // Incrementaly build the constraint.
47 //
48 // In case of duplicate variables on the same constraint, the code assumes
49 // constraints are built in-order as it checks for duplication.
50 //
51 // Note that the code assume that a Boolean variable DO NOT appear both in the
52 // enforcement list and in the constraint. Otherwise the update will just be
53 // wrong. This should be enforced by our presolve.
54 void AddEnforcementLiteral(int ct_index, int lit);
55 void AddLiteral(int ct_index, int lit, int64_t coeff = 1);
56 void AddTerm(int ct_index, int var, int64_t coeff, int64_t offset = 0);
57 void AddOffset(int ct_index, int64_t offset);
58 void AddLinearExpression(int ct_index, const LinearExpressionProto& expr,
59 int64_t multiplier);
60
61 // Important: this needs to be called after all constraint has been added
62 // and before the class starts to be used. This is DCHECKed.
63 void PrecomputeCompactView(absl::Span<const int64_t> var_max_variation);
64
65 // Compute activities.
66 void ComputeInitialActivities(absl::Span<const int64_t> solution);
67
68 // Update the activities of each constraints.
69 // Also update the current score for the given deltas.
70 //
71 // Note that the score of the changed variable will not be updated correctly!
73 int var, int64_t delta, absl::Span<const double> weights,
74 absl::Span<const int64_t> jump_deltas, absl::Span<double> jump_scores,
75 std::vector<int>* constraints_with_changed_violations);
76
77 // Also for feasibility jump.
78 void UpdateScoreOnWeightUpdate(int c, absl::Span<const int64_t> jump_deltas,
79 absl::Span<double> var_to_score_change);
80
81 // Variables whose score/jump might have changed since the last clear.
82 //
83 // Note that because we reason on a per-constraint basis, this is actually
84 // independent of the set of positive constraint weight used.
86 absl::Span<const int> VariablesAffectedByLastUpdate() const {
87 return last_affected_variables_.PositionsSetAtLeastOnce();
88 }
89
90 // Query violation.
91 int64_t Activity(int c) const;
92 int64_t Violation(int c) const;
93 bool IsViolated(int c) const;
94 bool AppearsInViolatedConstraints(int var) const;
95
96 // Used to DCHECK the state of the evaluator.
97 bool VarIsConsistent(int var) const;
98
99 // Intersect constraint bounds with [lb..ub].
100 // It returns true if a reduction of the domain took place.
101 bool ReduceBounds(int c, int64_t lb, int64_t ub);
102
103 // Model getters.
104 int num_constraints() const { return num_constraints_; }
105
106 // Returns the weighted sum of the violation.
107 // Note that weights might have more entries than the number of constraints.
108 double WeightedViolation(absl::Span<const double> weights) const;
109
110 // Computes how much the weighted violation will change if var was changed
111 // from its old value by += delta.
112 double WeightedViolationDelta(absl::Span<const double> weights, int var,
113 int64_t delta) const;
114
115 // The violation for each constraint is a piecewise linear function. This
116 // computes and aggregates all the breakpoints for the given variable and its
117 // domain.
118 //
119 // Note that if the domain contains less than two values, we return an empty
120 // vector. This function is not meant to be used for such domains.
121 std::vector<int64_t> SlopeBreakpoints(int var, int64_t current_value,
122 const Domain& var_domain) const;
123
124 // Checks if the jump value of a variable is always optimal.
125 bool ViolationChangeIsConvex(int var) const;
126
127 double DeterministicTime() const {
128 return 5e-9 * static_cast<double>(num_ops_);
129 }
130
131 int64_t ObjectiveCoefficient(int var) const {
132 if (var >= columns_.size()) return 0.0;
133 const SpanData& data = columns_[var];
134 if (data.num_linear_entries == 0) return 0.0;
135 const int i = data.start + data.num_neg_literal + data.num_pos_literal;
136 const int c = ct_buffer_[i];
137 if (c != 0) return 0.0;
138 return coeff_buffer_[data.linear_start];
139 }
140
141 absl::Span<const int> ConstraintToVars(int c) const {
142 const SpanData& data = rows_[c];
143 const int size =
144 data.num_pos_literal + data.num_neg_literal + data.num_linear_entries;
145 if (size == 0) return {};
146 return absl::MakeSpan(&row_var_buffer_[data.start], size);
147 }
148
149 private:
150 // Cell in the sparse matrix.
151 struct Entry {
152 int ct_index;
153 int64_t coefficient;
154 };
155
156 // Column-view of the enforcement literals.
157 struct LiteralEntry {
158 int ct_index;
159 bool positive; // bool_var or its negation.
160 };
161
162 struct SpanData {
163 int start = 0;
164 int num_pos_literal = 0;
165 int num_neg_literal = 0;
166 int linear_start = 0;
167 int num_linear_entries = 0;
168 };
169
170 absl::Span<const int> VarToConstraints(int var) const {
171 if (var >= columns_.size()) return {};
172 const SpanData& data = columns_[var];
173 const int size =
174 data.num_pos_literal + data.num_neg_literal + data.num_linear_entries;
175 if (size == 0) return {};
176 return absl::MakeSpan(&ct_buffer_[data.start], size);
177 }
178
179 void ComputeAndCacheDistance(int ct_index);
180
181 // Incremental row-based update.
182 void UpdateScoreOnNewlyEnforced(int c, double weight,
183 absl::Span<const int64_t> jump_deltas,
184 absl::Span<double> jump_scores);
185 void UpdateScoreOnNewlyUnenforced(int c, double weight,
186 absl::Span<const int64_t> jump_deltas,
187 absl::Span<double> jump_scores);
188 void UpdateScoreOfEnforcementIncrease(int c, double score_change,
189 absl::Span<const int64_t> jump_deltas,
190 absl::Span<double> jump_scores);
191 void UpdateScoreOnActivityChange(int c, double weight, int64_t activity_delta,
192 absl::Span<const int64_t> jump_deltas,
193 absl::Span<double> jump_scores);
194
195 // Constraint indexed data (static).
196 int num_constraints_ = 0;
197 std::vector<Domain> domains_;
198 std::vector<int64_t> offsets_;
199
200 // Variable indexed data.
201 // Note that this is just used at construction and is replaced by a compact
202 // view when PrecomputeCompactView() is called.
203 bool creation_phase_ = true;
204 std::vector<std::vector<Entry>> var_entries_;
205 std::vector<std::vector<LiteralEntry>> literal_entries_;
206
207 // Memory efficient column based data (static).
208 std::vector<SpanData> columns_;
209 std::vector<int> ct_buffer_;
210 std::vector<int64_t> coeff_buffer_;
211
212 // Memory efficient row based data (static).
213 std::vector<SpanData> rows_;
214 std::vector<int> row_var_buffer_;
215 std::vector<int64_t> row_coeff_buffer_;
216
217 // In order to avoid scanning long constraint we compute for each of them
218 // the maximum activity variation of one variable (max-min) * abs(coeff).
219 // If the current activity plus this is still feasible, then the constraint
220 // do not need to be scanned.
221 std::vector<int64_t> row_max_variations_;
222
223 // Temporary data.
224 std::vector<int> tmp_row_sizes_;
225 std::vector<int> tmp_row_num_positive_literals_;
226 std::vector<int> tmp_row_num_negative_literals_;
227 std::vector<int> tmp_row_num_linear_entries_;
228
229 // Constraint indexed data (dynamic).
230 std::vector<bool> is_violated_;
231 std::vector<int64_t> activities_;
232 std::vector<int64_t> distances_;
233 std::vector<int> num_false_enforcement_;
234
235 // Code to update the scores on a variable change.
236 std::vector<int64_t> old_distances_;
237 std::vector<int> old_num_false_enforcement_;
238 std::vector<int64_t> cached_deltas_;
239 std::vector<double> cached_scores_;
240
241 SparseBitset<int> last_affected_variables_;
242
243 mutable size_t num_ops_ = 0;
244};
245
246// View of a generic (non linear) constraint for the LsEvaluator.
248 public:
250 virtual ~CompiledConstraint() = default;
251
252 // Recomputes the violation of the constraint from scratch.
253 void InitializeViolation(absl::Span<const int64_t> solution);
254
255 // Updates the violation with the new value.
256 virtual void PerformMove(int var, int64_t old_value,
257 absl::Span<const int64_t> solution_with_new_value);
258
259 // Returns the delta if var changes from old_value to solution[var].
260 virtual int64_t ViolationDelta(
261 int var, int64_t old_value,
262 absl::Span<const int64_t> solution_with_new_value);
263
264 // Returns the sorted vector of variables used by this constraint. This is
265 // used to known when a violation might change, and is only called once during
266 // initialization, so speed is not to much of a concern here.
267 //
268 // The global proto is needed to resolve interval variables reference.
269 virtual std::vector<int> UsedVariables(
270 const CpModelProto& model_proto) const = 0;
271
272 // The cached violation of this constraint.
273 int64_t violation() const { return violation_; }
274
275 protected:
276 // Computes the violation of a constraint.
277 //
278 // This is called by InitializeViolation() and also the default implementation
279 // of ViolationDelta().
280 virtual int64_t ComputeViolation(absl::Span<const int64_t> solution) = 0;
281
282 int64_t violation_;
283};
284
285// Intermediate class for all constraints that store directly their proto as
286// part of their implementation.
288 public:
290 ~CompiledConstraintWithProto() override = default;
291
292 const ConstraintProto& ct_proto() const { return ct_proto_; }
293
294 int64_t ComputeViolation(absl::Span<const int64_t> solution) final;
295
296 // Returns the delta if var changes from old_value to solution[var].
297 int64_t ViolationDelta(
298 int var, int64_t old_value,
299 absl::Span<const int64_t> solution_with_new_value) final;
300
301 // This just returns the variables used by the stored ct_proto_.
302 std::vector<int> UsedVariables(const CpModelProto& model_proto) const final;
303
304 protected:
305 // Computes the violation of a constraint when it is enforced.
307 absl::Span<const int64_t> solution) = 0;
308
309 // Returns the delta if var changes from old_value to solution[var], assuming
310 // that the constraint was and stays enforced after the change.
311 virtual int64_t ViolationDeltaWhenEnforced(
312 int var, int64_t old_value,
313 absl::Span<const int64_t> solution_with_new_value);
314
315 private:
316 const ConstraintProto& ct_proto_;
317};
318
319// Evaluation container for the local search.
320//
321// TODO(user): Ideas for constraint generated moves or sequences of moves?
322
323// Note(user): This class do not handle ALL constraint yet. So it is not because
324// there is no violation here that the solution will be feasible. It is
325// important to check feasibility once this is used. Note that in practice, we
326// can be lucky, and feasible on a subset of hard constraint is enough.
328 public:
329 // The cp_model must outlive this class.
330 LsEvaluator(const CpModelProto& cp_model, const SatParameters& params,
331 TimeLimit* time_limit);
332 LsEvaluator(const CpModelProto& cp_model, const SatParameters& params,
333 const std::vector<bool>& ignored_constraints,
334 absl::Span<const ConstraintProto> additional_constraints,
335 TimeLimit* time_limit);
336
337 // Intersects the domain of the objective with [lb..ub].
338 // It returns true if a reduction of the domain took place.
339 bool ReduceObjectiveBounds(int64_t lb, int64_t ub);
340
341 // Recomputes the violations of all constraints (resp only non-linear one).
342 void ComputeAllViolations(absl::Span<const int64_t> solution);
343 void ComputeAllNonLinearViolations(absl::Span<const int64_t> solution);
344
345 // Recomputes the violations of all impacted non linear constraints.
346 void UpdateNonLinearViolations(int var, int64_t old_value,
347 absl::Span<const int64_t> new_solution);
348
349 // Function specific to the linear only feasibility jump.
350 void UpdateLinearScores(int var, int64_t old_value, int64_t new_value,
351 absl::Span<const double> weights,
352 absl::Span<const int64_t> jump_deltas,
353 absl::Span<double> jump_scores);
354
355 // Must be called after UpdateLinearScores() / UpdateNonLinearViolations()
356 // in order to update the ViolatedConstraints().
357 void UpdateViolatedList();
358
359 absl::Span<const int> VariablesAffectedByLastLinearUpdate() const {
360 return linear_evaluator_.VariablesAffectedByLastUpdate();
361 }
362
363 // Simple summation metric for the constraint and objective violations.
364 int64_t SumOfViolations();
365
366 // Returns the objective activity in the current state.
367 int64_t ObjectiveActivity() const;
368
369 bool IsObjectiveConstraint(int c) const {
370 return cp_model_.has_objective() && c == 0;
371 }
372
373 int64_t ObjectiveCoefficient(int var) const {
374 return cp_model_.has_objective()
375 ? linear_evaluator_.ObjectiveCoefficient(var)
376 : 0;
377 }
378
379 // The number of "internal" constraints in the evaluator. This might not
380 // be the same as the number of initial constraints of the model.
381 int NumLinearConstraints() const;
382 int NumNonLinearConstraints() const;
383 int NumEvaluatorConstraints() const;
384 int NumInfeasibleConstraints() const;
385
386 // Returns the weighted sum of violation. The weights must have the same
387 // size as NumEvaluatorConstraints().
388 int64_t Violation(int c) const;
389 bool IsViolated(int c) const;
390 double WeightedViolation(absl::Span<const double> weights) const;
391
392 // Computes the delta in weighted violation if solution[var] += delta.
393 // We need a temporary mutable solution to evaluate the violation of generic
394 // constraints. If linear_only is true, only the linear violation will be
395 // used.
396 double WeightedViolationDelta(bool linear_only,
397 absl::Span<const double> weights, int var,
398 int64_t delta,
399 absl::Span<int64_t> mutable_solution) const;
400
402 return linear_evaluator_;
403 }
404
406 return &linear_evaluator_;
407 }
408
409 // List of the currently violated constraints.
410 // - It is initialized by RecomputeViolatedList()
411 // - And incrementally maintained by UpdateVariableValue()
412 //
413 // The order depends on the algorithm used and shouldn't be relied on.
414 void RecomputeViolatedList(bool linear_only);
415 absl::Span<const int> ViolatedConstraints() const {
416 return violated_constraints_.values();
417 }
418
419 // Returns the number of constraints in ViolatedConstraints containing `var`.
421 return num_violated_constraint_per_var_ignoring_objective_[var];
422 }
423
424 // Indicates if the computed jump value is always the best choice.
426
427 const std::vector<int>& last_update_violation_changes() const {
428 return last_update_violation_changes_;
429 }
430
431 absl::Span<const int> ConstraintToVars(int c) const {
432 if (c < NumLinearConstraints()) {
433 return linear_evaluator_.ConstraintToVars(c);
434 }
435 return absl::MakeConstSpan(constraint_to_vars_[c - NumLinearConstraints()]);
436 }
437
438 // Note that the constraint indexing is different here than in the other
439 // functions.
440 absl::Span<const int> VarToGeneralConstraints(int var) const {
441 return var_to_constraints_[var];
442 }
443 absl::Span<const int> GeneralConstraintToVars(int general_c) const {
444 return constraint_to_vars_[general_c];
445 }
446
447 // TODO(user): Properly account all big time consumers.
448 double DeterministicTime() const {
449 return linear_evaluator_.DeterministicTime() + dtime_;
450 }
451
452 private:
453 void CompileConstraintsAndObjective(
454 const std::vector<bool>& ignored_constraints,
455 absl::Span<const ConstraintProto> additional_constraints);
456
457 void CompileOneConstraint(const ConstraintProto& ct_proto);
458 void BuildVarConstraintGraph();
459 void UpdateViolatedList(int c);
460
461 const CpModelProto& cp_model_;
462 const SatParameters& params_;
463 CpModelProto expanded_constraints_;
464 LinearIncrementalEvaluator linear_evaluator_;
465 std::vector<std::unique_ptr<CompiledConstraint>> constraints_;
466 std::vector<std::vector<int>> var_to_constraints_;
467 std::vector<double> var_to_dtime_estimate_;
468 std::vector<std::vector<int>> constraint_to_vars_;
469 std::vector<bool> jump_value_optimal_;
470 TimeLimit* time_limit_;
471
472 UnsafeDenseSet<int> violated_constraints_;
473 std::vector<int> num_violated_constraint_per_var_ignoring_objective_;
474
475 // Constraint index with changed violations.
476 std::vector<int> last_update_violation_changes_;
477
478 mutable double dtime_ = 0;
479};
480
481// ================================
482// Individual compiled constraints.
483// ================================
484
485// The violation of a bool_xor constraint is 0 or 1.
486class CompiledBoolXorConstraint : public CompiledConstraintWithProto {
487 public:
489 ~CompiledBoolXorConstraint() override = default;
490
492 absl::Span<const int64_t> solution) override;
494 int var, int64_t old_value,
495 absl::Span<const int64_t> solution_with_new_value) override;
496};
497
498// The violation of a lin_max constraint is:
499// - the sum(max(0, expr_value - target_value) forall expr). This part will be
500// maintained by the linear part.
501// - target_value - max(expressions) if positive.
503 public:
505 ~CompiledLinMaxConstraint() override = default;
506
508 absl::Span<const int64_t> solution) override;
509};
510
511// The violation of an int_prod constraint is
512// abs(value(target) - prod(value(expr)).
514 public:
516 ~CompiledIntProdConstraint() override = default;
517
519 absl::Span<const int64_t> solution) override;
520};
521
522// The violation of an int_div constraint is
523// abs(value(target) - value(expr0) / value(expr1)).
525 public:
527 ~CompiledIntDivConstraint() override = default;
528
530 absl::Span<const int64_t> solution) override;
531};
532
533// The violation of an int_mod constraint is defined as follow:
534//
535// if target and expr0 have the same sign:
536// min(
537// abs(value(target) - (value(expr0) % value(expr1))),
538// abs(value(target)) + abs((value(expr0) % value(expr1)) - value(expr1)),
539// abs(value(expr0) % value(expr1)) + abs(value(target) - value(expr1)),
540// )
541//
542// if target and expr0 have different sign:
543// abs(target) + abs(expr0)
544// Note: the modulo (expr1) is always fixed.
546 public:
548 ~CompiledIntModConstraint() override = default;
549
551 absl::Span<const int64_t> solution) override;
552};
553
554// The violation of a all_diff is the number of unordered pairs of expressions
555// with the same value.
557 public:
559 ~CompiledAllDiffConstraint() override = default;
560
562 absl::Span<const int64_t> solution) override;
563
564 private:
565 std::vector<int64_t> values_;
566};
567
568// This is more compact and faster to destroy than storing a
569// LinearExpressionProto.
572 const LinearExpressionProto& proto) {
573 if (!proto.vars().empty()) {
574 DCHECK_EQ(proto.vars().size(), 1);
575 var = proto.vars(0);
576 coeff = proto.coeffs(0);
577 }
578 offset = proto.offset();
579 }
580
581 void AppendVarTo(std::vector<int>& result) const {
582 if (coeff != 0) result.push_back(var);
584
585 int var = 0;
586 int64_t coeff = 0;
587 int64_t offset = 0;
590// Special constraint for no overlap between two intervals.
591// We usually expand small no-overlap in n^2 such constraints, so we want to
592// be compact and efficient here.
593template <bool has_enforcement = true>
595 public:
596 struct Interval {
597 explicit Interval(const ConstraintProto& x)
598 : start(x.interval().start()), end(x.interval().end()) {}
601 };
603 CompiledNoOverlapWithTwoIntervals(absl::Span<const int> enforcement_literals,
604 const ConstraintProto& x1,
606 : interval1_(x1), interval2_(x2) {
607 if (has_enforcement) {
608 enforcements_.assign(enforcement_literals.begin(),
609 enforcement_literals.end());
610 enforcements_.insert(enforcements_.end(),
611 x1.enforcement_literal().begin(),
612 x1.enforcement_literal().end());
613 enforcements_.insert(enforcements_.end(),
614 x2.enforcement_literal().begin(),
615 x2.enforcement_literal().end());
616 gtl::STLSortAndRemoveDuplicates(&enforcements_);
617 }
618 }
619
620 ~CompiledNoOverlapWithTwoIntervals() final = default;
621
622 int64_t ComputeViolation(absl::Span<const int64_t> solution) final {
623 // Optimization hack: If we create a ComputeViolationInternal() that we call
624 // from here and in ViolationDelta(), then the later is not inlined below in
625 // ViolationDelta() where it matter a lot for performance.
626 violation_ = 0;
628 return violation_;
629 }
630
631 int64_t ViolationDelta(
632 int /*var*/, int64_t /*old_value*/,
633 absl::Span<const int64_t> solution_with_new_value) final;
634
635 std::vector<int> UsedVariables(const CpModelProto& model_proto) const final;
636
637 private:
638 std::vector<int> enforcements_;
639 const Interval interval1_;
640 const Interval interval2_;
641};
642
643class CompiledNoOverlap2dConstraint : public CompiledConstraintWithProto {
644 public:
646 const CpModelProto& cp_model);
647 ~CompiledNoOverlap2dConstraint() override = default;
648
650 absl::Span<const int64_t> solution) override;
651
652 private:
653 const CpModelProto& cp_model_;
654};
655
656template <bool has_enforcement = true>
658 public:
659 struct Box {
660 Box(const ConstraintProto& x, const ConstraintProto& y)
661 : x_min(x.interval().start()),
662 x_max(x.interval().end()),
663 y_min(y.interval().start()),
664 y_max(y.interval().end()) {}
669 };
671 CompiledNoOverlap2dWithTwoBoxes(absl::Span<const int> enforcement_literals,
672 const ConstraintProto& x1,
674 const ConstraintProto& x2,
675 const ConstraintProto& y2)
676 : box1_(x1, y1), box2_(x2, y2) {
677 if (has_enforcement) {
678 enforcements_.assign(enforcement_literals.begin(),
679 enforcement_literals.end());
680 enforcements_.insert(enforcements_.end(),
681 x1.enforcement_literal().begin(),
682 x1.enforcement_literal().end());
683 enforcements_.insert(enforcements_.end(),
684 y1.enforcement_literal().begin(),
685 y1.enforcement_literal().end());
686 enforcements_.insert(enforcements_.end(),
687 x2.enforcement_literal().begin(),
688 x2.enforcement_literal().end());
689 enforcements_.insert(enforcements_.end(),
690 y2.enforcement_literal().begin(),
691 y2.enforcement_literal().end());
692 gtl::STLSortAndRemoveDuplicates(&enforcements_);
693 }
694 }
695
696 ~CompiledNoOverlap2dWithTwoBoxes() final = default;
697
698 int64_t ComputeViolation(absl::Span<const int64_t> solution) final {
699 // Optimization hack: If we create a ComputeViolationInternal() that we call
700 // from here and in ViolationDelta(), then the later is not inlined below in
701 // ViolationDelta() where it matter a lot for performance.
702 violation_ = 0;
704 return violation_;
705 }
706
707 // Note(user): this is the same implementation as the base one, but it
708 // avoid one virtual call !
709 int64_t ViolationDelta(
710 int /*var*/, int64_t /*old_value*/,
711 absl::Span<const int64_t> solution_with_new_value) final;
712
713 std::vector<int> UsedVariables(const CpModelProto& model_proto) const final;
714
715 private:
716 std::vector<int> enforcements_;
717 const Box box1_;
718 const Box box2_;
719};
720
721// This can be used to encode reservoir or a cumulative constraints for LS. We
722// have a set of event time, and we use for overall violation the sum of
723// overload over time.
724//
725// This version support an incremental computation when just a few events
726// changes, which is roughly O(n) instead of O(n log n) which makes it
727// significantly faster than recomputing and sorting the profile on each
728// ViolationDelta().
729class CompiledReservoirConstraint : public CompiledConstraint {
730 public:
731 CompiledReservoirConstraint(std::vector<int> enforcement_literals,
732 LinearExpressionProto capacity,
733 std::vector<std::optional<int>> is_active,
734 std::vector<LinearExpressionProto> times,
735 std::vector<LinearExpressionProto> demands)
736 : enforcement_literals_(std::move(enforcement_literals)),
737 capacity_(std::move(capacity)),
738 is_active_(std::move(is_active)),
739 times_(std::move(times)),
740 demands_(std::move(demands)) {
741 const int num_events = times_.size();
742 time_values_.resize(num_events, 0);
743 demand_values_.resize(num_events, 0);
744 InitializeDenseIndexToEvents();
745 }
746
747 int64_t ComputeViolation(absl::Span<const int64_t> solution) final;
748
749 void PerformMove(int /*var*/, int64_t /*old_value*/,
750 absl::Span<const int64_t> solution_with_new_value) final {
751 // TODO(user): we could probably be more incremental here, but it is a bit
752 // tricky to get right and not too important since the time is dominated by
753 // evaluating moves, not taking them.
754 ComputeViolation(solution_with_new_value);
755 }
756
757 int64_t ViolationDelta(
758 int var, int64_t /*old_value*/,
759 absl::Span<const int64_t> solution_with_new_value) final {
760 return IncrementalViolation(var, solution_with_new_value) - violation_;
761 }
762
763 std::vector<int> UsedVariables(const CpModelProto& model_proto) const final;
764
765 private:
766 // This works in O(n log n).
767 int64_t BuildProfileAndReturnViolation(absl::Span<const int64_t> solution);
768
769 // This works in O(n) + O(d log d) where d is the number of modified events
770 // compare to the base solution. In most situation it should be O(1).
771 int64_t IncrementalViolation(int var, absl::Span<const int64_t> solution);
772
773 // This is used to speed up IncrementalViolation().
774 void InitializeDenseIndexToEvents();
775 void AppendVariablesForEvent(int i, std::vector<int>* result) const;
776
777 // The const data from the constructor.
778 // Note that is_active_ might be empty if all events are mandatory.
779 const std::vector<int> enforcement_literals_;
780 const LinearExpressionProto capacity_;
781 const std::vector<std::optional<int>> is_active_;
782 const std::vector<LinearExpressionProto> times_;
783 const std::vector<LinearExpressionProto> demands_;
784
785 // Remap all UsedVariables() to a dense index in [0, num_used_vars).
786 absl::flat_hash_map<int, int> var_to_dense_index_;
787 CompactVectorVector<int, int> dense_index_to_events_;
788
789 struct Event {
790 int64_t time;
791 int64_t demand;
792 bool operator<(const Event& o) const { return time < o.time; }
793 };
794 std::vector<Event> profile_;
795 std::vector<Event> profile_delta_;
796
797 // This is filled by BuildProfileAndReturnViolation() and correspond to the
798 // value in the current solutions.
799 int64_t capacity_value_;
800 std::vector<int64_t> time_values_;
801 std::vector<int64_t> demand_values_;
802};
803
804} // namespace sat
805} // namespace operations_research
806
807#endif // ORTOOLS_SAT_CONSTRAINT_VIOLATION_H_
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
int64_t ViolationDeltaWhenEnforced(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value) override
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
int64_t ComputeViolation(absl::Span< const int64_t > solution) final
int64_t ViolationDelta(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value) final
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
virtual int64_t ViolationDeltaWhenEnforced(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value)
virtual int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution)=0
virtual std::vector< int > UsedVariables(const CpModelProto &model_proto) const =0
virtual void PerformMove(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value)
virtual int64_t ComputeViolation(absl::Span< const int64_t > solution)=0
void InitializeViolation(absl::Span< const int64_t > solution)
virtual int64_t ViolationDelta(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value)
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
int64_t ComputeViolationWhenEnforced(absl::Span< const int64_t > solution) override
CompiledNoOverlap2dConstraint(const ConstraintProto &ct_proto, const CpModelProto &cp_model)
int64_t ViolationDelta(int, int64_t, absl::Span< const int64_t > solution_with_new_value) final
int64_t ComputeViolation(absl::Span< const int64_t > solution) final
CompiledNoOverlap2dWithTwoBoxes(absl::Span< const int > enforcement_literals, const ConstraintProto &x1, const ConstraintProto &y1, const ConstraintProto &x2, const ConstraintProto &y2)
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
int64_t ViolationDelta(int, int64_t, absl::Span< const int64_t > solution_with_new_value) final
int64_t ComputeViolation(absl::Span< const int64_t > solution) final
CompiledNoOverlapWithTwoIntervals(absl::Span< const int > enforcement_literals, const ConstraintProto &x1, const ConstraintProto &x2)
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
CompiledReservoirConstraint(std::vector< int > enforcement_literals, LinearExpressionProto capacity, std::vector< std::optional< int > > is_active, std::vector< LinearExpressionProto > times, std::vector< LinearExpressionProto > demands)
void PerformMove(int, int64_t, absl::Span< const int64_t > solution_with_new_value) final
int64_t ComputeViolation(absl::Span< const int64_t > solution) final
int64_t ViolationDelta(int var, int64_t, absl::Span< const int64_t > solution_with_new_value) final
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
::int32_t enforcement_literal(int index) const
void UpdateScoreOnWeightUpdate(int c, absl::Span< const int64_t > jump_deltas, absl::Span< double > var_to_score_change)
double WeightedViolationDelta(absl::Span< const double > weights, int var, int64_t delta) const
void ComputeInitialActivities(absl::Span< const int64_t > solution)
void AddLiteral(int ct_index, int lit, int64_t coeff=1)
double WeightedViolation(absl::Span< const double > weights) const
void UpdateVariableAndScores(int var, int64_t delta, absl::Span< const double > weights, absl::Span< const int64_t > jump_deltas, absl::Span< double > jump_scores, std::vector< int > *constraints_with_changed_violations)
absl::Span< const int > VariablesAffectedByLastUpdate() const
void AddLinearExpression(int ct_index, const LinearExpressionProto &expr, int64_t multiplier)
void PrecomputeCompactView(absl::Span< const int64_t > var_max_variation)
absl::Span< const int > ConstraintToVars(int c) const
std::vector< int64_t > SlopeBreakpoints(int var, int64_t current_value, const Domain &var_domain) const
void AddTerm(int ct_index, int var, int64_t coeff, int64_t offset=0)
int NumViolatedConstraintsForVarIgnoringObjective(int var) const
double WeightedViolation(absl::Span< const double > weights) const
void ComputeAllNonLinearViolations(absl::Span< const int64_t > solution)
const LinearIncrementalEvaluator & LinearEvaluator()
void UpdateLinearScores(int var, int64_t old_value, int64_t new_value, absl::Span< const double > weights, absl::Span< const int64_t > jump_deltas, absl::Span< double > jump_scores)
bool VariableOnlyInLinearConstraintWithConvexViolationChange(int var) const
void ComputeAllViolations(absl::Span< const int64_t > solution)
void UpdateNonLinearViolations(int var, int64_t old_value, absl::Span< const int64_t > new_solution)
bool ReduceObjectiveBounds(int64_t lb, int64_t ub)
absl::Span< const int > ViolatedConstraints() const
absl::Span< const int > VariablesAffectedByLastLinearUpdate() const
LsEvaluator(const CpModelProto &cp_model, const SatParameters &params, TimeLimit *time_limit)
absl::Span< const int > ConstraintToVars(int c) const
LinearIncrementalEvaluator * MutableLinearEvaluator()
absl::Span< const int > VarToGeneralConstraints(int var) const
double WeightedViolationDelta(bool linear_only, absl::Span< const double > weights, int var, int64_t delta, absl::Span< int64_t > mutable_solution) const
absl::Span< const int > GeneralConstraintToVars(int general_c) const
const std::vector< int > & last_update_violation_changes() const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:55
OR-Tools root namespace.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
DenseSet< T, false > UnsafeDenseSet
Definition dense_set.h:135
ClosedInterval::Iterator end(ClosedInterval interval)
STL namespace.
Box(const ConstraintProto &x, const ConstraintProto &y)
ViewOfAffineLinearExpressionProto(const LinearExpressionProto &proto)