Skip to content

Commit

Permalink
obj:multi:weight: intuitive objective weights #240
Browse files Browse the repository at this point in the history
  • Loading branch information
glebbelov committed May 24, 2024
1 parent c73901b commit 063687c
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 8 deletions.
7 changes: 4 additions & 3 deletions include/mp/backend-std.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class StdBackend :
virtual void ObjPriorities(ArrayRef<int>)
{ MP_UNSUPPORTED("Backend::ObjPriorities"); }
/// Placeholder: set objective weights.
/// Presolve the values if needed
/// Presolve the values if needed.
/// @note The weights are provided in the format common for many solver APIs,
/// corresponding to the legacy setting of the option obj:multi:weight.
virtual void ObjWeights(ArrayRef<double>) { }
/// Placeholder: set objective abs tol
/// Presolve the values if needed
Expand Down Expand Up @@ -290,7 +292,7 @@ class StdBackend :
if (multiobj() && multiobj_has_native()) {
if (auto suf = ReadSuffix(suf_objpriority))
ObjPriorities( suf );
if (auto suf = ReadSuffix(suf_objweight))
if (auto suf = GetMM().GetObjWeightsAdapted())
ObjWeights( suf );
if (auto suf = ReadSuffix(suf_objabstol))
ObjAbsTol( suf );
Expand Down Expand Up @@ -957,7 +959,6 @@ class StdBackend :
//////////////////////////////////////////////////////////////////////////////
private:
const SuffixDef<int> suf_objpriority = { "objpriority", suf::OBJ | suf::INPUT };
const SuffixDef<double> suf_objweight = { "objweight", suf::OBJ | suf::INPUT };
const SuffixDef<double> suf_objabstol = { "objabstol", suf::OBJ | suf::INPUT };
const SuffixDef<double> suf_objreltol = { "objreltol", suf::OBJ | suf::INPUT };

Expand Down
3 changes: 3 additions & 0 deletions include/mp/converter-base.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class BasicConverter : public EnvKeeper {
/// Process solve iteration solution
virtual void ProcessIterationSolution(const Solution& , int status) = 0;

/// Objective weights
virtual ArrayRef<double> GetObjWeightsAdapted() = 0;

/// Fill model traits
virtual void FillModelTraits(AMPLS_ModelTraits& ) = 0;

Expand Down
3 changes: 3 additions & 0 deletions include/mp/flat/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ class FlatConverter :
MPD( ProcessMOIterationPostsolvedSolution(sol, status) );
}

/// Objective weights
ArrayRef<double> GetObjWeightsAdapted() { return MPD( GetMOWeightsLegacy() ); }


protected:
//////////////////////////// CUSTOM CONSTRAINTS CONVERSION ////////////////////////////
Expand Down
23 changes: 21 additions & 2 deletions include/mp/flat/converter_multiobj.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,11 @@ class MOManager {
///////////////// Read / set default suffixes ///////////////////
std::vector<double> objpr = MPD( ReadDblSuffix( {"objpriority", suf::OBJ} ) );
objpr.resize(obj_orig.size(), 0.0); // blend objectives by default
std::vector<double> objwgt = MPD( ReadDblSuffix( {"objweight", suf::OBJ} ) );
objwgt.resize(obj_orig.size(), 1.0);
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 All @@ -124,6 +127,7 @@ class MOManager {
for (const auto& pr_level: pr_map) {
const auto& i0_vec = pr_level.second;
obj_new_.push_back(obj_orig.at(i0_vec.front()));
obj_new_.back().set_sense(obj_orig.front().obj_sense()); // "Legacy" obj:multi:weight
obj_new_.back().GetLinTerms() *= objwgt.at(i0_vec.front()); // Use weight
obj_new_.back().GetQPTerms() *= objwgt.at(i0_vec.front());
obj_new_tola_.push_back(objtola.at(i0_vec.front()));
Expand Down Expand Up @@ -210,6 +214,21 @@ class MOManager {
MPD( SetObjectiveTo( MPD(GetModelAPI()), 0, obj_new_[i_current_obj_]) );
}

ArrayRef<double> GetMOWeightsLegacy() {
std::vector<double> objw = MPD( ReadDblSuffix( {"objweight", suf::OBJ} ) );
if (objw.size() && 2==MPD( GetEnv() ).multiobj_weight()) { // user wants "intuitive"
FlipDiffSenseSigns(objw); // Backend / Emulator want "legacy"
}
return objw;
}

/// 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];
}

private:
MOManagerStatus status_ {MOManagerStatus::NOT_SET};
Expand Down
2 changes: 2 additions & 0 deletions include/mp/flat/obj_std.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class LinearObjective {
name_(std::move(nm)){ }
/// Get sense
obj::Type obj_sense() const { return sense_; }
/// Set sense
void set_sense(obj::Type s) { sense_ = s; }
/// Get lin terms, const
const LinTerms& GetLinTerms() const { return lt_; }
/// Get lin terms
Expand Down
4 changes: 4 additions & 0 deletions include/mp/flat/problem_flattener.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ class ProblemFlattener :
void ProcessIterationSolution(const Solution& sol, int status) override
{ GetFlatCvt().ProcessSolveIterationSolution(sol, status); }

/// Objective weights
ArrayRef<double> GetObjWeightsAdapted() override
{ return GetFlatCvt().GetObjWeightsAdapted(); }


/// Fill model traits
void FillModelTraits(AMPLS_ModelTraits& mt) override {
Expand Down
3 changes: 3 additions & 0 deletions include/mp/model-mgr-base.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class BasicModelManager {
/// Process solve iteration solution
virtual void ProcessIterationSolution(const Solution& , int status) = 0;

/// Objective weights in the 'legacy' format of the obj:multi:weight option
virtual ArrayRef<double> GetObjWeightsAdapted() = 0;

/// Integrality flags of the variables in the original instance.
/// Used for solution rounding
virtual const std::vector<bool>& IsVarInt() const = 0;
Expand Down
4 changes: 4 additions & 0 deletions include/mp/model-mgr-with-pb.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ class ModelManagerWithProblemBuilder :
void ProcessIterationSolution(const Solution& sol, int status) override
{ GetCvt().ProcessIterationSolution(sol, status); }

/// Objective weights
ArrayRef<double> GetObjWeightsAdapted() override
{ return GetCvt().GetObjWeightsAdapted(); }


const std::vector<bool>& IsVarInt() const override {
return GetModel().IsVarInt();
Expand Down
4 changes: 4 additions & 0 deletions include/mp/solver-base.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ class BasicSolver : private ErrorHandler,
/// Whether the solver natively supports multiobj
bool multiobj_has_native() const { return multiobj_has_native_; }

/// Option obj:multi:weight
int multiobj_weight() const { return multiobj_weight_; }

/// >0 if the timing is enabled
int timing() const { return timing_; }

Expand Down Expand Up @@ -611,6 +614,7 @@ class BasicSolver : private ErrorHandler,

bool multiobj_ {false};
bool multiobj_has_native_ {false};
int multiobj_weight_ {2};

bool has_errors_ {false};
OutputHandler *output_handler_ {this};
Expand Down
4 changes: 3 additions & 1 deletion solvers/visitor/visitorbackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ class VisitorBackend :
/////////////// OPTIONAL STANDARD FEATURES /////////////////
////////////////////////////////////////////////////////////
// Use this section to declare and implement some standard features
// that may or may not need additional functions.
// that may or may not need additional functions.
// For a full list of features possible,
// grep "STD_FEATURE".
USING_STD_FEATURES;

/**
Expand Down
21 changes: 19 additions & 2 deletions src/solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -652,10 +652,27 @@ void BasicSolver::InitMetaInfoAndOptions(
".objpriority, .objweight, .objreltol, and .objabstol on the "
"objectives are relevant. Objectives with greater .objpriority "
"values (integer values) have higher priority. Objectives with "
"the same .objpriority are weighted by .objweight. Objectives "
"the same .objpriority are weighted by .objweight, "
"according to the option obj:multi:weight.\n"
"\n"
"Objectives "
"with positive .objabstol or .objreltol are allowed to be "
"degraded by lower priority objectives by amounts not exceeding "
"the .objabstol (absolute) and .objreltol (relative) limits. " )));
"the .objabstol (absolute) and .objreltol (relative) limits. ")));

static const mp::OptionValueInfo values_multiobjweight_[] = {
{ "1", "relative to the sense of the 1st objective", 1},
{ "2", "relative to its own sense (default)", 2}
};
AddStoredOption("obj:multi:weight multiobjweight obj:multi:weights multiobjweights",
"How to interpret each objective's weight sign:\n"
"\n.. value-table::\n\n"
"With the 1st option (legacy behaviour), negative .objweight "
"for objective i would make "
"objective i's sense the opposite of the model's 1st objective. "
"Otherwise, it would make objective i's sense the opposite to its sense "
"defined in the model.",
multiobj_weight_, values_multiobjweight_);
}

AddIntOption("tech:timing timing tech:report_times report_times",
Expand Down
12 changes: 12 additions & 0 deletions test/end2end/cases/categorized/fast/multi_obj/modellist.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@
"name" : "obj_suf_01 multiobj=1",
"tags" : ["linear", "continuous", "multiobj"],
"options": { "ANYSOLVER_options": "multiobj=1" },
"values": {
"x": 1,
"y": 0,
"z": 0,
"_sobj[1]": 1,
"_sobj[2]": 0
}
},
{
"name" : "obj_suf_01 multiobj=1 obj:multi:weight=1",
"tags" : ["linear", "continuous", "multiobj"],
"options": { "ANYSOLVER_options": "multiobj=1 obj:multi:weight=1" },
"values": {
"x": 0,
"y": 1,
Expand Down

0 comments on commit 063687c

Please sign in to comment.