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

Documentation generated for frePPLe by  doxygen