[bazel] Update bazel files
This commit is contained in:
committed by
Corentin Le Molgat
parent
d8ad3a8f9b
commit
54ae17fa91
@@ -285,7 +285,7 @@ cc_library(
|
||||
deps = [
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/base:nullability",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/container:flat_hash_set",
|
||||
"@abseil-cpp//absl/hash",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
@@ -299,6 +299,7 @@ cc_test(
|
||||
"//ortools/base:gmock_main",
|
||||
"@abseil-cpp//absl/algorithm:container",
|
||||
"@abseil-cpp//absl/base:nullability",
|
||||
"@abseil-cpp//absl/hash",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/random",
|
||||
"@abseil-cpp//absl/random:distributions",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/nullability.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/hash/hash.h"
|
||||
#include "absl/log/check.h"
|
||||
|
||||
@@ -59,8 +59,6 @@ class DoubleLinkedList;
|
||||
// http://dimacs.rutgers.edu/~graham/pubs/papers/freqvldbj.pdf
|
||||
//
|
||||
// This class is thread-compatible.
|
||||
//
|
||||
// TODO(user): Support move-only types.
|
||||
template <typename T, typename Hash = absl::Hash<T>,
|
||||
typename Eq = std::equal_to<T>>
|
||||
class SpaceSavingMostFrequent {
|
||||
@@ -81,6 +79,9 @@ class SpaceSavingMostFrequent {
|
||||
// Complexity: O(1).
|
||||
void FullyRemove(const T& value);
|
||||
|
||||
// Returns the `num_samples` most frequent elements in the data structure
|
||||
// sorted by decreasing count. Note: this does not work with non-copyable
|
||||
// types.
|
||||
// TODO(user): Replace this by an iterator with a begin() and end().
|
||||
std::vector<std::pair<T, int64_t>> GetMostFrequent(int num_samples) const;
|
||||
|
||||
@@ -120,6 +121,12 @@ class SpaceSavingMostFrequent {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveFromLinkedList(Item* absl_nonnull item) {
|
||||
Bucket* absl_nonnull bucket = item->bucket;
|
||||
item_alloc_.Return(bucket->items.erase(item));
|
||||
RemoveIfEmpty(bucket);
|
||||
}
|
||||
|
||||
Bucket* absl_nonnull GetBucketForCountOne() {
|
||||
if (!buckets_.empty() && buckets_.back()->count == 1) {
|
||||
return buckets_.back();
|
||||
@@ -134,7 +141,29 @@ class SpaceSavingMostFrequent {
|
||||
ssmf_internal::BoundedAllocator<Item> item_alloc_;
|
||||
ssmf_internal::BoundedAllocator<Bucket> bucket_alloc_;
|
||||
BucketList buckets_; // front with highest count.
|
||||
absl::flat_hash_map<T, Item* absl_nonnull, Hash, Eq> elem_to_item_;
|
||||
|
||||
struct HashItemPtr {
|
||||
using is_transparent = void;
|
||||
size_t operator()(const Item* absl_nonnull value) const {
|
||||
return Hash()(value->value);
|
||||
}
|
||||
size_t operator()(const T& value) const { return Hash()(value); }
|
||||
};
|
||||
|
||||
struct EqItemPtr {
|
||||
using is_transparent = void;
|
||||
bool operator()(const Item* absl_nonnull a,
|
||||
const Item* absl_nonnull b) const {
|
||||
return Eq()(a->value, b->value);
|
||||
}
|
||||
bool operator()(const Item* absl_nonnull a, const T& b) const {
|
||||
return Eq()(a->value, b);
|
||||
}
|
||||
bool operator()(const T& a, const Item* absl_nonnull b) const {
|
||||
return Eq()(a, b->value);
|
||||
}
|
||||
};
|
||||
absl::flat_hash_set<Item* absl_nonnull, HashItemPtr, EqItemPtr> item_ptr_set_;
|
||||
};
|
||||
|
||||
template <typename T, typename Hash, typename Eq>
|
||||
@@ -143,7 +172,7 @@ SpaceSavingMostFrequent<T, Hash, Eq>::SpaceSavingMostFrequent(int storage_size)
|
||||
item_alloc_(storage_size),
|
||||
bucket_alloc_(storage_size + 1) {
|
||||
CHECK_GT(storage_size, 0);
|
||||
elem_to_item_.reserve(storage_size + 1);
|
||||
item_ptr_set_.reserve(2 * storage_size);
|
||||
}
|
||||
|
||||
// Properly return all buckets and items to their allocators to ensure proper
|
||||
@@ -169,21 +198,21 @@ void SpaceSavingMostFrequent<T, Hash, Eq>::Add(T value) {
|
||||
if (buckets_.empty()) {
|
||||
// We are adding an element to an empty data structure.
|
||||
DCHECK(item_alloc_.empty());
|
||||
DCHECK(elem_to_item_.empty());
|
||||
DCHECK(item_ptr_set_.empty());
|
||||
Bucket* absl_nonnull bucket = buckets_.insert_back(bucket_alloc_.New());
|
||||
Item* absl_nonnull const item =
|
||||
bucket->items.insert_front(item_alloc_.New());
|
||||
item->bucket = bucket;
|
||||
item->value = value;
|
||||
item->value = std::move(value);
|
||||
bucket->count = 1;
|
||||
elem_to_item_.emplace(value, item);
|
||||
item_ptr_set_.emplace(item);
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(!buckets_.empty());
|
||||
|
||||
auto [it, inserted] = elem_to_item_.try_emplace(value);
|
||||
if (inserted) {
|
||||
auto it = item_ptr_set_.find(value);
|
||||
if (it == item_ptr_set_.end()) {
|
||||
// We are adding a new element. First, check if we are full, and if so,
|
||||
// remove the least frequent element.
|
||||
if (item_alloc_.full()) {
|
||||
@@ -193,18 +222,18 @@ void SpaceSavingMostFrequent<T, Hash, Eq>::Add(T value) {
|
||||
// the real least frequent of the bucket since it was unseen for longer.
|
||||
Item* absl_nonnull recycled_item = last_bucket->items.front();
|
||||
// Reclaim its storage for the newly added element.
|
||||
elem_to_item_.erase(recycled_item->value);
|
||||
item_ptr_set_.erase(recycled_item);
|
||||
item_alloc_.Return(last_bucket->items.pop_front());
|
||||
RemoveIfEmpty(last_bucket);
|
||||
}
|
||||
Bucket* absl_nonnull bucket = GetBucketForCountOne();
|
||||
DCHECK_EQ(bucket->count, 1);
|
||||
Item* absl_nonnull item = bucket->items.insert_back(item_alloc_.New());
|
||||
item->value = value;
|
||||
item->value = std::move(value);
|
||||
item->bucket = bucket;
|
||||
it->second = item; // set item pointer back in map.
|
||||
item_ptr_set_.emplace_hint(it, item);
|
||||
} else {
|
||||
Item* absl_nonnull item = it->second;
|
||||
Item* absl_nonnull item = *it;
|
||||
Bucket* absl_nonnull bucket = item->bucket;
|
||||
ItemList& current_bucket_items = bucket->items;
|
||||
const int64_t new_count = bucket->count + 1;
|
||||
@@ -239,13 +268,9 @@ void SpaceSavingMostFrequent<T, Hash, Eq>::Add(T value) {
|
||||
|
||||
template <typename T, typename Hash, typename Eq>
|
||||
void SpaceSavingMostFrequent<T, Hash, Eq>::FullyRemove(const T& value) {
|
||||
auto it = elem_to_item_.find(value);
|
||||
if (it == elem_to_item_.end()) return;
|
||||
Item* absl_nonnull item = it->second;
|
||||
Bucket* absl_nonnull bucket = item->bucket;
|
||||
item_alloc_.Return(bucket->items.erase(item));
|
||||
RemoveIfEmpty(bucket);
|
||||
elem_to_item_.erase(it);
|
||||
auto node = item_ptr_set_.extract(value);
|
||||
if (node.empty()) return;
|
||||
RemoveFromLinkedList(node.value());
|
||||
}
|
||||
|
||||
template <typename T, typename Hash, typename Eq>
|
||||
@@ -269,8 +294,11 @@ SpaceSavingMostFrequent<T, Hash, Eq>::GetMostFrequent(int num_samples) const {
|
||||
template <typename T, typename Hash, typename Eq>
|
||||
T SpaceSavingMostFrequent<T, Hash, Eq>::PopMostFrequent() {
|
||||
CHECK(!buckets_.empty());
|
||||
const T value = buckets_.front()->items.back()->value;
|
||||
FullyRemove(value);
|
||||
Item* absl_nonnull item = buckets_.front()->items.back();
|
||||
DCHECK(item_ptr_set_.contains(item));
|
||||
item_ptr_set_.erase(item);
|
||||
T value = std::move(item->value);
|
||||
RemoveFromLinkedList(item);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
|
||||
#include "ortools/algorithms/space_saving_most_frequent.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <string>
|
||||
@@ -24,6 +26,7 @@
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/base/nullability.h"
|
||||
#include "absl/hash/hash.h"
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/random/distributions.h"
|
||||
#include "absl/random/random.h"
|
||||
@@ -416,6 +419,55 @@ TEST(SpaceSavingMostFrequent, RandomInstances) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SpaceSavingMostFrequent, WorksWithUniquePtr) {
|
||||
SpaceSavingMostFrequentNaive<std::string> naive_most_frequent(5);
|
||||
|
||||
struct StringPtrHash {
|
||||
std::size_t operator()(const std::unique_ptr<std::string>& s) const {
|
||||
return absl::Hash<std::string>()(*s);
|
||||
}
|
||||
};
|
||||
struct StringPtrEq {
|
||||
bool operator()(const std::unique_ptr<std::string>& a,
|
||||
const std::unique_ptr<std::string>& b) const {
|
||||
return *a == *b;
|
||||
}
|
||||
};
|
||||
SpaceSavingMostFrequent<std::unique_ptr<std::string>, StringPtrHash,
|
||||
StringPtrEq>
|
||||
most_frequent(5);
|
||||
|
||||
auto add = [&](const std::string& value) {
|
||||
most_frequent.Add(std::make_unique<std::string>(value));
|
||||
naive_most_frequent.Add(value);
|
||||
};
|
||||
|
||||
add("a");
|
||||
add("b");
|
||||
add("c");
|
||||
add("d");
|
||||
add("e");
|
||||
add("a");
|
||||
add("a");
|
||||
add("a");
|
||||
|
||||
add("b");
|
||||
add("c");
|
||||
add("d");
|
||||
add("e");
|
||||
add("f");
|
||||
add("g");
|
||||
|
||||
std::vector<std::pair<std::string, int64_t>> res;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
const int64_t count = most_frequent.CountOfMostFrequent();
|
||||
if (count == 0) break;
|
||||
res.push_back({*most_frequent.PopMostFrequent(), count});
|
||||
}
|
||||
|
||||
EXPECT_EQ(res, naive_most_frequent.GetMostFrequent(10));
|
||||
}
|
||||
|
||||
template <int kElementSize>
|
||||
struct Element {
|
||||
Element() = default;
|
||||
|
||||
@@ -53,8 +53,6 @@ using operations_research::sat::CpObjectiveProto;
|
||||
using operations_research::sat::CpSolverResponse;
|
||||
using operations_research::sat::CpSolverStatus;
|
||||
using operations_research::sat::IntegerVariableProto;
|
||||
using operations_research::sat::kMaxIntegerValue;
|
||||
using operations_research::sat::kMinIntegerValue;
|
||||
using operations_research::sat::LinearConstraintProto;
|
||||
using operations_research::sat::Model;
|
||||
using operations_research::sat::NewSatParameters;
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
load("@rules_cc//cc:cc_library.bzl", "cc_library")
|
||||
load("@rules_cc//cc:cc_test.bzl", "cc_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
package(
|
||||
# Code specific to Glpk solver used by linear_solver/ and math_opt/.
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "glpk_env_deleter",
|
||||
|
||||
@@ -20,10 +20,10 @@ 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/cleanup",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
|
||||
9
ortools/linear_solver/testdata/BUILD.bazel
vendored
9
ortools/linear_solver/testdata/BUILD.bazel
vendored
@@ -11,10 +11,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Test files for the linear_solver packages.
|
||||
# Test files for the linear_solver component.
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"maximization.mps",
|
||||
"small_model.lp",
|
||||
"large_model.mps.gz",
|
||||
])
|
||||
exports_files(glob(["*"]))
|
||||
|
||||
@@ -18,6 +18,7 @@ package(default_visibility = ["//ortools/math_opt:__subpackages__"])
|
||||
pybind_extension(
|
||||
name = "solver",
|
||||
srcs = ["solver.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps =
|
||||
select({
|
||||
"//ortools/linear_solver:use_cp_sat": ["//ortools/math_opt/solvers:cp_sat_solver"],
|
||||
|
||||
@@ -39,7 +39,7 @@ pybind_extension(
|
||||
srcs = [
|
||||
"elemental.cc",
|
||||
],
|
||||
visibility = ["//ortools/math_opt/python:__subpackages__"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/math_opt/elemental",
|
||||
|
||||
@@ -20,6 +20,7 @@ package(default_visibility = ["//visibility:public"])
|
||||
pybind_extension(
|
||||
name = "mps_converter",
|
||||
srcs = ["mps_converter.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps =
|
||||
[
|
||||
"//ortools/math_opt:model_cc_proto",
|
||||
|
||||
@@ -29,9 +29,11 @@ cc_library(
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/base:stl_util",
|
||||
"//ortools/base:types",
|
||||
"//ortools/graph:topologicalsorter",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -41,10 +43,12 @@ cc_library(
|
||||
hdrs = ["arc_flow_solver.h"],
|
||||
deps = [
|
||||
":arc_flow_builder",
|
||||
":vector_bin_packing_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/packing:vector_bin_packing_cc_proto",
|
||||
"@abseil-cpp//absl/container:btree",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
],
|
||||
)
|
||||
@@ -54,12 +58,10 @@ cc_library(
|
||||
proto_library(
|
||||
name = "vector_bin_packing_proto",
|
||||
srcs = ["vector_bin_packing.proto"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_proto_library(
|
||||
name = "vector_bin_packing_cc_proto",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":vector_bin_packing_proto"],
|
||||
)
|
||||
|
||||
@@ -67,10 +69,9 @@ cc_library(
|
||||
name = "vector_bin_packing_parser",
|
||||
srcs = ["vector_bin_packing_parser.cc"],
|
||||
hdrs = ["vector_bin_packing_parser.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":vector_bin_packing_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/base:types",
|
||||
"//ortools/util:filelineiter",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
@@ -80,14 +81,15 @@ cc_binary(
|
||||
name = "vector_bin_packing",
|
||||
srcs = ["vector_bin_packing_main.cc"],
|
||||
deps = [
|
||||
":arc_flow_solver",
|
||||
":vector_bin_packing_cc_proto",
|
||||
":vector_bin_packing_parser",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/packing:arc_flow_builder",
|
||||
"//ortools/packing:arc_flow_solver",
|
||||
"//ortools/packing:vector_bin_packing_cc_proto",
|
||||
"//ortools/packing:vector_bin_packing_parser",
|
||||
"//ortools/linear_solver",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:globals",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
)
|
||||
@@ -116,12 +118,10 @@ cc_test(
|
||||
proto_library(
|
||||
name = "multiple_dimensions_bin_packing_proto",
|
||||
srcs = ["multiple_dimensions_bin_packing.proto"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_proto_library(
|
||||
name = "multiple_dimensions_bin_packing_cc_proto",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":multiple_dimensions_bin_packing_proto"],
|
||||
)
|
||||
|
||||
@@ -129,10 +129,10 @@ cc_library(
|
||||
name = "binpacking_2d_parser",
|
||||
srcs = ["binpacking_2d_parser.cc"],
|
||||
hdrs = ["binpacking_2d_parser.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":multiple_dimensions_bin_packing_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/base:types",
|
||||
"//ortools/util:filelineiter",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
|
||||
12
ortools/packing/testdata/BUILD.bazel
vendored
12
ortools/packing/testdata/BUILD.bazel
vendored
@@ -11,11 +11,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files(
|
||||
[
|
||||
"1D__bpp_scholl__bin2data.N2W2B1R0.vbp",
|
||||
"Class_01.2bp",
|
||||
],
|
||||
)
|
||||
exports_files([
|
||||
"1D__bpp_scholl__bin2data.N2W2B1R0.vbp",
|
||||
"Class_01.2bp",
|
||||
])
|
||||
|
||||
@@ -27,7 +27,7 @@ cc_library(
|
||||
":solvers_cc_proto",
|
||||
"//ortools/base:threadpool",
|
||||
"@abseil-cpp//absl/functional:any_invocable",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/synchronization",
|
||||
"@eigen",
|
||||
],
|
||||
)
|
||||
@@ -40,7 +40,7 @@ cc_test(
|
||||
":scheduler",
|
||||
":solvers_cc_proto",
|
||||
"//ortools/base:gmock_main",
|
||||
"@abseil-cpp//absl/functional:any_invocable",
|
||||
"@abseil-cpp//absl/log",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -73,9 +73,7 @@ cc_proto_library(
|
||||
|
||||
py_proto_library(
|
||||
name = "solvers_py_pb2",
|
||||
deps = [
|
||||
":solvers_proto",
|
||||
],
|
||||
deps = [":solvers_proto"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -88,8 +86,8 @@ cc_library(
|
||||
":sharder",
|
||||
":solve_log_cc_proto",
|
||||
":solvers_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/base:mathutil",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/random:distributions",
|
||||
"@eigen",
|
||||
],
|
||||
@@ -107,7 +105,7 @@ cc_test(
|
||||
":solvers_cc_proto",
|
||||
":test_util",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/base:protobuf_util",
|
||||
"//ortools/base:parse_text_proto",
|
||||
"@eigen",
|
||||
],
|
||||
)
|
||||
@@ -127,7 +125,6 @@ cc_library(
|
||||
":solvers_proto_validation",
|
||||
":termination",
|
||||
":trust_region",
|
||||
"//ortools/base",
|
||||
"//ortools/base:mathutil",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/glop:parameters_cc_proto",
|
||||
@@ -139,6 +136,8 @@ cc_library(
|
||||
"//ortools/util:logging",
|
||||
"@abseil-cpp//absl/algorithm:container",
|
||||
"@abseil-cpp//absl/base:nullability",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
@@ -164,13 +163,14 @@ cc_test(
|
||||
":solvers_cc_proto",
|
||||
":termination",
|
||||
":test_util",
|
||||
"//ortools/base",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/glop:parameters_cc_proto",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/lp_data",
|
||||
"//ortools/lp_data:base",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@eigen",
|
||||
@@ -182,9 +182,9 @@ cc_library(
|
||||
srcs = ["quadratic_program.cc"],
|
||||
hdrs = ["quadratic_program.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
@@ -200,8 +200,7 @@ cc_test(
|
||||
":quadratic_program",
|
||||
":test_util",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/base:protobuf_util",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/base:parse_text_proto",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
@@ -215,8 +214,10 @@ cc_library(
|
||||
hdrs = ["quadratic_program_io.h"],
|
||||
deps = [
|
||||
":quadratic_program",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:gzipfile",
|
||||
"//ortools/base:mathutil",
|
||||
"//ortools/base:recordio",
|
||||
"//ortools/base:status_macros",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/linear_solver:model_exporter",
|
||||
@@ -224,6 +225,8 @@ cc_library(
|
||||
"//ortools/util:file_util",
|
||||
"@abseil-cpp//absl/base",
|
||||
"@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",
|
||||
@@ -241,8 +244,9 @@ cc_library(
|
||||
":sharded_quadratic_program",
|
||||
":sharder",
|
||||
":solve_log_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/base:mathutil",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/random:distributions",
|
||||
"@eigen",
|
||||
],
|
||||
@@ -274,9 +278,9 @@ cc_library(
|
||||
":scheduler",
|
||||
":sharder",
|
||||
":solvers_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/util:logging",
|
||||
"@abseil-cpp//absl/memory",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@eigen",
|
||||
],
|
||||
@@ -302,7 +306,6 @@ cc_library(
|
||||
hdrs = ["sharder.h"],
|
||||
deps = [
|
||||
":scheduler",
|
||||
"//ortools/base",
|
||||
"//ortools/base:mathutil",
|
||||
"//ortools/base:timer",
|
||||
"@abseil-cpp//absl/log",
|
||||
@@ -323,9 +326,9 @@ cc_test(
|
||||
":scheduler",
|
||||
":sharder",
|
||||
":solvers_cc_proto",
|
||||
"//ortools/base",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/base:mathutil",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/random:distributions",
|
||||
"@eigen",
|
||||
],
|
||||
@@ -350,7 +353,7 @@ cc_test(
|
||||
":solvers_cc_proto",
|
||||
":solvers_proto_validation",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/base:protobuf_util",
|
||||
"//ortools/base:parse_text_proto",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/strings",
|
||||
],
|
||||
@@ -363,7 +366,7 @@ cc_library(
|
||||
deps = [
|
||||
":solve_log_cc_proto",
|
||||
":solvers_cc_proto",
|
||||
"//ortools/base",
|
||||
"@abseil-cpp//absl/log",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -376,7 +379,7 @@ cc_test(
|
||||
":solvers_cc_proto",
|
||||
":termination",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/base:protobuf_util",
|
||||
"//ortools/base:parse_text_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -387,8 +390,10 @@ cc_library(
|
||||
hdrs = ["test_util.h"],
|
||||
deps = [
|
||||
":quadratic_program",
|
||||
"//ortools/base",
|
||||
"//ortools/base:gmock",
|
||||
"//ortools/base:path",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
"@eigen",
|
||||
],
|
||||
@@ -399,8 +404,8 @@ cc_test(
|
||||
srcs = ["test_util_test.cc"],
|
||||
deps = [
|
||||
":test_util",
|
||||
"//ortools/base",
|
||||
"//ortools/base:gmock_main",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/types:span",
|
||||
"@eigen",
|
||||
],
|
||||
@@ -415,9 +420,10 @@ cc_library(
|
||||
":sharded_optimization_utils",
|
||||
":sharded_quadratic_program",
|
||||
":sharder",
|
||||
"//ortools/base",
|
||||
"//ortools/base:mathutil",
|
||||
"@abseil-cpp//absl/algorithm:container",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@eigen",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "Eigen/Core"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/protobuf_util.h"
|
||||
#include "ortools/base/parse_text_proto.h"
|
||||
#include "ortools/pdlp/quadratic_program.h"
|
||||
#include "ortools/pdlp/sharded_quadratic_program.h"
|
||||
#include "ortools/pdlp/solve_log.pb.h"
|
||||
@@ -31,16 +31,459 @@
|
||||
namespace operations_research::pdlp {
|
||||
namespace {
|
||||
|
||||
using ::google::protobuf::util::ParseTextOrDie;
|
||||
using ::google::protobuf::contrib::parse_proto::ParseTextOrDie;
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Each;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using ::testing::EqualsProto;
|
||||
using ::testing::Ge;
|
||||
using ::testing::Le;
|
||||
using ::testing::Ne;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::proto::Approximately;
|
||||
using ::testing::proto::Partially;
|
||||
|
||||
// The following block relies heavily on `EqualsProto`, which isn't open source.
|
||||
|
||||
void CheckScaledAndUnscaledConvergenceInformation(
|
||||
QuadraticProgram qp, const Eigen::VectorXd& primal_solution,
|
||||
const Eigen::VectorXd& dual_solution,
|
||||
const double componentwise_primal_residual_offset,
|
||||
const double componentwise_dual_residual_offset,
|
||||
const ConvergenceInformation& expected_stats) {
|
||||
const int num_threads = 2;
|
||||
const int num_shards = 10;
|
||||
ShardedQuadraticProgram sharded_qp(std::move(qp), num_threads, num_shards);
|
||||
EXPECT_THAT(
|
||||
ComputeScaledConvergenceInformation(
|
||||
PrimalDualHybridGradientParams(), sharded_qp, primal_solution,
|
||||
dual_solution, componentwise_primal_residual_offset,
|
||||
componentwise_dual_residual_offset, POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(expected_stats))));
|
||||
|
||||
// Rescale the problem so that the primal and dual solutions have elements
|
||||
// equal to -1.0, 0.0, or 1.0.
|
||||
Eigen::VectorXd col_scaling_vec = primal_solution.unaryExpr(
|
||||
[](double x) { return x != 0.0 ? std::abs(x) : 1.0; });
|
||||
Eigen::VectorXd row_scaling_vec = dual_solution.unaryExpr(
|
||||
[](double x) { return x != 0.0 ? std::abs(x) : 1.0; });
|
||||
Eigen::VectorXd scaled_primal_solution =
|
||||
primal_solution.cwiseQuotient(col_scaling_vec);
|
||||
Eigen::VectorXd scaled_dual_solution =
|
||||
dual_solution.cwiseQuotient(row_scaling_vec);
|
||||
sharded_qp.RescaleQuadraticProgram(col_scaling_vec, row_scaling_vec);
|
||||
EXPECT_THAT(
|
||||
ComputeConvergenceInformation(
|
||||
PrimalDualHybridGradientParams(), sharded_qp, col_scaling_vec,
|
||||
row_scaling_vec, scaled_primal_solution, scaled_dual_solution,
|
||||
componentwise_primal_residual_offset,
|
||||
componentwise_dual_residual_offset, POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(expected_stats))));
|
||||
|
||||
// Also check that the iteration stats for the scaled problem have the correct
|
||||
// objectives and norms.
|
||||
ConvergenceInformation expected_scaled_stats;
|
||||
expected_scaled_stats.set_primal_objective(expected_stats.primal_objective());
|
||||
expected_scaled_stats.set_dual_objective(expected_stats.dual_objective());
|
||||
expected_scaled_stats.set_l_inf_primal_variable(1.0);
|
||||
expected_scaled_stats.set_l_inf_dual_variable(1.0);
|
||||
|
||||
EXPECT_THAT(
|
||||
ComputeScaledConvergenceInformation(
|
||||
PrimalDualHybridGradientParams(), sharded_qp, scaled_primal_solution,
|
||||
scaled_dual_solution, componentwise_primal_residual_offset,
|
||||
componentwise_dual_residual_offset, POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(expected_scaled_stats))));
|
||||
}
|
||||
|
||||
void CheckScaledAndUnscaledInfeasibilityStats(
|
||||
QuadraticProgram qp, const Eigen::VectorXd& primal_ray,
|
||||
const Eigen::VectorXd& dual_ray,
|
||||
const Eigen::VectorXd& primal_solution_for_residual_tests,
|
||||
const InfeasibilityInformation& expected_infeasibility_info) {
|
||||
const int num_threads = 2;
|
||||
const int num_shards = 2;
|
||||
ShardedQuadraticProgram sharded_qp(std::move(qp), num_threads, num_shards);
|
||||
EXPECT_THAT(
|
||||
ComputeInfeasibilityInformation(
|
||||
PrimalDualHybridGradientParams(), sharded_qp,
|
||||
Eigen::VectorXd::Ones(sharded_qp.PrimalSize()),
|
||||
Eigen::VectorXd::Ones(sharded_qp.DualSize()), primal_ray, dual_ray,
|
||||
primal_solution_for_residual_tests, POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(expected_infeasibility_info))));
|
||||
|
||||
// Rescale the problem so that the primal and dual certificates have elements
|
||||
// equal to -1.0, 0.0, or 1.0.
|
||||
Eigen::VectorXd col_scaling_vec = primal_ray.unaryExpr(
|
||||
[](double x) { return x != 0.0 ? std::abs(x) : 1.0; });
|
||||
Eigen::VectorXd row_scaling_vec =
|
||||
dual_ray.unaryExpr([](double x) { return x != 0.0 ? std::abs(x) : 1.0; });
|
||||
Eigen::VectorXd scaled_primal_solution =
|
||||
primal_ray.cwiseQuotient(col_scaling_vec);
|
||||
Eigen::VectorXd scaled_dual_solution =
|
||||
dual_ray.cwiseQuotient(row_scaling_vec);
|
||||
Eigen::VectorXd scaled_primal_solution_for_residual_tests =
|
||||
primal_solution_for_residual_tests.cwiseQuotient(col_scaling_vec);
|
||||
sharded_qp.RescaleQuadraticProgram(col_scaling_vec, row_scaling_vec);
|
||||
EXPECT_THAT(
|
||||
ComputeInfeasibilityInformation(
|
||||
PrimalDualHybridGradientParams(), sharded_qp, col_scaling_vec,
|
||||
row_scaling_vec, scaled_primal_solution, scaled_dual_solution,
|
||||
scaled_primal_solution_for_residual_tests,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(expected_infeasibility_info))));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleLpAtOptimum) {
|
||||
const Eigen::VectorXd primal_solution{{-1.0, 8.0, 1.0, 2.5}};
|
||||
const Eigen::VectorXd dual_solution{{-2.0, 0.0, 2.375, 2.0 / 3}};
|
||||
CheckScaledAndUnscaledConvergenceInformation(
|
||||
TestLp(), primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
ParseTextOrDie<ConvergenceInformation>(R"pb(
|
||||
primal_objective: -34.0
|
||||
dual_objective: -34.0
|
||||
corrected_dual_objective: -34.0
|
||||
l_inf_primal_residual: 0.0
|
||||
l2_primal_residual: 0.0
|
||||
l_inf_componentwise_primal_residual: 0.0
|
||||
l_inf_dual_residual: 0.0
|
||||
l2_dual_residual: 0.0
|
||||
l_inf_componentwise_dual_residual: 0.0
|
||||
l_inf_primal_variable: 8.0
|
||||
l2_primal_variable: 8.5
|
||||
l_inf_dual_variable: 2.375
|
||||
l2_dual_variable: 3.1756998353818715
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleLpWithPrimalResidual) {
|
||||
// This is the optimal solution, except that x_3 (`primal_solution[3]`) has
|
||||
// been changed from 2.5 to 3.5, increasing the objective by 1, but causing
|
||||
// the first constraint to be violated by 2 and the last constraint by 1.
|
||||
const Eigen::VectorXd primal_solution{{-1.0, 8.0, 1.0, 3.5}};
|
||||
const Eigen::VectorXd dual_solution{{-2.0, 0.0, 2.375, 2.0 / 3}};
|
||||
CheckScaledAndUnscaledConvergenceInformation(
|
||||
TestLp(), primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
ParseTextOrDie<ConvergenceInformation>(R"pb(
|
||||
primal_objective: -33.0
|
||||
dual_objective: -34.0
|
||||
corrected_dual_objective: -34.0
|
||||
l_inf_primal_residual: 2.0
|
||||
l2_primal_residual: 2.2360679774997896
|
||||
l_inf_componentwise_primal_residual: 0.5
|
||||
l_inf_dual_residual: 0.0
|
||||
l2_dual_residual: 0.0
|
||||
l_inf_componentwise_dual_residual: 0.0
|
||||
l_inf_primal_variable: 8.0
|
||||
l2_primal_variable: 8.8459030064770662
|
||||
l_inf_dual_variable: 2.375
|
||||
l2_dual_variable: 3.1756998353818715
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleLpWithDualResidual) {
|
||||
// This is the optimal solution, except that y_1 (`dual_solution[1]`) has been
|
||||
// changed from 0 to -1, causing x_0 and x_2 to have primal gradients (dual
|
||||
// residuals) of 1.0.
|
||||
const Eigen::VectorXd primal_solution{{-1.0, 8.0, 1.0, 2.5}};
|
||||
const Eigen::VectorXd dual_solution{{-2.0, -1.0, 2.375, 2.0 / 3}};
|
||||
CheckScaledAndUnscaledConvergenceInformation(
|
||||
TestLp(), primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
ParseTextOrDie<ConvergenceInformation>(R"pb(
|
||||
primal_objective: -34.0
|
||||
dual_objective: -41.0
|
||||
corrected_dual_objective: -inf
|
||||
l_inf_primal_residual: 0.0
|
||||
l2_primal_residual: 0.0
|
||||
l_inf_componentwise_primal_residual: 0.0
|
||||
l_inf_dual_residual: 1.0
|
||||
l2_dual_residual: 1.4142135623730950
|
||||
l_inf_componentwise_dual_residual: 0.5
|
||||
l_inf_primal_variable: 8.0
|
||||
l2_primal_variable: 8.5
|
||||
l_inf_dual_variable: 2.375
|
||||
l2_dual_variable: 3.3294247918288294
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleLpWithBothResiduals) {
|
||||
// This is the optimal solution, except that x_3 (`primal_solution[3]`) has
|
||||
// been changed from 2.5 to 3.5, increasing the objective by 1, but causing
|
||||
// the first constraint to be violated by 2 and the last constraint by 1, and
|
||||
// y_1 (`dual_solution[1]`) has been changed from 0 to -1, causing x_0 and x_2
|
||||
// to have primal gradients (dual residuals) of 1.0. The primal and dual
|
||||
// componentwise_residual_offset values are different, to check that the
|
||||
// correct offset is applied when computing the
|
||||
// l_inf_componentwise_XXX_residual values.
|
||||
const Eigen::VectorXd primal_solution{{-1.0, 8.0, 1.0, 3.5}};
|
||||
const Eigen::VectorXd dual_solution{{-2.0, -1.0, 2.375, 2.0 / 3}};
|
||||
CheckScaledAndUnscaledConvergenceInformation(
|
||||
TestLp(), primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/3.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
ParseTextOrDie<ConvergenceInformation>(R"pb(
|
||||
primal_objective: -33.0
|
||||
dual_objective: -41.0
|
||||
corrected_dual_objective: -inf
|
||||
l_inf_primal_residual: 2.0
|
||||
l2_primal_residual: 2.2360679774997896
|
||||
l_inf_componentwise_primal_residual: 0.25
|
||||
l_inf_dual_residual: 1.0
|
||||
l2_dual_residual: 1.4142135623730950
|
||||
l_inf_componentwise_dual_residual: 0.5
|
||||
l_inf_primal_variable: 8.0
|
||||
l2_primal_variable: 8.8459030064770662
|
||||
l_inf_dual_variable: 2.375
|
||||
l2_dual_variable: 3.3294247918288294
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleQpAtOptimum) {
|
||||
const Eigen::VectorXd primal_solution{{1.0, 0.0}};
|
||||
const Eigen::VectorXd dual_solution{{-1.0}};
|
||||
CheckScaledAndUnscaledConvergenceInformation(
|
||||
TestDiagonalQp1(), primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
ParseTextOrDie<ConvergenceInformation>(R"pb(
|
||||
primal_objective: 6.0
|
||||
dual_objective: 6.0
|
||||
corrected_dual_objective: 6.0
|
||||
l_inf_primal_residual: 0.0
|
||||
l2_primal_residual: 0.0
|
||||
l_inf_componentwise_primal_residual: 0.0
|
||||
l_inf_dual_residual: 0.0
|
||||
l2_dual_residual: 0.0
|
||||
l_inf_componentwise_dual_residual: 0.0
|
||||
l_inf_primal_variable: 1.0
|
||||
l2_primal_variable: 1.0
|
||||
l_inf_dual_variable: 1.0
|
||||
l2_dual_variable: 1.0
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleLpWithGapResidualsAndZeroPrimalSolution) {
|
||||
const int num_threads = 2;
|
||||
const int num_shards = 10;
|
||||
ShardedQuadraticProgram sharded_qp(TestLp(), num_threads, num_shards);
|
||||
|
||||
const Eigen::VectorXd primal_solution = Eigen::VectorXd::Zero(4);
|
||||
const Eigen::VectorXd dual_solution{{1.0, 0.0, 0.0, -1.0}};
|
||||
|
||||
PrimalDualHybridGradientParams params_true, params_false;
|
||||
params_true.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
|
||||
true);
|
||||
params_false.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
|
||||
false);
|
||||
|
||||
// c is: [5.5, -2, -1, 1]
|
||||
// -A^T y is: [-2, -1, 0.5, -3]
|
||||
// c - A^T y is: [3.5, -3.0, -0.5, -2.0].
|
||||
// When the primal variable is 0.0 and the bound is not 0.0, the bound
|
||||
// corresponding to c - A^T y is handled as infinite when
|
||||
// `handle_some_primal_gradients_on_finite_bounds_as_residuals` is true.
|
||||
// Thus, for the all zero primal solution: when
|
||||
// `handle_some_primal_gradients_on_finite_bounds_as_residuals` is true, the
|
||||
// residuals are [3.5, -3.0, -0.5, -2.0] and all bounds are treated as
|
||||
// infinite. When `handle_some_primal_gradients_on_finite_bounds_as_residuals`
|
||||
// is false, the residuals are [3.5, -3.0, 0, 0] and the corresponding bound
|
||||
// terms are [0.0, -2, 6, 3.5].
|
||||
EXPECT_THAT(ComputeScaledConvergenceInformation(
|
||||
params_true, sharded_qp, primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(R"pb(
|
||||
dual_objective: -3.0
|
||||
corrected_dual_objective: -inf
|
||||
l_inf_dual_residual: 3.5
|
||||
# 5.0497524691810389 = L_2(3.5, -3.0, -0.5, -2.0)
|
||||
l2_dual_residual: 5.0497524691810389
|
||||
)pb"))));
|
||||
EXPECT_THAT(ComputeScaledConvergenceInformation(
|
||||
params_false, sharded_qp, primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(R"pb(
|
||||
dual_objective: -7.0
|
||||
corrected_dual_objective: -inf
|
||||
l_inf_dual_residual: 3.5
|
||||
# 4.6097722286464436 = L_2(3.5, -3.0, 0.0, 0.0)
|
||||
l2_dual_residual: 4.6097722286464436
|
||||
)pb"))));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleLpWithGapResidualsAndNonZeroPrimalSolution) {
|
||||
const int num_threads = 2;
|
||||
const int num_shards = 10;
|
||||
ShardedQuadraticProgram sharded_qp(TestLp(), num_threads, num_shards);
|
||||
|
||||
const Eigen::VectorXd primal_solution{{0.0, 0.0, 4.0, 3.0}};
|
||||
const Eigen::VectorXd dual_solution{{1.0, 0.0, 0.0, -1.0}};
|
||||
|
||||
PrimalDualHybridGradientParams params_true, params_false;
|
||||
params_true.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
|
||||
true);
|
||||
params_false.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
|
||||
false);
|
||||
|
||||
// c is: [5.5, -2, -1, 1]
|
||||
// -A^T y is: [-2, -1, 0.5, -3]
|
||||
// c - A^T y is: [3.5, -3.0, -0.5, -2.0].
|
||||
// When the primal variable is 0.0 and the bound is not 0.0, the bound
|
||||
// corresponding to c - A^T y is treated as infinite when
|
||||
// `handle_some_primal_gradients_on_finite_bounds_as_residuals` is true.
|
||||
// Thus, for primal_solution [0, 0, 4, 3], whether
|
||||
// `handle_some_primal_gradients_on_finite_bounds_as_residuals` is true or
|
||||
// not, the residuals are [3.5, -3.0, 0.0, 0.0] and the corresponding bound
|
||||
// terms are [0.0, -2, 6, 3.5].
|
||||
EXPECT_THAT(ComputeScaledConvergenceInformation(
|
||||
params_true, sharded_qp, primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(R"pb(
|
||||
dual_objective: -13.0
|
||||
corrected_dual_objective: -inf
|
||||
l_inf_dual_residual: 3.5
|
||||
# 4.6097722286464436 = L_2(3.5, -3.0, 0.0, 0.0)
|
||||
l2_dual_residual: 4.6097722286464436
|
||||
)pb"))));
|
||||
EXPECT_THAT(ComputeScaledConvergenceInformation(
|
||||
params_false, sharded_qp, primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(R"pb(
|
||||
dual_objective: -7.0
|
||||
corrected_dual_objective: -inf
|
||||
l_inf_dual_residual: 3.5
|
||||
# 4.6097722286464436 = L_2(3.5, -3.0, 0.0, 0.0)
|
||||
l2_dual_residual: 4.6097722286464436
|
||||
)pb"))));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, SimpleQp) {
|
||||
const int num_threads = 2;
|
||||
const int num_shards = 10;
|
||||
ShardedQuadraticProgram sharded_qp(TestDiagonalQp1(), num_threads,
|
||||
num_shards);
|
||||
|
||||
const Eigen::VectorXd primal_solution{{1.0, 2.0}};
|
||||
const Eigen::VectorXd dual_solution{{0.0}};
|
||||
PrimalDualHybridGradientParams params_true, params_false;
|
||||
params_true.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
|
||||
true);
|
||||
params_false.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
|
||||
false);
|
||||
// Q*x is: [4.0, 2.0]
|
||||
// c is: [-1, -1]
|
||||
// A^T y is zero.
|
||||
// If `handle_some_primal_gradients_on_finite_bounds_as_residuals` is
|
||||
// true the second primal gradient term is handled as a residual, not a
|
||||
// reduced cost.
|
||||
// Other than the reduced cost terms, the dual objective is 5 (objective
|
||||
// offset) - 4 (1/2 x^T Q x) = 1
|
||||
EXPECT_THAT(ComputeScaledConvergenceInformation(
|
||||
params_true, sharded_qp, primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(R"pb(
|
||||
dual_objective: 8
|
||||
corrected_dual_objective: 2
|
||||
l_inf_dual_residual: 1.0
|
||||
l2_dual_residual: 1.0
|
||||
)pb"))));
|
||||
EXPECT_THAT(ComputeScaledConvergenceInformation(
|
||||
params_false, sharded_qp, primal_solution, dual_solution,
|
||||
/*componentwise_primal_residual_offset=*/1.0,
|
||||
/*componentwise_dual_residual_offset=*/1.0,
|
||||
POINT_TYPE_CURRENT_ITERATE),
|
||||
Partially(Approximately(EqualsProto(R"pb(
|
||||
dual_objective: 2
|
||||
corrected_dual_objective: 2
|
||||
l_inf_dual_residual: 0.0
|
||||
l2_dual_residual: 0.0
|
||||
)pb"))));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, InfeasibilityInformationWithCertificateLp) {
|
||||
const Eigen::VectorXd primal_ray{{0.0, 0.0}};
|
||||
const Eigen::VectorXd dual_ray{{-1.0, -1.0}};
|
||||
CheckScaledAndUnscaledInfeasibilityStats(
|
||||
SmallPrimalInfeasibleLp(), primal_ray, dual_ray, primal_ray,
|
||||
ParseTextOrDie<InfeasibilityInformation>(R"pb(
|
||||
max_primal_ray_infeasibility: 0
|
||||
primal_ray_linear_objective: 0
|
||||
primal_ray_quadratic_norm: 0
|
||||
max_dual_ray_infeasibility: 0
|
||||
dual_ray_objective: 1
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, InfeasibilityInformationWithoutCertificateLp) {
|
||||
const Eigen::VectorXd primal_ray{{2.0, 1.0}};
|
||||
const Eigen::VectorXd dual_ray{{-1.0, -3.0}};
|
||||
CheckScaledAndUnscaledInfeasibilityStats(
|
||||
SmallPrimalInfeasibleLp(), primal_ray, dual_ray, primal_ray,
|
||||
ParseTextOrDie<InfeasibilityInformation>(R"pb(
|
||||
max_primal_ray_infeasibility: 0.5
|
||||
primal_ray_linear_objective: 1.5
|
||||
primal_ray_quadratic_norm: 0
|
||||
max_dual_ray_infeasibility: 0.66666666666666663
|
||||
dual_ray_objective: 1.6666666666666667
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(IterationStatsTest, DetectsDualRayHasInfeasibleComponent) {
|
||||
const Eigen::VectorXd primal_ray{{0.0, 0.0}};
|
||||
const Eigen::VectorXd dual_ray{{1.0, 1.0}};
|
||||
// Components with the wrong sign cause the dual ray objective to be -inf.
|
||||
CheckScaledAndUnscaledInfeasibilityStats(
|
||||
SmallPrimalInfeasibleLp(), primal_ray, dual_ray, primal_ray,
|
||||
ParseTextOrDie<InfeasibilityInformation>(R"pb(
|
||||
max_dual_ray_infeasibility: 0.0
|
||||
dual_ray_objective: -inf
|
||||
)pb"));
|
||||
}
|
||||
|
||||
// Regression test for failures of math_opt's
|
||||
// SimpleLpTest.OptimalAfterInfeasible test.
|
||||
TEST(IterationStatsTest, HandlesReducedCostsOnDualRayCorrectly) {
|
||||
// A trivial LP mimicking the one used in math_opt's test:
|
||||
// min x
|
||||
// Constraint: 2 <= x
|
||||
// Variable: 0 <= x <= 1
|
||||
QuadraticProgram lp(1, 1);
|
||||
lp.objective_vector = Eigen::VectorXd{{1}};
|
||||
lp.constraint_lower_bounds = Eigen::VectorXd{{2}};
|
||||
lp.constraint_upper_bounds =
|
||||
Eigen::VectorXd{{std::numeric_limits<double>::infinity()}};
|
||||
lp.variable_lower_bounds = Eigen::VectorXd{{0}};
|
||||
lp.variable_upper_bounds = Eigen::VectorXd{{1}};
|
||||
lp.constraint_matrix.coeffRef(0, 0) = 1.0;
|
||||
lp.constraint_matrix.makeCompressed();
|
||||
const Eigen::VectorXd primal_solution{{1.0}};
|
||||
const Eigen::VectorXd primal_ray{{0.0}};
|
||||
const Eigen::VectorXd dual_ray{{1.0}};
|
||||
// `dual_ray_objective` = 2 (objective term) - 1 (reduced cost on x) = 1.
|
||||
CheckScaledAndUnscaledInfeasibilityStats(
|
||||
lp, primal_ray, dual_ray, primal_solution,
|
||||
ParseTextOrDie<InfeasibilityInformation>(R"pb(
|
||||
max_dual_ray_infeasibility: 0.0
|
||||
dual_ray_objective: 1.0
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(CorrectedDualTest, SimpleLpWithSuboptimalDual) {
|
||||
const int num_threads = 2;
|
||||
|
||||
@@ -39,15 +39,13 @@ py_test(
|
||||
name = "pdlp_test",
|
||||
size = "small",
|
||||
srcs = ["pdlp_test.py"],
|
||||
data = [
|
||||
":pdlp.so",
|
||||
],
|
||||
data = [":pdlp.so"],
|
||||
deps = [
|
||||
"//ortools/pdlp:solve_log_py_pb2",
|
||||
"//ortools/pdlp:solvers_py_pb2",
|
||||
requirement("absl-py"),
|
||||
requirement("numpy"),
|
||||
requirement("scipy"),
|
||||
"//ortools/linear_solver:linear_solver_py_pb2",
|
||||
"//ortools/pdlp:solve_log_py_pb2",
|
||||
"//ortools/pdlp:solvers_py_pb2",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -26,18 +26,19 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/protobuf_util.h"
|
||||
#include "ortools/base/parse_text_proto.h"
|
||||
#include "ortools/linear_solver/linear_solver.pb.h"
|
||||
#include "ortools/pdlp/test_util.h"
|
||||
|
||||
namespace operations_research::pdlp {
|
||||
namespace {
|
||||
|
||||
using ::google::protobuf::util::ParseTextOrDie;
|
||||
using ::google::protobuf::contrib::parse_proto::ParseTextOrDie;
|
||||
using ::operations_research::pdlp::internal::CombineRepeatedTripletsInPlace;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::EndsWith;
|
||||
using ::testing::Eq;
|
||||
using ::testing::EqualsProto;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::Optional;
|
||||
@@ -45,6 +46,7 @@ using ::testing::PrintToString;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::StartsWith;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::status::IsOkAndHolds;
|
||||
|
||||
const double kInfinity = std::numeric_limits<double>::infinity();
|
||||
|
||||
@@ -231,6 +233,27 @@ TEST_P(ConvertQpMpModelProtoTest, LpFromMpModelProto) {
|
||||
VerifyTestLp(*lp, maximize);
|
||||
}
|
||||
|
||||
TEST_P(ConvertQpMpModelProtoTest, LpToMpModelProto) {
|
||||
const bool maximize = GetParam();
|
||||
QuadraticProgram lp = TestLp();
|
||||
if (maximize) {
|
||||
lp.objective_scaling_factor = -1;
|
||||
lp.objective_vector *= -1;
|
||||
lp.objective_offset *= -1;
|
||||
}
|
||||
EXPECT_THAT(QpToMpModelProto(lp),
|
||||
IsOkAndHolds(EqualsProto(TestLpProto(maximize))));
|
||||
}
|
||||
|
||||
TEST_P(ConvertQpMpModelProtoTest, LpRoundTrip) {
|
||||
const bool maximize = GetParam();
|
||||
ASSERT_OK_AND_ASSIGN(QuadraticProgram qp,
|
||||
QpFromMpModelProto(TestLpProto(maximize),
|
||||
/*relax_integer_variables=*/false));
|
||||
EXPECT_THAT(QpToMpModelProto(qp),
|
||||
IsOkAndHolds(EqualsProto(TestLpProto(maximize))));
|
||||
}
|
||||
|
||||
// The QP:
|
||||
// optimize x_0^2 + x_1^2 + 3 x_0 - 4 s.t.
|
||||
// x_0 + x_1 <= 42
|
||||
@@ -310,6 +333,29 @@ TEST(CanFitInMpModelProto, SmallQpOk) {
|
||||
EXPECT_TRUE(CanFitInMpModelProto(*qp).ok());
|
||||
}
|
||||
|
||||
TEST(CanFitInMpModelProto, TooManyVariablesFails) {
|
||||
QuadraticProgram qp(1024, 5);
|
||||
EXPECT_THAT(internal::TestableCanFitInMpModelProto(qp, 1023),
|
||||
testing::status::StatusIs(absl::StatusCode::kInvalidArgument,
|
||||
HasSubstr("variable")));
|
||||
}
|
||||
|
||||
TEST(CanFitInMpModelProto, TooManyConstraintsFails) {
|
||||
QuadraticProgram qp(3, 1024);
|
||||
EXPECT_THAT(internal::TestableCanFitInMpModelProto(qp, 1023),
|
||||
testing::status::StatusIs(absl::StatusCode::kInvalidArgument,
|
||||
HasSubstr("constraint")));
|
||||
}
|
||||
|
||||
TEST_P(ConvertQpMpModelProtoTest, QpRoundTrip) {
|
||||
const bool maximize = GetParam();
|
||||
ASSERT_OK_AND_ASSIGN(QuadraticProgram qp,
|
||||
QpFromMpModelProto(TestQpProto(maximize),
|
||||
/*relax_integer_variables=*/false));
|
||||
EXPECT_THAT(QpToMpModelProto(qp),
|
||||
IsOkAndHolds(EqualsProto(TestQpProto(maximize))));
|
||||
}
|
||||
|
||||
// The ILP:
|
||||
// optimize x_0 + 2 * x_1 s.t.
|
||||
// x_0 + x_1 <= 1
|
||||
|
||||
@@ -11,6 +11,33 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
load(":code_samples.bzl", "code_sample_cc")
|
||||
load("@pip_deps//:requirements.bzl", "requirement")
|
||||
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
|
||||
load("@rules_python//python:py_binary.bzl", "py_binary")
|
||||
|
||||
code_sample_cc(name = "simple_pdlp_program")
|
||||
cc_binary(
|
||||
name = "simple_pdlp_program_cc",
|
||||
srcs = ["simple_pdlp_program.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/pdlp:iteration_stats",
|
||||
"//ortools/pdlp:primal_dual_hybrid_gradient",
|
||||
"//ortools/pdlp:quadratic_program",
|
||||
"//ortools/pdlp:solve_log_cc_proto",
|
||||
"//ortools/pdlp:solvers_cc_proto",
|
||||
"@eigen",
|
||||
],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "simple_pdlp_program_py",
|
||||
srcs = ["simple_pdlp_program.py"],
|
||||
main = "simple_pdlp_program.py",
|
||||
deps = [
|
||||
"//ortools/pdlp:solve_log_py_pb2",
|
||||
"//ortools/pdlp:solvers_py_pb2",
|
||||
"//ortools/pdlp/python:pdlp",
|
||||
requirement("numpy"),
|
||||
requirement("scipy"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,48 +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.
|
||||
|
||||
"""Helper macro to compile and test code samples."""
|
||||
|
||||
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
|
||||
load("@rules_cc//cc:cc_test.bzl", "cc_test")
|
||||
|
||||
def code_sample_cc(name):
|
||||
cc_binary(
|
||||
name = name + "_cc",
|
||||
srcs = [name + ".cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/pdlp:iteration_stats",
|
||||
"//ortools/pdlp:primal_dual_hybrid_gradient",
|
||||
"//ortools/pdlp:quadratic_program",
|
||||
"//ortools/pdlp:solve_log_cc_proto",
|
||||
"//ortools/pdlp:solvers_cc_proto",
|
||||
Label("@eigen"),
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = name + "_cc_test",
|
||||
size = "small",
|
||||
srcs = [name + ".cc"],
|
||||
deps = [
|
||||
":" + name + "_cc",
|
||||
"//ortools/base",
|
||||
"//ortools/pdlp:iteration_stats",
|
||||
"//ortools/pdlp:primal_dual_hybrid_gradient",
|
||||
"//ortools/pdlp:quadratic_program",
|
||||
"//ortools/pdlp:solve_log_cc_proto",
|
||||
"//ortools/pdlp:solvers_cc_proto",
|
||||
Label("@eigen"),
|
||||
],
|
||||
)
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <utility>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/synchronization/blocking_counter.h"
|
||||
#include "ortools/base/threadpool.h"
|
||||
#include "ortools/pdlp/solvers.pb.h"
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/protobuf_util.h"
|
||||
#include "ortools/base/parse_text_proto.h"
|
||||
#include "ortools/pdlp/solvers.pb.h"
|
||||
|
||||
namespace operations_research::pdlp {
|
||||
namespace {
|
||||
|
||||
using ::google::protobuf::util::ParseTextOrDie;
|
||||
using ::google::protobuf::contrib::parse_proto::ParseTextOrDie;
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
@@ -49,8 +49,7 @@ void TestTerminationCriteriaValidation(
|
||||
absl::string_view termination_criteria_string,
|
||||
absl::string_view error_substring) {
|
||||
TerminationCriteria termination_criteria =
|
||||
ParseTextOrDie<TerminationCriteria>(
|
||||
std::string(termination_criteria_string));
|
||||
ParseTextOrDie<TerminationCriteria>(termination_criteria_string);
|
||||
const absl::Status status = ValidateTerminationCriteria(termination_criteria);
|
||||
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument)
|
||||
<< "With termination criteria \"" << termination_criteria_string << "\"";
|
||||
|
||||
@@ -20,44 +20,16 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/protobuf_util.h"
|
||||
#include "ortools/base/parse_text_proto.h"
|
||||
#include "ortools/pdlp/solve_log.pb.h"
|
||||
#include "ortools/pdlp/solvers.pb.h"
|
||||
|
||||
namespace operations_research::pdlp {
|
||||
bool operator==(const TerminationCriteria::DetailedOptimalityCriteria& lhs,
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& rhs) {
|
||||
if (lhs.eps_optimal_primal_residual_absolute() !=
|
||||
rhs.eps_optimal_primal_residual_absolute()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.eps_optimal_primal_residual_relative() !=
|
||||
rhs.eps_optimal_primal_residual_relative()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.eps_optimal_dual_residual_absolute() !=
|
||||
rhs.eps_optimal_dual_residual_absolute()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.eps_optimal_dual_residual_relative() !=
|
||||
rhs.eps_optimal_dual_residual_relative()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.eps_optimal_objective_gap_absolute() !=
|
||||
rhs.eps_optimal_objective_gap_absolute()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.eps_optimal_objective_gap_relative() !=
|
||||
rhs.eps_optimal_objective_gap_relative()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using ::google::protobuf::util::ParseTextOrDie;
|
||||
using ::testing::Eq;
|
||||
using ::google::protobuf::contrib::parse_proto::ParseTextOrDie;
|
||||
using ::testing::EqualsProto;
|
||||
using ::testing::FieldsAre;
|
||||
using ::testing::Optional;
|
||||
|
||||
@@ -150,17 +122,16 @@ TEST(EffectiveOptimalityCriteriaTest, SimpleOptimalityCriteriaOverload) {
|
||||
const auto criteria =
|
||||
ParseTextOrDie<TerminationCriteria::SimpleOptimalityCriteria>(
|
||||
R"pb(eps_optimal_absolute: 1.0e-4 eps_optimal_relative: 2.0e-4)pb");
|
||||
EXPECT_THAT(
|
||||
EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
EqualsProto(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(EffectiveOptimalityCriteriaTest, SimpleOptimalityCriteriaInput) {
|
||||
@@ -169,17 +140,16 @@ TEST(EffectiveOptimalityCriteriaTest, SimpleOptimalityCriteriaInput) {
|
||||
eps_optimal_absolute: 1.0e-4
|
||||
eps_optimal_relative: 2.0e-4
|
||||
})pb");
|
||||
EXPECT_THAT(
|
||||
EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
EqualsProto(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST(EffectiveOptimalityCriteriaTest, DetailedOptimalityCriteriaInput) {
|
||||
@@ -193,23 +163,22 @@ TEST(EffectiveOptimalityCriteriaTest, DetailedOptimalityCriteriaInput) {
|
||||
eps_optimal_objective_gap_relative: 6.0e-4
|
||||
})pb");
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
Eq(criteria.detailed_optimality_criteria()));
|
||||
EqualsProto(criteria.detailed_optimality_criteria()));
|
||||
}
|
||||
|
||||
TEST(EffectiveOptimalityCriteriaTest, DeprecatedInput) {
|
||||
const auto criteria = ParseTextOrDie<TerminationCriteria>(
|
||||
R"pb(eps_optimal_absolute: 1.0e-4 eps_optimal_relative: 2.0e-4)pb");
|
||||
EXPECT_THAT(
|
||||
EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
EqualsProto(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb"));
|
||||
}
|
||||
|
||||
TEST_P(DetailedRelativeTerminationTest, TerminationWithNearOptimal) {
|
||||
|
||||
9
ortools/routing/parsers/testdata/BUILD.bazel
vendored
9
ortools/routing/parsers/testdata/BUILD.bazel
vendored
@@ -11,23 +11,28 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"c1_10_2-90-42222.96.txt",
|
||||
"C1_10_2.TXT",
|
||||
"carp_gdb19.dat", # https://github.com/vidalt/HGS-CARP/blob/master/Instances/CARP/gdb19.dat
|
||||
# https://www.sciencedirect.com/science/article/abs/pii/0305054883900266
|
||||
"carp_gdb19_diferente_deposito.dat",
|
||||
"carp_gdb19_incorrecto_vertices.dat",
|
||||
"carp_gdb19_incorrecta_lista_aristas_req.dat",
|
||||
"carp_gdb19_incorrecto_arinoreq.dat",
|
||||
"carp_gdb19_incorrecto_arireq.dat",
|
||||
"carp_gdb19_incorrecto_arista.dat",
|
||||
"carp_gdb19_incorrecto_capacidad.dat",
|
||||
"carp_gdb19_incorrecto_coste.dat",
|
||||
"carp_gdb19_incorrecto_deposito.dat",
|
||||
"carp_gdb19_incorrecto_tipo.dat",
|
||||
"carp_gdb19_incorrecto_vehiculos.dat",
|
||||
"carp_gdb19_incorrecto_vertices.dat",
|
||||
"carp_gdb19_mixed_arcs.dat",
|
||||
"carp_gdb19_no_arista_req.dat",
|
||||
"carp_toy.dat",
|
||||
"carp_toy.sol",
|
||||
"lilim.zip",
|
||||
"n20w20.001.txt",
|
||||
"n20w20.002.txt",
|
||||
@@ -39,6 +44,7 @@ exports_files([
|
||||
"rc201.0",
|
||||
"solomon_bp_c101.mps",
|
||||
"solomon_bp_c101.pb",
|
||||
"solomon_check_id.md",
|
||||
"solomon_check_id.txt",
|
||||
"solomon_google2.delivery_at_depot_location.pb.txt",
|
||||
"solomon_google2.delivery_only.pb.txt",
|
||||
@@ -48,5 +54,6 @@ exports_files([
|
||||
"solomon.zip",
|
||||
"tsplib_ar9152.tour",
|
||||
"tsplib_ar9152.tsp",
|
||||
"tsplib_F-n45-k4.vrp",
|
||||
"tsplib_Kytojoki_33.vrp",
|
||||
])
|
||||
|
||||
@@ -81,9 +81,7 @@ pybind_extension(
|
||||
srcs = ["pybind_solve_interrupter.cc"],
|
||||
# This library is not meant to be consumed end users; only pybind11 code
|
||||
# that needs a SolverInterrupter.
|
||||
visibility = [
|
||||
"//ortools/math_opt/core/python:__subpackages__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":py_solve_interrupter_lib"],
|
||||
)
|
||||
|
||||
@@ -91,6 +89,7 @@ pybind_extension(
|
||||
name = "pybind_solve_interrupter_testing",
|
||||
testonly = True,
|
||||
srcs = ["pybind_solve_interrupter_testing.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":py_solve_interrupter_testing_lib"],
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user