Skip to content

Commit

Permalink
Added C API and logging during multi-objective optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
jajhall committed Nov 23, 2024
1 parent cda67ca commit 3c71c59
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 43 deletions.
65 changes: 50 additions & 15 deletions check/TestCAPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -1876,23 +1876,18 @@ void test_multiObjective() {
double a_value[6] = {3, 1, 1, 1, 1, 2};
HighsInt integrality[2] = {kHighsVarTypeInteger, kHighsVarTypeInteger};

// Highs_setBoolOptionValue(highs, "output_flag", dev_run);
Highs_setBoolOptionValue(highs, "output_flag", dev_run);
HighsInt return_status = Highs_passLp(highs, num_col, num_row, num_nz, a_format, sense,
offset, col_cost, col_lower, col_upper,
row_lower, row_upper, a_start, a_index, a_value);
assert(return_status == kHighsStatusOk);
return_status = Highs_run(highs);
assert(return_status == kHighsStatusOk);
HighsInt model_status = Highs_getModelStatus(highs);
assert(model_status == kHighsModelStatusOptimal);
Highs_writeSolutionPretty(highs, "");

return_status = Highs_clearLinearObjectives(highs);
assert(return_status == kHighsStatusOk);

double weight = -1;
double linear_objective_offset = -1;
double coefficients[2] = {2, 1};
double coefficients[2] = {1, 1};
double abs_tolerance = 0;
double rel_tolerance = 0;
HighsInt priority = 10;
Expand All @@ -1909,7 +1904,7 @@ void test_multiObjective() {

return_status = Highs_run(highs);
assert(return_status == kHighsStatusOk);
model_status = Highs_getModelStatus(highs);
HighsInt model_status = Highs_getModelStatus(highs);
assert(model_status == kHighsModelStatusOptimal);

Highs_writeSolutionPretty(highs, "");
Expand All @@ -1921,13 +1916,53 @@ void test_multiObjective() {

Highs_setBoolOptionValue(highs, "blend_multi_objectives", 0);

// double weight = {};
// double offset = {};
// double coefficients = {};
// double abs_tolerance = {};
// double rel_tolerance = {};
// HighsInt priority = {};

if (dev_run) printf("\n***************\nLexicographic 1\n***************\n");
double weight2[2] = {-1, 1e-4};
double linear_objective_offset2[2] = {-1, 0};
double coefficients2[4] = {1, 1, 1, 0};
double abs_tolerance2[2] = {0, -1};
double rel_tolerance2[2] = {0, -1};
HighsInt priority2[2] = {10, 0};
return_status = Highs_passLinearObjectives(highs, 2, weight2, linear_objective_offset2, coefficients2, abs_tolerance2, rel_tolerance2, priority2);
return_status = Highs_run(highs);
assert(return_status == kHighsStatusOk);
model_status = Highs_getModelStatus(highs);
assert(model_status == kHighsModelStatusOptimal);
Highs_writeSolutionPretty(highs, "");
return_status =
Highs_getSolution(highs, col_value, NULL, NULL, NULL);
assertDoubleValuesEqual("col_value[0]", col_value[0], 2);
assertDoubleValuesEqual("col_value[1]", col_value[1], 6);

// weight2[1] = 1e-5;
coefficients2[0] = 1.0001;
abs_tolerance2[0] = 1e-5;
rel_tolerance2[0] = 0.05;
return_status = Highs_passLinearObjectives(highs, 2, weight2, linear_objective_offset2, coefficients2, abs_tolerance2, rel_tolerance2, priority2);
return_status = Highs_run(highs);
assert(return_status == kHighsStatusOk);
model_status = Highs_getModelStatus(highs);
assert(model_status == kHighsModelStatusOptimal);
Highs_writeSolutionPretty(highs, "");
return_status =
Highs_getSolution(highs, col_value, NULL, NULL, NULL);
assertDoubleValuesEqual("col_value[0]", col_value[0], 4.9);
assertDoubleValuesEqual("col_value[1]", col_value[1], 3.1);

if (dev_run) printf("\n***************\nLexicographic 2\n***************\n");
abs_tolerance2[0] = -1;

return_status = Highs_passLinearObjectives(highs, 2, weight2, linear_objective_offset2, coefficients2, abs_tolerance2, rel_tolerance2, priority2);
return_status = Highs_run(highs);
assert(return_status == kHighsStatusOk);
model_status = Highs_getModelStatus(highs);
assert(model_status == kHighsModelStatusOptimal);
Highs_writeSolutionPretty(highs, "");
return_status =
Highs_getSolution(highs, col_value, NULL, NULL, NULL);
assertDoubleValuesEqual("col_value[0]", col_value[0], 1.30069);
assertDoubleValuesEqual("col_value[1]", col_value[1], 6.34966);

Highs_destroy(highs);
free(col_value);
}
Expand Down
2 changes: 1 addition & 1 deletion check/TestMultiObjective.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ TEST_CASE("multi-objective", "[util]") {
if (dev_run) printf("\nPass illegal linear objective\n");
linear_objective.weight = -obj_mu;
linear_objective.offset = -obj_mu;
linear_objective.coefficients = {obj_mu * 2, obj_mu * 1, obj_mu * 0};
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);
Expand Down
4 changes: 2 additions & 2 deletions src/Highs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1524,8 +1524,6 @@ class Highs {
HighsStatus returnFromRun(const HighsStatus return_status,
const bool undo_mods);
HighsStatus returnFromHighs(const HighsStatus return_status);
HighsStatus returnFromLexicographicOptimization(
const HighsStatus return_status, HighsInt original_lp_num_row);
void reportSolvedLpQpStats();

// Interface methods
Expand Down Expand Up @@ -1623,6 +1621,8 @@ class Highs {
HighsInt* iis_col_index, HighsInt* iis_row_index,
HighsInt* iis_col_bound, HighsInt* iis_row_bound);

HighsStatus returnFromLexicographicOptimization(
const HighsStatus return_status, HighsInt original_lp_num_row);
HighsStatus multiobjectiveSolve();

bool aFormatOk(const HighsInt num_nz, const HighsInt format);
Expand Down
37 changes: 24 additions & 13 deletions src/interfaces/highs_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,34 +291,45 @@ HighsInt Highs_passHessian(void* highs, const HighsInt dim,
->passHessian(dim, num_nz, format, start, index, value);
}

HighsInt Highs_passLinearObjectives(const void* highs, const HighsInt num_linear_objective,
const double* weight, const double* offset, const double* coefficients,
const double* abs_tolerance, const double* rel_tolerance, const HighsInt* priority) {
HighsInt Highs_passLinearObjectives(const void* highs,
const HighsInt num_linear_objective,
const double* weight, const double* offset,
const double* coefficients,
const double* abs_tolerance,
const double* rel_tolerance,
const HighsInt* priority) {
HighsInt status = Highs_clearLinearObjectives(highs);
if (status != kHighsStatusOk) return status;
HighsInt num_col = Highs_getNumCol(highs);
for (HighsInt iObj = 0; iObj < num_linear_objective; iObj++) {
status = Highs_addLinearObjectiveWithIndex(highs, weight[iObj], offset[iObj], &coefficients[iObj*num_col], abs_tolerance[iObj], rel_tolerance[iObj], priority[iObj], iObj);
status = Highs_addLinearObjectiveWithIndex(
highs, weight[iObj], offset[iObj], &coefficients[iObj * num_col],
abs_tolerance[iObj], rel_tolerance[iObj], priority[iObj], iObj);
if (status != kHighsStatusOk) return status;
}
return kHighsStatusOk;
}

HighsInt Highs_addLinearObjective(const void* highs,
const double weight, const double offset, const double* coefficients,
const double abs_tolerance, const double rel_tolerance, const HighsInt priority) {
return Highs_addLinearObjectiveWithIndex(highs, weight, offset, coefficients, abs_tolerance, rel_tolerance, priority, -1);
HighsInt Highs_addLinearObjective(const void* highs, const double weight,
const double offset,
const double* coefficients,
const double abs_tolerance,
const double rel_tolerance,
const HighsInt priority) {
return Highs_addLinearObjectiveWithIndex(highs, weight, offset, coefficients,
abs_tolerance, rel_tolerance,
priority, -1);
}

HighsInt Highs_addLinearObjectiveWithIndex(const void* highs,
const double weight, const double offset, const double* coefficients,
const double abs_tolerance, const double rel_tolerance, const HighsInt priority,
const HighsInt iObj) {
HighsInt Highs_addLinearObjectiveWithIndex(
const void* highs, const double weight, const double offset,
const double* coefficients, const double abs_tolerance,
const double rel_tolerance, const HighsInt priority, const HighsInt iObj) {
HighsLinearObjective linear_objective;
HighsInt num_col = Highs_getNumCol(highs);
linear_objective.weight = weight;
linear_objective.offset = offset;
for (HighsInt iCol = 0; iCol < num_col; iCol++)
for (HighsInt iCol = 0; iCol < num_col; iCol++)
linear_objective.coefficients.push_back(coefficients[iCol]);
linear_objective.abs_tolerance = abs_tolerance;
linear_objective.rel_tolerance = rel_tolerance;
Expand Down
31 changes: 19 additions & 12 deletions src/interfaces/highs_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -557,18 +557,25 @@ HighsInt Highs_passHessian(void* highs, const HighsInt dim,
const HighsInt* start, const HighsInt* index,
const double* value);

HighsInt Highs_passLinearObjectives(const void* highs, const HighsInt num_linear_objective,
const double* weight, const double* offset, const double* coefficients,
const double* abs_tolerance, const double* rel_tolerance, const HighsInt* priority);

HighsInt Highs_addLinearObjective(const void* highs,
const double weight, const double offset, const double* coefficients,
const double abs_tolerance, const double rel_tolerance, const HighsInt priority);

HighsInt Highs_addLinearObjectiveWithIndex(const void* highs,
const double weight, const double offset, const double* coefficients,
const double abs_tolerance, const double rel_tolerance, const HighsInt priority,
const HighsInt iObj);
HighsInt Highs_passLinearObjectives(const void* highs,
const HighsInt num_linear_objective,
const double* weight, const double* offset,
const double* coefficients,
const double* abs_tolerance,
const double* rel_tolerance,
const HighsInt* priority);

HighsInt Highs_addLinearObjective(const void* highs, const double weight,
const double offset,
const double* coefficients,
const double abs_tolerance,
const double rel_tolerance,
const HighsInt priority);

HighsInt Highs_addLinearObjectiveWithIndex(
const void* highs, const double weight, const double offset,
const double* coefficients, const double abs_tolerance,
const double rel_tolerance, const HighsInt priority, const HighsInt iObj);

HighsInt Highs_clearLinearObjectives(const void* highs);
/**
Expand Down
81 changes: 81 additions & 0 deletions src/lp_data/HighsInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3664,7 +3664,9 @@ HighsStatus Highs::returnFromLexicographicOptimization(
}

HighsStatus Highs::multiobjectiveSolve() {
const HighsInt coeff_logging_size_limit = 10;
HighsInt num_linear_objective = this->multi_linear_objective_.size();

assert(num_linear_objective > 0);
HighsLp& lp = this->model_.lp_;
for (HighsInt iObj = 0; iObj < num_linear_objective; iObj++) {
Expand All @@ -3680,6 +3682,37 @@ HighsStatus Highs::multiobjectiveSolve() {
}
}

std::unique_ptr<std::stringstream> multi_objective_log;
highsLogUser(options_.log_options, HighsLogType::kInfo,
"Solving with %d multiple linear objectives, %s\n",
int(num_linear_objective),
this->options_.blend_multi_objectives
? "blending objectives by weight"
: "using lexicographic optimization by priority");
highsLogUser(
options_.log_options, HighsLogType::kInfo,
"Ix weight offset abs_tol rel_tol priority%s\n",
lp.num_col_ < coeff_logging_size_limit ? " coefficients" : "");
for (HighsInt iObj = 0; iObj < num_linear_objective; iObj++) {
HighsLinearObjective& linear_objective =
this->multi_linear_objective_[iObj];
multi_objective_log =
std::unique_ptr<std::stringstream>(new std::stringstream());
*multi_objective_log << highsFormatToString(
"%2d %11.6g %11.6g %11.6g %11.6g %11d ", int(iObj),
linear_objective.weight, linear_objective.offset,
linear_objective.abs_tolerance, linear_objective.rel_tolerance,
linear_objective.priority);
if (lp.num_col_ < coeff_logging_size_limit) {
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
*multi_objective_log << highsFormatToString(
"%s c_{%1d} = %g", iCol == 0 ? "" : ",", int(iCol),
linear_objective.coefficients[iCol]);
}
*multi_objective_log << "\n";
highsLogUser(options_.log_options, HighsLogType::kInfo, "%s",
multi_objective_log->str().c_str());
}
this->clearSolver();
if (this->options_.blend_multi_objectives) {
// Objectives are blended by weight and minimized
Expand All @@ -3695,6 +3728,22 @@ HighsStatus Highs::multiobjectiveSolve() {
multi_linear_objective.coefficients[iCol];
}
lp.sense_ = ObjSense::kMinimize;

multi_objective_log =
std::unique_ptr<std::stringstream>(new std::stringstream());
*multi_objective_log << highsFormatToString(
"Solving with blended objective");
if (lp.num_col_ < coeff_logging_size_limit) {
*multi_objective_log << highsFormatToString(
": %s %g", lp.sense_ == ObjSense::kMinimize ? "min" : "max",
lp.offset_);
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
*multi_objective_log << highsFormatToString(
" + (%g) x[%d]", lp.col_cost_[iCol], int(iCol));
}
*multi_objective_log << "\n";
highsLogUser(options_.log_options, HighsLogType::kInfo, "%s",
multi_objective_log->str().c_str());
return this->solve();
}

Expand Down Expand Up @@ -3761,6 +3810,21 @@ HighsStatus Highs::multiobjectiveSolve() {
highsBoolToString(feasible).c_str());
}
}
multi_objective_log =
std::unique_ptr<std::stringstream>(new std::stringstream());
*multi_objective_log << highsFormatToString("Solving with objective %d",
int(iObj));
if (lp.num_col_ < coeff_logging_size_limit) {
*multi_objective_log << highsFormatToString(
": %s %g", lp.sense_ == ObjSense::kMinimize ? "min" : "max",
lp.offset_);
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
*multi_objective_log << highsFormatToString(
" + (%g) x[%d]", lp.col_cost_[iCol], int(iCol));
}
*multi_objective_log << "\n";
highsLogUser(options_.log_options, HighsLogType::kInfo, "%s",
multi_objective_log->str().c_str());
HighsStatus solve_status = this->solve();
if (solve_status == HighsStatus::kError)
return returnFromLexicographicOptimization(HighsStatus::kError,
Expand All @@ -3772,6 +3836,7 @@ HighsStatus Highs::multiobjectiveSolve() {
return returnFromLexicographicOptimization(HighsStatus::kWarning,
original_lp_num_row);
}
this->writeSolution("", kSolutionStylePretty);
if (iIx == num_linear_objective - 1) break;
if (lp.isMip()) {
// Save the solution to provide an integer feasible solution of
Expand Down Expand Up @@ -3840,6 +3905,22 @@ HighsStatus Highs::multiobjectiveSolve() {
" and relative tolerance being %g < 0\n",
int(priority), linear_objective.abs_tolerance,
linear_objective.rel_tolerance);
multi_objective_log =
std::unique_ptr<std::stringstream>(new std::stringstream());
*multi_objective_log << highsFormatToString(
"Add constraint for objective %d: ", int(iObj));
if (nnz < coeff_logging_size_limit) {
*multi_objective_log << highsFormatToString("%g <= ", lower_bound);
for (HighsInt iEl = 0; iEl < nnz; iEl++)
*multi_objective_log << highsFormatToString(
"%s(%g) x[%d]", iEl > 0 ? " + " : "", value[iEl], int(index[iEl]));
*multi_objective_log << highsFormatToString(" <= %g\n", upper_bound);
} else {
*multi_objective_log << highsFormatToString("Bounds [%g, %g]\n",
lower_bound, upper_bound);
}
highsLogUser(options_.log_options, HighsLogType::kInfo, "%s",
multi_objective_log->str().c_str());
add_row_status =
this->addRow(lower_bound, upper_bound, nnz, index.data(), value.data());
assert(add_row_status == HighsStatus::kOk);
Expand Down

0 comments on commit 3c71c59

Please sign in to comment.