OR-Tools  9.0
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"
22 #include "ortools/base/logging.h"
23 
24 namespace operations_research {
25 namespace {
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.
32 int 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 
49 inline bool IsAtBounds(int64_t value) {
50  return value == kint64min || value == kint64max;
51 }
52 
53 inline 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.
60 inline 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 
66 uint64_t UnsignedCapAdd(uint64_t left, uint64_t right) {
67  return left > kuint64max - right ? kuint64max : left + right;
68 }
69 
70 uint64_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 
77 PiecewiseSegment::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 
86 int64_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 
120 int64_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 
175 int64_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 
237 void PiecewiseSegment::ExpandEnd(int64_t end_x) {
238  end_x_ = std::max(end_x_, end_x);
239 }
240 
241 void 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 
251 void 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 
259 std::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 
270 PiecewiseLinearFunction::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 
405 bool PiecewiseLinearFunction::InDomain(int64_t x) const {
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 
431 int64_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 
441 int64_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 
475 int64_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 
517 std::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 
524 std::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 
531 namespace {
532 std::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 
548 std::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 
592 std::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 
641 std::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 
684 void 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 
703 void 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 
750 bool 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 
768 bool 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 
777 bool 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 
788 bool 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:895
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:894
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define CHECK_GE(val1, val2)
Definition: base/logging.h:709
#define CHECK_GT(val1, val2)
Definition: base/logging.h:710
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define LOG(severity)
Definition: base/logging.h:423
#define CHECK_LE(val1, val2)
Definition: base/logging.h:707
const std::vector< PiecewiseSegment > & segments() const
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
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