Skip to content

Commit 8068658

Browse files
committed
backport from main
1 parent c96c824 commit 8068658

30 files changed

+149
-130
lines changed

ortools/algorithms/binary_search.h

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,37 @@
1919
#include <functional>
2020
#include <utility>
2121

22+
#include "absl/base/log_severity.h"
2223
#include "absl/functional/function_ref.h"
2324
#include "absl/log/check.h"
2425
#include "absl/numeric/int128.h"
2526
#include "absl/types/span.h"
2627
#include "ortools/base/logging.h"
2728

2829
namespace operations_research {
29-
30+
// Finds a point in [x_true, x_false) where f changes from true to false.
31+
// If check_bounds is true, it will CHECK that f(x_true) = true and
32+
// f(x_false) = false.
33+
//
3034
// EXAMPLE:
3135
// // Finds the value x in [0,Pi/2] such that cos(x)=2sin(x):
3236
// BinarySearch<double>(/*x_true=*/0.0, /*x_false=*/M_PI/2,
3337
// [](double x) { return cos(x) >= 2*sin(x); });
3438
//
35-
// Note that x_true > x_false is supported: it works either way.
39+
// MONOTONIC FUNCTIONS: Suppose f is a monotonic boolean function. See below for
40+
// the NON-MONOTONIC case.
3641
//
37-
// Ideally, f is a monotonic boolean function, such that:
38-
// - f(x_true) = true
39-
// - f(x_false) = false
40-
// - there exists X such that f(x)=true for all x between x_true and X, and
41-
// f(x)=false for all x between X and x_false.
42+
// If x_true < x_false, this returns X such that:
43+
// - x_true < X < x_false,
44+
// - f((x_true, X]) = true (i.e. for all x in (x_true, X], f(x) = true),
45+
// - f((X, x_false)) = false (i.e. for all x in (X, x_false), f(x) = false)
46+
// or this returns x_true if such an X does not exist.
4247
//
43-
// In those conditions, this returns that value X (note that f(X) is true).
44-
// See below for the NON-MONOTONIC case.
48+
// If x_true > x_false, this function returns X such that:
49+
// - x_false < X < x_true
50+
// - f((x_false, X)) = false
51+
// - f([X, x_true)) = true
52+
// or this return x_true if such an X does not exist.
4553
//
4654
// Also note that 'Point' may be floating-point types: the function will still
4755
// converge when the midpoint can't be distinguished from one of the limits,
@@ -68,7 +76,7 @@ namespace operations_research {
6876
// Note also that even if f() is non-deterministic, i.e. f(X) can sometimes
6977
// return true and sometimes false for the same X, then the binary search will
7078
// still finish, but it's hard to say anything about the returned X.
71-
template <class Point>
79+
template <class Point, bool check_bounds = DEBUG_MODE>
7280
Point BinarySearch(Point x_true, Point x_false, std::function<bool(Point)> f);
7381

7482
// Used by BinarySearch(). This is just (x+y)/2, with some DCHECKs to catch
@@ -207,10 +215,12 @@ Point BinarySearchMidpoint(Point x, Point y) {
207215
return midpoint;
208216
}
209217

210-
template <class Point>
218+
template <class Point, bool check_bounds>
211219
Point BinarySearch(Point x_true, Point x_false, std::function<bool(Point)> f) {
212-
DCHECK(f(x_true)) << x_true;
213-
DCHECK(!f(x_false)) << x_false;
220+
if constexpr (check_bounds) {
221+
CHECK(f(x_true)) << x_true;
222+
CHECK(!f(x_false)) << x_false;
223+
}
214224
int num_iterations = 0;
215225
constexpr int kMaxNumIterations = 1000000;
216226
while (true) {

ortools/algorithms/binary_search_test.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,53 @@ TEST(BinarySearchDeathTest, DiesIfEitherBoundaryConditionViolatedInFastbuild) {
175175
"");
176176
}
177177

178+
TEST(BinarySearchDeathTest, DiesIfBoundCheckIsEnabledAndABoundIsViolated) {
179+
// EXPECT_DEATH does not work with 2-parameter templates.
180+
auto bs = [](int x_true, int x_false, auto f) {
181+
return BinarySearch<int, true>(x_true, x_false, f);
182+
};
183+
EXPECT_DEATH(bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x < 999; }),
184+
"");
185+
EXPECT_DEATH(bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x < 0; }),
186+
"");
187+
EXPECT_DEATH(bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x > 20; }),
188+
"");
189+
}
190+
191+
TEST(BinarySearchTest, NoDeathIfBoundCheckIsDisabled) {
192+
// EXPECT_EQ does not work with 2-parameter templates.
193+
auto bs = [](int x_true, int x_false, auto f) {
194+
return BinarySearch<int, false>(x_true, x_false, f);
195+
};
196+
197+
// f is true on the whole interval ]0, 42[: return last value 41.
198+
EXPECT_EQ(bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x < 999; }),
199+
41);
200+
// f is true on reversed interval ]42, 0[: return last value 1.
201+
EXPECT_EQ(bs(/*x_true=*/42, /*x_false=*/0, [](int x) { return x < 999; }), 1);
202+
// f is false on the whole interval ]0, 42[: return x_true bound 0.
203+
EXPECT_EQ(bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x < 0; }), 0);
204+
// f is false on reversed interval ]42, 0[: return x_true bound 42.
205+
EXPECT_EQ(bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x < 0; }), 0);
206+
// f is false on x_true, true on x_false.
207+
// No DCHECK trigger, instead return a transition point of the function
208+
// f': x_true -> true, x_false -> false, x_true < x < x_false -> f(x).
209+
// The transitions are: 0 (true) -> 1 (false) and 41 (true) -> 42 (false).
210+
{
211+
const int x_transition =
212+
bs(/*x_true=*/0, /*x_false=*/42, [](int x) { return x > 20; });
213+
EXPECT_TRUE(x_transition == 0 || x_transition == 41);
214+
}
215+
// f is false on x_true, true on x_false, with a reversed interval.
216+
// No DCHECK trigger, return one of the transition points.
217+
// The transitions are: 42 (true) -> 41 (false) and 1 (true) -> 0 (false).
218+
{
219+
const int x_transition =
220+
bs(/*x_true=*/42, /*x_false=*/0, [](int x) { return x < 20; });
221+
EXPECT_TRUE(x_transition == 42 || x_transition == 1);
222+
}
223+
}
224+
178225
} // namespace
179226

180227
// Examples of cases where one needs to override BinarySearchMidpoint() to get

ortools/algorithms/dynamic_partition_test.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
#include <random>
2020
#include <vector>
2121

22+
#include "absl/base/log_severity.h"
2223
#include "absl/random/bit_gen_ref.h"
2324
#include "absl/random/random.h"
25+
#include "absl/types/span.h"
2426
#include "gtest/gtest.h"
2527
#include "ortools/base/gmock.h"
2628
#include "ortools/base/stl_util.h"

ortools/algorithms/hungarian_test.cc

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020
#include <random>
2121
#include <vector>
2222

23+
#include "absl/base/macros.h"
2324
#include "absl/container/flat_hash_map.h"
2425
#include "absl/random/distributions.h"
2526
#include "absl/types/span.h"
2627
#include "gtest/gtest.h"
27-
#include "ortools/base/macros.h"
2828
#include "ortools/base/map_util.h"
29-
#include "ortools/base/types.h"
3029

3130
namespace operations_research {
3231

@@ -94,24 +93,24 @@ TEST(LinearAssignmentTest, InvalidMatrix) {
9493
TestMinimization(cost_nan, 0, expected_agents, expected_tasks);
9594
}
9695

97-
#define MATRIX_TEST \
98-
{ \
99-
std::vector<std::vector<double>> cost(kMatrixHeight); \
100-
for (int row = 0; row < kMatrixHeight; ++row) { \
101-
cost[row].resize(kMatrixWidth); \
102-
for (int col = 0; col < kMatrixWidth; ++col) { \
103-
cost[row][col] = kCost[row][col]; \
104-
} \
105-
} \
106-
EXPECT_EQ(arraysize(expected_agents_for_min), \
107-
arraysize(expected_tasks_for_min)); \
108-
EXPECT_EQ(arraysize(expected_agents_for_max), \
109-
arraysize(expected_tasks_for_max)); \
110-
const int assignment_size = arraysize(expected_agents_for_max); \
111-
TestMinimization(cost, assignment_size, expected_agents_for_min, \
112-
expected_tasks_for_min); \
113-
TestMaximization(cost, assignment_size, expected_agents_for_max, \
114-
expected_tasks_for_max); \
96+
#define MATRIX_TEST \
97+
{ \
98+
std::vector<std::vector<double>> cost(kMatrixHeight); \
99+
for (int row = 0; row < kMatrixHeight; ++row) { \
100+
cost[row].resize(kMatrixWidth); \
101+
for (int col = 0; col < kMatrixWidth; ++col) { \
102+
cost[row][col] = kCost[row][col]; \
103+
} \
104+
} \
105+
EXPECT_EQ(ABSL_ARRAYSIZE(expected_agents_for_min), \
106+
ABSL_ARRAYSIZE(expected_tasks_for_min)); \
107+
EXPECT_EQ(ABSL_ARRAYSIZE(expected_agents_for_max), \
108+
ABSL_ARRAYSIZE(expected_tasks_for_max)); \
109+
const int assignment_size = ABSL_ARRAYSIZE(expected_agents_for_max); \
110+
TestMinimization(cost, assignment_size, expected_agents_for_min, \
111+
expected_tasks_for_min); \
112+
TestMaximization(cost, assignment_size, expected_agents_for_max, \
113+
expected_tasks_for_max); \
115114
}
116115

117116
// Test on a 1x1 matrix

ortools/algorithms/knapsack_solver_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#include <limits>
1818
#include <vector>
1919

20+
#include "absl/base/macros.h"
2021
#include "gtest/gtest.h"
21-
#include "ortools/base/macros.h"
2222
#include "ortools/util/time_limit.h"
2323

2424
namespace operations_research {

ortools/base/BUILD.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ cc_library(
4444
hdrs = [
4545
"commandlineflags.h",
4646
"init_google.h",
47-
"logging.h",
4847
"stl_logging.h",
4948
"types.h",
5049
"version.h",
@@ -63,7 +62,6 @@ cc_library(
6362
deps = [
6463
":commandlineflags",
6564
":logging",
66-
":macros",
6765
":types",
6866
"@com_google_absl//absl/base",
6967
"@com_google_absl//absl/container:flat_hash_map",
@@ -73,7 +71,10 @@ cc_library(
7371
"@com_google_absl//absl/flags:flag",
7472
"@com_google_absl//absl/flags:parse",
7573
"@com_google_absl//absl/flags:usage",
74+
"@com_google_absl//absl/log",
75+
"@com_google_absl//absl/log:check",
7676
"@com_google_absl//absl/log:die_if_null",
77+
"@com_google_absl//absl/log:globals",
7778
"@com_google_absl//absl/log:initialize",
7879
"@com_google_absl//absl/strings",
7980
"@com_google_absl//absl/synchronization",
@@ -617,7 +618,6 @@ cc_library(
617618
srcs = ["timer.cc"],
618619
hdrs = ["timer.h"],
619620
deps = [
620-
":macros",
621621
"@com_google_absl//absl/log:check",
622622
"@com_google_absl//absl/time",
623623
],

ortools/base/adjustable_priority_queue-inl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414
#ifndef OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_INL_H_
1515
#define OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_INL_H_
1616

17-
#include "ortools/base/adjustable_priority_queue.h"
17+
#include "ortools/base/adjustable_priority_queue.h" // IWYU pragma: export
1818

1919
#endif // OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_INL_H_

ortools/base/adjustable_priority_queue.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
#include <list>
2121
#include <vector>
2222

23-
#include "ortools/base/macros.h"
24-
2523
template <typename T, typename Comparator>
2624
class LowerPriorityThan {
2725
public:

ortools/base/constant_divisor_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ TEST(ConstantDivisorTemplateTest, Simple) {
6868
}
6969

7070
TEST(ConstantDivisorUint64Test, Bugs) {
71-
// If forumula (27) from p231 is ever implemented, these divisors will break
71+
// If formula (27) from p231 is ever implemented, these divisors will break
7272
// if a >= is accidentally used instead of >.
7373
EXPECT_EQ(uint64_t{828560257293048160},
7474
ConstantDivisor<uint64_t>(21).div(uint64_t{17399765403154011380u}));

ortools/base/gzipfile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ enum class AppendedStreams {
2828
kIgnoreAppendedData,
2929
};
3030

31-
// Return a readonly file that contains a uncompressed version of
31+
// Return a read-only file that contains a uncompressed version of
3232
// another File.
3333
//
3434
// If "ownership == TAKE_OWNERSHIP", the file takes ownership of

0 commit comments

Comments
 (0)