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

Documentation generated for frePPLe by  doxygen