OR-Tools  9.2
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"
23 #include "ortools/base/logging.h"
24 
25 namespace operations_research {
26 namespace {
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.
33 int 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 
50 inline bool IsAtBounds(int64_t value) {
51  return value == kint64min || value == kint64max;
52 }
53 
54 inline 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.
61 inline 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 
67 uint64_t UnsignedCapAdd(uint64_t left, uint64_t right) {
68  return left > kuint64max - right ? kuint64max : left + right;
69 }
70 
71 uint64_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 
78 PiecewiseSegment::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 
87 int64_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 
121 int64_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 
176 int64_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 
238 void PiecewiseSegment::ExpandEnd(int64_t end_x) {
239  end_x_ = std::max(end_x_, end_x);
240 }
241 
242 void PiecewiseSegment::AddConstantToX(int64_t constant) {
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 
252 void PiecewiseSegment::AddConstantToY(int64_t constant) {
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 
260 std::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 
271 PiecewiseLinearFunction::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 
406 bool PiecewiseLinearFunction::InDomain(int64_t x) const {
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 
432 int64_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 
442 int64_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 
476 int64_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 
518 std::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 
525 std::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 
532 namespace {
533 std::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 
549 std::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 
593 std::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 
642 std::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 
685 void 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 
704 void 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 
751 bool 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 
769 bool 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 
778 bool 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 
789 bool 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
PiecewiseSegment(int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x)
int64_t CapSub(int64_t x, int64_t y)
static PiecewiseLinearFunction * CreateFixedChargeFunction(int64_t slope, int64_t value)
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK_GE(val1, val2)
Definition: base/logging.h:706
const int FATAL
Definition: log_severity.h:32
#define CHECK_GT(val1, val2)
Definition: base/logging.h:707
const int ERROR
Definition: log_severity.h:32
std::vector< PiecewiseLinearFunction * > DecomposeToConvexFunctions() const
#define LOG(severity)
Definition: base/logging.h:420
static PiecewiseLinearFunction * CreateFullDomainFunction(int64_t initial_level, std::vector< int64_t > points_x, std::vector< int64_t > slopes)
int64_t CapProd(int64_t x, int64_t y)
static bool SortComparator(const PiecewiseSegment &segment1, const PiecewiseSegment &segment2)
static PiecewiseLinearFunction * CreateStepFunction(std::vector< int64_t > points_x, std::vector< int64_t > points_y, std::vector< int64_t > other_points_x)
int64_t b
void Subtract(const PiecewiseLinearFunction &other)
int64_t max
Definition: alldiff_cst.cc:140
static const int64_t kint64min
double upper_bound
static PiecewiseLinearFunction * CreateOneSegmentFunction(int64_t point_x, int64_t point_y, int64_t slope, int64_t other_point_x)
static const int64_t kint64max
int64_t CapAdd(int64_t x, int64_t y)
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:891
std::pair< int64_t, int64_t > GetSmallestRangeLessThanValue(int64_t range_start, int64_t range_end, int64_t value) const
#define CHECK_LE(val1, val2)
Definition: base/logging.h:704
std::pair< int64_t, int64_t > GetSmallestRangeGreaterThanValue(int64_t range_start, int64_t range_end, int64_t value) const
static PiecewiseLinearFunction * CreateLeftRayFunction(int64_t point_x, int64_t point_y, int64_t slope)
std::pair< int64_t, int64_t > GetSmallestRangeInValueRange(int64_t range_start, int64_t range_end, int64_t value_min, int64_t value_max) const
int index
Definition: pack.cc:509
static const uint64_t kuint64max
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:894
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
static bool FindComparator(int64_t point, const PiecewiseSegment &segment)
const std::vector< PiecewiseSegment > & segments() const
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:892
Collection of objects used to extend the Constraint Solver library.
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 * CreateRightRayFunction(int64_t point_x, int64_t point_y, int64_t slope)
static PiecewiseLinearFunction * CreateEarlyTardyFunction(int64_t reference, int64_t earliness_slope, int64_t tardiness_slope)
int64_t value
void Add(const PiecewiseLinearFunction &other)
int64_t a