diff --git a/include/mp/solver-opt.h b/include/mp/solver-opt.h index f07f7ff46..901e4935b 100644 --- a/include/mp/solver-opt.h +++ b/include/mp/solver-opt.h @@ -180,7 +180,7 @@ class SolverOption { ValueArrayRef values() const { return values_; } /// Returns true if this option is a flag, i.e. it doesn't take a value. - bool is_flag() const { return is_flag_; } + virtual bool is_flag() const { return is_flag_; } /// Returns the option value. virtual void GetValue(fmt::LongLong &) const { @@ -312,7 +312,7 @@ class TypedSolverOption : public SolverOption { ValueArrayRef values = ValueArrayRef()) : SolverOption(name, description, values) {} - void Write(fmt::Writer &w) { w << GetValue(); } + void Write(fmt::Writer &w) override { w << GetValue(); } /// Parses the string for an option value, throws an exception if the value /// is invalid. @@ -322,7 +322,7 @@ class TypedSolverOption : public SolverOption { /// so it is now in the format of null terminated substrings. /// If parsed from the environment variable, the string is /// monolithic, space separated and quotes have to be considered. - void Parse(const char *&s, bool splitString=false) { + void Parse(const char *&s, bool splitString=false) override { const char *start = s; T value = internal::OptionHelper::Parse(s, splitString); if (*s && !std::isspace(*s)) { @@ -333,7 +333,7 @@ class TypedSolverOption : public SolverOption { SetValue(value); } - virtual Option_Type type() { + virtual Option_Type type() override { if (std::is_integral::value) return Option_Type::INT; if (std::is_arithmetic::value) @@ -489,7 +489,8 @@ class SolverOptionManager { public: - /// Stored option: references a variable + /// Stored option: references a variable. + /// StoredOption is specialized. template class StoredOption : public mp::TypedSolverOption { Value& value_; @@ -497,7 +498,8 @@ class SolverOptionManager { using value_type = Value; StoredOption(const char *name_list, const char *description, Value& v, ValueArrayRef values = ValueArrayRef()) - : mp::TypedSolverOption(name_list, description, values), value_(v) {} + : mp::TypedSolverOption(name_list, description, values), + value_(v) {} void GetValue(Value &v) const override { v = value_; } void SetValue(typename internal::OptionHelper::Arg v) override @@ -795,9 +797,36 @@ class SolverOptionManager { set_option_iterator set_option_end() const { return set_option_iterator(options_.end()); } - ~SolverOptionManager(); + virtual ~SolverOptionManager(); }; + +/// Stored option . +/// Can only be set to True. +template <> +class SolverOptionManager::StoredOption + : public mp::TypedSolverOption { + bool& value_; +public: + using value_type = bool; + StoredOption(const char *name_list, const char *description, + bool& v, ValueArrayRef values = ValueArrayRef()) + : mp::TypedSolverOption(name_list, description, values), + value_(v) {} + + /// We are a flag + bool is_flag() const override { return true; } + + /// Parse: only set to True + void Parse(const char *&, bool =false) override + { value_ = true; } + + /// Dummy + void GetValue(int &v) const override { v = value_; } + void SetValue(int v) override { value_ = v; } +}; + + } // namespace mp #endif // SOLVEROPT_H diff --git a/solvers/visitor/visitorbackend.cc b/solvers/visitor/visitorbackend.cc index 66496ab2a..984465f6f 100644 --- a/solvers/visitor/visitorbackend.cc +++ b/solvers/visitor/visitorbackend.cc @@ -337,16 +337,22 @@ void VisitorBackend::InitCustomOptions() { "\n" " ampl: option visitor_options 'mipgap=1e-6';\n"); + // Use AddSolverOption() for proper solver parameters. + // Below are examples of options stored in variables for own use. + AddStoredOption("tech:option_example opt_example example_opt", "Example option. " "Default = \"\" (don't work too hard).", storedOptions_.option_example_); + AddStoredOption("tech:flag1 flag1", + "Flag option. Use without value. Can only be set to True.", + storedOptions_.flag_option_); + AddListOption("tech:list_option opt_list multi_valued_option", "Multi-valued option when repeated.", storedOptions_.list_option_); - // Use AddSolverOption() for solver parameters } diff --git a/solvers/visitor/visitorbackend.h b/solvers/visitor/visitorbackend.h index d93616945..2e962debf 100644 --- a/solvers/visitor/visitorbackend.h +++ b/solvers/visitor/visitorbackend.h @@ -179,6 +179,7 @@ class VisitorBackend : /// These options are stored in the class struct Options { std::string option_example_; + bool flag_option_ = false; std::vector list_option_; }; Options storedOptions_; diff --git a/solvers/xpress/xpressbackend.cc b/solvers/xpress/xpressbackend.cc index 70da55589..4ac37dc94 100644 --- a/solvers/xpress/xpressbackend.cc +++ b/solvers/xpress/xpressbackend.cc @@ -219,7 +219,7 @@ std::string XpressmpBackend::XPRESSSolveFlags() { if (bool(storedOptions_.fBarrier_) + bool(storedOptions_.fPrimal_) + bool(storedOptions_.fDual_) - + bool(storedOptions_.fNetwork_)) { + + bool(storedOptions_.fNetwork_) >= 2) { AddWarning("Ambiguous LP method", "Only one of barrier/primal/dual/network should be specified."); } diff --git a/solvers/xpress/xpressbackend.h b/solvers/xpress/xpressbackend.h index 77f83d447..6e725a8ff 100644 --- a/solvers/xpress/xpressbackend.h +++ b/solvers/xpress/xpressbackend.h @@ -211,10 +211,10 @@ class XpressmpBackend : int pooldupcol_; int poollimit_ = 10; int nPoolMode_ = 0; - int fBarrier_ = 0; - int fPrimal_ = 0; - int fDual_ = 0; - int fNetwork_ = 0; + bool fBarrier_ = 0; + bool fPrimal_ = 0; + bool fDual_ = 0; + bool fNetwork_ = 0; std::string tunebase_; std::string tunename_; std::string logFile_; diff --git a/test/end2end/cases/categorized/fast/tech/modellist.json b/test/end2end/cases/categorized/fast/tech/modellist.json index 476284505..9698b5bab 100644 --- a/test/end2end/cases/categorized/fast/tech/modellist.json +++ b/test/end2end/cases/categorized/fast/tech/modellist.json @@ -18,5 +18,12 @@ "solve_result_num": 500, "solve_exitcode": 0 } + }, + { + "name" : "Check_Barrier_Flag", + "tags" : ["option_flag_barrier"], + "files" : ["diet.mod", "diet.dat"], + "options": { "ANYSOLVER_options": "outlev 1 barrier outlev 0" }, + "objective": 88.2 } ] diff --git a/test/end2end/scripts/python/Model.py b/test/end2end/scripts/python/Model.py index d6f69f0ef..f28b84292 100644 --- a/test/end2end/scripts/python/Model.py +++ b/test/end2end/scripts/python/Model.py @@ -75,6 +75,9 @@ class ModelTags(enum.Enum): check_sos2_from_pl = 51000 # Solvers accepting SOS2 constraints but wishing # to test SOS2 conversion, by acc:sos2=1 + ## Specific options (e.g., XPRESS) + option_flag_barrier = 61000 + ## Solver-specific gurobi_cloud = 100000 gurobi_server=100001 diff --git a/test/end2end/scripts/python/Solver.py b/test/end2end/scripts/python/Solver.py index 5cef18b33..6fe424520 100644 --- a/test/end2end/scripts/python/Solver.py +++ b/test/end2end/scripts/python/Solver.py @@ -721,6 +721,8 @@ def __init__(self, exeName, timeout=None, nthreads=None, ModelTags.writelp, ModelTags.writesol, + ModelTags.option_flag_barrier, + } super().__init__(exeName, timeout, nthreads, otherOptions, stags)