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: 1505 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2011 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 (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, &parameters);
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(&parameters);
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

Documentation generated for frePPLe by  doxygen