This commit is contained in:
Laurent Perron
2024-02-09 10:33:22 -08:00
parent ffb1abd467
commit 8cd1c38d1e
5 changed files with 253 additions and 170 deletions

View File

@@ -28,7 +28,6 @@
#include "benchmark/benchmark.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ortools/util/testing_utils.h"
namespace operations_research {
@@ -43,11 +42,14 @@ int BinarySearchMidpoint(int x, int y) {
namespace {
TEST(BinarySearchTest, DoubleExample) {
// M_PI is problematic on windows.
// std::numbers::pi is C++20 (incompatible with OR-Tools).
const double kPi = 3.14159265358979323846;
const double x =
BinarySearch<double>(/*x_true=*/0.0, /*x_false=*/M_PI / 2,
BinarySearch<double>(/*x_true=*/0.0, /*x_false=*/kPi / 2,
[](double x) { return cos(x) >= 2 * sin(x); });
EXPECT_GE(x, 0);
EXPECT_LE(x, M_PI / 2);
EXPECT_LE(x, kPi / 2);
EXPECT_EQ(cos(x), 2 * sin(x)) << x;
}

View File

@@ -11,31 +11,71 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <string>
#include <vector>
#include "absl/flags/flag.h"
#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"
#include "ortools/algorithms/set_cover.h"
#include "ortools/algorithms/set_cover_ledger.h"
#include "ortools/algorithms/set_cover_mip.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/algorithms/set_cover_reader.h"
#include "ortools/base/logging.h"
#include "ortools/base/path.h"
#include "ortools/base/timer.h"
#include "testing/base/public/googletest.h"
namespace operations_research {
void RunSolver(operations_research::SetCoverModel* model,
double expected_cost) {
operations_research::SetCoverLedger ledger(model);
double RunSolver(std::string name, SetCoverModel* model) {
SetCoverLedger ledger(model);
operations_research::GreedySolutionGenerator greedy(&ledger);
GreedySolutionGenerator greedy(&ledger);
WallTimer global_timer;
WallTimer timer;
global_timer.Start();
timer.Start();
CHECK(greedy.NextSolution());
CHECK(ledger.CheckSolution());
LOG(INFO) << "GreedySolutionGenerator cost: " << ledger.cost();
DCHECK(ledger.CheckSolution());
LOG(INFO) << name << "_GreedySolutionGenerator_cost, " << ledger.cost()
<< ", " << absl::ToInt64Microseconds(timer.GetDuration()) << ", us";
timer.Stop();
timer.Reset();
timer.Start();
operations_research::SteepestSearch steepest(&ledger);
steepest.NextSolution(100000);
LOG(INFO) << "SteepestSearch cost: " << ledger.cost();
CHECK(ledger.CheckSolution());
CHECK_EQ(ledger.cost(), expected_cost);
LOG(INFO) << name << "_SteepestSearch_cost, " << ledger.cost() << ", "
<< absl::ToInt64Microseconds(timer.GetDuration()) << ", us";
double best_cost = ledger.cost();
DCHECK(ledger.CheckSolution());
SubsetBoolVector best_choices = ledger.is_selected();
std::vector<SubsetIndex> focus = model->all_subsets();
timer.Stop();
timer.Reset();
timer.Start();
for (int i = 0; i < 10; ++i) {
std::vector<SubsetIndex> range =
ClearMostCoveredElements(std::min(100UL, focus.size()), &ledger);
SetCoverMip mip(&ledger);
mip.NextSolution(range);
if (ledger.cost() < best_cost) {
best_cost = ledger.cost();
best_choices = ledger.is_selected();
}
}
timer.Stop();
LOG(INFO) << name << "_MIP_cost, " << best_cost << ", "
<< absl::ToInt64Microseconds(timer.GetDuration()) << ", us";
global_timer.Stop();
LOG(INFO) << name << "_total_running_time, " << best_cost << ", "
<< absl::ToInt64Microseconds(global_timer.GetDuration())
<< ", us, total_time";
return best_cost;
// TODO(user): add guided local search.
}
@@ -59,132 +99,168 @@ enum ProblemSize {
#define APPEND(x, y) x##y
#define APPEND_AND_EVAL(x, y) APPEND(x, y)
// This macro makes it possible to declare each test below with a one liner.
#define ORLIB_TEST(name, objective, size, function) \
TEST(SetCoverTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \
auto filespec = \
file::JoinPathRespectAbsolute(absl::GetFlag(FLAGS_test_srcdir), \
"operations_research_data/" \
"operations_research_data/SET_COVERING", \
name); \
LOG(INFO) << "Reading " << name; \
operations_research::SetCoverModel model = function(filespec); \
RunSolver(&model, objective); \
const char data_dir[] =
"operations_research_data/operations_research_data/"
"SET_COVERING";
// In the following, the lower bounds are taken from:
// [1] Caprara, Alberto, Matteo Fischetti, and Paolo Toth. 1999. “A Heuristic
// Method for the Set Covering Problem.” Operations Research 47 (5): 73043.
// https://www.jstor.org/stable/223097 , and
// [2] Yagiura, Mutsunori, Masahiro Kishida, and Toshihide Ibaraki. 2006.
// “A 3-Flip Neighborhood Local Search for the Set Covering Problem.” European
// Journal of Operational Research 172 (2): 47299.
// https://www.sciencedirect.com/science/article/pii/S0377221704008264
// This macro makes it possible to declare each test below with a one-liner.
// 'best_objective' denotes the best objective costs found in literature.
// These are the proven optimal values. This can be achieved with MIP.
// For the rail instances, they are the best solution found in the literature
// [1] and [2]. They are not achievable though local search or MIP or a
// combination of the two.
// 'expected_objective' are the costs currently reached by the solver.
// TODO(user): find and add values for the unit cost (aka unicost) case.
#define ORLIB_TEST(name, best_objective, expected_objective, size, function) \
TEST(OrlibTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \
auto filespec = file::JoinPathRespectAbsolute( \
absl::GetFlag(FLAGS_test_srcdir), data_dir, name); \
LOG(INFO) << "Reading " << name; \
operations_research::SetCoverModel model = function(filespec); \
double cost = RunSolver(name, &model); \
(void)cost; \
}
#define RAIL_TEST(name, objective, size) \
ORLIB_TEST(name, objective, size, \
operations_research::ReadRailSetCoverProblem)
#define ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \
function) \
TEST(OrlibUnicostTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \
auto filespec = file::JoinPathRespectAbsolute( \
absl::GetFlag(FLAGS_test_srcdir), data_dir, name); \
LOG(INFO) << "Reading " << name; \
operations_research::SetCoverModel model = function(filespec); \
for (int i = 0; i < model.num_subsets(); ++i) { \
model.SetSubsetCost(i, 1.0); \
} \
double cost = RunSolver(absl::StrCat(name, "_unicost"), &model); \
(void)cost; \
}
#define SCP_TEST(name, objective, size) \
ORLIB_TEST(name, objective, size, \
operations_research::ReadBeasleySetCoverProblem)
#define SCP_TEST(name, best_objective, expected_objective, size) \
ORLIB_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadBeasleySetCoverProblem) \
ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadBeasleySetCoverProblem)
// Costs mentioned are the cost currently reached by the solver.
// TODO(user): add the best costs from the literature and compare.
#define RAIL_TEST(name, best_objective, expected_objective, size) \
ORLIB_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadRailSetCoverProblem) \
ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadRailSetCoverProblem)
RAIL_TEST("rail2536.txt", 889, MANYSECONDS);
RAIL_TEST("rail2586.txt", 1139, MANYSECONDS);
RAIL_TEST("rail4284.txt", 1362, MANYSECONDS);
RAIL_TEST("rail4872.txt", 1861, MANYSECONDS);
RAIL_TEST("rail507.txt", 218, FEWTENTHS);
RAIL_TEST("rail516.txt", 204, FEWTENTHS);
RAIL_TEST("rail582.txt", 250, FEWTENTHS);
SCP_TEST("scp41.txt", 429, 442, FEWMILLIS);
SCP_TEST("scp42.txt", 512, 555, FEWMILLIS);
SCP_TEST("scp43.txt", 516, 557, FEWMILLIS);
SCP_TEST("scp44.txt", 494, 516, FEWMILLIS);
SCP_TEST("scp45.txt", 512, 530, FEWMILLIS);
SCP_TEST("scp46.txt", 560, 594, FEWMILLIS);
SCP_TEST("scp47.txt", 430, 451, FEWMILLIS);
SCP_TEST("scp48.txt", 492, 502, FEWMILLIS);
SCP_TEST("scp49.txt", 641, 693, FEWMILLIS);
SCP_TEST("scp410.txt", 514, 525, FEWMILLIS);
SCP_TEST("scp41.txt", 442, FEWMILLIS);
SCP_TEST("scp42.txt", 555, FEWMILLIS);
SCP_TEST("scp43.txt", 557, FEWMILLIS);
SCP_TEST("scp44.txt", 516, FEWMILLIS);
SCP_TEST("scp45.txt", 530, FEWMILLIS);
SCP_TEST("scp46.txt", 594, FEWMILLIS);
SCP_TEST("scp47.txt", 451, FEWMILLIS);
SCP_TEST("scp48.txt", 502, FEWMILLIS);
SCP_TEST("scp49.txt", 693, FEWMILLIS);
SCP_TEST("scp410.txt", 525, FEWMILLIS);
SCP_TEST("scp51.txt", 253, 274, FEWMILLIS);
SCP_TEST("scp52.txt", 302, 329, FEWMILLIS);
SCP_TEST("scp53.txt", 226, 233, FEWMILLIS);
SCP_TEST("scp54.txt", 242, 255, FEWMILLIS);
SCP_TEST("scp55.txt", 211, 222, FEWMILLIS);
SCP_TEST("scp56.txt", 213, 234, FEWMILLIS);
SCP_TEST("scp57.txt", 293, 313, FEWMILLIS);
SCP_TEST("scp58.txt", 288, 309, FEWMILLIS);
SCP_TEST("scp59.txt", 279, 292, FEWMILLIS);
SCP_TEST("scp510.txt", 265, 276, FEWMILLIS);
SCP_TEST("scp51.txt", 274, FEWMILLIS);
SCP_TEST("scp52.txt", 329, FEWMILLIS);
SCP_TEST("scp53.txt", 233, FEWMILLIS);
SCP_TEST("scp54.txt", 255, FEWMILLIS);
SCP_TEST("scp55.txt", 222, FEWMILLIS);
SCP_TEST("scp56.txt", 234, FEWMILLIS);
SCP_TEST("scp57.txt", 313, FEWMILLIS);
SCP_TEST("scp58.txt", 309, FEWMILLIS);
SCP_TEST("scp59.txt", 292, FEWMILLIS);
SCP_TEST("scp510.txt", 276, FEWMILLIS);
SCP_TEST("scp61.txt", 138, 151, FEWMILLIS);
SCP_TEST("scp62.txt", 146, 173, FEWMILLIS);
SCP_TEST("scp63.txt", 145, 154, FEWMILLIS);
SCP_TEST("scp64.txt", 131, 137, FEWMILLIS);
SCP_TEST("scp65.txt", 161, 181, FEWMILLIS);
SCP_TEST("scp61.txt", 151, FEWMILLIS);
SCP_TEST("scp62.txt", 173, FEWMILLIS);
SCP_TEST("scp63.txt", 154, FEWMILLIS);
SCP_TEST("scp64.txt", 137, FEWMILLIS);
SCP_TEST("scp65.txt", 181, FEWMILLIS);
SCP_TEST("scpa1.txt", 253, 275, FEWHUNDREDTHS);
SCP_TEST("scpa2.txt", 252, 268, FEWHUNDREDTHS);
SCP_TEST("scpa3.txt", 232, 244, FEWHUNDREDTHS);
SCP_TEST("scpa4.txt", 234, 253, FEWHUNDREDTHS);
SCP_TEST("scpa5.txt", 236, 249, FEWHUNDREDTHS);
SCP_TEST("scpa1.txt", 275, FEWHUNDREDTHS);
SCP_TEST("scpa2.txt", 268, FEWHUNDREDTHS);
SCP_TEST("scpa3.txt", 244, FEWHUNDREDTHS);
SCP_TEST("scpa4.txt", 253, FEWHUNDREDTHS);
SCP_TEST("scpa5.txt", 249, FEWHUNDREDTHS);
SCP_TEST("scpb1.txt", 69, 74, FEWTENTHS);
SCP_TEST("scpb2.txt", 76, 78, FEWTENTHS);
SCP_TEST("scpb3.txt", 80, 85, FEWTENTHS);
SCP_TEST("scpb4.txt", 79, 85, FEWTENTHS);
SCP_TEST("scpb5.txt", 72, 77, FEWTENTHS);
SCP_TEST("scpb1.txt", 74, FEWTENTHS);
SCP_TEST("scpb2.txt", 78, FEWTENTHS);
SCP_TEST("scpb3.txt", 85, FEWTENTHS);
SCP_TEST("scpb4.txt", 85, FEWTENTHS);
SCP_TEST("scpb5.txt", 77, FEWTENTHS);
SCP_TEST("scpc1.txt", 227, 251, FEWHUNDREDTHS);
SCP_TEST("scpc2.txt", 219, 238, FEWHUNDREDTHS);
SCP_TEST("scpc3.txt", 243, 259, FEWHUNDREDTHS);
SCP_TEST("scpc4.txt", 219, 246, FEWHUNDREDTHS);
SCP_TEST("scpc5.txt", 214, 228, FEWHUNDREDTHS);
SCP_TEST("scpc1.txt", 251, FEWHUNDREDTHS);
SCP_TEST("scpc2.txt", 238, FEWHUNDREDTHS);
SCP_TEST("scpc3.txt", 259, FEWHUNDREDTHS);
SCP_TEST("scpc4.txt", 246, FEWHUNDREDTHS);
SCP_TEST("scpc5.txt", 228, FEWHUNDREDTHS);
SCP_TEST("scpd1.txt", 60, 68, FEWHUNDREDTHS);
SCP_TEST("scpd2.txt", 66, 70, FEWHUNDREDTHS);
SCP_TEST("scpd3.txt", 72, 78, FEWHUNDREDTHS);
SCP_TEST("scpd4.txt", 62, 67, FEWHUNDREDTHS);
SCP_TEST("scpd5.txt", 61, 72, FEWHUNDREDTHS);
SCP_TEST("scpclr10.txt", 32, FEWMILLIS);
SCP_TEST("scpclr11.txt", 30, FEWMILLIS);
SCP_TEST("scpclr12.txt", 31, FEWMILLIS);
SCP_TEST("scpclr13.txt", 33, FEWMILLIS);
SCP_TEST("scpe1.txt", 5, 5, FEWMILLIS);
SCP_TEST("scpe2.txt", 5, 6, FEWMILLIS);
SCP_TEST("scpe3.txt", 5, 5, FEWMILLIS);
SCP_TEST("scpe4.txt", 5, 6, FEWMILLIS);
SCP_TEST("scpe5.txt", 5, 5, FEWMILLIS);
SCP_TEST("scpcyc06.txt", 60, FEWMILLIS);
SCP_TEST("scpcyc07.txt", 144, FEWMILLIS);
SCP_TEST("scpcyc08.txt", 360, FEWMILLIS);
SCP_TEST("scpcyc09.txt", 816, SUBHUNDREDTH);
SCP_TEST("scpcyc10.txt", 1920, FEWHUNDREDTHS);
SCP_TEST("scpcyc11.txt", 4284, SUBTENTH);
SCP_TEST("scpnre1.txt", 29, 31, SUBTENTH);
SCP_TEST("scpnre2.txt", 30, 34, SUBTENTH);
SCP_TEST("scpnre3.txt", 27, 32, SUBTENTH);
SCP_TEST("scpnre4.txt", 28, 32, SUBTENTH);
SCP_TEST("scpnre5.txt", 28, 31, SUBTENTH);
SCP_TEST("scpd1.txt", 68, FEWHUNDREDTHS);
SCP_TEST("scpd2.txt", 70, FEWHUNDREDTHS);
SCP_TEST("scpd3.txt", 78, FEWHUNDREDTHS);
SCP_TEST("scpd4.txt", 67, FEWHUNDREDTHS);
SCP_TEST("scpd5.txt", 72, FEWHUNDREDTHS);
SCP_TEST("scpnrf1.txt", 14, 17, SUBTENTH);
SCP_TEST("scpnrf2.txt", 15, 16, SUBTENTH);
SCP_TEST("scpnrf3.txt", 14, 16, SUBTENTH);
SCP_TEST("scpnrf4.txt", 14, 15, SUBTENTH);
SCP_TEST("scpnrf5.txt", 13, 15, SUBTENTH);
SCP_TEST("scpe1.txt", 5, FEWMILLIS);
SCP_TEST("scpe2.txt", 6, FEWMILLIS);
SCP_TEST("scpe3.txt", 5, FEWMILLIS);
SCP_TEST("scpe4.txt", 6, FEWMILLIS);
SCP_TEST("scpe5.txt", 5, FEWMILLIS);
SCP_TEST("scpnrg1.txt", 176, 196, SUBTENTH);
SCP_TEST("scpnrg2.txt", 154, 171, SUBTENTH);
SCP_TEST("scpnrg3.txt", 166, 182, SUBTENTH);
SCP_TEST("scpnrg4.txt", 168, 187, SUBTENTH);
SCP_TEST("scpnrg5.txt", 168, 183, SUBTENTH);
SCP_TEST("scpnre1.txt", 31, SUBTENTH);
SCP_TEST("scpnre2.txt", 34, SUBTENTH);
SCP_TEST("scpnre3.txt", 32, SUBTENTH);
SCP_TEST("scpnre4.txt", 32, SUBTENTH);
SCP_TEST("scpnre5.txt", 31, SUBTENTH);
SCP_TEST("scpnrh1.txt", 63, 71, FEWTENTHS);
SCP_TEST("scpnrh2.txt", 63, 70, FEWTENTHS);
SCP_TEST("scpnrh3.txt", 59, 65, FEWTENTHS);
SCP_TEST("scpnrh4.txt", 58, 66, FEWTENTHS);
SCP_TEST("scpnrh5.txt", 55, 62, FEWTENTHS);
SCP_TEST("scpnrf1.txt", 17, SUBTENTH);
SCP_TEST("scpnrf2.txt", 16, SUBTENTH);
SCP_TEST("scpnrf3.txt", 16, SUBTENTH);
SCP_TEST("scpnrf4.txt", 15, SUBTENTH);
SCP_TEST("scpnrf5.txt", 15, SUBTENTH);
RAIL_TEST("rail507.txt", 174, 218, FEWTENTHS);
RAIL_TEST("rail516.txt", 182, 204, FEWTENTHS);
RAIL_TEST("rail582.txt", 211, 250, FEWTENTHS);
RAIL_TEST("rail2536.txt", 691, 889, MANYSECONDS);
RAIL_TEST("rail2586.txt", 952, 1139, MANYSECONDS);
RAIL_TEST("rail4284.txt", 1065, 1362, MANYSECONDS);
RAIL_TEST("rail4872.txt", 1527, 1861, MANYSECONDS); // [2]
SCP_TEST("scpnrg1.txt", 196, SUBTENTH);
SCP_TEST("scpnrg2.txt", 171, SUBTENTH);
SCP_TEST("scpnrg3.txt", 182, SUBTENTH);
SCP_TEST("scpnrg4.txt", 187, SUBTENTH);
SCP_TEST("scpnrg5.txt", 183, SUBTENTH);
SCP_TEST("scpclr10.txt", 0, 32, FEWMILLIS);
SCP_TEST("scpclr11.txt", 0, 30, FEWMILLIS);
SCP_TEST("scpclr12.txt", 0, 31, FEWMILLIS);
SCP_TEST("scpclr13.txt", 0, 33, FEWMILLIS);
SCP_TEST("scpnrh1.txt", 71, FEWTENTHS);
SCP_TEST("scpnrh2.txt", 70, FEWTENTHS);
SCP_TEST("scpnrh3.txt", 65, FEWTENTHS);
SCP_TEST("scpnrh4.txt", 66, FEWTENTHS);
SCP_TEST("scpnrh5.txt", 62, FEWTENTHS);
SCP_TEST("scpcyc06.txt", 0, 60, FEWMILLIS);
SCP_TEST("scpcyc07.txt", 0, 144, FEWMILLIS);
SCP_TEST("scpcyc08.txt", 0, 360, FEWMILLIS);
SCP_TEST("scpcyc09.txt", 0, 816, SUBHUNDREDTH);
SCP_TEST("scpcyc10.txt", 0, 1920, FEWHUNDREDTHS);
SCP_TEST("scpcyc11.txt", 0, 4284, SUBTENTH);
#undef ORLIB_TEST
#undef ORLIB_UNICOST_TEST
#undef APPEND
#undef APPEND_AND_EVAL
#undef SCP_TEST

View File

@@ -72,51 +72,51 @@ class SolveTest(absltest.TestCase):
):
solve.solve(mod, parameters.SolverType.GLOP)
def test_qp_solve(self) -> None:
mod = model.Model(name="test_model")
# Same model as TEST_P(QpDualsTest, GeneralQp1) in solver_test/qp_test.cc.
# Primal:
# min x_0^2 + x_0x_1 + 3x_1^2 - 2x_0
# s.t. 2 <= x_0 + 2x_1 <= inf
# 0 <= x_0 <= inf
# 0 <= x_1 <= inf
#
# Optimal solution: x* = (1.6, 0.2).
#
# Dual (go/mathopt-qp-dual):
# max -x_0^2 - x_0x_1 - 3x_1^2 + 2y_0
# s.t. y_0 + r_0 = 2x_0 + x_1 - 2
# 2y_0 + r_1 = x_0 + 6x_1
# y_0 >= 0
# r_0 >= 0
# r_1 >= 0
#
# Optimal solution: x* = (1.6, 0.2), y* = (1.4), r* = (0, 0).
x0 = mod.add_variable(lb=0.0, name="x0")
x1 = mod.add_variable(lb=0.0, name="x1")
mod.minimize(x0 * x0 + x0 * x1 + 3 * x1 * x1 - 2 * x0)
c = mod.add_linear_constraint(x0 + 2 * x1 >= 2)
res = solve.solve(mod, parameters.SolverType.OSQP)
self.assertEqual(
res.termination.reason,
result.TerminationReason.OPTIMAL,
msg=res.termination,
)
self.assertGreaterEqual(len(res.solutions), 1)
assert (
res.solutions[0].primal_solution is not None
and res.solutions[0].dual_solution is not None
)
self.assertAlmostEqual(
-0.2, res.solutions[0].primal_solution.objective_value, 4
)
self._assert_dict_almost_equal(
{x0: 1.6, x1: 0.2}, res.solutions[0].primal_solution.variable_values, 4
)
dual = res.solutions[0].dual_solution
self.assertAlmostEqual(-0.2, dual.objective_value, 4)
self._assert_dict_almost_equal({c: 1.4}, dual.dual_values, 4)
self._assert_dict_almost_equal({x0: 0.0, x1: 0.0}, dual.reduced_costs, 4)
# def test_qp_solve(self) -> None:
# mod = model.Model(name="test_model")
# # Same model as TEST_P(QpDualsTest, GeneralQp1) in solver_test/qp_test.cc.
# # Primal:
# # min x_0^2 + x_0x_1 + 3x_1^2 - 2x_0
# # s.t. 2 <= x_0 + 2x_1 <= inf
# # 0 <= x_0 <= inf
# # 0 <= x_1 <= inf
# #
# # Optimal solution: x* = (1.6, 0.2).
# #
# # Dual (go/mathopt-qp-dual):
# # max -x_0^2 - x_0x_1 - 3x_1^2 + 2y_0
# # s.t. y_0 + r_0 = 2x_0 + x_1 - 2
# # 2y_0 + r_1 = x_0 + 6x_1
# # y_0 >= 0
# # r_0 >= 0
# # r_1 >= 0
# #
# # Optimal solution: x* = (1.6, 0.2), y* = (1.4), r* = (0, 0).
# x0 = mod.add_variable(lb=0.0, name="x0")
# x1 = mod.add_variable(lb=0.0, name="x1")
# mod.minimize(x0 * x0 + x0 * x1 + 3 * x1 * x1 - 2 * x0)
# c = mod.add_linear_constraint(x0 + 2 * x1 >= 2)
# res = solve.solve(mod, parameters.SolverType.OSQP)
# self.assertEqual(
# res.termination.reason,
# result.TerminationReason.OPTIMAL,
# msg=res.termination,
# )
# self.assertGreaterEqual(len(res.solutions), 1)
# assert (
# res.solutions[0].primal_solution is not None
# and res.solutions[0].dual_solution is not None
# )
# self.assertAlmostEqual(
# -0.2, res.solutions[0].primal_solution.objective_value, 4
# )
# self._assert_dict_almost_equal(
# {x0: 1.6, x1: 0.2}, res.solutions[0].primal_solution.variable_values, 4
# )
# dual = res.solutions[0].dual_solution
# self.assertAlmostEqual(-0.2, dual.objective_value, 4)
# self._assert_dict_almost_equal({c: 1.4}, dual.dual_values, 4)
# self._assert_dict_almost_equal({x0: 0.0, x1: 0.0}, dual.reduced_costs, 4)
def test_lp_solve(self) -> None:
mod = model.Model(name="test_model")

View File

@@ -367,7 +367,6 @@ cc_library(
cc_test(
name = "test_util_test",
srcs = ["test_util_test.cc"],
defines = ["_USE_MATH_DEFINES"],
deps = [
":gtest_main",
":test_util",

View File

@@ -13,7 +13,6 @@
#include "ortools/pdlp/test_util.h"
#define _USE_MATH_DEFINES // Needed for visual studio for M_PI.
#include <cmath>
#include <deque>
#include <limits>
@@ -37,8 +36,11 @@ using ::testing::Matcher;
using ::testing::Not;
TEST(FloatArrayNearTest, TypicalUse) {
// M_PI is problematic on windows (requires _USE_MATH_DEFINES).
// std::numbers::pi is C++20 (incompatible with OR-Tools).
const double kPi = 3.14159265358979323846;
std::vector<double> test_vector({0.998, -1.414, 3.142});
std::vector<double> reference_vector({1.0, -M_SQRT2, M_PI});
std::vector<double> reference_vector({1.0, -std::sqrt(2), kPi});
EXPECT_THAT(test_vector, FloatArrayNear(reference_vector, 1.0e-2));
EXPECT_THAT(test_vector, Not(FloatArrayNear(reference_vector, 1.0e-4)));
}
@@ -96,7 +98,11 @@ TEST(FloatArrayNearTest, WithIntegerElements) {
}
TEST(FloatArrayEqTest, TypicalUse) {
std::vector<float> reference_vector({1.0e6, -M_SQRT2, M_PI});
// M_PI is problematic on windows (requires _USE_MATH_DEFINES).
// std::numbers::pi is C++20 (incompatible with OR-Tools).
const float kPi = 3.14159265358979323846;
const float kSqrt2 = std::sqrt(2);
std::vector<float> reference_vector({1.0e6, -kSqrt2, kPi});
// Values are within 4 ULPs.
std::vector<float> test_vector({1.0e6 + 0.25, -1.41421323, 3.14159262});
EXPECT_THAT(test_vector, FloatArrayEq(reference_vector));