From deac24217e5de1c350838dadd6ac4b9306f4a571 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Fri, 28 Jul 2023 12:04:22 +1000 Subject: [PATCH] ASL-style: repeated 'writeprob' and 'writesol' #128 --- include/mp/backend-std.h | 106 ++++++++++++++++-------------- include/mp/solver-opt.h | 53 ++++++++++++++- solvers/visitor/visitorbackend.cc | 22 +++---- solvers/visitor/visitorbackend.h | 11 +++- 4 files changed, 124 insertions(+), 68 deletions(-) diff --git a/include/mp/backend-std.h b/include/mp/backend-std.h index 513482d5e..2ee5dd6dd 100644 --- a/include/mp/backend-std.h +++ b/include/mp/backend-std.h @@ -211,7 +211,7 @@ class StdBackend : SetupTimerAndInterrupter(); if (exportFileMode() > 0) - ExportModel(export_file_name()); + ExportModel(export_file_names()); // exportFileMode == 2 -> do not solve, just export if (exportFileMode() != 2) @@ -702,13 +702,14 @@ class StdBackend : int round_=0; double round_reptol_=1e-9; - // For write prob - std::string export_file_; - std::string just_export_file_; - // For write sol - std::string export_sol_; + /// For write prob + std::vector export_files_; + std::vector just_export_files_; + /// For write sol + std::vector export_sol_files_; } storedOptions_; + /// Once Impl allows FEASRELAX, /// it should check this via feasrelax() class FeasrelaxIO { @@ -758,23 +759,18 @@ class StdBackend : int exportFileMode() const { if (IMPL_HAS_STD_FEATURE(WRITE_PROBLEM)) { - if (!storedOptions_.export_file_.empty()) + if (!storedOptions_.export_files_.empty()) return 1; - if (!storedOptions_.just_export_file_.empty()) + if (!storedOptions_.just_export_files_.empty()) return 2; } return 0; } - std::string export_file_name() const { - std::string name - = storedOptions_.export_file_.empty() - ? storedOptions_.just_export_file_ - : storedOptions_.export_file_; - if (((name.front() == '"') && (name.back() == '"')) || - ((name.front() == '\'') && (name.back() == '\''))) - return name.substr(1, name.length() - 2); - else - return name; + const std::vector& export_file_names() const { + return + storedOptions_.export_files_.empty() + ? storedOptions_.just_export_files_ + : storedOptions_.export_files_; } @@ -836,26 +832,27 @@ class StdBackend : } if (IMPL_HAS_STD_FEATURE(WRITE_PROBLEM)) { - AddStoredOption("tech:writemodel writeprob writemodel", - "Specifies the name of a file where to export the model before " - "solving it. This file name can have extension ``.lp[.7zip]``, ``.mps``, etc. " - "Default = \"\" (don't export the model).", - storedOptions_.export_file_); - - AddStoredOption("tech:writemodelonly justwriteprob justwritemodel", - "Specifies the name of a file where to export the model, do not solve it. " - "This file name can have extension ``.dlp``, ``.mps``, etc. " - "Default = \"\" (don't export the model).", - storedOptions_.just_export_file_); + AddListOption("tech:writemodel writeprob writemodel tech:exportfile", + "Specifies files where to export the model before " + "solving (repeat the option for several files.) " + "File name extensions can be ``.lp[.7z]``, ``.mps``, etc.", + storedOptions_.export_files_); + + AddListOption("tech:writemodelonly justwriteprob justwritemodel", + "Specifies files where to export the model, no solving " + "(option can be repeated.) " + "File extensions can be ``.dlp``, ``.mps``, etc.", + storedOptions_.just_export_files_); } if (IMPL_HAS_STD_FEATURE(WRITE_SOLUTION)) - AddStoredOption("tech:writesolution writesol writesolution", - "Specifies the name of a file where to export the solution " - "or other result file. This file name can have extension " - "``.sol[.tar.gz]``, ``.json``, ``.bas``, ``.ilp``, etc. " - "Default = \"\" (don't export results).", - storedOptions_.export_sol_); + AddListOption("tech:writesolution writesol writesolution", + "Specifies the names of files where to export the solution " + "and/or other result files in solver's native formats. " + "Option can be repeated. " + "File name extensions can be " + "``.sol[.tar.gz]``, ``.json``, ``.bas``, ``.ilp``, etc.", + storedOptions_.export_sol_files_); } virtual void InitCustomOptions() { } @@ -940,27 +937,34 @@ class StdBackend : public: - /// Via solver's output + /// Write solution result in solver native format + /// via solver's output. virtual void ReportSolutionViaSolver() { if (IMPL_HAS_STD_FEATURE(WRITE_SOLUTION)) - if (!storedOptions_.export_sol_.empty()) - DoWriteSolution(storedOptions_.export_sol_); + if (!storedOptions_.export_sol_files_.empty()) + for (const auto& fln: storedOptions_.export_sol_files_) + DoWriteSolution(fln); } /// Write model - virtual void ExportModel(const std::string& filename) { - try { - DoWriteProblem(filename); - } catch (const std::exception& exc) { - auto msg - = std::string("Model export failed:\n") - + exc.what(); - if (IMPL_HAS_STD_FEATURE(WRITE_SOLUTION)) - msg += - "\n Note: to export solutions and results\n" - " in the solver's native formats,\n" - " use option 'tech:writesolution'"; - MP_RAISE(msg); + virtual void ExportModel( + const std::vector& filenames) { + for (const auto& fln: filenames) { + try { + DoWriteProblem(fln); + } catch (const std::exception& exc) { + auto msg + = std::string("Model export to file '") + + fln + + "' failed:\n" + + exc.what(); + if (IMPL_HAS_STD_FEATURE(WRITE_SOLUTION)) + msg += + "\n Note: to export solutions and results\n" + " in the solver's native formats,\n" + " use option 'tech:writesolution'"; + MP_RAISE(msg); + } } } diff --git a/include/mp/solver-opt.h b/include/mp/solver-opt.h index f1127e2fc..919c31936 100644 --- a/include/mp/solver-opt.h +++ b/include/mp/solver-opt.h @@ -471,6 +471,7 @@ class SolverOptionManager { public: + /// Stored option: references a variable template class StoredOption : public mp::TypedSolverOption { Value& value_; @@ -495,7 +496,9 @@ class SolverOptionManager { } /// Simple stored option referencing a variable; min, max values - /// (currently unused) + /// (they are unused but to deduce type; + /// we aree too lazy to maintain correct min/max + /// between solver versions.) template void AddStoredOption(const char *name, const char *description, Value& value, Value , Value ) { @@ -511,12 +514,60 @@ class SolverOptionManager { AddStoredOption(name, description, value, lb, ub); } + + /// List option: references a container of values. + /// Any container with value_type, back() and push_back(). + template + class ListOption + : public mp::TypedSolverOption + { + Container& value_; + public: + using value_type = typename Container::value_type; + ListOption(const char *name_list, const char *description, + Container& v, ValueArrayRef values = ValueArrayRef()) + : mp::TypedSolverOption( + name_list, description, values), value_(v) {} + + void GetValue(value_type &v) const override + { assert(value_.size()); v = value_.back(); } + void SetValue( + typename internal::OptionHelper::Arg v) override + { value_.push_back(v); } + }; + + /// Add list option referencing a container + template + void AddListOption(const char *name, const char *description, + Value& value, ValueArrayRef values = ValueArrayRef()) { + AddOption(OptionPtr( + new ListOption( + name, description, value, values))); + } + + /// Add list option referencing a container; min, max values + /// (they are unused but to deduce type; + /// we aree too lazy to maintain correct min/max + /// between solver versions.) + template + void AddListOption(const char *name, const char *description, + Value& value, + typename Value::value_type , + typename Value::value_type ) { + AddOption(OptionPtr( + new ListOption( + name, description, value, ValueArrayRef()))); + } + + + /// Replace option descr void ReplaceOptionDescription(const char* name, const char* desc) { auto pOption = FindOption(name); assert(pOption); pOption->set_description(desc); } + /// Add to option descr void AddToOptionDescription(const char* name, const char* desc_add) { auto pOption = FindOption(name); assert(pOption); diff --git a/solvers/visitor/visitorbackend.cc b/solvers/visitor/visitorbackend.cc index 91e898b02..66496ab2a 100644 --- a/solvers/visitor/visitorbackend.cc +++ b/solvers/visitor/visitorbackend.cc @@ -181,11 +181,6 @@ int VisitorBackend::BarrierIterations() const { // return getIntAttr(VISITOR_INTATTR_BARRIERITER); } -void VisitorBackend::ExportModel(const std::string &file) { - // TODO export proper by file extension - //VISITOR_CCALL(VISITOR_WriteLp(lp(), file.data())); -} - void VisitorBackend::SetInterrupter(mp::Interrupter *inter) { inter->SetHandler(InterruptVisitor, lp()); @@ -194,9 +189,6 @@ void VisitorBackend::SetInterrupter(mp::Interrupter *inter) { } void VisitorBackend::Solve() { - if (!storedOptions_.exportFile_.empty()) { - ExportModel(storedOptions_.exportFile_); - } //VISITOR_CCALL(VISITOR_Solve(lp())); WindupVISITORSolve(); } @@ -345,12 +337,16 @@ void VisitorBackend::InitCustomOptions() { "\n" " ampl: option visitor_options 'mipgap=1e-6';\n"); - AddStoredOption("tech:exportfile writeprob writemodel", - "Specifies the name of a file where to export the model before " - "solving it. This file name can have extension ``.lp()``, ``.mps``, etc. " - "Default = \"\" (don't export the model).", - storedOptions_.exportFile_); + AddStoredOption("tech:option_example opt_example example_opt", + "Example option. " + "Default = \"\" (don't work too hard).", + storedOptions_.option_example_); + + 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 a92f69496..d93616945 100644 --- a/solvers/visitor/visitorbackend.h +++ b/solvers/visitor/visitorbackend.h @@ -141,8 +141,6 @@ class VisitorBackend : void OpenSolver(); void CloseSolver(); - void ExportModel(const std::string& file); - double ObjectiveValue() const; /// Solution values. The vectors are emptied if not available @@ -180,11 +178,18 @@ class VisitorBackend : private: /// These options are stored in the class struct Options { - std::string exportFile_; + std::string option_example_; + std::vector list_option_; }; Options storedOptions_; +protected: + const std::string& get_example_option() const + { return storedOptions_.option_example_; } + const std::vector& get_list_option() const + { return storedOptions_.list_option_; } + }; } // namespace mp