Skip to content

Commit

Permalink
MO Emulator: fix no-solution case #239
Browse files Browse the repository at this point in the history
Don't recompute objectives when no solution available
  • Loading branch information
glebbelov committed Jun 4, 2024
1 parent b6c299a commit a16d853
Show file tree
Hide file tree
Showing 9 changed files with 6,403 additions and 10 deletions.
2 changes: 1 addition & 1 deletion include/mp/backend-std.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ class StdBackend :
/// to AMPL via SOL file
virtual void ReportSolution2AMPL() {
double obj_value = std::numeric_limits<double>::quiet_NaN();
auto sol = GetSolution(); // even if just dual
auto sol = GetSolution(); // even if just dual or infeasible
fmt::MemoryWriter writer;
writer.write("{}: {}", MP_DISPATCH( long_name() ), SolveStatus());
if (IsProblemSolvedOrFeasible()) {
Expand Down
4 changes: 4 additions & 0 deletions include/mp/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ enum Status {
INTERRUPTED = SPECIFIC
};

/// Status string ("solved", ...).
/// Major status only.
const char* GetStatusName(sol::Status stt);

/** Following the taxonomy of the enum sol::Status, returns true if
we have an optimal solution or a feasible solution for a
satisfaction problem */
Expand Down
19 changes: 10 additions & 9 deletions include/mp/flat/converter_multiobj.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,16 @@ class MOManager {
void ProcessMOIterationUnpostsolvedSolution(pre::ModelValuesDbl& sol) {
if (IsMOActive()) {
auto& objs = sol.GetObjValues()();
assert(1 == objs.size());
objval_last_ = objs.front(); // 0. save emulated obj value
if (objs.size())
objval_last_ = objs.front(); // 0. save emulated obj value
// @todo 1. check if the solver correctly reports the current emulated obj
// 2. Let's recompute the original objectives
objs.resize( MPCD(num_objs()) );
for (int i=0; i<(int)objs.size(); ++i)
objs[i] = ComputeValue(MPCD(get_obj(i)), sol.GetVarValues()());
const auto& xx = sol.GetVarValues()();
if (xx.size()) { // This can be invoked w/o solution
objs.resize( MPCD(num_objs()) );
for (int i=0; i<(int)objs.size(); ++i)
objs[i] = ComputeValue(MPCD(get_obj(i)), xx);
}
}
}

Expand Down Expand Up @@ -187,11 +190,9 @@ class MOManager {
if (!proc_sol.first) {
if (MPD( GetEnv() ).verbose_mode())
MPD( GetEnv() ).Print(
"\n"
"MULTI-OBJECTIVE MODE: objective {} (out of {}):\n"
" ABORTING due to the previous iteration's solve result ({}).\n"
" ... ABORTING: previous iteration's solve result: {} (code {}.)\n"
"==============================================================================\n\n"
, i_current_obj_+1, obj_new_.size(), proc_sol.second);
, sol::GetStatusName(proc_sol.second), proc_sol.second);
return false;
}
RestrictLastObjVal();
Expand Down
5 changes: 5 additions & 0 deletions solvers/highsmp/highsmpbackend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ std::pair<int, std::string> HighsBackend::GetSolveResult() {
auto obj = Highs_getObjectiveValue(lp());
auto inf = Highs_getInfinity(lp());
bool hasSol = (-inf < obj && obj < inf);
int primal_solution_status;
Highs_getIntInfoValue(lp(),
"primal_solution_status", &primal_solution_status);
switch (optstatus) {
case kHighsModelStatusOptimal:
return { sol::SOLVED, "optimal solution" };
Expand All @@ -359,6 +362,8 @@ std::pair<int, std::string> HighsBackend::GetSolveResult() {
return { sol::LIMIT_INF_UNB, "unbounded or infeasible" };
case kHighsModelStatusModelError:
case kHighsModelStatusLoadError:
if (kHighsSolutionStatusInfeasible == primal_solution_status) // HiGHS 7
return { sol::INFEASIBLE, "infeasible problem" };
return { sol::FAILURE, "solver error" };
case kHighsModelStatusPresolveError:
case kHighsModelStatusSolveError:
Expand Down
23 changes: 23 additions & 0 deletions src/solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
Authors: Victor Zverovich, Gleb Belov
*/

#include <map>
#include <cctype>
#include <climits>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -237,6 +239,27 @@ void RSTFormatter::HandleDirective(const char *type) {

namespace mp {

namespace sol {

const char* GetStatusName(sol::Status stt) {
static const std::map<int, const char*> stt_map {
{ INT_MIN, "wrong_status_code" },
{ NOT_SET, "not_set" },
{ UNKNOWN, "unknown" },
{ SOLVED, "solved" },
{ UNCERTAIN, "solved?" },
{ INFEASIBLE, "infeasible" },
{ UNBOUNDED, "unbounded" },
{ LIMIT, "limit" },
{ FAILURE, "failure" }
};
auto it = stt_map.lower_bound(stt);
assert(stt_map.end() != it);
return it->second;
}

}

namespace internal {

void FormatRST(fmt::Writer &w,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

set I; # Trainees
set T; # Training sessions
set V{I} in T; # Valid sessions for each trainee
set Pos; # Positons
set PosPlus; # Meta-positions (e.g, All, Cockpit, Cabin)
set MAXPlusWhich{Pos} in PosPlus;

param P{I} in Pos; # Trainee's position
param S{I}; # Seniority (smaller value <=> higher seniority)
param L{I}; # Language (0 - both, 1 or 2 - one only)
param E{I}; # Expiration: 0 - this month, 1 - next month, 2 - in 2 months
param Pr{I, T}; # Priority: 0 - not wanted, larger value <=> higher preference
param MAX{Pos}; # Position capacity
param MAXPlus{PosPlus}; # Aggregated capacities (All, CK, CB)

var x{i in I, t in T} binary <= if t in V[i] then 1 else 0;
var y{i in I} binary <= if E[i]>0 then 1 else 0; # Trainee i unassigned
var u{T} binary; # 1 <=> language 1, 0 <=> language 2
var w >=0; # Number of unassigned trainees

s.t. Assign_E0 {i in I: E[i]==0}:
sum {t in V[i]} x[i, t] == 1;

s.t. Unassigned_E12 {i in I: E[i]>0}:
sum {t in V[i]} x[i, t] + y[i] == 1;

s.t. Sum_Unassigned: w == sum {i in I: E[i]>0} y[i];

s.t. Language_1 {t in T}:
u[t]<0.5 ==> sum {i in I: L[i]==1 and t in V[i]} x[i, t] <= 0;

s.t. Language_2 {t in T}:
u[t]>=0.5 ==> sum {i in I: L[i]==2 and t in V[i]} x[i, t] <= 0;

s.t. Capacity {p in Pos, t in T}:
sum {i in I: p==P[i] and t in V[i]} x[i, t] <= MAX[p];

s.t. Capacity_Meta {p in PosPlus, t in T}:
sum {i in I: p in MAXPlusWhich[P[i]] and t in V[i]} x[i, t] <= MAXPlus[p];

suffix objpriority;
param S_range := max {i in I} S[i] - min {i in I} S[i];

# The primary objective
minimize Total_Unassigned: w suffix objpriority 3*S_range + 1;
# let Total_Unassigned.objpriority := card(I) + 3;

# Trainee preferences, ranked by seniority
set SenLevels := setof {i in I} S[i];
param prefMax {i in I} := max {t in V[i]} Pr[i, t];
minimize PrefViolRanked {s in SenLevels}:
sum {i in I: s==S[i]}
(1.0 # penalty 1 for non-assignment
- sum {t in V[i]: Pr[i, t]>0}
Pr[i, t] / prefMax[i] # normalize
* x[i, t]
+ sum {t in V[i]: Pr[i, t]==0} x[i, t]) # penalty 2 for unwanted assignment
suffix objpriority max {j in I} S[j] + 2*S_range + 1 - s;
Loading

0 comments on commit a16d853

Please sign in to comment.