Skip to content

Commit 34260a4

Browse files
committed
Made @mathgeekcoder's changes, and computing the correct reduced objective value for maximization, as well as minimization problems; formatted
1 parent a2974ce commit 34260a4

File tree

6 files changed

+89
-25
lines changed

6 files changed

+89
-25
lines changed

check/TestMipSolver.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,8 +1037,8 @@ TEST_CASE("issue-2432", "[highs_test_mip_solver]") {
10371037
}
10381038

10391039
TEST_CASE("mip-race", "[highs_test_mip_solver]") {
1040-
const bool ci_test = false;
1041-
const std::string test_build_model = "fiball";
1040+
const bool ci_test = true;
1041+
const std::string test_build_model = "neos-3381206-awhea"; //"fiball";
10421042
const std::string model = ci_test ? "flugpl" : test_build_model;
10431043
// "neos-3381206-awhea";
10441044
const std::string model_file =
@@ -1050,8 +1050,18 @@ TEST_CASE("mip-race", "[highs_test_mip_solver]") {
10501050
h.setOptionValue("mip_race_concurrency", mip_race_concurrency);
10511051
h.setOptionValue("mip_race_read_solutions", true);
10521052
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
1053-
REQUIRE(h.run() == HighsStatus::kOk);
1054-
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1053+
for (int k = 0; k < 2; k++) {
1054+
if (k == 1) {
1055+
HighsLp lp = h.getLp();
1056+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1057+
lp.col_cost_[iCol] = -lp.col_cost_[iCol];
1058+
REQUIRE(h.changeColsCost(0, lp.num_col_ - 1, lp.col_cost_.data()) ==
1059+
HighsStatus::kOk);
1060+
REQUIRE(h.changeObjectiveSense(ObjSense::kMaximize) == HighsStatus::kOk);
1061+
}
1062+
REQUIRE(h.run() == HighsStatus::kOk);
1063+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1064+
}
10551065

10561066
if (ci_test) {
10571067
h.clearSolver();

highs/Highs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1717,7 +1717,7 @@ class Highs {
17171717
HighsStatus mipRaceResults(HighsMipSolverInfo& mip_solver_info,
17181718
const std::vector<HighsMipSolverInfo>& worker_info,
17191719
const std::vector<double>& mip_time,
1720-
const double& report_mip_time);
1720+
const double& report_mip_time);
17211721
};
17221722

17231723
// Start of deprecated methods not in the Highs class

highs/lp_data/Highs.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4106,9 +4106,8 @@ HighsStatus Highs::callSolveMip() {
41064106
});
41074107
loop_mip_time += timer_.read();
41084108
// Determine the winner and report on the solution
4109-
HighsStatus call_status =
4110-
this->mipRaceResults(mip_solver_info, worker_info, mip_time,
4111-
loop_mip_time);
4109+
HighsStatus call_status = this->mipRaceResults(mip_solver_info, worker_info,
4110+
mip_time, loop_mip_time);
41124111
if (call_status == HighsStatus::kError) {
41134112
const bool undo_mods = true;
41144113
return returnFromOptimizeModel(HighsStatus::kError, undo_mods);

highs/lp_data/HighsInterface.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4264,8 +4264,7 @@ void HighsMipSolverInfo::clear() {
42644264
HighsStatus Highs::mipRaceResults(
42654265
HighsMipSolverInfo& mip_solver_info,
42664266
const std::vector<HighsMipSolverInfo>& worker_info,
4267-
const std::vector<double>& mip_time,
4268-
const double& report_mip_time) {
4267+
const std::vector<double>& mip_time, const double& report_mip_time) {
42694268
const HighsInt mip_race_concurrency = this->options_.mip_race_concurrency;
42704269
HighsInt winning_instance = -1;
42714270
HighsModelStatus winning_model_status = HighsModelStatus::kNotset;
@@ -4276,7 +4275,7 @@ HighsStatus Highs::mipRaceResults(
42764275
instance == 0 ? mip_solver_info : worker_info[instance];
42774276
HighsModelStatus instance_model_status = solver_info.modelstatus;
42784277
highsLogUser(options_.log_options, HighsLogType::kInfo,
4279-
" Solver %2d has best objective %15.8g, gap %6.2f\% (time "
4278+
" Solver %2d has best objective %15.8g, gap %6.2f%% (time "
42804279
"= %6.2f), and status %s\n",
42814280
int(instance), solver_info.solution_objective,
42824281
1e2 * solver_info.gap, mip_time[instance],

highs/mip/HighsMipSolver.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#ifndef MIP_HIGHS_MIP_SOLVER_H_
99
#define MIP_HIGHS_MIP_SOLVER_H_
1010

11+
#include <atomic>
12+
1113
#include "Highs.h"
1214
#include "lp_data/HighsCallback.h"
1315
#include "lp_data/HighsOptions.h"
@@ -21,6 +23,7 @@ class HighsImplications;
2123

2224
const HighsInt kMipRaceNoSolution = -1;
2325

26+
/*
2427
struct MipRaceIncumbent {
2528
HighsInt start_write_incumbent = kMipRaceNoSolution;
2629
HighsInt finish_write_incumbent = kMipRaceNoSolution;
@@ -32,11 +35,11 @@ struct MipRaceIncumbent {
3235
HighsInt read(const HighsInt last_incumbent_read, double& objective_,
3336
std::vector<double>& solution_) const;
3437
};
38+
*/
3539

36-
/*
37-
struct MipRaceIncumbent {
38-
std::atomic<HighsInt> start_write_incumbent = kMipRaceNoSolution;
39-
std::atomic<HighsInt> finish_write_incumbent = kMipRaceNoSolution;
40+
struct MipRaceIncumbent {
41+
std::atomic<HighsInt> start_write_incumbent{kMipRaceNoSolution};
42+
std::atomic<HighsInt> finish_write_incumbent{kMipRaceNoSolution};
4043
double objective = -kHighsInf;
4144
std::vector<double> solution;
4245
void clear();
@@ -61,7 +64,7 @@ struct MipRaceIncumbent {
6164
solution = std::move(moving.solution);
6265
}
6366
};
64-
*/
67+
6568
struct MipRaceRecord {
6669
std::vector<MipRaceIncumbent> incumbent;
6770
void clear();
@@ -83,7 +86,7 @@ struct MipRace {
8386
const HighsLogOptions log_options_);
8487
HighsInt concurrency() const;
8588
void update(const double objective, const std::vector<double>& solution);
86-
bool newSolution(const HighsInt instance, double objective,
89+
bool newSolution(const HighsInt instance, double& objective,
8790
std::vector<double>& solution);
8891
void report() const;
8992
};

highs/mip/HighsMipSolverData.cpp

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,20 @@ bool HighsMipSolverData::addIncumbent(const std::vector<double>& sol,
14011401
sol, possibly_store_as_new_incumbent)
14021402
: 0;
14031403

1404+
if (solution_source == kSolutionSourceHighsSolution) {
1405+
printf(
1406+
"HighsMipSolverData::addIncumbent HiGHS solution Offset = %15.8g; Obj "
1407+
"= %15.8g; UB = %15.8g; PossAdd = %s",
1408+
mipsolver.model_->offset_, solobj, upper_bound,
1409+
possibly_store_as_new_incumbent ? "T" : "F");
1410+
if (possibly_store_as_new_incumbent) {
1411+
printf("; TransObj = %15.8g; TransSolobj < UB %s \n", transformed_solobj,
1412+
transformed_solobj < upper_bound ? "T" : "F");
1413+
} else {
1414+
printf("\n");
1415+
}
1416+
fflush(stdout);
1417+
}
14041418
if (possibly_store_as_new_incumbent) {
14051419
solobj = transformed_solobj;
14061420
if (solobj >= upper_bound) return false;
@@ -2677,18 +2691,57 @@ void HighsMipSolverData::queryExternalSolution(
26772691
if (!mipsolver.options_mip_->mip_race_read_solutions) return;
26782692
MipRace& mip_race = mipsolver.mip_race_;
26792693
if (!mip_race.record) return;
2680-
double instance_solution_objective_value = kHighsInf;
2694+
double instance_objective_value = kHighsInf;
26812695
std::vector<double> instance_solution;
26822696
for (HighsInt instance = 0; instance < mipRaceConcurrency(); instance++) {
26832697
if (instance == mip_race.my_instance) continue;
2684-
if (!mip_race.newSolution(instance, instance_solution_objective_value,
2698+
if (!mip_race.newSolution(instance, instance_objective_value,
26852699
instance_solution))
26862700
continue;
26872701
// Have read a new incumbent
2688-
std::vector<double> reduced_instance_solution;
2689-
reduced_instance_solution =
2702+
//
2703+
// Objective is assumed to be original_offset + (original_c)^T(original_x),
2704+
// but MIP solver bounds are based on the reduced objective
2705+
// (reduced_c)^T(reduced_x)
2706+
//
2707+
// Now, original_sense*[reduced_offset + (reduced_c)^T(reduced_x)] is an
2708+
// objective in the original space, so
2709+
//
2710+
// f0 + c0^Tx0 = s*(f1 + c1^Tx1)
2711+
//
2712+
// where 0 => original; 1 => reduced
2713+
//
2714+
// This allows the reduced objective value to be deduced as
2715+
//
2716+
// c1^Tx1 = s*(f0 + c0^Tx0) - f1
2717+
//
2718+
// (reduced_c)^T(reduced_x) = original_sense*[original_offset +
2719+
// (original_c)^T(original_x) - reduced_offset]
2720+
//
2721+
double reduced_instance_objective_value = instance_objective_value;
2722+
reduced_instance_objective_value *= int(mipsolver.orig_model_->sense_);
2723+
reduced_instance_objective_value -= mipsolver.model_->offset_;
2724+
// Get the solution in the reduced space
2725+
std::vector<double> reduced_instance_solution =
26902726
postSolveStack.getReducedPrimalSolution(instance_solution);
2691-
addIncumbent(reduced_instance_solution, instance_solution_objective_value,
2727+
2728+
double check_objective_value = 0;
2729+
for (HighsInt iCol = 0; iCol < mipsolver.model_->num_col_; iCol++)
2730+
check_objective_value +=
2731+
mipsolver.colCost(iCol) * reduced_instance_solution[iCol];
2732+
double dl_objective_value =
2733+
std::fabs(check_objective_value - reduced_instance_objective_value);
2734+
assert(dl_objective_value < 1e-12 * (1 + std::fabs(check_objective_value)));
2735+
printf(
2736+
"HighsMipSolverData::queryExternalSolution: (sense = %d; offset = "
2737+
"%11.4g) modified objective from %11.4g to %11.4g (Check = %11.4g; "
2738+
"Delta = %11.4g)\n",
2739+
int(mipsolver.orig_model_->sense_), mipsolver.model_->offset_,
2740+
instance_objective_value, reduced_instance_objective_value,
2741+
check_objective_value, dl_objective_value);
2742+
fflush(stdout);
2743+
2744+
addIncumbent(reduced_instance_solution, reduced_instance_objective_value,
26922745
kSolutionSourceHighsSolution);
26932746
}
26942747
}
@@ -2956,15 +3009,15 @@ void MipRaceRecord::report(const HighsLogOptions log_options) const {
29563009
highsLogUser(log_options, HighsLogType::kInfo, "\nStartWrite: ");
29573010
for (HighsInt instance = 0; instance < mip_race_concurrency; instance++)
29583011
highsLogUser(log_options, HighsLogType::kInfo, " %20d",
2959-
this->incumbent[instance].start_write_incumbent);
3012+
int(this->incumbent[instance].start_write_incumbent));
29603013
highsLogUser(log_options, HighsLogType::kInfo, "\nObjective: ");
29613014
for (HighsInt instance = 0; instance < mip_race_concurrency; instance++)
29623015
highsLogUser(log_options, HighsLogType::kInfo, " %20.12g",
29633016
this->incumbent[instance].objective);
29643017
highsLogUser(log_options, HighsLogType::kInfo, "\nFinishWrite: ");
29653018
for (HighsInt instance = 0; instance < mip_race_concurrency; instance++)
29663019
highsLogUser(log_options, HighsLogType::kInfo, " %20d",
2967-
this->incumbent[instance].finish_write_incumbent);
3020+
int(this->incumbent[instance].finish_write_incumbent));
29683021
highsLogUser(log_options, HighsLogType::kInfo, "\n");
29693022
}
29703023

@@ -2997,7 +3050,7 @@ void MipRace::update(const double objective,
29973050
// this->report();
29983051
}
29993052

3000-
bool MipRace::newSolution(const HighsInt instance, double objective,
3053+
bool MipRace::newSolution(const HighsInt instance, double& objective,
30013054
std::vector<double>& solution) {
30023055
assert(this->record);
30033056
HighsInt new_incumbent_read = this->record->incumbent[instance].read(

0 commit comments

Comments
 (0)