diff --git a/CoMISo/NSolver/GUROBISolver.cc b/CoMISo/NSolver/GUROBISolver.cc index 522b2df8b249f0ebcc73962409f0848beb7c29dc..84ae436169d3529e73fe1dd29ac187172a45fd79 100644 --- a/CoMISo/NSolver/GUROBISolver.cc +++ b/CoMISo/NSolver/GUROBISolver.cc @@ -21,12 +21,11 @@ namespace COMISO { //== IMPLEMENTATION ========================================================== -//// Constructor -//GUROBISolver:: -//GUROBISolver() -//{ -// -//} + +GUROBISolver:: +GUROBISolver() +{ +} //----------------------------------------------------------------------------- @@ -140,18 +139,49 @@ solve(NProblemInterface* _problem, model.set(GRB_IntAttr_ModelSense, 1); model.setObjective(objective); + //---------------------------------------------- // 4. solve problem //---------------------------------------------- + + if (solution_input_path_.empty()) + { + if (!problem_env_output_path_.empty()) + { + std::cout << "Writing problem's environment into file \"" << problem_env_output_path_ << "\"." << std::endl; + model.getEnv().writeParams(problem_env_output_path_); + } + if (!problem_output_path_.empty()) + { + std::cout << "Writing problem into file \"" << problem_output_path_ << "\"." << std::endl; + GurobiHelper::outputModelToMpsGz(model, problem_output_path_); + } + + model.optimize(); + } + else + { + std::cout << "Reading solution from file \"" << solution_input_path_ << "\"." << std::endl; + } + model.optimize(); //---------------------------------------------- // 5. store result //---------------------------------------------- - for(unsigned int i=0; istore_result(P(x)); @@ -174,6 +204,39 @@ solve(NProblemInterface* _problem, } +//----------------------------------------------------------------------------- + + +void +GUROBISolver:: +set_problem_output_path( const std::string &_problem_output_path) +{ + problem_output_path_ = _problem_output_path; +} + + +//----------------------------------------------------------------------------- + + +void +GUROBISolver:: +set_problem_env_output_path( const std::string &_problem_env_output_path) +{ + problem_env_output_path_ = _problem_env_output_path; +} + + +//----------------------------------------------------------------------------- + + +void +GUROBISolver:: +set_solution_input_path(const std::string &_solution_input_path) +{ + solution_input_path_ = _solution_input_path; +} + + //============================================================================= } // namespace COMISO //============================================================================= diff --git a/CoMISo/NSolver/GUROBISolver.hh b/CoMISo/NSolver/GUROBISolver.hh index d07dce6747ac44bf63a0f094e8d3a828fbfdd340..13c34f457746f8050a0f0eae9c1774abb9106c2d 100644 --- a/CoMISo/NSolver/GUROBISolver.hh +++ b/CoMISo/NSolver/GUROBISolver.hh @@ -17,9 +17,11 @@ #include #include +#include #include "NProblemInterface.hh" #include "NConstraintInterface.hh" #include "VariableType.hh" +#include "GurobiHelper.hh" #include @@ -45,8 +47,8 @@ public: typedef std::pair PairUiV; - /// Default constructor -> set up IpOptApplication - GUROBISolver() {} + /// Default constructor + GUROBISolver(); /// Destructor ~GUROBISolver() {} @@ -57,6 +59,10 @@ public: std::vector& _discrete_constraints, // discrete constraints const double _time_limit = 60 ); // time limit in seconds + void set_problem_output_path ( const std::string &_problem_output_path); + void set_problem_env_output_path( const std::string &_problem_env_output_path); + void set_solution_input_path ( const std::string &_solution_input_path); + protected: double* P(std::vector& _v) { @@ -68,6 +74,11 @@ protected: private: + // filenames for exporting/importing gurobi solutions + // if string is empty nothing is imported or exported + std::string problem_output_path_; + std::string problem_env_output_path_; + std::string solution_input_path_; }; diff --git a/CoMISo/NSolver/GurobiHelper.cc b/CoMISo/NSolver/GurobiHelper.cc new file mode 100644 index 0000000000000000000000000000000000000000..394b74d5ddd7c9e6458f9008bafb317e322b4e68 --- /dev/null +++ b/CoMISo/NSolver/GurobiHelper.cc @@ -0,0 +1,175 @@ +/* + * GurobiHelper.cc + * + * Created on: Jan 4, 2012 + * Author: ebke + */ + +#include "GurobiHelper.hh" + +#if COMISO_GUROBI_AVAILABLE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define OUTPUT_UNCOMPRESSED_WITH_CONSTANT_COMMENT 0 +#define OUTPUT_CONSTANT_AS_CONT 1 + +namespace COMISO { + +/** + * Helper class that ensures exception safe deletion + * of a temporary file. + */ +class TempFileGuard { + public: + TempFileGuard(const std::string &_filePath) : filePath_(_filePath) { + } + + ~TempFileGuard() { + if (boost::filesystem::exists(filePath_)) + boost::filesystem::remove(filePath_); + } + + const boost::filesystem::path &filePath() const { return filePath_; }; + + private: + boost::filesystem::path filePath_; +}; + +static void moveConstantTermIntoConstrainedVariable(GRBModel &model) { + const double constantTerm = model.getObjective().getLinExpr().getConstant(); + //tmpModel.getObjective().addConstant(-constantTerm); + model.getObjective() -= constantTerm; +#if OUTPUT_CONSTANT_AS_CONT + model.addVar(constantTerm, constantTerm, 1, GRB_CONTINUOUS, "constant"); +#else + model.addVar(1, 1, constantTerm, GRB_INTEGER, "constant"); +#endif +} + +static void copyFile(const char *from, const char *to) { + FILE *inF = fopen(from, "r"); + FILE *outF = fopen(to, "w"); + + const int bufsize = 4096; + unsigned char buffer[bufsize]; + + do { + size_t readBytes = fread(buffer, 1, bufsize, inF); + fwrite(buffer, 1, readBytes, outF); + } while(!feof(inF)); + + fclose(inF); + fclose(outF); +} + +/** + * WARNING: Never call outputModelToMpsGz and importInitialSolutionIntoModel + * on the same model. Both try to move the constant term into a variable and + * consequently, the second attempt to do so will fail. + */ +void GurobiHelper::outputModelToMpsGz(GRBModel &model, const std::string &problem_output_path_) { +#if OUTPUT_UNCOMPRESSED_WITH_CONSTANT_COMMENT + boost::scoped_ptr tempFileGuard; + { + QTemporaryFile tempFile("XXXXXX.mps"); + tempFile.setAutoRemove(false); + tempFile.open(); + + // In order to minimize the likelihood of race conditions, + // we initialize tempFileGuard right here. + tempFileGuard.reset(new TempFileGuard(QFileInfo(tempFile).absoluteFilePath().toStdString())); + tempFile.close(); + } + + const std::string fileName = tempFileGuard->filePath().string(); + + model.write(fileName); + const double constantTerm = model.getObjective().getLinExpr().getConstant(); + + FILE *inF = fopen(fileName.c_str(), "r"); + FILE *outF = fopen(problem_output_path_.c_str(), "w"); + + fprintf(outF, "* Constant Term: %.16e\n", constantTerm); + const int bufsize = 4096; + unsigned char buffer[bufsize]; + int readBytes; + do { + readBytes = fread(buffer, 1, bufsize, inF); + fwrite(buffer, 1, readBytes, outF); + } while(!feof(inF)); + fclose(inF); + fclose(outF); +#else + GRBModel tmpModel(model); + + moveConstantTermIntoConstrainedVariable(tmpModel); + + tmpModel.update(); + tmpModel.write(problem_output_path_); +#endif +} + +/** + * WARNING: Never call outputModelToMpsGz and importInitialSolutionIntoModel + * on the same model. Both try to move the constant term into a variable and + * consequently, the second attempt to do so will fail. + */ +void GurobiHelper::importInitialSolutionIntoModel(GRBModel &model, const std::string &solution_path_) { + boost::scoped_ptr tempFileGuard; + { + QTemporaryFile tempFile("XXXXXX.mst"); + tempFile.setAutoRemove(false); + tempFile.open(); + + // In order to minimize the likelihood of race conditions, + // we initialize tempFileGuard right here. + tempFileGuard.reset(new TempFileGuard(QFileInfo(tempFile).absoluteFilePath().toStdString())); + tempFile.close(); + } + + const std::string fileName = tempFileGuard->filePath().string(); + + copyFile(solution_path_.c_str(), fileName.c_str()); + + //moveConstantTermIntoConstrainedVariable(model); + const double constantTerm = model.getObjective().getLinExpr().getConstant(); + model.addVar(constantTerm, constantTerm, 0, GRB_CONTINUOUS, "constant"); + + model.update(); + model.read(fileName); + model.update(); +} + +void GurobiHelper::readSolutionVectorFromSOL(std::vector &out_solution_, const std::string &fileName_) { + std::ifstream solFile(fileName_.c_str()); + //if (!solFile.good()) + // throw std::runtime_error("Unable to open file \"" + fileName + "\"."); + + static const boost::regex commentRe("\\s*#", boost::regex_constants::perl); + static const boost::regex variableRe("\\s*\\S+\\s+([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)", boost::regex_constants::perl); + + std::string line; + while (solFile) { + std::getline(solFile, line); + if (boost::regex_search(line, commentRe, boost::match_continuous)) continue; + boost::smatch match; + if (boost::regex_search(line, match, variableRe, boost::match_continuous)) { + out_solution_.push_back(boost::lexical_cast(match[1])); + } + } +} + +} /* namespace COMISO */ + +#endif diff --git a/CoMISo/NSolver/GurobiHelper.hh b/CoMISo/NSolver/GurobiHelper.hh new file mode 100644 index 0000000000000000000000000000000000000000..79642faa0000091d93264bafdb76a255523023ab --- /dev/null +++ b/CoMISo/NSolver/GurobiHelper.hh @@ -0,0 +1,51 @@ +/* + * GurobiHelper.hh + * + * Created on: Jan 4, 2012 + * Author: ebke + */ + + +#ifndef GUROBIHELPER_HH_ +#define GUROBIHELPER_HH_ + +//== COMPILE-TIME PACKAGE REQUIREMENTS ======================================== +#include +#if COMISO_GUROBI_AVAILABLE +//============================================================================= + +#include +#include +#include + +namespace COMISO { + +class GurobiHelper { + public: + + /** + * WARNING: Never call outputModelToMpsGz and importInitialSolutionIntoModel + * on the same model. Both try to move the constant term into a variable and + * consequently, the second attempt to do so will fail. + */ + static void outputModelToMpsGz(GRBModel &model, const std::string &problem_output_path_); + + /** + * WARNING: Never call outputModelToMpsGz and importInitialSolutionIntoModel + * on the same model. Both try to move the constant term into a variable and + * consequently, the second attempt to do so will fail. + */ + static void importInitialSolutionIntoModel(GRBModel &model, const std::string &solution_path_); + + /** + * Reads the solution vector from a SOL file and appends it to + * out_solution_. + */ + static void readSolutionVectorFromSOL(std::vector &out_solution_, const std::string &fileName_); +}; + +} /* namespace COMISO */ +#endif /* COMISO_GUROBI_AVAILABLE */ +#endif /* GUROBIHELPER_HH_ */ + +