#include "Highs.h"
#include "catch.hpp"
const bool dev_run = false;
bool smallDoubleDifference(double v0, double v1) {
double difference = std::fabs(v0 - v1);
return difference < 1e-4;
}
TEST_CASE("multi-objective", "[util]") {
HighsLp lp;
lp.num_col_ = 2;
lp.num_row_ = 3;
lp.col_cost_ = {0, 0};
lp.col_lower_ = {0, 0};
lp.col_upper_ = {kHighsInf, kHighsInf};
lp.row_lower_ = {-kHighsInf, -kHighsInf, -kHighsInf};
lp.row_upper_ = {18, 8, 14};
lp.a_matrix_.start_ = {0, 3, 6};
lp.a_matrix_.index_ = {0, 1, 2, 0, 1, 2};
lp.a_matrix_.value_ = {3, 1, 1, 1, 1, 2};
Highs h;
h.setOptionValue("output_flag", dev_run);
for (HighsInt k = 0; k < 2; k++) {
if (dev_run)
printf(
"\n******************\nPass %d: var type is %s\n******************\n",
int(k), k == 0 ? "continuous" : "integer");
for (HighsInt l = 0; l < 2; l++) {
double obj_mu = l == 0 ? 1 : -1;
if (dev_run)
printf(
"\n******************\nPass %d: objective multiplier is "
"%g\n******************\n",
int(l), obj_mu);
if (k == 0) {
lp.integrality_.clear();
} else if (k == 1) {
lp.integrality_ = {HighsVarType::kInteger, HighsVarType::kInteger};
}
h.passModel(lp);
h.setOptionValue("blend_multi_objectives", true);
HighsLinearObjective linear_objective;
std::vector<HighsLinearObjective> linear_objectives;
REQUIRE(h.clearLinearObjectives() == HighsStatus::kOk);
if (dev_run) printf("\nPass illegal linear objective\n");
linear_objective.weight = -obj_mu;
linear_objective.offset = -obj_mu;
linear_objective.coefficients = {obj_mu * 1, obj_mu * 1, obj_mu * 0};
linear_objective.abs_tolerance = 0.0;
linear_objective.rel_tolerance = 0.0;
REQUIRE(h.addLinearObjective(linear_objective) == HighsStatus::kError);
if (dev_run) printf("\nPass legal linear objective\n");
linear_objective.coefficients = {obj_mu * 1, obj_mu * 1};
REQUIRE(h.addLinearObjective(linear_objective) == HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
REQUIRE(smallDoubleDifference(h.getInfo().objective_function_value, -7));
linear_objectives.push_back(linear_objective);
if (dev_run) printf("\nPass second linear objective\n");
linear_objective.weight = obj_mu * 1e-4;
linear_objective.offset = 0;
linear_objective.coefficients = {obj_mu * 1, obj_mu * 0};
REQUIRE(h.addLinearObjective(linear_objective) == HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 2));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 6));
linear_objectives.push_back(linear_objective);
if (dev_run) printf("\nClear and pass two linear objectives\n");
REQUIRE(h.clearLinearObjectives() == HighsStatus::kOk);
REQUIRE(h.passLinearObjectives(2, linear_objectives.data()) ==
HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 2));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 6));
if (dev_run)
printf(
"\nSetting priorities that will be illegal when using "
"lexicographic "
"optimization\n");
linear_objectives[0].priority = 0;
linear_objectives[1].priority = 0;
REQUIRE(h.passLinearObjectives(2, linear_objectives.data()) ==
HighsStatus::kOk);
h.setOptionValue("blend_multi_objectives", false);
if (dev_run) printf("\nLexicographic using illegal priorities\n");
REQUIRE(h.run() == HighsStatus::kError);
if (dev_run)
printf(
"\nSetting priorities that are illegal now blend_multi_objectives "
"= "
"false\n");
REQUIRE(h.passLinearObjectives(2, linear_objectives.data()) ==
HighsStatus::kError);
if (dev_run)
printf(
"\nSetting legal priorities for blend_multi_objectives = false\n");
linear_objectives[0].priority = 10;
REQUIRE(h.passLinearObjectives(2, linear_objectives.data()) ==
HighsStatus::kOk);
if (dev_run)
printf("\nLexicographic using existing multi objective data\n");
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 2));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 6));
h.setOptionValue("blend_multi_objectives", true);
REQUIRE(h.clearLinearObjectives() == HighsStatus::kOk);
linear_objectives[0].coefficients = {obj_mu * 1.0001, obj_mu * 1};
linear_objectives[0].abs_tolerance = 1e-5;
linear_objectives[0].rel_tolerance = 0.05;
linear_objectives[1].weight = obj_mu * 1e-3;
if (dev_run)
printf(
"\nBlending: first solve objective just giving unique optimal "
"solution\n");
REQUIRE(h.passLinearObjectives(1, linear_objectives.data()) ==
HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
REQUIRE(h.passLinearObjectives(2, linear_objectives.data()) ==
HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
h.setOptionValue("blend_multi_objectives", false);
if (dev_run) printf("\nLexicographic using non-trivial tolerances\n");
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
if (k == 0) {
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 4.9));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 3.1));
} else {
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 5));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 3));
}
linear_objectives[0].abs_tolerance = kHighsInf;
REQUIRE(h.passLinearObjectives(2, linear_objectives.data()) ==
HighsStatus::kOk);
REQUIRE(h.run() == HighsStatus::kOk);
h.writeSolution("", kSolutionStylePretty);
if (k == 0) {
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 1.30069));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 6.34966));
} else {
REQUIRE(smallDoubleDifference(h.getSolution().col_value[0], 2));
REQUIRE(smallDoubleDifference(h.getSolution().col_value[1], 6));
}
}
}
h.resetGlobalScheduler(true);
}