lpsolver.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/modules/lp_solver/lpsolver.cpp $ 00003 version : $LastChangedRevision: 1656 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 2012) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba * 00010 * * 00011 * This library is free software; you can redistribute it and/or modify it * 00012 * under the terms of the GNU Lesser General Public License as published * 00013 * by the Free Software Foundation; either version 2.1 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 * This library is distributed in the hope that it will be useful, * 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * 00019 * General Public License for more details. * 00020 * * 00021 * You should have received a copy of the GNU Lesser General Public * 00022 * License along with this library; if not, write to the Free Software * 00023 * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * 00024 * USA * 00025 * * 00026 ***************************************************************************/ 00027 00028 #include "lpsolver.h" 00029 00030 namespace module_lp_solver 00031 { 00032 00033 const MetaClass *LPSolver::metadata; 00034 00035 const Keyword tag_datafile("datafile"); 00036 const Keyword tag_modelfile("modelfile"); 00037 const Keyword tag_solutionfile("solutionfile"); 00038 const Keyword tag_objective("objective"); 00039 00040 00041 MODULE_EXPORT const char* initialize(const Environment::ParameterList& z) 00042 { 00043 // Initialize only once 00044 static bool init = false; 00045 static const char* name = "lpsolver"; 00046 if (init) 00047 { 00048 logger << "Warning: Initializing module lpsolver more than once." << endl; 00049 return name; 00050 } 00051 init = true; 00052 00053 // Register the Python extension 00054 PyGILState_STATE state = PyGILState_Ensure(); 00055 try 00056 { 00057 // Register new Python data types 00058 if (LPSolver::initialize()) 00059 throw RuntimeException("Error registering Python solver_lp extension"); 00060 PyGILState_Release(state); 00061 } 00062 catch (const exception &e) 00063 { 00064 PyGILState_Release(state); 00065 logger << "Error: " << e.what() << endl; 00066 } 00067 catch (...) 00068 { 00069 PyGILState_Release(state); 00070 logger << "Error: unknown exception" << endl; 00071 } 00072 00073 // Return the name of the module 00074 return name; 00075 } 00076 00077 00078 int LPSolver::initialize() 00079 { 00080 // Initialize the metadata. 00081 metadata = new MetaClass("solver", "solver_lp", 00082 Object::createString<LPSolver>); 00083 00084 // Initialize the Python class 00085 return FreppleClass<LPSolver,Solver>::initialize(); 00086 } 00087 00088 00089 void LPSolver::solveObjective(const string& colname) 00090 { 00091 // Set the objective coefficient 00092 if (colname.empty()) throw DataException("Empty objective name"); 00093 int col = glp_find_col(lp, colname.c_str()); 00094 if (!col) 00095 throw DataException("Unknown objective name '" + string(colname) + "'"); 00096 lpx_set_obj_coef(lp, col, 1.0); 00097 00098 // Message 00099 if (getLogLevel()>0) 00100 logger << "Solving for " << colname << "..." << endl; 00101 00102 // Solve 00103 int result = glp_simplex(lp, ¶meters); 00104 00105 // Echo the result 00106 double val = lpx_get_obj_val(lp); 00107 if (getLogLevel()>0) 00108 { 00109 if (result) 00110 logger << " Error " << result << endl; 00111 else 00112 logger << " Optimum " << val << " found at " << Date::now() << endl; 00113 } 00114 00115 // Freeze the column bounds 00116 lpx_set_col_bnds(lp, col, LPX_DB, 00117 val>=ROUNDING_ERROR ? val-ROUNDING_ERROR : 0.0, 00118 val>=-ROUNDING_ERROR ? val+ROUNDING_ERROR : 0.0); 00119 00120 // Remove from the objective 00121 lpx_set_obj_coef(lp, col, 0.0); 00122 00123 // No more presolving required after 1 objective 00124 if (parameters.presolve) parameters.presolve = 0; 00125 } 00126 00127 00128 void LPSolver::solve(void *v) 00129 { 00130 if (getLogLevel()>0) 00131 logger << "Start running the solver at " << Date::now() << endl; 00132 00133 // Capture all terminal output of the solver 00134 glp_term_hook(solveroutputredirect,NULL); 00135 00136 // Configure verbosity of the output 00137 glp_init_smcp(¶meters); 00138 if (getLogLevel() == 0) 00139 parameters.msg_lev = GLP_MSG_OFF; 00140 else if (getLogLevel() == 1) 00141 parameters.msg_lev = GLP_MSG_ERR; 00142 else if (getLogLevel() == 2) 00143 parameters.msg_lev = GLP_MSG_ON; 00144 else 00145 parameters.msg_lev = GLP_MSG_ALL; 00146 00147 // Read the problem from a file in the GNU MathProg language. 00148 if (modelfilename.empty()) 00149 throw DataException("No model file specified"); 00150 if (datafilename.empty()) 00151 lp = lpx_read_model(modelfilename.c_str(), NULL, NULL); 00152 else 00153 lp = lpx_read_model(modelfilename.c_str(), datafilename.c_str(), NULL); 00154 if (lp == NULL) 00155 throw RuntimeException("Cannot read model file '" + modelfilename + "'"); 00156 00157 // Optinally, write the model in MPS format. This format can be read 00158 // directly by other Linear Programming packages. 00159 if (getLogLevel()>2) 00160 { 00161 string c = modelfilename + ".mps"; 00162 lpx_write_mps(lp,c.c_str()); 00163 } 00164 00165 // Scale the problem data 00166 lpx_scale_prob(lp); 00167 00168 // Enable pre-solving 00169 // After the first objective, the presolving is switched off. 00170 parameters.presolve = 1; 00171 00172 // Minimize the goal 00173 glp_set_obj_dir(lp, minimum ? GLP_MIN : GLP_MAX); 00174 00175 // Create an index for quick searching on names 00176 glp_create_index(lp); 00177 00178 if (getLogLevel()>0) 00179 logger << "Finished solver initialisation at " << Date::now() << endl; 00180 00181 // Solving... 00182 if (objectives.empty()) 00183 throw DataException("No solver objectives are specified"); 00184 for (list<string>::const_iterator i = objectives.begin(); 00185 i != objectives.end(); ++i) 00186 solveObjective(*i); 00187 00188 // Write solution 00189 if (!solutionfilename.empty()) 00190 lpx_print_sol(lp,solutionfilename.c_str()); 00191 00192 // Cleanup 00193 lpx_delete_prob(lp); 00194 glp_term_hook(NULL,NULL); 00195 00196 if (getLogLevel()>0) 00197 logger << "Finished running the solver at " << Date::now() << endl; 00198 } 00199 00200 00201 string LPSolver::replaceSpaces(const string& input) 00202 { 00203 string x = input; 00204 for (string::iterator i = x.begin(); i != x.end(); ++i) 00205 if (*i == ' ') *i = '_'; 00206 return x; 00207 } 00208 00209 00210 void LPSolver::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00211 { 00212 // Writing a reference 00213 if (m == REFERENCE) 00214 { 00215 o->writeElement(tag, Tags::tag_name, getName()); 00216 return; 00217 } 00218 00219 // Write the complete object 00220 if (m != NOHEADER) o->BeginObject 00221 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00222 00223 // Fields 00224 if (getMinimum()) 00225 o->writeElement(Tags::tag_minimum, true); 00226 else 00227 o->writeElement(Tags::tag_maximum, true); 00228 o->writeElement(tag_modelfile, getModelFile()); 00229 o->writeElement(tag_datafile, getDataFile()); 00230 o->writeElement(tag_solutionfile, getSolutionFile()); 00231 for (list<string>::const_iterator i = objectives.begin(); 00232 i != objectives.end(); ++i) 00233 o->writeElement(tag_objective, *i); 00234 Solver::writeElement(o, tag, NOHEADER); 00235 } 00236 00237 00238 void LPSolver::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00239 { 00240 if (pAttr.isA(Tags::tag_minimum)) 00241 setMinimum(pElement.getBool()); 00242 else if (pAttr.isA(Tags::tag_maximum)) 00243 setMinimum(!pElement.getBool()); 00244 else if (pAttr.isA(tag_datafile)) 00245 setDataFile(pElement.getString()); 00246 else if (pAttr.isA(tag_modelfile)) 00247 setModelFile(pElement.getString()); 00248 else if (pAttr.isA(tag_solutionfile)) 00249 setSolutionFile(pElement.getString()); 00250 else if (pAttr.isA(tag_objective)) 00251 addObjective(pElement.getString()); 00252 else 00253 // The standard fields of a solver... 00254 Solver::endElement(pIn, pAttr, pElement); 00255 } 00256 00257 00258 PyObject* LPSolver::getattro(const Attribute& attr) 00259 { 00260 if (attr.isA(Tags::tag_minimum)) 00261 return PythonObject(getMinimum()); 00262 else if (attr.isA(Tags::tag_maximum)) 00263 return PythonObject(!(getMinimum())); 00264 else if (attr.isA(tag_datafile)) 00265 return PythonObject(getDataFile()); 00266 else if (attr.isA(tag_modelfile)) 00267 return PythonObject(getModelFile()); 00268 else if (attr.isA(tag_solutionfile)) 00269 return PythonObject(getSolutionFile()); 00270 else if (attr.isA(tag_objective)) 00271 { 00272 // The list of objectives is returned as a list of strings 00273 PyObject* result = PyList_New(getObjectives().size()); 00274 int count = 0; 00275 for (list<string>::const_iterator i = getObjectives().begin(); 00276 i != getObjectives().end(); ++i) 00277 PyList_SetItem(result, count++, PythonObject(*i)); 00278 return result; 00279 } 00280 return Solver::getattro(attr); 00281 } 00282 00283 00284 int LPSolver::setattro(const Attribute& attr, const PythonObject& field) 00285 { 00286 if (attr.isA(Tags::tag_minimum)) 00287 setMinimum(field.getBool()); 00288 else if (attr.isA(Tags::tag_maximum)) 00289 setMinimum(!field.getBool()); 00290 else if (attr.isA(tag_datafile)) 00291 setDataFile(field.getString()); 00292 else if (attr.isA(tag_modelfile)) 00293 setModelFile(field.getString()); 00294 else if (attr.isA(tag_solutionfile)) 00295 setSolutionFile(field.getString()); 00296 else if (attr.isA(tag_objective)) 00297 { 00298 // The objective argument is a list of strings 00299 PyObject* seq = PySequence_Fast(static_cast<PyObject*>(field), "expected a list"); 00300 if (!PyList_Check(seq)) 00301 { 00302 PyErr_SetString(PythonDataException, "expected a list"); 00303 return -1; // Error 00304 } 00305 int len = PySequence_Size(static_cast<PyObject*>(field)); 00306 PythonObject item; 00307 for (int i = 0; i < len; i++) 00308 { 00309 item = PyList_GET_ITEM(seq, i); 00310 addObjective(item.getString()); 00311 } 00312 } 00313 else 00314 return Solver::setattro(attr, field); 00315 return 0; // OK 00316 } 00317 00318 } // End namespace