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