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