Skip to content

Commit

Permalink
SCIP: QP objective
Browse files Browse the repository at this point in the history
Represent as 1 constraint (was split before)

Also: fix obj sense propagation using obj weights in obj:multi mode #239
  • Loading branch information
glebbelov committed Jul 29, 2024
1 parent 7fbc241 commit 26002c4
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 19 deletions.
17 changes: 17 additions & 0 deletions include/mp/flat/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ class FlatConverter :
}
}

/// Propagate objective contexts
void PropagateObjContexts() {
const auto& objs = MPD( get_objectives() );
const auto objwgt = MPD( GetMOWeights() );
if (GetEnv().multiobj()) // only in obj:multi mode
assert(objs.size() == objwgt.size());
for (size_t i=0; i<objs.size(); ++i) {
auto isMax = obj::MAX==objs[i].obj_sense();
if (GetEnv().multiobj() && objwgt[i]<0.0) // only in obj:multi
isMax = !isMax;
auto ctx = isMax ? Context::CTX_POS : Context::CTX_NEG;
MPD( PropagateResult2LinTerms(objs[i].GetLinTerms(),
MPD( MinusInfty() ), MPD( Infty() ), ctx) );
MPD( PropagateResult2QuadTerms(objs[i].GetQPTerms(),
MPD( MinusInfty() ), MPD( Infty() ), ctx) );
}
}

public:
//////////////////////////////////// VISITOR ADAPTERS /////////////////////////////////////////
Expand Down
34 changes: 26 additions & 8 deletions include/mp/flat/converter_multiobj.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ class MOManager {
std::vector<int> objpr = MPD( ReadIntSuffix( {"objpriority", suf::OBJ} ) ); // int only
objpr.resize(obj_orig.size(), 0.0); // blend objectives by default
std::vector<double> objwgt = MPD( GetMOWeightsLegacy() );
if (objwgt.empty()) {
objwgt.resize(obj_orig.size(), 1.0); // Default "intuitive" weights
FlipDiffSenseSigns(objwgt); // We handle "legacy" format below
}
std::vector<double> objtola = MPD( ReadDblSuffix( {"objabstol", suf::OBJ} ) );
objtola.resize(obj_orig.size(), 0.0);
std::vector<double> objtolr = MPD( ReadDblSuffix( {"objreltol", suf::OBJ} ) );
Expand Down Expand Up @@ -237,9 +233,29 @@ class MOManager {
MPD( SetObjectiveTo( MPD(GetModelAPI()), 0, obj_new_[i_current_obj_]) );
}

/// The "intuitive" objective weights
/// @return Always a full vector (for all objs)
/// @note All these methods assume the obj list is completed
ArrayRef<double> GetMOWeights() {
std::vector<double> objwgt = MPD( GetMOWeightsLegacy() );
const auto& obj_orig = MPD( get_objectives() ); // no linking
if (objwgt.empty()) {
objwgt.resize(obj_orig.size(), 1.0); // Default "intuitive" weights
} else {
FlipDiffSenseSigns(objwgt); // We handle "legacy" format below
}
return objwgt;
}

/// @return Legacy weights (relative to the 1st obj),
/// if .objweight provided, or default
ArrayRef<double> GetMOWeightsLegacy() {
std::vector<double> objw = MPD( ReadDblSuffix( {"objweight", suf::OBJ} ) );
if (objw.size() && 2==MPD( GetEnv() ).multiobj_weight()) { // user wants "intuitive"
const auto& obj_orig = MPD( get_objectives() ); // no linking
if (objw.empty()) {
objw.resize(obj_orig.size(), 1.0); // Default "intuitive" weights
FlipDiffSenseSigns(objw); // Backend / Emulator want "legacy"
} else if (2==MPD( GetEnv() ).multiobj_weight()) { // user gave "intuitive" values
FlipDiffSenseSigns(objw); // Backend / Emulator want "legacy"
}
return objw;
Expand All @@ -248,9 +264,11 @@ class MOManager {
/// Convert between the options of obj:multi:weight
void FlipDiffSenseSigns(std::vector<double>& objw) {
const auto& obj = MPD( get_objectives() );
for (auto i=obj.size(); --i; ) // forall i>1
if (obj[i].obj_sense() != obj.front().obj_sense())
objw[i] = -objw[i];
if (obj.size() > 1) {
for (auto i=obj.size(); --i; ) // forall i>1
if (obj[i].obj_sense() != obj.front().obj_sense())
objw[i] = -objw[i];
}
}

private:
Expand Down
25 changes: 15 additions & 10 deletions include/mp/flat/problem_flattener.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class ProblemFlattener :
MPD( ExportObj(i) );
MP_DISPATCH( Convert( GetModel().obj(i) ) );
}
GetFlatCvt().PropagateObjContexts(); // all now, because of GetMOWeights()

////////////////////////// Algebraic constraints
ifFltCon_ = 1;
Expand Down Expand Up @@ -340,17 +341,18 @@ class ProblemFlattener :
}
/// Sort/merge terms, otherwise we lose repeated terms
/// in Gurobi where we just set 'obj attributes'
/// to variables
/// to variables.
/// Context is propagated after adding all objectives.
le.sort_terms();
eexpr.GetQPTerms().sort_terms();
/// Propagate context
auto ctx = obj::MAX==obj.type() ? Context::CTX_POS : Context::CTX_NEG;
GetFlatCvt().PropagateResult2LinTerms(le,
GetFlatCvt().MinusInfty(),
GetFlatCvt().Infty(), ctx);
GetFlatCvt().PropagateResult2QuadTerms(eexpr.GetQPTerms(),
GetFlatCvt().MinusInfty(),
GetFlatCvt().Infty(), ctx);
if (!GetFlatCvt().IfPassQuadObj() // SCIP 10
&& eexpr.GetQPTerms().size()) {
EExpr qpnew;
qpnew.GetQPTerms() = std::move(eexpr.GetQPTerms());
int qpres = Convert2Var(std::move(qpnew));
eexpr.GetQPTerms().clear(); // explicitly remove obj qp terms
le.add_term(1.0, qpres);
}
/// Add linear / quadratic obj
LinearObjective lo { obj.type(),
std::move(le.coefs()), std::move(le.vars()) };
Expand Down Expand Up @@ -1200,7 +1202,10 @@ class ProblemFlattener :
/// What about common expressions?
int IfMultOutQPTerms() const {
return IfFlatteningAConstraint() ?
GetFlatCvt().IfPassQuadCon() : GetFlatCvt().IfPassQuadObj();
GetFlatCvt().IfPassQuadCon() :
// For objectives, still multiply out
// if we move the QP terms into contraints (SCIP)
( GetFlatCvt().IfPassQuadObj() || GetFlatCvt().IfPassQuadCon() );
}

/// Quadratize Pow2 exactly when we pass QP terms
Expand Down
3 changes: 2 additions & 1 deletion test/end2end/cases/categorized/fast/qp/modellist.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@
"tags" : ["quadratic_obj"],
"options": {
"highs_options": "timelim=3",
"comment_highs": "takes too long as of Sept 2022"
"comment_highs": "takes too long as of Sept 2022",
"scip_options": "timelim=3"
}
},
{
Expand Down
2 changes: 2 additions & 0 deletions test/end2end/scripts/python/Solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,8 @@ def _setNThreads(self, threads):
def __init__(self, exeName, timeout=None, nthreads=None, otherOptions=None):
stags = {ModelTags.continuous, ModelTags.integer, ModelTags.binary,
ModelTags.quadratic,
ModelTags.quadratic_obj,
ModelTags.quadratic_obj_nonconvex,
ModelTags.quadraticnonconvex,
ModelTags.socp, ModelTags.socp_hard_to_recognize,
ModelTags.sos,
Expand Down

0 comments on commit 26002c4

Please sign in to comment.