base: backport rework from main
This commit is contained in:
@@ -110,6 +110,9 @@ if(BUILD_absl)
|
||||
set(ABSL_USE_SYSTEM_INCLUDES ON)
|
||||
# We want Abseil to declare what C++ standard it was compiled with.
|
||||
set(ABSL_PROPAGATE_CXX_STD ON)
|
||||
set(ABSL_BUILD_TEST_HELPERS ON)
|
||||
set(ABSL_USE_EXTERNAL_GOOGLETEST ON)
|
||||
set(ABSL_FIND_GOOGLETEST OFF)
|
||||
# We want Abseil to keep the INSTALL rules enabled, even though it is a
|
||||
# subproject. Otherwise the install rules in this project break.
|
||||
set(ABSL_ENABLE_INSTALL ON)
|
||||
@@ -527,8 +530,8 @@ if(BUILD_googletest)
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../../patches/googletest-v1.17.0.patch"
|
||||
)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
set(INSTALL_GTEST OFF)
|
||||
set(GTEST_HAS_ABSL ON)
|
||||
set(INSTALL_GTEST ON)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
message(CHECK_PASS "fetched")
|
||||
|
||||
@@ -81,7 +81,6 @@ add_library(glop)
|
||||
target_sources(glop PRIVATE
|
||||
ortools/base/accurate_sum.h
|
||||
ortools/base/base_export.h
|
||||
ortools/base/basictypes.h
|
||||
ortools/base/commandlineflags.h
|
||||
ortools/base/file.cc
|
||||
ortools/base/file.h
|
||||
@@ -89,7 +88,6 @@ target_sources(glop PRIVATE
|
||||
ortools/base/hash.h
|
||||
ortools/base/int_type.h
|
||||
ortools/base/logging.h
|
||||
ortools/base/macros.h
|
||||
ortools/base/sysinfo.cc
|
||||
ortools/base/sysinfo.h
|
||||
ortools/base/timer.h
|
||||
@@ -302,14 +300,12 @@ install(DIRECTORY ortools/glop
|
||||
install(FILES
|
||||
ortools/base/accurate_sum.h
|
||||
ortools/base/base_export.h
|
||||
ortools/base/basictypes.h
|
||||
ortools/base/commandlineflags.h
|
||||
ortools/base/file.h
|
||||
ortools/base/gzipstring.h
|
||||
ortools/base/hash.h
|
||||
ortools/base/int_type.h
|
||||
ortools/base/logging.h
|
||||
ortools/base/macros.h
|
||||
ortools/base/recordio.h
|
||||
ortools/base/strong_int.h
|
||||
ortools/base/strong_vector.h
|
||||
|
||||
@@ -38,7 +38,6 @@ cc_library(
|
||||
|
||||
cc_library(
|
||||
name = "array",
|
||||
srcs = ["array_internal.h"],
|
||||
hdrs = ["array.h"],
|
||||
deps = ["@abseil-cpp//absl/utility"],
|
||||
)
|
||||
@@ -88,11 +87,6 @@ cc_library(
|
||||
hdrs = ["base_export.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "basictypes",
|
||||
hdrs = ["basictypes.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "bitmap",
|
||||
srcs = ["bitmap.cc"],
|
||||
@@ -172,12 +166,6 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "encodingutils",
|
||||
hdrs = ["encodingutils.h"],
|
||||
deps = [":base"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "flags",
|
||||
hdrs = ["flags.h"],
|
||||
@@ -214,7 +202,7 @@ cc_library(
|
||||
hdrs = ["gmock.h"],
|
||||
deps = [
|
||||
":protocol-buffer-matchers",
|
||||
":status-matchers",
|
||||
"@abseil-cpp//absl/status:status_matchers",
|
||||
"@googletest//:gtest",
|
||||
],
|
||||
)
|
||||
@@ -234,7 +222,6 @@ cc_library(
|
||||
hdrs = ["gzipfile.h"],
|
||||
deps = [
|
||||
":base",
|
||||
":basictypes",
|
||||
":file",
|
||||
":path",
|
||||
"@abseil-cpp//absl/strings",
|
||||
@@ -298,7 +285,7 @@ cc_library(
|
||||
srcs = ["logging.cc"],
|
||||
hdrs = ["logging.h"],
|
||||
deps = [
|
||||
":macros",
|
||||
":base_export",
|
||||
"@abseil-cpp//absl/base:log_severity",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"@abseil-cpp//absl/flags:usage",
|
||||
@@ -314,14 +301,6 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "macros",
|
||||
hdrs = ["macros.h"],
|
||||
deps = [
|
||||
":base_export",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "map_util",
|
||||
hdrs = ["map_util.h"],
|
||||
@@ -470,18 +449,6 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "status-matchers",
|
||||
hdrs = ["status-matchers.h"],
|
||||
deps = [
|
||||
":base",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@googletest//:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "status_macros",
|
||||
hdrs = ["status_macros.h"],
|
||||
@@ -613,11 +580,6 @@ cc_library(
|
||||
hdrs = ["top_n.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "typeid",
|
||||
hdrs = ["typeid.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "types",
|
||||
hdrs = ["types.h"],
|
||||
@@ -628,7 +590,6 @@ cc_library(
|
||||
srcs = ["zipfile.cc"],
|
||||
hdrs = ["zipfile.h"],
|
||||
deps = [
|
||||
":basictypes",
|
||||
":file",
|
||||
":path",
|
||||
":stl_util",
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
file(GLOB _SRCS "*.h" "*.cc")
|
||||
list(FILTER _SRCS EXCLUDE REGEX "/.*_test.cc")
|
||||
list(FILTER _SRCS EXCLUDE REGEX "/gmock\.h")
|
||||
list(FILTER _SRCS EXCLUDE REGEX "/status-matchers\.h")
|
||||
list(FILTER _SRCS EXCLUDE REGEX "/protocol-buffer-matchers\..*")
|
||||
|
||||
set(NAME ${PROJECT_NAME}_base)
|
||||
@@ -54,12 +53,12 @@ ortools_cxx_library(
|
||||
"gmock.h"
|
||||
"protocol-buffer-matchers.cc"
|
||||
"protocol-buffer-matchers.h"
|
||||
"status-matchers.h"
|
||||
TYPE
|
||||
STATIC
|
||||
LINK_LIBRARIES
|
||||
absl::log
|
||||
absl::strings
|
||||
absl::status_matchers
|
||||
GTest::gtest
|
||||
GTest::gmock
|
||||
protobuf::libprotobuf
|
||||
|
||||
@@ -21,20 +21,25 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/utility/utility.h"
|
||||
#include "ortools/base/array_internal.h"
|
||||
|
||||
namespace gtl {
|
||||
|
||||
/// A utility function to build `std::array` objects from built-in arrays
|
||||
/// without specifying their size.
|
||||
///
|
||||
/// Example:
|
||||
/// @code{.cpp}
|
||||
/// auto b = gtl::to_array<std::pair<int, int>>({
|
||||
/// {1, 2},
|
||||
/// {3, 4},
|
||||
/// });
|
||||
/// @endcode
|
||||
namespace internal_array {
|
||||
|
||||
template <typename T, std::size_t N, std::size_t... Idx>
|
||||
constexpr std::array<std::remove_cv_t<T>, N> to_array_internal(
|
||||
T (&ts)[N], absl::index_sequence<Idx...>) {
|
||||
return {{ts[Idx]...}};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, std::size_t... Idx>
|
||||
constexpr std::array<std::remove_cv_t<T>, N> to_array_internal(
|
||||
T (&&ts)[N], absl::index_sequence<Idx...>) {
|
||||
return {{std::move(ts[Idx])...}};
|
||||
}
|
||||
|
||||
} // namespace internal_array
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&ts)[N]) {
|
||||
return internal_array::to_array_internal(ts, absl::make_index_sequence<N>{});
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef OR_TOOLS_BASE_ARRAY_INTERNAL_H_
|
||||
#define OR_TOOLS_BASE_ARRAY_INTERNAL_H_
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/utility/utility.h"
|
||||
|
||||
namespace gtl::internal_array {
|
||||
|
||||
template <typename T, std::size_t N, std::size_t... Idx>
|
||||
constexpr std::array<std::remove_cv_t<T>, N> to_array_internal(
|
||||
T (&ts)[N], absl::index_sequence<Idx...>) {
|
||||
return {{ts[Idx]...}};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, std::size_t... Idx>
|
||||
constexpr std::array<std::remove_cv_t<T>, N> to_array_internal(
|
||||
T (&&ts)[N], absl::index_sequence<Idx...>) {
|
||||
return {{std::move(ts[Idx])...}};
|
||||
}
|
||||
|
||||
} // namespace gtl::internal_array
|
||||
|
||||
#endif // OR_TOOLS_BASE_ARRAY_INTERNAL_H_
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Basic integer type definitions for various platforms
|
||||
//
|
||||
#ifndef OR_TOOLS_BASE_BASICTYPES_H_
|
||||
#define OR_TOOLS_BASE_BASICTYPES_H_
|
||||
|
||||
// Argument type used in interfaces that can optionally take ownership
|
||||
// of a passed in argument. If TAKE_OWNERSHIP is passed, the called
|
||||
// object takes ownership of the argument. Otherwise it does not.
|
||||
enum Ownership { DO_NOT_TAKE_OWNERSHIP, TAKE_OWNERSHIP };
|
||||
|
||||
#endif // OR_TOOLS_BASE_BASICTYPES_H_
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef OR_TOOLS_BASE_ENCODINGUTILS_H_
|
||||
#define OR_TOOLS_BASE_ENCODINGUTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace EncodingUtils {
|
||||
|
||||
// Returns the number of characters of a UTF8-encoded string.
|
||||
inline int UTF8StrLen(const std::string& utf8_str) {
|
||||
if (utf8_str.empty()) return 0;
|
||||
const char* c = utf8_str.c_str();
|
||||
int count = 0;
|
||||
while (*c != '\0') {
|
||||
++count;
|
||||
// See http://en.wikipedia.org/wiki/UTF-8#Description .
|
||||
const unsigned char x = *c;
|
||||
c += x < 0xC0 ? 1 : x < 0xE0 ? 2 : x < 0xF0 ? 3 : 4;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace EncodingUtils
|
||||
|
||||
#endif // OR_TOOLS_BASE_ENCODINGUTILS_H_
|
||||
@@ -14,8 +14,33 @@
|
||||
#ifndef OR_TOOLS_BASE_GMOCK_H_
|
||||
#define OR_TOOLS_BASE_GMOCK_H_
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "absl/status/status_matchers.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/protocol-buffer-matchers.h" // IWYU pragma: export
|
||||
#include "ortools/base/status-matchers.h" // IWYU pragma: export
|
||||
|
||||
namespace testing::status {
|
||||
using ::absl_testing::IsOk;
|
||||
using ::absl_testing::IsOkAndHolds;
|
||||
using ::absl_testing::StatusIs;
|
||||
} // namespace testing::status
|
||||
|
||||
// Macros for testing the results of functions that return absl::Status or
|
||||
// absl::StatusOr<T> (for any type T).
|
||||
#define EXPECT_OK(expression) EXPECT_THAT(expression, ::testing::status::IsOk())
|
||||
#define ASSERT_OK(expression) ASSERT_THAT(expression, ::testing::status::IsOk())
|
||||
|
||||
#define STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y) x##y
|
||||
#define STATUS_MATCHERS_IMPL_CONCAT_(x, y) \
|
||||
STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y)
|
||||
|
||||
#undef ASSERT_OK_AND_ASSIGN
|
||||
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
|
||||
ASSERT_OK_AND_ASSIGN_IMPL_( \
|
||||
STATUS_MATCHERS_IMPL_CONCAT_(_status_or_value, __COUNTER__), lhs, rexpr)
|
||||
|
||||
#define ASSERT_OK_AND_ASSIGN_IMPL_(statusor, lhs, rexpr) \
|
||||
auto statusor = (rexpr); \
|
||||
ASSERT_TRUE(statusor.ok()) << statusor.status(); \
|
||||
lhs = std::move(statusor.value())
|
||||
|
||||
#endif // OR_TOOLS_BASE_GMOCK_H_
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
#include <zlib.h> // for Z_DEFAULT_COMPRESSION
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "ortools/base/basictypes.h" // for Ownership enum
|
||||
|
||||
class File;
|
||||
|
||||
// Argument type used in interfaces that can optionally take ownership
|
||||
// of a passed in argument. If TAKE_OWNERSHIP is passed, the called
|
||||
// object takes ownership of the argument. Otherwise it does not.
|
||||
enum Ownership { DO_NOT_TAKE_OWNERSHIP, TAKE_OWNERSHIP };
|
||||
|
||||
// Argument type used in interfaces that can optionally accept appended
|
||||
// compressed streams. If kConcatenateStreams is passed, the output will
|
||||
// include all streams. Otherwise only the first stream is output.
|
||||
|
||||
@@ -28,7 +28,12 @@
|
||||
#include "absl/strings/str_cat.h" // IWYU pragma: export
|
||||
#include "absl/strings/string_view.h" // IWYU pragma: export
|
||||
#include "ortools/base/base_export.h" // IWYU pragma: export
|
||||
#include "ortools/base/macros.h" // IWYU pragma: export
|
||||
|
||||
#ifdef NDEBUG
|
||||
const bool DEBUG_MODE = false;
|
||||
#else // NDEBUG
|
||||
const bool DEBUG_MODE = true;
|
||||
#endif // NDEBUG
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef OR_TOOLS_BASE_MACROS_H_
|
||||
#define OR_TOOLS_BASE_MACROS_H_
|
||||
|
||||
#include "ortools/base/base_export.h" // IWYU pragma: export
|
||||
|
||||
#define COMPILE_ASSERT(x, msg)
|
||||
|
||||
#ifdef NDEBUG
|
||||
const bool DEBUG_MODE = false;
|
||||
#else // NDEBUG
|
||||
const bool DEBUG_MODE = true;
|
||||
#endif // NDEBUG
|
||||
|
||||
#endif // OR_TOOLS_BASE_MACROS_H_
|
||||
@@ -1,294 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// emulates g3/testing/base/public/gmock_utils/status-matchers.h
|
||||
#ifndef ORTOOLS_BASE_STATUS_MATCHERS_H_
|
||||
#define ORTOOLS_BASE_STATUS_MATCHERS_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace testing::status {
|
||||
|
||||
inline const ::absl::Status& GetStatus(const ::absl::Status& status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const ::absl::Status& GetStatus(const ::absl::StatusOr<T>& status) {
|
||||
return status.status();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Monomorphic implementation of matcher IsOkAndHolds(m).
|
||||
// StatusOrType is a reference to StatusOr<T>.
|
||||
template <typename StatusOrType>
|
||||
class IsOkAndHoldsMatcherImpl
|
||||
: public ::testing::MatcherInterface<StatusOrType> {
|
||||
public:
|
||||
using value_type =
|
||||
typename std::remove_reference<StatusOrType>::type::value_type;
|
||||
|
||||
template <typename InnerMatcher>
|
||||
explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
|
||||
: inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
|
||||
std::forward<InnerMatcher>(inner_matcher))) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const override {
|
||||
*os << "is OK and has a value that ";
|
||||
inner_matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const override {
|
||||
*os << "is not OK or has a value that ";
|
||||
inner_matcher_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
bool MatchAndExplain(
|
||||
StatusOrType actual_value,
|
||||
::testing::MatchResultListener* result_listener) const override {
|
||||
if (!actual_value.ok()) {
|
||||
*result_listener << "which has status " << actual_value.status();
|
||||
return false;
|
||||
}
|
||||
|
||||
::testing::StringMatchResultListener inner_listener;
|
||||
const bool matches =
|
||||
inner_matcher_.MatchAndExplain(*actual_value, &inner_listener);
|
||||
const std::string inner_explanation = inner_listener.str();
|
||||
if (!inner_explanation.empty()) {
|
||||
*result_listener << "which contains value "
|
||||
<< ::testing::PrintToString(*actual_value) << ", "
|
||||
<< inner_explanation;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
private:
|
||||
const ::testing::Matcher<const value_type&> inner_matcher_;
|
||||
};
|
||||
|
||||
// Implements IsOkAndHolds(m) as a polymorphic matcher.
|
||||
template <typename InnerMatcher>
|
||||
class IsOkAndHoldsMatcher {
|
||||
public:
|
||||
explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
|
||||
: inner_matcher_(std::move(inner_matcher)) {}
|
||||
|
||||
// Converts this polymorphic matcher to a monomorphic matcher of the
|
||||
// given type. StatusOrType can be either StatusOr<T> or a
|
||||
// reference to StatusOr<T>.
|
||||
template <typename StatusOrType>
|
||||
operator ::testing::Matcher<StatusOrType>() const { // NOLINT
|
||||
return ::testing::Matcher<StatusOrType>(
|
||||
new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_));
|
||||
}
|
||||
|
||||
private:
|
||||
const InnerMatcher inner_matcher_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Returns a gMock matcher that matches a StatusOr<> whose status is
|
||||
// OK and whose value matches the inner matcher.
|
||||
template <typename InnerMatcher>
|
||||
internal::IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>
|
||||
IsOkAndHolds(InnerMatcher&& inner_matcher) {
|
||||
return internal::IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>(
|
||||
std::forward<InnerMatcher>(inner_matcher));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Implementation of IsOk().
|
||||
namespace internal {
|
||||
|
||||
// Monomorphic implementation of matcher IsOk() for a given type T.
|
||||
// T can be Status, StatusOr<>, or a reference to either of them.
|
||||
template <typename T>
|
||||
class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> {
|
||||
public:
|
||||
void DescribeTo(std::ostream* os) const override { *os << "is OK"; }
|
||||
void DescribeNegationTo(std::ostream* os) const override {
|
||||
*os << "is not OK";
|
||||
}
|
||||
bool MatchAndExplain(T actual_value,
|
||||
::testing::MatchResultListener*) const override {
|
||||
return GetStatus(actual_value).ok();
|
||||
}
|
||||
};
|
||||
|
||||
// Implements IsOk() as a polymorphic matcher.
|
||||
class IsOkMatcher {
|
||||
public:
|
||||
template <typename T>
|
||||
operator ::testing::Matcher<T>() const { // NOLINT
|
||||
return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>());
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
// Returns a gMock matcher that matches a Status or StatusOr<> which is OK.
|
||||
inline internal::IsOkMatcher IsOk() { return internal::IsOkMatcher(); }
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Implementation of StatusIs().
|
||||
//
|
||||
// StatusIs() is a polymorphic matcher. This class is the common
|
||||
// implementation of it shared by all types T where StatusIs() can be used as
|
||||
// a Matcher<T>.
|
||||
namespace internal {
|
||||
|
||||
class StatusIsMatcherCommonImpl {
|
||||
public:
|
||||
StatusIsMatcherCommonImpl(
|
||||
::testing::Matcher<const absl::StatusCode> code_matcher,
|
||||
::testing::Matcher<absl::string_view> message_matcher)
|
||||
: code_matcher_(std::move(code_matcher)),
|
||||
message_matcher_(std::move(message_matcher)) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const {
|
||||
*os << "has a status code that ";
|
||||
code_matcher_.DescribeTo(os);
|
||||
*os << ", and has an error message that ";
|
||||
message_matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const {
|
||||
*os << "has a status code that ";
|
||||
code_matcher_.DescribeNegationTo(os);
|
||||
*os << ", or has an error message that ";
|
||||
message_matcher_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
bool MatchAndExplain(const absl::Status& status,
|
||||
::testing::MatchResultListener* result_listener) const {
|
||||
::testing::StringMatchResultListener inner_listener;
|
||||
|
||||
inner_listener.Clear();
|
||||
if (!code_matcher_.MatchAndExplain(status.code(), &inner_listener)) {
|
||||
*result_listener << (inner_listener.str().empty()
|
||||
? "whose status code is wrong"
|
||||
: "which has a status code " +
|
||||
inner_listener.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!message_matcher_.Matches(std::string(status.message()))) {
|
||||
*result_listener << "whose error message is wrong";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const ::testing::Matcher<const absl::StatusCode> code_matcher_;
|
||||
const ::testing::Matcher<absl::string_view> message_matcher_;
|
||||
};
|
||||
|
||||
// Monomorphic implementation of matcher StatusIs() for a given type T. T can
|
||||
// be Status, StatusOr<>, or a reference to either of them.
|
||||
template <typename T>
|
||||
class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface<T> {
|
||||
public:
|
||||
explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl)
|
||||
: common_impl_(std::move(common_impl)) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const override {
|
||||
common_impl_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const override {
|
||||
common_impl_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
bool MatchAndExplain(
|
||||
T actual_value,
|
||||
::testing::MatchResultListener* result_listener) const override {
|
||||
return common_impl_.MatchAndExplain(GetStatus(actual_value),
|
||||
result_listener);
|
||||
}
|
||||
|
||||
private:
|
||||
StatusIsMatcherCommonImpl common_impl_;
|
||||
};
|
||||
|
||||
// Implements StatusIs() as a polymorphic matcher.
|
||||
class StatusIsMatcher {
|
||||
public:
|
||||
StatusIsMatcher(::testing::Matcher<const absl::StatusCode> code_matcher,
|
||||
::testing::Matcher<absl::string_view> message_matcher)
|
||||
: common_impl_(
|
||||
::testing::MatcherCast<const absl::StatusCode>(code_matcher),
|
||||
::testing::MatcherCast<absl::string_view>(message_matcher)) {}
|
||||
|
||||
// Converts this polymorphic matcher to a monomorphic matcher of the given
|
||||
// type. T can be StatusOr<>, Status, or a reference to either of them.
|
||||
template <typename T>
|
||||
operator ::testing::Matcher<T>() const { // NOLINT
|
||||
return ::testing::MakeMatcher(new MonoStatusIsMatcherImpl<T>(common_impl_));
|
||||
}
|
||||
|
||||
private:
|
||||
const StatusIsMatcherCommonImpl common_impl_;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
// Returns a matcher that matches a Status or StatusOr<> whose status code
|
||||
// matches code_matcher, and whose error message matches message_matcher.
|
||||
template <typename CodeMatcher, typename MessageMatcher>
|
||||
internal::StatusIsMatcher StatusIs(CodeMatcher code_matcher,
|
||||
MessageMatcher message_matcher) {
|
||||
return internal::StatusIsMatcher(std::move(code_matcher),
|
||||
std::move(message_matcher));
|
||||
}
|
||||
|
||||
// Returns a matcher that matches a Status or StatusOr<> whose status code
|
||||
// matches code_matcher.
|
||||
template <typename CodeMatcher>
|
||||
internal::StatusIsMatcher StatusIs(CodeMatcher code_matcher) {
|
||||
return StatusIs(std::move(code_matcher), ::testing::_);
|
||||
}
|
||||
|
||||
} // namespace testing::status
|
||||
|
||||
// Macros for testing the results of functions that return absl::Status or
|
||||
// absl::StatusOr<T> (for any type T).
|
||||
#define EXPECT_OK(expression) EXPECT_THAT(expression, ::testing::status::IsOk())
|
||||
#define ASSERT_OK(expression) ASSERT_THAT(expression, ::testing::status::IsOk())
|
||||
|
||||
#define STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y) x##y
|
||||
#define STATUS_MATCHERS_IMPL_CONCAT_(x, y) \
|
||||
STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y)
|
||||
|
||||
#undef ASSERT_OK_AND_ASSIGN
|
||||
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
|
||||
ASSERT_OK_AND_ASSIGN_IMPL_( \
|
||||
STATUS_MATCHERS_IMPL_CONCAT_(_status_or_value, __COUNTER__), lhs, rexpr)
|
||||
|
||||
#define ASSERT_OK_AND_ASSIGN_IMPL_(statusor, lhs, rexpr) \
|
||||
auto statusor = (rexpr); \
|
||||
ASSERT_TRUE(statusor.ok()) << statusor.status(); \
|
||||
lhs = std::move(statusor.value())
|
||||
|
||||
#endif // ORTOOLS_BASE_STATUS_MATCHERS_H_
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef OR_TOOLS_BASE_TYPEID_H_
|
||||
#define OR_TOOLS_BASE_TYPEID_H_
|
||||
|
||||
#include <cstddef>
|
||||
namespace gtl {
|
||||
template <typename T>
|
||||
inline size_t FastTypeId() {
|
||||
static char d;
|
||||
return reinterpret_cast<size_t>(&d);
|
||||
}
|
||||
} // namespace gtl
|
||||
#endif // OR_TOOLS_BASE_TYPEID_H_
|
||||
@@ -4828,9 +4828,9 @@ bool ChristofidesFilteredHeuristic::BuildSolutionInternal() {
|
||||
ChristofidesPathSolver<int64_t, int64_t, int, Cost>::
|
||||
MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
|
||||
}
|
||||
if (christofides_solver.Solve()) {
|
||||
if (christofides_solver.Solve().ok()) {
|
||||
path_per_cost_class[cost_class] =
|
||||
christofides_solver.TravelingSalesmanPath();
|
||||
christofides_solver.TravelingSalesmanPath().value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,13 +272,13 @@ cc_library(
|
||||
":minimum_spanning_tree",
|
||||
":perfect_matching",
|
||||
"//ortools/base",
|
||||
"//ortools/base:types",
|
||||
"//ortools/graph",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/util:saturated_arithmetic",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -289,7 +289,7 @@ cc_test(
|
||||
":christofides",
|
||||
"//ortools/base",
|
||||
"//ortools/base:gmock_main",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/strings:str_format",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
"@google_benchmark//:benchmark",
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/graph/eulerian_path.h"
|
||||
#include "ortools/graph/graph.h"
|
||||
@@ -48,6 +49,7 @@ namespace operations_research {
|
||||
|
||||
using ::util::CompleteGraph;
|
||||
|
||||
// TODO(user): Only support integer weights/costs.
|
||||
template <typename CostType, typename ArcIndex = int64_t,
|
||||
typename NodeIndex = int32_t,
|
||||
typename CostFunction = std::function<CostType(NodeIndex, NodeIndex)>>
|
||||
@@ -66,23 +68,22 @@ class ChristofidesPathSolver {
|
||||
// (MINIMUM_WEIGHT_MATCHING) guarantees the 3/2 upper bound to the optimal
|
||||
// solution. A minimal weight perfect matching (MINIMAL_WEIGHT_MATCHING)
|
||||
// finds a locally minimal weight matching which does not offer any bound
|
||||
// guarantee but, as of 1/2017, is orders of magnitude faster than the
|
||||
// minimum matching.
|
||||
// By default, MINIMAL_WEIGHT_MATCHING is selected.
|
||||
// TODO(user): Change the default when minimum matching gets faster.
|
||||
// guarantee but, as of 09/2025, can sometimes be faster on larger problems.
|
||||
// By default, MINIMUM_WEIGHT_MATCHING is selected.
|
||||
void SetMatchingAlgorithm(MatchingAlgorithm matching) {
|
||||
matching_ = matching;
|
||||
}
|
||||
|
||||
// Returns the cost of the approximate TSP tour.
|
||||
CostType TravelingSalesmanCost();
|
||||
// If the problem was not solved, solves it with the Christofides algorithm,
|
||||
// and returns the cost of the approximate TSP tour.
|
||||
absl::StatusOr<CostType> TravelingSalesmanCost();
|
||||
|
||||
// Returns the approximate TSP tour.
|
||||
std::vector<NodeIndex> TravelingSalesmanPath();
|
||||
// If the problem was not solved, solves it with the Christofides algorithm,
|
||||
// and returns the approximate TSP tour.
|
||||
absl::StatusOr<const std::vector<NodeIndex>&> TravelingSalesmanPath();
|
||||
|
||||
// Runs the Christofides algorithm. Returns true if a solution was found,
|
||||
// false otherwise.
|
||||
bool Solve();
|
||||
// If the problem was not solved, solves it with the Christofides algorithm.
|
||||
absl::Status Solve();
|
||||
|
||||
private:
|
||||
// Safe addition operator to avoid overflows when possible.
|
||||
@@ -117,13 +118,17 @@ class ChristofidesPathSolver {
|
||||
};
|
||||
|
||||
// Computes a minimum weight perfect matching on an undirected graph.
|
||||
template <typename WeightFunctionType, typename GraphType>
|
||||
template <typename CostType, typename WeightFunctionType, typename GraphType>
|
||||
absl::StatusOr<std::vector<
|
||||
std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
|
||||
ComputeMinimumWeightMatching(const GraphType& graph,
|
||||
const WeightFunctionType& weight) {
|
||||
using ArcIndex = typename GraphType::ArcIndex;
|
||||
using NodeIndex = typename GraphType::NodeIndex;
|
||||
if constexpr (!std::is_integral_v<CostType>) {
|
||||
DLOG(WARNING) << "Weights are being cast to int64_t. This might result "
|
||||
"in loss of precision or overflows.";
|
||||
}
|
||||
MinCostPerfectMatching matching(graph.num_nodes());
|
||||
for (NodeIndex tail : graph.AllNodes()) {
|
||||
for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
|
||||
@@ -135,8 +140,10 @@ ComputeMinimumWeightMatching(const GraphType& graph,
|
||||
}
|
||||
}
|
||||
MinCostPerfectMatching::Status status = matching.Solve();
|
||||
if (status != MinCostPerfectMatching::OPTIMAL) {
|
||||
return absl::InvalidArgumentError("Perfect matching failed");
|
||||
if (status != MinCostPerfectMatching::OPTIMAL &&
|
||||
status != MinCostPerfectMatching::COST_OVERFLOW) {
|
||||
return absl::InvalidArgumentError(
|
||||
absl::StrCat("Perfect matching failed: ", status));
|
||||
}
|
||||
std::vector<std::pair<NodeIndex, NodeIndex>> match;
|
||||
for (NodeIndex tail : graph.AllNodes()) {
|
||||
@@ -232,7 +239,7 @@ template <typename CostType, typename ArcIndex, typename NodeIndex,
|
||||
typename CostFunction>
|
||||
ChristofidesPathSolver<CostType, ArcIndex, NodeIndex, CostFunction>::
|
||||
ChristofidesPathSolver(NodeIndex num_nodes, CostFunction costs)
|
||||
: matching_(MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING),
|
||||
: matching_(MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING),
|
||||
graph_(num_nodes),
|
||||
costs_(std::move(costs)),
|
||||
tsp_cost_(0),
|
||||
@@ -240,30 +247,30 @@ ChristofidesPathSolver<CostType, ArcIndex, NodeIndex, CostFunction>::
|
||||
|
||||
template <typename CostType, typename ArcIndex, typename NodeIndex,
|
||||
typename CostFunction>
|
||||
CostType ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
CostFunction>::TravelingSalesmanCost() {
|
||||
absl::StatusOr<CostType> ChristofidesPathSolver<
|
||||
CostType, ArcIndex, NodeIndex, CostFunction>::TravelingSalesmanCost() {
|
||||
if (!solved_) {
|
||||
bool const ok = Solve();
|
||||
DCHECK(ok);
|
||||
const absl::Status status = Solve();
|
||||
if (!status.ok()) return status;
|
||||
}
|
||||
return tsp_cost_;
|
||||
}
|
||||
|
||||
template <typename CostType, typename ArcIndex, typename NodeIndex,
|
||||
typename CostFunction>
|
||||
std::vector<NodeIndex> ChristofidesPathSolver<
|
||||
absl::StatusOr<const std::vector<NodeIndex>&> ChristofidesPathSolver<
|
||||
CostType, ArcIndex, NodeIndex, CostFunction>::TravelingSalesmanPath() {
|
||||
if (!solved_) {
|
||||
const bool ok = Solve();
|
||||
DCHECK(ok);
|
||||
const absl::Status status = Solve();
|
||||
if (!status.ok()) return status;
|
||||
}
|
||||
return tsp_path_;
|
||||
}
|
||||
|
||||
template <typename CostType, typename ArcIndex, typename NodeIndex,
|
||||
typename CostFunction>
|
||||
bool ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
CostFunction>::Solve() {
|
||||
absl::Status
|
||||
ChristofidesPathSolver<CostType, ArcIndex, NodeIndex, CostFunction>::Solve() {
|
||||
const NodeIndex num_nodes = graph_.num_nodes();
|
||||
tsp_path_.clear();
|
||||
tsp_cost_ = 0;
|
||||
@@ -271,7 +278,7 @@ bool ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
tsp_path_ = {0, 0};
|
||||
}
|
||||
if (num_nodes <= 1) {
|
||||
return true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
// Compute Minimum Spanning Tree.
|
||||
const std::vector<ArcIndex> mst =
|
||||
@@ -291,21 +298,20 @@ bool ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
}
|
||||
}
|
||||
// Find minimum-weight perfect matching on odd-degree-node complete graph.
|
||||
// TODO(user): Make this code available as an independent algorithm.
|
||||
const NodeIndex reduced_size = odd_degree_nodes.size();
|
||||
DCHECK_NE(0, reduced_size);
|
||||
CompleteGraph<NodeIndex, ArcIndex> reduced_graph(reduced_size);
|
||||
std::vector<std::pair<NodeIndex, NodeIndex>> closure_arcs;
|
||||
switch (matching_) {
|
||||
case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING: {
|
||||
auto result = ComputeMinimumWeightMatching(
|
||||
auto result = ComputeMinimumWeightMatching<CostType>(
|
||||
reduced_graph, [this, &reduced_graph,
|
||||
&odd_degree_nodes](CompleteGraph<>::ArcIndex arc) {
|
||||
return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
|
||||
odd_degree_nodes[reduced_graph.Head(arc)]);
|
||||
});
|
||||
if (!result.ok()) {
|
||||
return false;
|
||||
return result.status();
|
||||
}
|
||||
result->swap(closure_arcs);
|
||||
break;
|
||||
@@ -319,7 +325,7 @@ bool ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
odd_degree_nodes[reduced_graph.Head(arc)]);
|
||||
});
|
||||
if (!result.ok()) {
|
||||
return false;
|
||||
return result.status();
|
||||
}
|
||||
result->swap(closure_arcs);
|
||||
break;
|
||||
@@ -336,7 +342,7 @@ bool ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
|
||||
odd_degree_nodes[reduced_graph.Head(arc)]);
|
||||
}
|
||||
std::sort(ordered_arcs.begin(), ordered_arcs.end(),
|
||||
absl::c_sort(ordered_arcs,
|
||||
[&ordered_arc_costs](ArcIndex arc_a, ArcIndex arc_b) {
|
||||
return ordered_arc_costs[arc_a] < ordered_arc_costs[arc_b];
|
||||
});
|
||||
@@ -379,7 +385,7 @@ bool ChristofidesPathSolver<CostType, ArcIndex, NodeIndex,
|
||||
SafeAdd(tsp_cost_, tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), 0));
|
||||
tsp_path_.push_back(0);
|
||||
solved_ = true;
|
||||
return true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
|
||||
@@ -21,15 +21,21 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::status::IsOkAndHolds;
|
||||
using ::testing::status::StatusIs;
|
||||
|
||||
// Displays the path.
|
||||
std::string PathToString(absl::Span<const int> path) {
|
||||
std::string path_string;
|
||||
@@ -44,16 +50,17 @@ std::string PathToString(absl::Span<const int> path) {
|
||||
template <typename C>
|
||||
void ComputeAndShow(const std::string& name,
|
||||
ChristofidesPathSolver<C>* chris_solver) {
|
||||
LOG(INFO) << name << " TSP cost = " << chris_solver->TravelingSalesmanCost();
|
||||
LOG(INFO) << name
|
||||
<< " TSP cost = " << chris_solver->TravelingSalesmanCost().value();
|
||||
LOG(INFO) << name << " TSP path = "
|
||||
<< PathToString(chris_solver->TravelingSalesmanPath());
|
||||
<< PathToString(chris_solver->TravelingSalesmanPath().value());
|
||||
}
|
||||
|
||||
void TestChristofides(const std::string& name, const int size,
|
||||
absl::Span<const int> cost_data,
|
||||
bool use_minimal_matching, bool use_mip,
|
||||
const int expected_cost,
|
||||
absl::string_view expected_solution) {
|
||||
const std::vector<int>& expected_solution) {
|
||||
using MatchingAlgorithm = ChristofidesPathSolver<int>::MatchingAlgorithm;
|
||||
std::vector<std::vector<int>> cost_mat(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
@@ -89,9 +96,10 @@ void TestChristofides(const std::string& name, const int size,
|
||||
MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING);
|
||||
}
|
||||
ComputeAndShow(name, &chris_solver);
|
||||
EXPECT_EQ(expected_cost, chris_solver.TravelingSalesmanCost());
|
||||
EXPECT_EQ(expected_solution,
|
||||
PathToString(chris_solver.TravelingSalesmanPath()));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(),
|
||||
IsOkAndHolds(expected_cost));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanPath(),
|
||||
IsOkAndHolds(expected_solution));
|
||||
}
|
||||
|
||||
// Gr17 as taken from TSPLIB:
|
||||
@@ -113,12 +121,15 @@ TEST(HamiltonianPathTest, Gr17) {
|
||||
165, 383, 240, 140, 448, 202, 57, 0, 246, 745, 472, 237, 528, 364,
|
||||
332, 349, 202, 685, 542, 157, 289, 426, 483, 0, 121, 518, 142, 84,
|
||||
297, 35, 29, 36, 236, 390, 238, 301, 55, 96, 153, 336, 0};
|
||||
TestChristofides("Gr17", kGr17Size, gr17_data, false, /*use_mip=*/true, 2190,
|
||||
"0 12 6 7 5 10 4 1 9 2 14 13 16 3 8 11 15 0 ");
|
||||
TestChristofides("Gr17", kGr17Size, gr17_data, false, /*use_mip=*/false, 2190,
|
||||
"0 12 6 7 5 10 4 1 9 2 14 13 16 3 8 11 15 0 ");
|
||||
TestChristofides("Gr17", kGr17Size, gr17_data, true, /*use_mip=*/false, 2421,
|
||||
"0 12 3 8 11 15 1 4 10 9 2 14 13 16 6 7 5 0 ");
|
||||
TestChristofides(
|
||||
"Gr17", kGr17Size, gr17_data, false, /*use_mip=*/true, 2190,
|
||||
{0, 12, 6, 7, 5, 10, 4, 1, 9, 2, 14, 13, 16, 3, 8, 11, 15, 0});
|
||||
TestChristofides(
|
||||
"Gr17", kGr17Size, gr17_data, false, /*use_mip=*/false, 2190,
|
||||
{0, 12, 6, 7, 5, 10, 4, 1, 9, 2, 14, 13, 16, 3, 8, 11, 15, 0});
|
||||
TestChristofides(
|
||||
"Gr17", kGr17Size, gr17_data, true, /*use_mip=*/false, 2421,
|
||||
{0, 12, 3, 8, 11, 15, 1, 4, 10, 9, 2, 14, 13, 16, 6, 7, 5, 0});
|
||||
}
|
||||
|
||||
// Gr24 as taken from TSPLIB:
|
||||
@@ -149,15 +160,15 @@ TEST(HamiltonianPathTest, Gr24) {
|
||||
235, 108, 119, 165, 178, 154, 71, 136, 262, 110, 74, 96, 264, 187, 182,
|
||||
261, 239, 165, 151, 221, 0, 121, 142, 99, 84, 35, 29, 42, 36, 220,
|
||||
70, 126, 55, 249, 104, 178, 60, 96, 175, 153, 146, 47, 135, 169, 0};
|
||||
TestChristofides(
|
||||
"Gr24", kGr24Size, gr24_data, false, /*use_mip=*/true, 1407,
|
||||
"0 15 5 6 2 10 7 20 4 9 16 21 17 18 1 14 19 12 8 22 13 23 11 3 0 ");
|
||||
TestChristofides(
|
||||
"Gr24", kGr24Size, gr24_data, false, /*use_mip=*/false, 1407,
|
||||
"0 15 5 6 2 10 7 20 4 9 16 21 17 18 1 14 19 12 8 22 13 23 11 3 0 ");
|
||||
TestChristofides(
|
||||
"Gr24", kGr24Size, gr24_data, true, /*use_mip=*/false, 1607,
|
||||
"0 15 5 6 7 20 4 9 16 21 17 18 1 19 14 13 22 8 12 10 2 23 11 3 0 ");
|
||||
TestChristofides("Gr24", kGr24Size, gr24_data, false, /*use_mip=*/true, 1407,
|
||||
{0, 15, 5, 6, 2, 10, 7, 20, 4, 9, 16, 21, 17,
|
||||
18, 1, 14, 19, 12, 8, 22, 13, 23, 11, 3, 0});
|
||||
TestChristofides("Gr24", kGr24Size, gr24_data, false, /*use_mip=*/false, 1407,
|
||||
{0, 15, 5, 6, 2, 10, 7, 20, 4, 9, 16, 21, 17,
|
||||
18, 1, 14, 19, 12, 8, 22, 13, 23, 11, 3, 0});
|
||||
TestChristofides("Gr24", kGr24Size, gr24_data, true, /*use_mip=*/false, 1607,
|
||||
{0, 15, 5, 6, 7, 20, 4, 9, 16, 21, 17, 18, 1,
|
||||
19, 14, 13, 22, 8, 12, 10, 2, 23, 11, 3, 0});
|
||||
}
|
||||
|
||||
// This is the geographic distance as defined in TSPLIB. It is used here to
|
||||
@@ -208,35 +219,53 @@ TEST(HamiltonianPathTest, Ulysses) {
|
||||
ChristofidesPathSolver<
|
||||
double>::MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
|
||||
ComputeAndShow("Ulysses22", &chris_solver);
|
||||
EXPECT_EQ(7448, chris_solver.TravelingSalesmanCost());
|
||||
EXPECT_EQ("0 7 21 16 1 2 3 17 12 13 14 4 10 8 9 18 19 20 11 6 5 15 0 ",
|
||||
PathToString(chris_solver.TravelingSalesmanPath()));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(), IsOkAndHolds(7448));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanPath(),
|
||||
IsOkAndHolds(ElementsAre(0, 7, 21, 16, 1, 2, 3, 17, 12, 13, 14, 4,
|
||||
10, 8, 9, 18, 19, 20, 11, 6, 5, 15, 0)));
|
||||
}
|
||||
|
||||
TEST(ChristofidesTest, EmptyModel) {
|
||||
ChristofidesPathSolver<int> chris_solver(0, [](int, int) { return 0; });
|
||||
EXPECT_EQ(0, chris_solver.TravelingSalesmanCost());
|
||||
EXPECT_TRUE(chris_solver.TravelingSalesmanPath().empty());
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(), IsOkAndHolds(0));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanPath(), IsOkAndHolds(IsEmpty()));
|
||||
}
|
||||
|
||||
TEST(ChristofidesTest, SingleNodeModel) {
|
||||
ChristofidesPathSolver<int> chris_solver(1, [](int, int) { return 0; });
|
||||
EXPECT_EQ(0, chris_solver.TravelingSalesmanCost());
|
||||
EXPECT_EQ("0 0 ", PathToString(chris_solver.TravelingSalesmanPath()));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(), IsOkAndHolds(0));
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanPath(),
|
||||
IsOkAndHolds(ElementsAre(0, 0)));
|
||||
}
|
||||
|
||||
TEST(ChristofidesTest, Int64Overflow) {
|
||||
TEST(ChristofidesTest, Int64OverflowMinimalMatching) {
|
||||
ChristofidesPathSolver<int64_t> chris_solver(
|
||||
10, [](int, int) { return std::numeric_limits<int64_t>::max() / 2; });
|
||||
EXPECT_EQ(std::numeric_limits<int64_t>::max(),
|
||||
chris_solver.TravelingSalesmanCost());
|
||||
chris_solver.SetMatchingAlgorithm(
|
||||
ChristofidesPathSolver<
|
||||
int64_t>::MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING);
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(),
|
||||
IsOkAndHolds(std::numeric_limits<int64_t>::max()));
|
||||
}
|
||||
|
||||
TEST(ChristofidesTest, SaturatedDouble) {
|
||||
TEST(ChristofidesTest, Int64OverflowMinimumMatching) {
|
||||
ChristofidesPathSolver<int64_t> chris_solver(
|
||||
10, [](int, int) { return std::numeric_limits<int64_t>::max() / 2; });
|
||||
chris_solver.SetMatchingAlgorithm(
|
||||
ChristofidesPathSolver<
|
||||
int64_t>::MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
|
||||
EXPECT_THAT(chris_solver.Solve(),
|
||||
StatusIs(absl::StatusCode::kInvalidArgument));
|
||||
}
|
||||
|
||||
TEST(ChristofidesTest, SaturatedDoubleWithMinimalMatching) {
|
||||
ChristofidesPathSolver<double> chris_solver(
|
||||
10, [](int, int) { return std::numeric_limits<double>::max() / 2.0; });
|
||||
EXPECT_EQ(std::numeric_limits<double>::infinity(),
|
||||
chris_solver.TravelingSalesmanCost());
|
||||
chris_solver.SetMatchingAlgorithm(
|
||||
ChristofidesPathSolver<
|
||||
double>::MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING);
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(),
|
||||
IsOkAndHolds(std::numeric_limits<double>::infinity()));
|
||||
}
|
||||
|
||||
TEST(ChristofidesTest, NoPerfectMatching) {
|
||||
@@ -253,7 +282,8 @@ TEST(ChristofidesTest, NoPerfectMatching) {
|
||||
chris_solver.SetMatchingAlgorithm(
|
||||
ChristofidesPathSolver<
|
||||
int64_t>::MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
|
||||
EXPECT_FALSE(chris_solver.Solve());
|
||||
EXPECT_THAT(chris_solver.Solve(),
|
||||
StatusIs(absl::StatusCode::kInvalidArgument));
|
||||
}
|
||||
|
||||
// Benchmark for the Christofides algorithm on a 'size' by 'size' grid of nodes.
|
||||
@@ -290,11 +320,12 @@ void BM_ChristofidesPathSolver(benchmark::State& state) {
|
||||
chris_solver.SetMatchingAlgorithm(
|
||||
MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
|
||||
}
|
||||
EXPECT_NE(0, chris_solver.TravelingSalesmanCost());
|
||||
EXPECT_THAT(chris_solver.TravelingSalesmanCost(),
|
||||
IsOkAndHolds(testing::Gt(0)));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_TEMPLATE(BM_ChristofidesPathSolver, true)->Range(2, 1 << 5);
|
||||
BENCHMARK_TEMPLATE(BM_ChristofidesPathSolver, false)->Range(2, 1 << 4);
|
||||
BENCHMARK_TEMPLATE(BM_ChristofidesPathSolver, false)->Range(2, 1 << 5);
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -636,6 +636,12 @@ struct GraphTraits {
|
||||
// + (ArcIndexType + NodeIndexType) * arc_capacity().
|
||||
// - Has an efficient Tail() but need an extra NodeIndexType/arc memory for it.
|
||||
// - Never changes the initial arc index returned by AddArc().
|
||||
//
|
||||
// OutgoingArcs(), OutgoingArcsStartingFrom(), and the [] operator are all
|
||||
// deterministic. Specifically, for two graphs constructed from the same
|
||||
// sequence of node and arc creation calls, these iterators will return the
|
||||
// result in the same order).
|
||||
//
|
||||
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
|
||||
class ListGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
|
||||
typedef BaseGraph<NodeIndexType, ArcIndexType, false> Base;
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
// possible.
|
||||
//
|
||||
// We don't use the interface from
|
||||
// operations_research/algorithms/hungarian.h because we want to be
|
||||
// cs/ortools/algorithms/hungarian.h because we want to be
|
||||
// able to express sparse problems efficiently.
|
||||
//
|
||||
// When asked to solve the given assignment problem we return a
|
||||
|
||||
@@ -219,7 +219,7 @@ class HeldWolfeCrowderEvaluator {
|
||||
// bounds lead to faster convergence.
|
||||
ChristofidesPathSolver<CostType, int64_t, int, CostFunction> solver(
|
||||
number_of_nodes, cost);
|
||||
upper_bound_ = solver.TravelingSalesmanCost();
|
||||
upper_bound_ = solver.TravelingSalesmanCost().value();
|
||||
}
|
||||
|
||||
bool Next() {
|
||||
|
||||
@@ -20,6 +20,8 @@ cc_library(
|
||||
srcs = ["gurobi_isv.cc"],
|
||||
hdrs = ["gurobi_isv.h"],
|
||||
deps = [
|
||||
"//ortools/base:status_builder",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/math_opt/solvers:gurobi_cc_proto",
|
||||
"//ortools/third_party_solvers:gurobi_environment",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
|
||||
@@ -352,6 +352,7 @@ cc_library(
|
||||
srcs = ["gurobi_util.cc"],
|
||||
hdrs = ["gurobi_util.h"],
|
||||
deps = [
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/third_party_solvers:gurobi_environment",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"@abseil-cpp//absl/log",
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -24,15 +23,14 @@
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "ortools/base/accurate_sum.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/linear_solver/linear_solver.pb.h"
|
||||
#include "ortools/port/file.h"
|
||||
|
||||
@@ -35,21 +35,25 @@ cc_library(
|
||||
hdrs = ["lp_types.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/util:bitset",
|
||||
"//ortools/util:strong_integers",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
)
|
||||
|
||||
# Handling of permutations.
|
||||
|
||||
cc_library(
|
||||
name = "permutation",
|
||||
hdrs = ["permutation.h"],
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
"//ortools/base",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/util:return_macros",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/random",
|
||||
],
|
||||
)
|
||||
@@ -60,13 +64,13 @@ cc_library(
|
||||
hdrs = ["scattered_vector.h"],
|
||||
deps = [
|
||||
":base",
|
||||
"//ortools/base",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/util:bitset",
|
||||
"//ortools/util:strong_integers",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
)
|
||||
|
||||
# Compressed sparse columns.
|
||||
|
||||
cc_library(
|
||||
name = "sparse_vector",
|
||||
hdrs = ["sparse_vector.h"],
|
||||
@@ -75,9 +79,9 @@ cc_library(
|
||||
":base",
|
||||
":permutation",
|
||||
"//ortools/base",
|
||||
"//ortools/base:types",
|
||||
"//ortools/graph:iterators",
|
||||
"//ortools/util:return_macros",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/strings:str_format",
|
||||
],
|
||||
)
|
||||
@@ -89,8 +93,10 @@ cc_library(
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
":permutation",
|
||||
":sparse_vector",
|
||||
"//ortools/base",
|
||||
"//ortools/util:return_macros",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -99,9 +105,8 @@ cc_library(
|
||||
hdrs = ["sparse_row.h"],
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
":sparse_vector",
|
||||
"//ortools/base",
|
||||
"//ortools/base:strong_vector",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -114,20 +119,21 @@ cc_library(
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
":matrix_scaler_hdr",
|
||||
":permutation",
|
||||
":scattered_vector",
|
||||
":sparse_column",
|
||||
"//ortools/base",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/util:fp_utils",
|
||||
"//ortools/base:types",
|
||||
"//ortools/graph:iterators",
|
||||
"//ortools/util:bitset",
|
||||
"//ortools/util:return_macros",
|
||||
"//ortools/util:strong_integers",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/strings:str_format",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
# Matrix scaler.
|
||||
|
||||
cc_library(
|
||||
name = "matrix_scaler",
|
||||
srcs = ["matrix_scaler.cc"],
|
||||
@@ -135,27 +141,24 @@ cc_library(
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
":lp_data",
|
||||
":lp_utils",
|
||||
":sparse",
|
||||
":sparse_column",
|
||||
"//ortools/base",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/base:types",
|
||||
"//ortools/glop:parameters_cc_proto",
|
||||
"//ortools/glop:revised_simplex",
|
||||
"//ortools/glop:status",
|
||||
"//ortools/util:fp_utils",
|
||||
"//ortools/util:return_macros",
|
||||
"//ortools/util:time_limit",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/strings:str_format",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "matrix_scaler_hdr",
|
||||
hdrs = ["matrix_scaler.h"],
|
||||
deps = [
|
||||
":base",
|
||||
"//ortools/base",
|
||||
"//ortools/base:strong_vector",
|
||||
],
|
||||
)
|
||||
# Linear Programming data storage.
|
||||
|
||||
cc_library(
|
||||
name = "lp_data",
|
||||
@@ -169,6 +172,7 @@ cc_library(
|
||||
":matrix_utils",
|
||||
":permutation",
|
||||
":sparse",
|
||||
":sparse_column",
|
||||
"//ortools/base",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/base:strong_vector",
|
||||
@@ -177,7 +181,9 @@ cc_library(
|
||||
"//ortools/util:strong_integers",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/container:flat_hash_set",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/strings:str_format",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -189,10 +195,16 @@ cc_library(
|
||||
":base",
|
||||
":lp_data",
|
||||
":matrix_scaler",
|
||||
":scattered_vector",
|
||||
":sparse_column",
|
||||
"//ortools/glop:parameters_cc_proto",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
# Lp utilities.
|
||||
|
||||
cc_library(
|
||||
name = "lp_utils",
|
||||
srcs = ["lp_utils.cc"],
|
||||
@@ -200,10 +212,12 @@ cc_library(
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
":permutation",
|
||||
":scattered_vector",
|
||||
":sparse_column",
|
||||
"//ortools/base",
|
||||
"//ortools/base:accurate_sum",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -215,8 +229,9 @@ cc_library(
|
||||
deps = [
|
||||
":base",
|
||||
":sparse",
|
||||
"//ortools/base",
|
||||
":sparse_column",
|
||||
"//ortools/base:hash",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -229,15 +244,20 @@ cc_library(
|
||||
":base",
|
||||
":lp_data",
|
||||
":proto_utils",
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/container:flat_hash_set",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@re2",
|
||||
],
|
||||
)
|
||||
|
||||
# Linear Programming printing utilities.
|
||||
|
||||
cc_library(
|
||||
name = "lp_print_utils",
|
||||
srcs = ["lp_print_utils.cc"],
|
||||
@@ -245,39 +265,47 @@ cc_library(
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
"//ortools/base",
|
||||
"//ortools/base:types",
|
||||
"//ortools/util:rational_approximation",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/strings:str_format",
|
||||
],
|
||||
)
|
||||
|
||||
# Proto conversion
|
||||
|
||||
cc_library(
|
||||
name = "proto_utils",
|
||||
srcs = ["proto_utils.cc"],
|
||||
hdrs = ["proto_utils.h"],
|
||||
copts = SAFE_FP_CODE,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":base",
|
||||
":lp_data",
|
||||
"//ortools/base",
|
||||
":sparse",
|
||||
":sparse_column",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
)
|
||||
|
||||
# Test utilities.
|
||||
|
||||
# MPS reader.
|
||||
cc_library(
|
||||
name = "mps_reader_template",
|
||||
srcs = ["mps_reader_template.cc"],
|
||||
hdrs = ["mps_reader_template.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/util:filelineiter",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/container:flat_hash_set",
|
||||
"@abseil-cpp//absl/container:inlined_vector",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
@@ -290,21 +318,47 @@ cc_library(
|
||||
hdrs = ["mps_reader.h"],
|
||||
copts = SAFE_FP_CODE,
|
||||
deps = [
|
||||
":base",
|
||||
":lp_data",
|
||||
":lp_print_utils",
|
||||
":mps_reader_template",
|
||||
"//ortools/base",
|
||||
"//ortools/base:protobuf_util",
|
||||
"//ortools/base:status_builder",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/container:btree",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
# Utility to represent the MPS layout as a PNG.
|
||||
|
||||
cc_library(
|
||||
name = "sol_reader",
|
||||
srcs = ["sol_reader.cc"],
|
||||
hdrs = ["sol_reader.h"],
|
||||
deps = [
|
||||
":base",
|
||||
":lp_data",
|
||||
"//ortools/base:numbers",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/util:file_util",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
# Library to show matrix fill-in as a PNG bitmap.
|
||||
|
||||
# Decompose a LinearProgram into several independent LinearPrograms.
|
||||
|
||||
cc_library(
|
||||
name = "lp_decomposer",
|
||||
srcs = ["lp_decomposer.cc"],
|
||||
@@ -313,30 +367,13 @@ cc_library(
|
||||
deps = [
|
||||
":base",
|
||||
":lp_data",
|
||||
":lp_utils",
|
||||
":sparse",
|
||||
":sparse_column",
|
||||
"//ortools/algorithms:dynamic_partition",
|
||||
"//ortools/base",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/glop:parameters_cc_proto",
|
||||
"//ortools/util:bitset",
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "sol_reader",
|
||||
srcs = ["sol_reader.cc"],
|
||||
hdrs = ["sol_reader.h"],
|
||||
deps = [
|
||||
":base",
|
||||
":lp_data",
|
||||
":proto_utils",
|
||||
"//ortools/base",
|
||||
"//ortools/base:numbers",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/util:file_util",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -43,12 +43,6 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "utf8",
|
||||
hdrs = ["utf8.h"],
|
||||
deps = ["//ortools/base:encodingutils"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "file",
|
||||
srcs = ["file.cc"],
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright 2010-2025 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef OR_TOOLS_PORT_UTF8_H_
|
||||
#define OR_TOOLS_PORT_UTF8_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef __PORTABLE_PLATFORM__
|
||||
#include "ortools/base/encodingutils.h"
|
||||
#endif
|
||||
|
||||
namespace operations_research {
|
||||
namespace utf8 {
|
||||
|
||||
// str_type should be string/StringPiece/Cord
|
||||
template <typename StrType>
|
||||
int UTF8StrLen(StrType str_type) {
|
||||
#if defined(__PORTABLE_PLATFORM__)
|
||||
return str_type.size();
|
||||
#else
|
||||
return ::EncodingUtils::UTF8StrLen(str_type);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace utf8
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_PORT_UTF8_H_
|
||||
@@ -22,6 +22,7 @@ import "ortools/service/v1/mathopt/solution.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.google.ortools.service.v1.mathopt";
|
||||
|
||||
option csharp_namespace = "Google.OrTools.Service";
|
||||
|
||||
// Problem feasibility status as claimed by the solver (solver is not required
|
||||
|
||||
@@ -21,6 +21,7 @@ import "ortools/service/v1/mathopt/sparse_containers.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.google.ortools.service.v1.mathopt";
|
||||
|
||||
option csharp_namespace = "Google.OrTools.Service";
|
||||
|
||||
// Feasibility of a primal or dual solution as claimed by the solver.
|
||||
|
||||
@@ -19,6 +19,7 @@ package operations_research.service.v1.mathopt;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.google.ortools.service.v1.mathopt";
|
||||
|
||||
option csharp_namespace = "Google.OrTools.Service";
|
||||
|
||||
// This message is used to specify some hints on the resources a remote solve is
|
||||
|
||||
@@ -19,6 +19,7 @@ package operations_research.service.v1.mathopt;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.google.ortools.service.v1.mathopt";
|
||||
|
||||
option csharp_namespace = "Google.OrTools.Service";
|
||||
|
||||
// A sparse representation of a vector of doubles.
|
||||
|
||||
@@ -23,9 +23,8 @@ cc_library(
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:logging",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/strings:string_view",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -36,14 +35,10 @@ cc_library(
|
||||
deps = [
|
||||
":dynamic_library",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:status_macros",
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/base:no_destructor",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/synchronization",
|
||||
"@abseil-cpp//absl/types:optional",
|
||||
"@abseil-cpp//absl/strings:string_view",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -54,13 +49,11 @@ cc_library(
|
||||
deps = [
|
||||
":dynamic_library",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:status_macros",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"//ortools/base:base_export",
|
||||
"@abseil-cpp//absl/base",
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/synchronization",
|
||||
"@abseil-cpp//absl/types:optional",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -167,11 +167,8 @@ cc_library(
|
||||
hdrs = ["stats.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:stl_util",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/base:types",
|
||||
"//ortools/port:sysinfo",
|
||||
"//ortools/port:utf8",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/strings",
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/casts.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
@@ -65,34 +62,6 @@ void ExploreAndCountAllProtoPathsInInstance(
|
||||
// several proto instances to build a global count of proto paths.
|
||||
absl::flat_hash_map<std::string, int>* proto_path_counts);
|
||||
|
||||
// This recursive function lists all the fields of a given proto *type*
|
||||
// (not a proto instance), up to the given depth of nested sub-messages, and
|
||||
// inserts their proto paths into `proto_paths`.
|
||||
//
|
||||
// SIMPLE EXAMPLE: with this .proto message definition:
|
||||
// message YY { repeated int z; }
|
||||
// message XX { int a; int b; repeated int c; repeated YY d; },
|
||||
// ExploreAndInsertAllProtoPathsInType(
|
||||
// XX().GetDescriptor(), {}, {}, /*max_depth=*/3)
|
||||
// Returns: {"a", "b", "c", "d", "d.z"}.
|
||||
// See the unit test for more complex examples.
|
||||
absl::flat_hash_set<std::string> ExploreAndInsertAllProtoPathsInType(
|
||||
const google::protobuf::Descriptor* descriptor,
|
||||
// ADVANCED USAGE: Pruning the proto tree exploration with two possible
|
||||
// mechanisms: 1) based on proto path, or 2) based on proto type.
|
||||
// 1) List of proto paths to skip: those fields will not be output nor
|
||||
// explored, meaning that their descendants is also skipped.
|
||||
const absl::flat_hash_set<std::string>& skip_these_proto_paths,
|
||||
// 2) Maps a field's full *type* name (as in Descriptor::full_name(), e.g.,
|
||||
// "operations_research.MyProto") to the subset of its child field names
|
||||
// that may be explored.
|
||||
const absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>&
|
||||
proto_type_names_to_field_name_allowlist,
|
||||
// The maximum depth, when diving into sub-messages. Note that some protos
|
||||
// may have infinite potential depth, e.g. message X { X x; }, so we must
|
||||
// limit the recursion depth.
|
||||
int max_depth);
|
||||
|
||||
// =============================================================================
|
||||
// Implementation of function templates.
|
||||
|
||||
@@ -102,7 +71,8 @@ absl::StatusOr<Proto*> SafeProtoDownCast(google::protobuf::Message* proto) {
|
||||
Proto::default_instance().GetDescriptor();
|
||||
const google::protobuf::Descriptor* actual_descriptor =
|
||||
proto->GetDescriptor();
|
||||
if (actual_descriptor == expected_descriptor) return reinterpret_cast<Proto*>(proto);
|
||||
if (actual_descriptor == expected_descriptor)
|
||||
return reinterpret_cast<Proto*>(proto);
|
||||
return absl::InvalidArgumentError(absl::StrFormat(
|
||||
"Expected message type '%s', but got type '%s'",
|
||||
expected_descriptor->full_name(), actual_descriptor->full_name()));
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -23,10 +24,8 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/stl_util.h"
|
||||
#include "ortools/base/types.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/port/sysinfo.h"
|
||||
#include "ortools/port/utf8.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -72,6 +71,25 @@ bool CompareStatPointers(const Stat* s1, const Stat* s2) {
|
||||
}
|
||||
}
|
||||
|
||||
// This function counts the number of codepoints in the string and assumes well
|
||||
// formed UTF-8 strings. Codepoints can take up to 4 bytes.
|
||||
// * 1-byte codepoint : 0yyyzzzz
|
||||
// * 2-byte codepoint : 110xxxyy 10yyzzzz
|
||||
// * 3-byte codepoint : 1110wwww 10xxxxyy 10yyzzzz
|
||||
// * 4-byte codepoint : 11110uvv 10vvwwww 10xxxxyy 10yyzzzz
|
||||
// We count one codepoint for bytes where the two most significant bits are
|
||||
// different of 0b10, effectively discarding all trailing bytes in multibyte
|
||||
// codepoints.
|
||||
int UTF8StrLen(absl::string_view str) {
|
||||
int len = 0;
|
||||
for (const char c : str) {
|
||||
if ((c & 0b11'00'0000) != 0b10'00'0000) {
|
||||
++len;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string StatsGroup::StatString() const {
|
||||
@@ -82,7 +100,7 @@ std::string StatsGroup::StatString() const {
|
||||
for (int i = 0; i < stats_.size(); ++i) {
|
||||
if (!stats_[i]->WorthPrinting()) continue;
|
||||
// We support UTF8 characters in the stat names.
|
||||
const int size = operations_research::utf8::UTF8StrLen(stats_[i]->Name());
|
||||
const int size = UTF8StrLen(stats_[i]->Name());
|
||||
longest_name_size = std::max(longest_name_size, size);
|
||||
sorted_stats.push_back(stats_[i]);
|
||||
}
|
||||
@@ -108,9 +126,7 @@ std::string StatsGroup::StatString() const {
|
||||
for (int i = 0; i < sorted_stats.size(); ++i) {
|
||||
result += " ";
|
||||
result += sorted_stats[i]->Name();
|
||||
result.append(longest_name_size - operations_research::utf8::UTF8StrLen(
|
||||
sorted_stats[i]->Name()),
|
||||
' ');
|
||||
result.append(longest_name_size - UTF8StrLen(sorted_stats[i]->Name()), ' ');
|
||||
result += " : " + sorted_stats[i]->ValueAsString();
|
||||
}
|
||||
result += "}\n";
|
||||
|
||||
@@ -69,8 +69,6 @@
|
||||
#define OR_TOOLS_UTIL_STATS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
|
||||
index 624a3c7..8d0493d 100644
|
||||
--- a/CMake/AbseilHelpers.cmake
|
||||
+++ b/CMake/AbseilHelpers.cmake
|
||||
@@ -345,7 +345,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
- if(ABSL_ENABLE_INSTALL)
|
||||
+ if(ABSL_ENABLE_INSTALL AND NOT ABSL_CC_LIB_TESTONLY)
|
||||
install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 1e7c856..a3c3dae 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -145,7 +145,7 @@ if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS)
|
||||
add_library(GTest::gmock ALIAS gmock)
|
||||
add_library(GTest::gmock_main ALIAS gmock_main)
|
||||
else()
|
||||
- message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.")
|
||||
+ message(WARNING "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
|
||||
index 8d2a856e..a1540467 100644
|
||||
index 8d2a856..a154046 100644
|
||||
--- a/absl/flags/declare.h
|
||||
+++ b/absl/flags/declare.h
|
||||
@@ -59,10 +59,15 @@ ABSL_NAMESPACE_END
|
||||
@@ -19,7 +45,7 @@ index 8d2a856e..a1540467 100644
|
||||
|
||||
#endif // ABSL_FLAGS_DECLARE_H_
|
||||
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
|
||||
index eb19bec0..13b2d240 100644
|
||||
index eb19bec..13b2d24 100644
|
||||
--- a/absl/log/CMakeLists.txt
|
||||
+++ b/absl/log/CMakeLists.txt
|
||||
@@ -47,6 +47,7 @@ absl_cc_library(
|
||||
@@ -31,7 +57,7 @@ index eb19bec0..13b2d240 100644
|
||||
absl::log_internal_nullguard
|
||||
absl::log_internal_nullstream
|
||||
diff --git a/absl/log/check_test_impl.inc b/absl/log/check_test_impl.inc
|
||||
index 5a7caf47..7bcedd40 100644
|
||||
index 5a7caf4..7bcedd4 100644
|
||||
--- a/absl/log/check_test_impl.inc
|
||||
+++ b/absl/log/check_test_impl.inc
|
||||
@@ -13,6 +13,8 @@
|
||||
@@ -63,7 +89,7 @@ index 5a7caf47..7bcedd40 100644
|
||||
enum { CASE_A, CASE_B };
|
||||
|
||||
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel
|
||||
index 1ba9d766..005861f9 100644
|
||||
index 1ba9d76..005861f 100644
|
||||
--- a/absl/log/internal/BUILD.bazel
|
||||
+++ b/absl/log/internal/BUILD.bazel
|
||||
@@ -82,6 +82,7 @@ cc_library(
|
||||
@@ -75,7 +101,7 @@ index 1ba9d766..005861f9 100644
|
||||
)
|
||||
|
||||
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h
|
||||
index 4554475d..c6078640 100644
|
||||
index 4554475..c607864 100644
|
||||
--- a/absl/log/internal/check_op.h
|
||||
+++ b/absl/log/internal/check_op.h
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
Reference in New Issue
Block a user