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, &parameters);
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(&parameters);
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  doxygen