OR-Tools  9.3
piecewise_linear_function.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <set>
18#include <string>
19#include <vector>
20
21#include "absl/container/btree_set.h"
22#include "absl/strings/str_format.h"
24
25namespace operations_research {
26namespace {
27// If the x value is in the function's domain, it returns the index of the
28// segment it belongs to. The segments are closed to the left and open to
29// the right, hence if x is a common endpoint of two segments, it returns
30// the index of the right segment. If the x value is not in the function's
31// domain, it returns the index of the previous segment or kNotFound if x
32// is before the first segment's start.
33int FindSegmentIndex(const std::vector<PiecewiseSegment>& segments, int64_t x) {
34 if (segments.empty() || segments.front().start_x() > x) {
36 }
37
38 // Returns an iterator pointing to the first segment whose the x coordinate
39 // of its start point which compares greater than the x value.
40 std::vector<PiecewiseSegment>::const_iterator position = std::upper_bound(
41 segments.begin(), segments.end(), x, PiecewiseSegment::FindComparator);
42 if (position == segments.end()) {
43 return segments.size() - 1;
44 }
45 position -= position->start_x() > x ? 1 : 0;
46
47 return position - segments.begin();
48}
49
50inline bool IsAtBounds(int64_t value) {
51 return value == kint64min || value == kint64max;
52}
53
54inline bool PointInsideRange(int64_t point, int64_t range_start,
55 int64_t range_end) {
56 return range_start <= point && range_end >= point;
57}
58
59// Checks whether two segments form a convex pair, i.e. they are continuous and
60// the slope of the right is bigger than the slope of the left.
61inline bool FormConvexPair(const PiecewiseSegment& left,
62 const PiecewiseSegment& right) {
63 return right.slope() >= left.slope() && right.start_x() == left.end_x() &&
64 right.start_y() == left.end_y();
65}
66
67uint64_t UnsignedCapAdd(uint64_t left, uint64_t right) {
68 return left > kuint64max - right ? kuint64max : left + right;
69}
70
71uint64_t UnsignedCapProd(uint64_t left, uint64_t right) {
72 if (right == 0) return 0;
73 if (left > kuint64max / right) return kuint64max;
74 return left * right;
75}
76} // namespace
77
78PiecewiseSegment::PiecewiseSegment(int64_t point_x, int64_t point_y,
79 int64_t slope, int64_t other_point_x)
80 : slope_(slope), reference_x_(point_x), reference_y_(point_y) {
81 start_x_ = std::min(point_x, other_point_x);
82 end_x_ = std::max(point_x, other_point_x);
83 intersection_y_ =
84 reference_x_ < 0 ? SafeValuePostReference(0) : SafeValuePreReference(0);
85}
86
87int64_t PiecewiseSegment::Value(int64_t x) const {
88 CHECK_GE(x, start_x_);
89 CHECK_LE(x, end_x_);
90
91 const int64_t span_x = CapSub(x, reference_x_);
92
93 if (span_x == kint64max) {
94 return SafeValuePostReference(x);
95 }
96 if (span_x == kint64min) {
97 return SafeValuePreReference(x);
98 }
99
100 const int64_t span_y = CapProd(slope_, span_x);
101 if (IsAtBounds(span_y)) {
102 if (span_x >= 0) {
103 return SafeValuePostReference(x);
104 } else {
105 return SafeValuePreReference(x);
106 }
107 }
108
109 const int64_t value = CapAdd(reference_y_, span_y);
110 if (IsAtBounds(value)) {
111 if (span_x >= 0) {
112 return SafeValuePostReference(x);
113 } else {
114 return SafeValuePreReference(x);
115 }
116 } else {
117 return value;
118 }
119}
120
121int64_t PiecewiseSegment::SafeValuePostReference(int64_t x) const {
122 DCHECK_GE(x, reference_x_);
123 const uint64_t span_x = static_cast<uint64_t>(x) - reference_x_;
124 if (span_x == 0) {
125 return reference_y_;
126 }
127 if (slope_ == 0) {
128 // Zero slope segment.
129 return reference_y_;
130 } else if (slope_ > 0) {
131 // Positive slope segment.
132 const uint64_t span_y = UnsignedCapProd(span_x, slope_);
133 if (reference_y_ == 0) {
134 return span_y > kint64max ? kint64max : span_y;
135 } else if (reference_y_ > 0) {
136 const uint64_t unsigned_sum = UnsignedCapAdd(reference_y_, span_y);
137 return unsigned_sum > kint64max ? kint64max
138 : static_cast<int64_t>(unsigned_sum);
139 } else {
140 const uint64_t opp_reference_y = -static_cast<uint64_t>(reference_y_);
141 if (span_y >= opp_reference_y) {
142 return span_y - opp_reference_y > kint64max
143 ? kint64max
144 : static_cast<int64_t>(span_y - opp_reference_y);
145 } else {
146 return opp_reference_y - span_y > static_cast<uint64_t>(kint64max) + 1
147 ? kint64min
148 : -static_cast<int64_t>(opp_reference_y - span_y);
149 }
150 }
151 } else {
152 // Negative slope segment.
153 const uint64_t span_y = UnsignedCapProd(span_x, -slope_);
154 if (reference_y_ == 0) {
155 return span_y > kint64max ? kint64min : -static_cast<int64_t>(span_y);
156 } else if (reference_y_ < 0) {
157 const uint64_t opp_reference_y = -static_cast<uint64_t>(reference_y_);
158 const uint64_t opp_unsigned_sum = UnsignedCapAdd(opp_reference_y, span_y);
159 return opp_unsigned_sum > kint64max
160 ? kint64min
161 : -static_cast<int64_t>(opp_unsigned_sum);
162 } else {
163 if (reference_y_ >= span_y) {
164 return reference_y_ - span_y > kint64max
165 ? kint64max
166 : static_cast<int64_t>(reference_y_ - span_y);
167 } else {
168 return span_y - reference_y_ > static_cast<uint64_t>(kint64max) + 1
169 ? kint64min
170 : -static_cast<int64_t>(span_y - reference_y_);
171 }
172 }
173 }
174}
175
176int64_t PiecewiseSegment::SafeValuePreReference(int64_t x) const {
177 DCHECK_LE(x, reference_x_);
178 const uint64_t span_x = static_cast<uint64_t>(reference_x_) - x;
179 if (slope_ == 0) {
180 // Zero slope segment.
181 return reference_y_;
182 } else if (slope_ > 0) {
183 // Positive slope segment.
184 const uint64_t span_y = UnsignedCapProd(span_x, slope_);
185 if (reference_y_ == 0) {
186 return span_y > kint64max ? kint64min : -static_cast<int64_t>(span_y);
187 } else if (reference_y_ > 0) {
188 if (reference_y_ >= span_y) {
189 return reference_y_ - span_y > kint64max
190 ? kint64max
191 : static_cast<int64_t>(reference_y_ - span_y);
192 } else {
193 return span_y - reference_y_ > static_cast<uint64_t>(kint64max) + 1
194 ? kint64min
195 : -static_cast<uint64_t>(span_y - reference_y_);
196 }
197 } else {
198 const uint64_t opp_reference_y = -static_cast<uint64_t>(reference_y_);
199 const uint64_t opp_unsigned_sum = UnsignedCapAdd(opp_reference_y, span_y);
200 return opp_unsigned_sum > kint64max
201 ? kint64min
202 : -static_cast<uint64_t>(opp_unsigned_sum);
203 }
204 } else {
205 // Negative slope segment.
206 const uint64_t span_y = UnsignedCapProd(span_x, -slope_);
207 if (reference_y_ == 0) {
208 return span_y > kint64max ? kint64max : span_y;
209 } else if (reference_y_ < 0) {
210 const uint64_t opp_reference_y = -static_cast<uint64_t>(reference_y_);
211 if (span_y >= opp_reference_y) {
212 return span_y - opp_reference_y > kint64max
213 ? kint64max
214 : static_cast<int64_t>(span_y - opp_reference_y);
215 } else {
216 return opp_reference_y - span_y > static_cast<uint64_t>(kint64max) + 1
217 ? kint64min
218 : -static_cast<uint64_t>(opp_reference_y - span_y);
219 }
220 } else {
221 const uint64_t unsigned_sum = UnsignedCapAdd(reference_y_, span_y);
222 return unsigned_sum > kint64max ? kint64max
223 : static_cast<int64_t>(unsigned_sum);
224 }
225 }
226}
227
229 const PiecewiseSegment& segment2) {
230 return segment1.start_x_ < segment2.start_x_;
231}
232
234 const PiecewiseSegment& segment) {
235 return point == kint64min || point < segment.start_x();
236}
237
238void PiecewiseSegment::ExpandEnd(int64_t end_x) {
239 end_x_ = std::max(end_x_, end_x);
240}
241
243 if (IsAtBounds(CapAdd(reference_x_, constant))) {
244 LOG(ERROR) << "Segment Overflow: " << DebugString();
245 return;
246 }
247 start_x_ = CapAdd(start_x_, constant);
248 end_x_ = CapAdd(end_x_, constant);
249 reference_x_ = CapAdd(reference_x_, constant);
250}
251
253 if (IsAtBounds(CapAdd(reference_y_, constant))) {
254 LOG(ERROR) << "Segment Overflow: " << DebugString();
255 return;
256 }
257 reference_y_ = CapAdd(reference_y_, constant);
258}
259
260std::string PiecewiseSegment::DebugString() const {
261 std::string result = absl::StrFormat(
262 "PiecewiseSegment(<start: (%d, %d), end: (%d, %d), "
263 "reference: (%d, %d), slope = %d>)",
264 start_x_, Value(start_x_), end_x_, Value(end_x_), reference_x_,
265 reference_y_, slope_);
266 return result;
267}
268
270
271PiecewiseLinearFunction::PiecewiseLinearFunction(
272 std::vector<PiecewiseSegment> segments)
273 : is_modified_(true),
274 is_convex_(false),
275 is_non_decreasing_(false),
276 is_non_increasing_(false) {
277 // Sort the segments in ascending order of start.
278 std::sort(segments.begin(), segments.end(), PiecewiseSegment::SortComparator);
279 // Check for overlapping segments.
280 for (int i = 0; i < segments.size() - 1; ++i) {
281 if (segments[i].end_x() > segments[i + 1].start_x()) {
282 LOG(FATAL) << "Overlapping segments: " << segments[i].DebugString()
283 << " & " << segments[i + 1].DebugString();
284 }
285 }
286 // Construct the piecewise linear function.
287 for (const auto& segment : segments) {
288 InsertSegment(segment);
289 }
290}
291
293 std::vector<int64_t> points_x, std::vector<int64_t> points_y,
294 std::vector<int64_t> slopes, std::vector<int64_t> other_points_x) {
295 CHECK_EQ(points_x.size(), points_y.size());
296 CHECK_EQ(points_x.size(), other_points_x.size());
297 CHECK_EQ(points_x.size(), slopes.size());
298 CHECK_GT(points_x.size(), 0);
299
300 std::vector<PiecewiseSegment> segments;
301 for (int i = 0; i < points_x.size(); ++i) {
302 segments.push_back(PiecewiseSegment(points_x[i], points_y[i], slopes[i],
303 other_points_x[i]));
304 }
305
306 return new PiecewiseLinearFunction(std::move(segments));
307}
308
310 std::vector<int64_t> points_x, std::vector<int64_t> points_y,
311 std::vector<int64_t> other_points_x) {
312 CHECK_EQ(points_x.size(), points_y.size());
313 CHECK_EQ(points_x.size(), other_points_x.size());
314 CHECK_GT(points_x.size(), 0);
315
316 std::vector<PiecewiseSegment> segments;
317 for (int i = 0; i < points_x.size(); ++i) {
318 segments.push_back(
319 PiecewiseSegment(points_x[i], points_y[i], 0, other_points_x[i]));
320 }
321
322 return new PiecewiseLinearFunction(std::move(segments));
323}
324
326 int64_t initial_level, std::vector<int64_t> points_x,
327 std::vector<int64_t> slopes) {
328 CHECK_EQ(points_x.size(), slopes.size() - 1);
329 CHECK_GT(points_x.size(), 0);
330
331 int64_t level = initial_level;
332 std::vector<PiecewiseSegment> segments;
333 PiecewiseSegment segment =
334 PiecewiseSegment(points_x[0], level, slopes[0], kint64min);
335 segments.push_back(segment);
336 level = segment.Value(points_x[0]);
337 for (int i = 1; i < points_x.size(); ++i) {
338 PiecewiseSegment segment =
339 PiecewiseSegment(points_x[i - 1], level, slopes[i], points_x[i]);
340 segments.push_back(segment);
341 level = segment.Value(points_x[i]);
342 }
343 segments.push_back(
344 PiecewiseSegment(points_x.back(), level, slopes.back(), kint64max));
345
346 return new PiecewiseLinearFunction(std::move(segments));
347}
348
350 int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x) {
351 // Visual studio 2013: We cannot inline the vector in the
352 // PiecewiseLinearFunction ctor.
353 std::vector<PiecewiseSegment> segments = {
354 PiecewiseSegment(point_x, point_y, slope, other_point_x)};
355 return new PiecewiseLinearFunction(std::move(segments));
356}
357
359 int64_t point_x, int64_t point_y, int64_t slope) {
360 std::vector<PiecewiseSegment> segments = {
361 PiecewiseSegment(point_x, point_y, slope, kint64max)};
362 return new PiecewiseLinearFunction(std::move(segments));
363}
364
366 int64_t point_x, int64_t point_y, int64_t slope) {
367 std::vector<PiecewiseSegment> segments = {
368 PiecewiseSegment(point_x, point_y, slope, kint64min)};
369 return new PiecewiseLinearFunction(std::move(segments));
370}
371
373 int64_t slope, int64_t value) {
374 std::vector<PiecewiseSegment> segments = {
375 PiecewiseSegment(0, 0, 0, kint64min),
376 PiecewiseSegment(0, value, slope, kint64max)};
377 CHECK_GE(slope, 0);
378 CHECK_GE(value, 0);
379 return new PiecewiseLinearFunction(std::move(segments));
380}
381
383 int64_t reference, int64_t earliness_slope, int64_t tardiness_slope) {
384 std::vector<PiecewiseSegment> segments = {
385 PiecewiseSegment(reference, 0, -earliness_slope, kint64min),
386 PiecewiseSegment(reference, 0, tardiness_slope, kint64max)};
387 CHECK_GE(earliness_slope, 0);
388 CHECK_GE(tardiness_slope, 0);
389 return new PiecewiseLinearFunction(std::move(segments));
390}
391
394 int64_t early_slack, int64_t late_slack, int64_t earliness_slope,
395 int64_t tardiness_slope) {
396 std::vector<PiecewiseSegment> segments = {
397 PiecewiseSegment(early_slack, 0, -earliness_slope, kint64min),
398 PiecewiseSegment(early_slack, 0, 0, late_slack),
399 PiecewiseSegment(late_slack, 0, tardiness_slope, kint64max)};
400
401 CHECK_GE(earliness_slope, 0);
402 CHECK_GE(tardiness_slope, 0);
403 return new PiecewiseLinearFunction(std::move(segments));
404}
405
407 int index = FindSegmentIndex(segments_, x);
408 if (index == kNotFound) {
409 return false;
410 }
411 if (segments_[index].end_x() < x) {
412 return false;
413 }
414 return true;
415}
416
418 const_cast<PiecewiseLinearFunction*>(this)->UpdateStatus();
419 return is_convex_;
420}
421
423 const_cast<PiecewiseLinearFunction*>(this)->UpdateStatus();
424 return is_non_decreasing_;
425}
426
428 const_cast<PiecewiseLinearFunction*>(this)->UpdateStatus();
429 return is_non_increasing_;
430}
431
432int64_t PiecewiseLinearFunction::Value(int64_t x) const {
433 if (!InDomain(x)) {
434 // TODO(user): Allow the user to specify the
435 // undefined value and use kint64max as the default.
436 return kint64max;
437 }
438 const int index = FindSegmentIndex(segments_, x);
439 return segments_[index].Value(x);
440}
441
442int64_t PiecewiseLinearFunction::GetMaximum(int64_t range_start,
443 int64_t range_end) const {
444 if (IsNonDecreasing() && InDomain(range_end)) {
445 return Value(range_end);
446 } else if (IsNonIncreasing() && InDomain(range_start)) {
447 return Value(range_start);
448 }
449 int start_segment = -1;
450 int end_segment = -1;
451 if (!FindSegmentIndicesFromRange(range_start, range_end, &start_segment,
452 &end_segment)) {
453 return kint64max;
454 }
455 CHECK_GE(end_segment, start_segment);
456
457 int64_t range_maximum = kint64min;
458 if (InDomain(range_start)) {
459 range_maximum = std::max(Value(range_start), range_maximum);
460 }
461 if (InDomain(range_end)) {
462 range_maximum = std::max(Value(range_end), range_maximum);
463 }
464
465 for (int i = std::max(0, start_segment); i <= end_segment; ++i) {
466 if (PointInsideRange(segments_[i].start_x(), range_start, range_end)) {
467 range_maximum = std::max(range_maximum, segments_[i].start_y());
468 }
469 if (PointInsideRange(segments_[i].end_x(), range_start, range_end)) {
470 range_maximum = std::max(range_maximum, segments_[i].end_y());
471 }
472 }
473 return range_maximum;
474}
475
476int64_t PiecewiseLinearFunction::GetMinimum(int64_t range_start,
477 int64_t range_end) const {
478 if (IsNonDecreasing() && InDomain(range_start)) {
479 return Value(range_start);
480 } else if (IsNonIncreasing() && InDomain(range_end)) {
481 return Value(range_end);
482 }
483 int start_segment = -1;
484 int end_segment = -1;
485 if (!FindSegmentIndicesFromRange(range_start, range_end, &start_segment,
486 &end_segment)) {
487 return kint64max;
488 }
489 CHECK_GE(end_segment, start_segment);
490
491 int64_t range_minimum = kint64max;
492 if (InDomain(range_start)) {
493 range_minimum = std::min(Value(range_start), range_minimum);
494 }
495 if (InDomain(range_end)) {
496 range_minimum = std::min(Value(range_end), range_minimum);
497 }
498
499 for (int i = std::max(0, start_segment); i <= end_segment; ++i) {
500 if (PointInsideRange(segments_[i].start_x(), range_start, range_end)) {
501 range_minimum = std::min(range_minimum, segments_[i].start_y());
502 }
503 if (PointInsideRange(segments_[i].end_x(), range_start, range_end)) {
504 range_minimum = std::min(range_minimum, segments_[i].end_y());
505 }
506 }
507 return range_minimum;
508}
509
511 return GetMaximum(segments_.front().start_x(), segments_.back().end_x());
512}
513
515 return GetMinimum(segments_.front().start_x(), segments_.back().end_x());
516}
517
518std::pair<int64_t, int64_t>
520 int64_t range_end,
521 int64_t value) const {
522 return GetSmallestRangeInValueRange(range_start, range_end, value, kint64max);
523}
524
525std::pair<int64_t, int64_t>
527 int64_t range_end,
528 int64_t value) const {
529 return GetSmallestRangeInValueRange(range_start, range_end, kint64min, value);
530}
531
532namespace {
533std::pair<int64_t, int64_t> ComputeXFromY(int64_t start_x, int64_t start_y,
534 int64_t slope, int64_t y) {
535 DCHECK_NE(slope, 0);
536 const int64_t delta_y = CapSub(y, start_y);
537 const int64_t delta_x = delta_y / slope;
538 if ((delta_y >= 0 && slope >= 0) || (delta_y <= 0 && slope <= 0)) {
539 const int64_t delta_x_down = delta_x;
540 const int64_t delta_x_up = delta_y % slope == 0 ? delta_x : delta_x + 1;
541 return {delta_x_down + start_x, delta_x_up + start_x};
542 } else {
543 const int64_t delta_x_down = delta_y % slope == 0 ? delta_x : delta_x - 1;
544 const int64_t delta_x_up = -(-delta_y / slope);
545 return {delta_x_down + start_x, delta_x_up + start_x};
546 }
547}
548
549std::pair<int64_t, int64_t> GetRangeInValueRange(int64_t start_x, int64_t end_x,
550 int64_t start_y, int64_t end_y,
551 int64_t slope,
552 int64_t value_min,
553 int64_t value_max) {
554 if ((start_y > value_max && end_y > value_max) ||
555 (start_y < value_min && end_y < value_min)) {
556 return {kint64max, kint64min};
557 }
558 std::pair<int64_t, int64_t> x_range_max = {kint64max, kint64min};
559 if (start_y <= value_max && end_y <= value_max) {
560 x_range_max = {start_x, end_x};
561 } else if (start_y <= value_max || end_y <= value_max) {
562 const auto x = start_x == kint64min
563 ? ComputeXFromY(end_x, end_y, slope, value_max)
564 : ComputeXFromY(start_x, start_y, slope, value_max);
565 if (end_y <= value_max) {
566 x_range_max = {x.second, end_x};
567 } else {
568 x_range_max = {start_x, x.first};
569 }
570 }
571 std::pair<int64_t, int64_t> x_range_min = {kint64max, kint64min};
572 if (start_y >= value_min && end_y >= value_min) {
573 x_range_min = {start_x, end_x};
574 } else if (start_y >= value_min || end_y >= value_min) {
575 const auto x = start_x == kint64min
576 ? ComputeXFromY(end_x, end_y, slope, value_min)
577 : ComputeXFromY(start_x, start_y, slope, value_min);
578 if (end_y >= value_min) {
579 x_range_min = {x.second, end_x};
580 } else {
581 x_range_min = {start_x, x.first};
582 }
583 }
584 if (x_range_min.first > x_range_max.second ||
585 x_range_max.first > x_range_min.second) {
586 return {kint64max, kint64min};
587 }
588 return {std::max(x_range_min.first, x_range_max.first),
589 std::min(x_range_min.second, x_range_max.second)};
590}
591} // namespace
592
593std::pair<int64_t, int64_t>
595 int64_t range_end,
596 int64_t value_min,
597 int64_t value_max) const {
598 int64_t reduced_range_start = kint64max;
599 int64_t reduced_range_end = kint64min;
600 int start_segment = -1;
601 int end_segment = -1;
602 if (!FindSegmentIndicesFromRange(range_start, range_end, &start_segment,
603 &end_segment)) {
604 return {reduced_range_start, reduced_range_end};
605 }
606 for (int i = std::max(0, start_segment); i <= end_segment; ++i) {
607 const auto& segment = segments_[i];
608 const int64_t start_x = std::max(range_start, segment.start_x());
609 const int64_t end_x = std::min(range_end, segment.end_x());
610 const int64_t start_y = segment.Value(start_x);
611 const int64_t end_y = segment.Value(end_x);
612 const std::pair<int64_t, int64_t> range = GetRangeInValueRange(
613 start_x, end_x, start_y, end_y, segment.slope(), value_min, value_max);
614 reduced_range_start = std::min(reduced_range_start, range.first);
615 reduced_range_end = std::max(reduced_range_end, range.second);
616 }
617 return {reduced_range_start, reduced_range_end};
618}
619
621 is_modified_ = true;
622 for (int i = 0; i < segments_.size(); ++i) {
623 segments_[i].AddConstantToX(constant);
624 }
625}
626
628 is_modified_ = true;
629 for (int i = 0; i < segments_.size(); ++i) {
630 segments_[i].AddConstantToY(constant);
631 }
632}
633
635 Operation(other, [](int64_t a, int64_t b) { return CapAdd(a, b); });
636}
637
639 Operation(other, [](int64_t a, int64_t b) { return CapSub(a, b); });
640}
641
642std::vector<PiecewiseLinearFunction*>
644 CHECK_GE(segments_.size(), 1);
645 if (IsConvex()) {
646 return {new PiecewiseLinearFunction(segments_)};
647 }
648
649 std::vector<PiecewiseLinearFunction*> convex_functions;
650 std::vector<PiecewiseSegment> convex_segments;
651
652 for (const PiecewiseSegment& segment : segments_) {
653 if (convex_segments.empty()) {
654 convex_segments.push_back(segment);
655 continue;
656 }
657
658 const PiecewiseSegment& last = convex_segments.back();
659 if (FormConvexPair(last, segment)) {
660 // The segment belongs to the convex sub-function formulated up to now.
661 convex_segments.push_back(segment);
662 } else {
663 convex_functions.push_back(new PiecewiseLinearFunction(convex_segments));
664 convex_segments.clear();
665 convex_segments.push_back(segment);
666 }
667 }
668
669 if (!convex_segments.empty()) {
670 convex_functions.push_back(
671 new PiecewiseLinearFunction(std::move(convex_segments)));
672 }
673 return convex_functions;
674}
675
677 std::string result = "PiecewiseLinearFunction(";
678 for (int i = 0; i < segments_.size(); ++i) {
679 result.append(segments_[i].DebugString());
680 result.append(" ");
681 }
682 return result;
683}
684
685void PiecewiseLinearFunction::InsertSegment(const PiecewiseSegment& segment) {
686 is_modified_ = true;
687 // No intersection.
688 if (segments_.empty() || segments_.back().end_x() < segment.start_x()) {
689 segments_.push_back(segment);
690 return;
691 }
692
693 // Common endpoint.
694 if (segments_.back().end_x() == segment.start_x()) {
695 if (segments_.back().end_y() == segment.start_y() &&
696 segments_.back().slope() == segment.slope()) {
697 segments_.back().ExpandEnd(segment.end_x());
698 return;
699 }
700 segments_.push_back(segment);
701 }
702}
703
704void PiecewiseLinearFunction::Operation(
705 const PiecewiseLinearFunction& other,
706 const std::function<int64_t(int64_t, int64_t)>& operation) {
707 is_modified_ = true;
708 std::vector<PiecewiseSegment> own_segments;
709 const std::vector<PiecewiseSegment>& other_segments = other.segments();
710 own_segments.swap(segments_);
711
712 absl::btree_set<int64_t> start_x_points;
713 for (int i = 0; i < own_segments.size(); ++i) {
714 start_x_points.insert(own_segments[i].start_x());
715 }
716 for (int i = 0; i < other_segments.size(); ++i) {
717 start_x_points.insert(other_segments[i].start_x());
718 }
719
720 for (int64_t start_x : start_x_points) {
721 const int own_index = FindSegmentIndex(own_segments, start_x);
722 const int other_index = FindSegmentIndex(other_segments, start_x);
723 if (own_index >= 0 && other_index >= 0) {
724 const PiecewiseSegment& own_segment = own_segments[own_index];
725 const PiecewiseSegment& other_segment = other_segments[other_index];
726
727 const int64_t end_x =
728 std::min(own_segment.end_x(), other_segment.end_x());
729 const int64_t start_y =
730 operation(own_segment.Value(start_x), other_segment.Value(start_x));
731 const int64_t end_y =
732 operation(own_segment.Value(end_x), other_segment.Value(end_x));
733 const int64_t slope =
734 operation(own_segment.slope(), other_segment.slope());
735
736 int64_t point_x, point_y, other_point_x;
737 if (IsAtBounds(start_y)) {
738 point_x = end_x;
739 point_y = end_y;
740 other_point_x = start_x;
741 } else {
742 point_x = start_x;
743 point_y = start_y;
744 other_point_x = end_x;
745 }
746 InsertSegment(PiecewiseSegment(point_x, point_y, slope, other_point_x));
747 }
748 }
749}
750
751bool PiecewiseLinearFunction::FindSegmentIndicesFromRange(
752 int64_t range_start, int64_t range_end, int* start_segment,
753 int* end_segment) const {
754 *start_segment = FindSegmentIndex(segments_, range_start);
755 *end_segment = FindSegmentIndex(segments_, range_end);
756 if (*start_segment == *end_segment) {
757 if (*start_segment < 0) {
758 // Given range before function's domain start.
759 return false;
760 }
761 if (segments_[*start_segment].end_x() < range_start) {
762 // Given range in a hole of the function's domain.
763 return false;
764 }
765 }
766 return true;
767}
768
769bool PiecewiseLinearFunction::IsConvexInternal() const {
770 for (int i = 1; i < segments_.size(); ++i) {
771 if (!FormConvexPair(segments_[i - 1], segments_[i])) {
772 return false;
773 }
774 }
775 return true;
776}
777
778bool PiecewiseLinearFunction::IsNonDecreasingInternal() const {
779 int64_t value = kint64min;
780 for (const auto& segment : segments_) {
781 const int64_t start_y = segment.start_y();
782 const int64_t end_y = segment.end_y();
783 if (end_y < start_y || start_y < value) return false;
784 value = end_y;
785 }
786 return true;
787}
788
789bool PiecewiseLinearFunction::IsNonIncreasingInternal() const {
790 int64_t value = kint64max;
791 for (const auto& segment : segments_) {
792 const int64_t start_y = segment.start_y();
793 const int64_t end_y = segment.end_y();
794 if (end_y > start_y || start_y > value) return false;
795 value = end_y;
796 }
797 return true;
798}
799
800} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define LOG(severity)
Definition: base/logging.h:420
#define CHECK_LE(val1, val2)
Definition: base/logging.h:705
static PiecewiseLinearFunction * CreateRightRayFunction(int64_t point_x, int64_t point_y, int64_t slope)
static PiecewiseLinearFunction * CreateLeftRayFunction(int64_t point_x, int64_t point_y, int64_t slope)
static PiecewiseLinearFunction * CreateStepFunction(std::vector< int64_t > points_x, std::vector< int64_t > points_y, std::vector< int64_t > other_points_x)
std::pair< int64_t, int64_t > GetSmallestRangeInValueRange(int64_t range_start, int64_t range_end, int64_t value_min, int64_t value_max) const
std::vector< PiecewiseLinearFunction * > DecomposeToConvexFunctions() const
void Add(const PiecewiseLinearFunction &other)
static PiecewiseLinearFunction * CreateOneSegmentFunction(int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x)
std::pair< int64_t, int64_t > GetSmallestRangeGreaterThanValue(int64_t range_start, int64_t range_end, int64_t value) const
void Subtract(const PiecewiseLinearFunction &other)
std::pair< int64_t, int64_t > GetSmallestRangeLessThanValue(int64_t range_start, int64_t range_end, int64_t value) const
const std::vector< PiecewiseSegment > & segments() const
static PiecewiseLinearFunction * CreatePiecewiseLinearFunction(std::vector< int64_t > points_x, std::vector< int64_t > points_y, std::vector< int64_t > slopes, std::vector< int64_t > other_points_x)
static PiecewiseLinearFunction * CreateEarlyTardyFunctionWithSlack(int64_t early_slack, int64_t late_slack, int64_t earliness_slope, int64_t tardiness_slope)
static PiecewiseLinearFunction * CreateFullDomainFunction(int64_t initial_level, std::vector< int64_t > points_x, std::vector< int64_t > slopes)
static PiecewiseLinearFunction * CreateEarlyTardyFunction(int64_t reference, int64_t earliness_slope, int64_t tardiness_slope)
static PiecewiseLinearFunction * CreateFixedChargeFunction(int64_t slope, int64_t value)
PiecewiseSegment(int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x)
static bool FindComparator(int64_t point, const PiecewiseSegment &segment)
static bool SortComparator(const PiecewiseSegment &segment1, const PiecewiseSegment &segment2)
int64_t b
int64_t a
int64_t value
double upper_bound
static const uint64_t kuint64max
static const int64_t kint64max
static const int64_t kint64min
int index
const int ERROR
Definition: log_severity.h:32
const int FATAL
Definition: log_severity.h:32
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
const double constant