solverplan.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/solver/solverplan.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 #define FREPPLE_CORE
00029 #include "frepple/solver.h"
00030 namespace frepple
00031 {
00032 
00033 DECLARE_EXPORT const MetaClass* SolverMRP::metadata;
00034 
00035 
00036 void LibrarySolver::initialize()
00037 {
00038   // Initialize only once
00039   static bool init = false;
00040   if (init)
00041   {
00042     logger << "Warning: Calling frepple::LibrarySolver::initialize() more "
00043     << "than once." << endl;
00044     return;
00045   }
00046   init = true;
00047 
00048   // Register all classes.
00049   if (SolverMRP::initialize())
00050     throw RuntimeException("Error registering solver_mrp Python type");
00051 }
00052 
00053 
00054 int SolverMRP::initialize()
00055 {
00056   // Initialize the metadata
00057   metadata = new MetaClass
00058     ("solver","solver_mrp",Object::createString<SolverMRP>,true);
00059 
00060   // Initialize the Python class
00061   FreppleClass<SolverMRP,Solver>::getType().addMethod("solve", solve, METH_VARARGS, "run the solver");
00062   FreppleClass<SolverMRP,Solver>::getType().addMethod("commit", commit, METH_NOARGS, "commit the plan changes");
00063   FreppleClass<SolverMRP,Solver>::getType().addMethod("rollback", rollback, METH_NOARGS, "rollback the plan changes");
00064   return FreppleClass<SolverMRP,Solver>::initialize();
00065 }
00066 
00067 
00068 DECLARE_EXPORT bool SolverMRP::demand_comparison(const Demand* l1, const Demand* l2)
00069 {
00070   if (l1->getPriority() != l2->getPriority())
00071     return l1->getPriority() < l2->getPriority();
00072   else if (l1->getDue() != l2->getDue())
00073     return l1->getDue() < l2->getDue();
00074   else
00075     return l1->getQuantity() < l2->getQuantity();
00076 }
00077 
00078 
00079 DECLARE_EXPORT void SolverMRP::SolverMRPdata::commit()
00080 {
00081   // Check
00082   if (!demands || !getSolver())
00083     throw LogicException("Missing demands or solver.");
00084 
00085   // Message
00086   SolverMRP* Solver = getSolver();
00087   if (Solver->getLogLevel()>0)
00088     logger << "Start solving cluster " << cluster << " at " << Date::now() << endl;
00089 
00090   // Solve the planning problem
00091   try
00092   {
00093     // Sort the demands of this problem.
00094     // We use a stable sort to get reproducible results between platforms
00095     // and STL implementations.
00096     stable_sort(demands->begin(), demands->end(), demand_comparison);
00097 
00098     // Loop through the list of all demands in this planning problem
00099     constrainedPlanning = (Solver->getPlanType() == 1);
00100     for (deque<Demand*>::const_iterator i = demands->begin();
00101         i != demands->end(); ++i)
00102     {
00103       CommandManager::Bookmark* topcommand = setBookmark();
00104       try
00105       {
00106         // Delete previous constraints
00107         (*i)->getConstraints().clear();
00108 
00109         // Create a state stack
00110         State* mystate = state;
00111         push();
00112 
00113         // Plan the demand
00114         planningDemand = *i;
00115         logConstraints = (Solver->getPlanType() == 1);
00116         try {(*i)->solve(*Solver,this);}
00117         catch (...)
00118         {
00119           while (state > mystate) pop();
00120           throw;
00121         }
00122         while (state > mystate) pop();
00123       }
00124       catch (...)
00125       {
00126         // Error message
00127         logger << "Error: Caught an exception while solving demand '"
00128           << (*i)->getName() << "':" << endl;
00129         try {throw;}
00130         catch (bad_exception&) {logger << "  bad exception" << endl;}
00131         catch (exception& e) {logger << "  " << e.what() << endl;}
00132         catch (...) {logger << "  Unknown type" << endl;}
00133 
00134         // Cleaning up
00135         rollback(topcommand);
00136       }
00137     }
00138 
00139     // Clean the list of demands of this cluster
00140     demands->clear();
00141   }
00142   catch (...)
00143   {
00144     // We come in this exception handling code only if there is a problem with
00145     // with this cluster that goes beyond problems with single orders.
00146     // If the problem is with single orders, the exception handling code above
00147     // will do a proper rollback.
00148 
00149     // Error message
00150     logger << "Error: Caught an exception while solving cluster "
00151     << cluster << ":" << endl;
00152     try {throw;}
00153     catch (bad_exception&){logger << "  bad exception" << endl;}
00154     catch (exception& e) {logger << "  " << e.what() << endl;}
00155     catch (...) {logger << "  Unknown type" << endl;}
00156 
00157     // Clean up the operationplans of this cluster
00158     for (Operation::iterator f=Operation::begin(); f!=Operation::end(); ++f)
00159       if (f->getCluster() == cluster)
00160         f->deleteOperationPlans();
00161 
00162     // Clean the list of demands of this cluster
00163     demands->clear();
00164   }
00165 
00166   // Message
00167   if (Solver->getLogLevel()>0)
00168     logger << "End solving cluster " << cluster << " at " << Date::now() << endl;
00169 }
00170 
00171 
00172 DECLARE_EXPORT void SolverMRP::solve(void *v)
00173 {
00174   // Categorize all demands in their cluster
00175   for (Demand::iterator i = Demand::begin(); i != Demand::end(); ++i)
00176     demands_per_cluster[i->getCluster()].push_back(&*i);
00177 
00178   // Delete of operationplans of the affected clusters
00179   // This deletion is not multi-threaded... But on the other hand we need to
00180   // loop through the operations only once (rather than as many times as there
00181   // are clusters)
00182   // A multi-threaded alternative would be to hash the operations here, and
00183   // then delete in each thread.
00184   if (getLogLevel()>0) logger << "Deleting previous plan" << endl;
00185   for (Operation::iterator e=Operation::begin(); e!=Operation::end(); ++e)
00186     // The next if-condition is actually redundant if we plan everything
00187     if (demands_per_cluster.find(e->getCluster())!=demands_per_cluster.end())
00188       e->deleteOperationPlans();
00189 
00190   // Count how many clusters we have to plan
00191   int cl = demands_per_cluster.size();
00192   if (cl<1) return;
00193 
00194   // Solve in parallel threads.
00195   // When not solving in silent and autocommit mode, we only use a single
00196   // solver thread.
00197   // Otherwise we use as many worker threads as processor cores.
00198   ThreadGroup threads;
00199   if (getLogLevel()>0 || !getAutocommit())
00200     threads.setMaxParallel(1);
00201 
00202   // Register all clusters to be solved
00203   for (classified_demand::iterator j = demands_per_cluster.begin();
00204       j != demands_per_cluster.end(); ++j)
00205     threads.add(SolverMRPdata::runme, new SolverMRPdata(this, j->first, &(j->second)));
00206 
00207   // Run the planning command threads and wait for them to exit
00208   threads.execute();
00209 
00210   // @todo Check the resource setups that were broken - needs to be removed
00211   for (Resource::iterator gres = Resource::begin(); gres != Resource::end(); ++gres)
00212     if (gres->getSetupMatrix()) gres->updateSetups();
00213 }
00214 
00215 
00216 DECLARE_EXPORT void SolverMRP::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00217 {
00218   // Writing a reference
00219   if (m == REFERENCE)
00220   {
00221     o->writeElement
00222     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00223     return;
00224   }
00225 
00226   // Write the complete object
00227   if (m != NOHEADER) o->BeginObject
00228     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00229 
00230   // Write the fields
00231   if (constrts != 15) o->writeElement(Tags::tag_constraints, constrts);
00232   if (plantype != 1) o->writeElement(Tags::tag_plantype, plantype);
00233   if (iteration_threshold != 1.0)
00234     o->writeElement(Tags::tag_iterationthreshold, iteration_threshold);
00235   if (iteration_accuracy != 0.01)
00236     o->writeElement(Tags::tag_iterationaccuracy, iteration_accuracy);
00237   if (!autocommit) o->writeElement(Tags::tag_autocommit, autocommit);
00238   if (userexit_flow)
00239     o->writeElement(Tags::tag_userexit_flow, static_cast<string>(userexit_flow));
00240   if (userexit_demand)
00241     o->writeElement(Tags::tag_userexit_demand, static_cast<string>(userexit_demand));
00242   if (userexit_buffer)
00243     o->writeElement(Tags::tag_userexit_buffer, static_cast<string>(userexit_buffer));
00244   if (userexit_resource)
00245     o->writeElement(Tags::tag_userexit_resource, static_cast<string>(userexit_resource));
00246   if (userexit_operation)
00247     o->writeElement(Tags::tag_userexit_operation, static_cast<string>(userexit_operation));
00248 
00249   // Write the parent class
00250   Solver::writeElement(o, tag, NOHEADER);
00251 }
00252 
00253 
00254 DECLARE_EXPORT void SolverMRP::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00255 {
00256   if (pAttr.isA(Tags::tag_constraints))
00257     setConstraints(pElement.getInt());
00258   else if (pAttr.isA(Tags::tag_iterationthreshold))
00259     setIterationThreshold(pElement.getDouble());
00260   else if (pAttr.isA(Tags::tag_iterationaccuracy))
00261     setIterationAccuracy(pElement.getDouble());
00262   else if (pAttr.isA(Tags::tag_autocommit))
00263     setAutocommit(pElement.getBool());
00264   else if (pAttr.isA(Tags::tag_userexit_flow))
00265     setUserExitFlow(pElement.getString());
00266   else if (pAttr.isA(Tags::tag_userexit_demand))
00267     setUserExitDemand(pElement.getString());
00268   else if (pAttr.isA(Tags::tag_userexit_buffer))
00269     setUserExitBuffer(pElement.getString());
00270   else if (pAttr.isA(Tags::tag_userexit_resource))
00271     setUserExitResource(pElement.getString());
00272   else if (pAttr.isA(Tags::tag_userexit_operation))
00273     setUserExitOperation(pElement.getString());
00274   else if (pAttr.isA(Tags::tag_plantype))
00275     setPlanType(pElement.getInt());
00276   else
00277     Solver::endElement(pIn, pAttr, pElement);
00278 }
00279 
00280 
00281 DECLARE_EXPORT PyObject* SolverMRP::getattro(const Attribute& attr)
00282 {
00283   if (attr.isA(Tags::tag_constraints))
00284     return PythonObject(getConstraints());
00285   if (attr.isA(Tags::tag_iterationthreshold))
00286     return PythonObject(getIterationThreshold());
00287   if (attr.isA(Tags::tag_iterationaccuracy))
00288     return PythonObject(getIterationAccuracy());
00289   if (attr.isA(Tags::tag_autocommit))
00290     return PythonObject(getAutocommit());
00291   if (attr.isA(Tags::tag_userexit_flow))
00292     return getUserExitFlow();
00293   if (attr.isA(Tags::tag_userexit_demand))
00294     return getUserExitDemand();
00295   if (attr.isA(Tags::tag_userexit_buffer))
00296     return getUserExitBuffer();
00297   if (attr.isA(Tags::tag_userexit_resource))
00298     return getUserExitResource();
00299   if (attr.isA(Tags::tag_userexit_operation))
00300     return getUserExitOperation();
00301   if (attr.isA(Tags::tag_plantype))
00302     return PythonObject(getPlanType());
00303   return Solver::getattro(attr);
00304 }
00305 
00306 
00307 DECLARE_EXPORT int SolverMRP::setattro(const Attribute& attr, const PythonObject& field)
00308 {
00309   if (attr.isA(Tags::tag_constraints))
00310     setConstraints(field.getInt());
00311   else if (attr.isA(Tags::tag_iterationthreshold))
00312     setIterationThreshold(field.getDouble());
00313   else if (attr.isA(Tags::tag_iterationaccuracy))
00314     setIterationAccuracy(field.getDouble());
00315   else if (attr.isA(Tags::tag_autocommit))
00316     setAutocommit(field.getBool());
00317   else if (attr.isA(Tags::tag_userexit_flow))
00318     setUserExitFlow(field);
00319   else if (attr.isA(Tags::tag_userexit_demand))
00320     setUserExitDemand(field);
00321   else if (attr.isA(Tags::tag_userexit_buffer))
00322     setUserExitBuffer(field);
00323   else if (attr.isA(Tags::tag_userexit_resource))
00324     setUserExitResource(field);
00325   else if (attr.isA(Tags::tag_userexit_operation))
00326     setUserExitOperation(field);
00327   else if (attr.isA(Tags::tag_plantype))
00328     setPlanType(field.getInt());
00329   else
00330     return Solver::setattro(attr, field);
00331   return 0;
00332 }
00333 
00334 
00335 DECLARE_EXPORT PyObject* SolverMRP::solve(PyObject *self, PyObject *args)
00336 {
00337   // Parse the argument
00338   PyObject *dem = NULL;
00339   if (args && !PyArg_ParseTuple(args, "|O:solve", &dem)) return NULL;
00340   if (dem && !PyObject_TypeCheck(dem, Demand::metadata->pythonClass))
00341     throw DataException("solver argument must be a demand");
00342 
00343   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00344   try
00345   {
00346     SolverMRP* sol = static_cast<SolverMRP*>(self);
00347     if (!dem)
00348     {
00349       // Complete replan
00350       sol->setAutocommit(true);
00351       sol->solve();
00352     }
00353     else
00354     {
00355       // Incrementally plan a single demand
00356       sol->setAutocommit(false);
00357       sol->commands.sol = sol;
00358       static_cast<Demand*>(dem)->solve(*sol, &(sol->commands));
00359     }
00360   }
00361   catch(...)
00362   {
00363     Py_BLOCK_THREADS;
00364     PythonType::evalException();
00365     return NULL;
00366   }
00367   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00368   return Py_BuildValue("");
00369 }
00370 
00371 
00372 DECLARE_EXPORT PyObject* SolverMRP::commit(PyObject *self, PyObject *args)
00373 {
00374   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00375   try
00376   {
00377     SolverMRP * me = static_cast<SolverMRP*>(self);
00378     me->scanExcess(&(me->commands));
00379     me->commands.CommandManager::commit();
00380   }
00381   catch(...)
00382   {
00383     Py_BLOCK_THREADS;
00384     PythonType::evalException();
00385     return NULL;
00386   }
00387   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00388   return Py_BuildValue("");
00389 }
00390 
00391 
00392 DECLARE_EXPORT PyObject* SolverMRP::rollback(PyObject *self, PyObject *args)
00393 {
00394   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00395   try
00396   {
00397     static_cast<SolverMRP*>(self)->commands.rollback();
00398   }
00399   catch(...)
00400   {
00401     Py_BLOCK_THREADS;
00402     PythonType::evalException();
00403     return NULL;
00404   }
00405   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00406   return Py_BuildValue("");
00407 }
00408 
00409 } // end namespace

Documentation generated for frePPLe by  doxygen