lpsolver.cpp
Go to the documentation of this file.00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/modules/lp_solver/lpsolver.cpp $ 00003 version : $LastChangedRevision: 1315 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2010-07-17 18:08:53 +0200 (Sat, 17 Jul 2010) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2010 by Johan De Taeye * 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 CommandLoadLibrary::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 try 00054 { 00055 // Register the Python extension 00056 PyThreadState *myThreadState = PyGILState_GetThisThreadState(); 00057 if (!Py_IsInitialized() || !myThreadState) 00058 throw RuntimeException("Python isn't initialized correctly"); 00059 try 00060 { 00061 // Get the global lock. 00062 PyEval_RestoreThread(myThreadState); 00063 // Register new Python data types 00064 if (LPSolver::initialize()) 00065 throw RuntimeException("Error registering Python solver_lp extension"); 00066 } 00067 // Release the global lock when leaving the function 00068 catch (...) 00069 { 00070 PyEval_ReleaseLock(); 00071 throw; // Rethrow the exception 00072 } 00073 PyEval_ReleaseLock(); 00074 } 00075 catch (exception &e) 00076 { 00077 // Avoid throwing errors during the initialization! 00078 logger << "Error: " << e.what() << endl; 00079 } 00080 catch (...) 00081 { 00082 logger << "Error: unknown exception" << endl; 00083 } 00084 00085 // Return the name of the module 00086 return name; 00087 } 00088 00089 00090 int LPSolver::initialize() 00091 { 00092 // Initialize the metadata. 00093 metadata = new MetaClass("solver", "solver_lp", 00094 Object::createString<LPSolver>); 00095 00096 // Initialize the Python class 00097 return FreppleClass<LPSolver,Solver>::initialize(); 00098 } 00099 00100 00101 void LPSolver::solveObjective(const string& colname) 00102 { 00103 // Set the objective coefficient 00104 if (colname.empty()) throw DataException("Empty objective name"); 00105 int col = glp_find_col(lp, colname.c_str()); 00106 if (!col) 00107 throw DataException("Unknown objective name '" + string(colname) + "'"); 00108 lpx_set_obj_coef(lp, col, 1.0); 00109 00110 // Message 00111 if (getLogLevel()>0) 00112 logger << "Solving for " << colname << "..." << endl; 00113 00114 // Solve 00115 int result = glp_simplex(lp, ¶meters); 00116 00117 // Echo the result 00118 double val = lpx_get_obj_val(lp); 00119 if (getLogLevel()>0) 00120 { 00121 if (result) 00122 logger << " Error " << result << endl; 00123 else 00124 logger << " Optimum " << val << " found at " << Date::now() << endl; 00125 } 00126 00127 // Freeze the column bounds 00128 lpx_set_col_bnds(lp, col, LPX_DB, 00129 val>=ROUNDING_ERROR ? val-ROUNDING_ERROR : 0.0, 00130 val>=-ROUNDING_ERROR ? val+ROUNDING_ERROR : 0.0); 00131 00132 // Remove from the objective 00133 lpx_set_obj_coef(lp, col, 0.0); 00134 00135 // No more presolving required after 1 objective 00136 if (parameters.presolve) parameters.presolve = 0; 00137 } 00138 00139 00140 void LPSolver::solve(void *v) 00141 { 00142 if (getLogLevel()>0) 00143 logger << "Start running the solver at " << Date::now() << endl; 00144 00145 // Capture all terminal output of the solver 00146 glp_term_hook(solveroutputredirect,NULL); 00147 00148 // Configure verbosity of the output 00149 glp_init_smcp(¶meters); 00150 if (getLogLevel() == 0) 00151 parameters.msg_lev = GLP_MSG_OFF; 00152 else if (getLogLevel() == 1) 00153 parameters.msg_lev = GLP_MSG_ERR; 00154 else if (getLogLevel() == 2) 00155 parameters.msg_lev = GLP_MSG_ON; 00156 else 00157 parameters.msg_lev = GLP_MSG_ALL; 00158 00159 // Read the problem from a file in the GNU MathProg language. 00160 if (modelfilename.empty()) 00161 throw DataException("No model file specified"); 00162 if (datafilename.empty()) 00163 lp = lpx_read_model(modelfilename.c_str(), NULL, NULL); 00164 else 00165 lp = lpx_read_model(modelfilename.c_str(), datafilename.c_str(), NULL); 00166 if (lp == NULL) 00167 throw RuntimeException("Cannot read model file '" + modelfilename + "'"); 00168 00169 // Optinally, write the model in MPS format. This format can be read 00170 // directly by other Linear Programming packages. 00171 if (getLogLevel()>2) 00172 { 00173 string c = modelfilename + ".mps"; 00174 lpx_write_mps(lp,c.c_str()); 00175 } 00176 00177 // Scale the problem data 00178 lpx_scale_prob(lp); 00179 00180 // Enable pre-solving 00181 // After the first objective, the presolving is switched off. 00182 parameters.presolve = 1; 00183 00184 // Minimize the goal 00185 glp_set_obj_dir(lp, minimum ? GLP_MIN : GLP_MAX); 00186 00187 // Create an index for quick searching on names 00188 glp_create_index(lp); 00189 00190 if (getLogLevel()>0) 00191 logger << "Finished solver initialisation at " << Date::now() << endl; 00192 00193 // Solving... 00194 if (objectives.empty()) 00195 throw DataException("No solver objectives are specified"); 00196 for (list<string>::const_iterator i = objectives.begin(); 00197 i != objectives.end(); ++i) 00198 solveObjective(*i); 00199 00200 // Write solution 00201 if (!solutionfilename.empty()) 00202 lpx_print_sol(lp,solutionfilename.c_str()); 00203 00204 // Cleanup 00205 lpx_delete_prob(lp); 00206 glp_term_hook(NULL,NULL); 00207 00208 if (getLogLevel()>0) 00209 logger << "Finished running the solver at " << Date::now() << endl; 00210 } 00211 00212 00213 string LPSolver::replaceSpaces(const string& input) 00214 { 00215 string x = input; 00216 for (string::iterator i = x.begin(); i != x.end(); ++i) 00217 if (*i == ' ') *i = '_'; 00218 return x; 00219 } 00220 00221 00222 void LPSolver::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00223 { 00224 // Writing a reference 00225 if (m == REFERENCE) 00226 { 00227 o->writeElement(tag, Tags::tag_name, getName()); 00228 return; 00229 } 00230 00231 // Write the complete object 00232 if (m != NOHEADER) o->BeginObject 00233 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00234 00235 // Fields 00236 if (getMinimum()) 00237 o->writeElement(Tags::tag_minimum, true); 00238 else 00239 o->writeElement(Tags::tag_maximum, true); 00240 o->writeElement(tag_modelfile, getModelFile()); 00241 o->writeElement(tag_datafile, getDataFile()); 00242 o->writeElement(tag_solutionfile, getSolutionFile()); 00243 for (list<string>::const_iterator i = objectives.begin(); 00244 i != objectives.end(); ++i) 00245 o->writeElement(tag_objective, *i); 00246 Solver::writeElement(o, tag, NOHEADER); 00247 } 00248 00249 00250 void LPSolver::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00251 { 00252 if (pAttr.isA(Tags::tag_minimum)) 00253 setMinimum(pElement.getBool()); 00254 else if (pAttr.isA(Tags::tag_maximum)) 00255 setMinimum(!pElement.getBool()); 00256 else if (pAttr.isA(tag_datafile)) 00257 setDataFile(pElement.getString()); 00258 else if (pAttr.isA(tag_modelfile)) 00259 setModelFile(pElement.getString()); 00260 else if (pAttr.isA(tag_solutionfile)) 00261 setSolutionFile(pElement.getString()); 00262 else if (pAttr.isA(tag_objective)) 00263 addObjective(pElement.getString()); 00264 else 00265 // The standard fields of a solver... 00266 Solver::endElement(pIn, pAttr, pElement); 00267 } 00268 00269 00270 PyObject* LPSolver::getattro(const Attribute& attr) 00271 { 00272 if (attr.isA(Tags::tag_minimum)) 00273 return PythonObject(getMinimum()); 00274 else if (attr.isA(Tags::tag_maximum)) 00275 return PythonObject(!(getMinimum())); 00276 else if (attr.isA(tag_datafile)) 00277 return PythonObject(getDataFile()); 00278 else if (attr.isA(tag_modelfile)) 00279 return PythonObject(getModelFile()); 00280 else if (attr.isA(tag_solutionfile)) 00281 return PythonObject(getSolutionFile()); 00282 else if (attr.isA(tag_objective)) 00283 { 00284 // The list of objectives is returned as a list of strings 00285 PyObject* result = PyList_New(getObjectives().size()); 00286 int count = 0; 00287 for (list<string>::const_iterator i = getObjectives().begin(); 00288 i != getObjectives().end(); ++i) 00289 PyList_SetItem(result, count++, PythonObject(*i)); 00290 return result; 00291 } 00292 return Solver::getattro(attr); 00293 } 00294 00295 00296 int LPSolver::setattro(const Attribute& attr, const PythonObject& field) 00297 { 00298 if (attr.isA(Tags::tag_minimum)) 00299 setMinimum(field.getBool()); 00300 else if (attr.isA(Tags::tag_maximum)) 00301 setMinimum(!field.getBool()); 00302 else if (attr.isA(tag_datafile)) 00303 setDataFile(field.getString()); 00304 else if (attr.isA(tag_modelfile)) 00305 setModelFile(field.getString()); 00306 else if (attr.isA(tag_solutionfile)) 00307 setSolutionFile(field.getString()); 00308 else if (attr.isA(tag_objective)) 00309 { 00310 // The objective argument is a list of strings 00311 PyObject* seq = PySequence_Fast(static_cast<PyObject*>(field), "expected a list"); 00312 if (!PyList_Check(seq)) 00313 { 00314 PyErr_SetString(PythonDataException, "expected a list"); 00315 return -1; // Error 00316 } 00317 int len = PySequence_Size(static_cast<PyObject*>(field)); 00318 PythonObject item; 00319 for (int i = 0; i < len; i++) 00320 { 00321 item = PyList_GET_ITEM(seq, i); 00322 addObjective(item.getString()); 00323 } 00324 } 00325 else 00326 return Solver::setattro(attr, field); 00327 return 0; // OK 00328 } 00329 00330 } // End namespace
Documentation generated for frePPLe by
