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: 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 #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("undo", undo, METH_NOARGS, "undo 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::execute()
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       Command* topcommand = getLastCommand();
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         undo(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   // Create the command list to control the execution
00195   CommandList threads;
00196 
00197   // Solve in parallel threads.
00198   // When not solving in silent and autocommit mode, we only use a single
00199   // solver thread.
00200   if (getLogLevel()>0 || !getAutocommit())
00201     threads.setMaxParallel(1);
00202   else
00203     threads.setMaxParallel( cl > getMaxParallel() ? getMaxParallel() : cl);
00204 
00205   // Make sure a problem in a single cluster doesn't spoil it all
00206   threads.setAbortOnError(false);
00207   for (classified_demand::iterator j = demands_per_cluster.begin();
00208       j != demands_per_cluster.end(); ++j)
00209     threads.add(new SolverMRPdata(this, j->first, &(j->second)));
00210 
00211   // Run the planning command threads and wait for them to exit
00212   threads.execute();
00213 
00214   // @todo Check the resource setups that were broken - needs to be removed
00215   for (Resource::iterator gres = Resource::begin(); gres != Resource::end(); ++gres)
00216   {
00217     if (gres->getSetupMatrix()) gres->updateSetups();
00218   }
00219 }
00220 
00221 
00222 DECLARE_EXPORT void SolverMRP::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00223 {
00224   // Writing a reference
00225   if (m == REFERENCE)
00226   {
00227     o->writeElement
00228     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00229     return;
00230   }
00231 
00232   // Write the complete object
00233   if (m != NOHEADER) o->BeginObject
00234     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00235 
00236   // Write the fields
00237   if (constrts != 15) o->writeElement(Tags::tag_constraints, constrts);
00238   if (plantype != 1) o->writeElement(Tags::tag_plantype, plantype);
00239   if (maxparallel) o->writeElement(Tags::tag_maxparallel, maxparallel);
00240   if (!autocommit) o->writeElement(Tags::tag_autocommit, autocommit);
00241   if (userexit_flow) 
00242     o->writeElement(Tags::tag_userexit_flow, static_cast<string>(userexit_flow));
00243   if (userexit_demand) 
00244     o->writeElement(Tags::tag_userexit_demand, static_cast<string>(userexit_demand));
00245   if (userexit_buffer) 
00246     o->writeElement(Tags::tag_userexit_buffer, static_cast<string>(userexit_buffer));
00247   if (userexit_resource) 
00248     o->writeElement(Tags::tag_userexit_resource, static_cast<string>(userexit_resource));
00249   if (userexit_operation) 
00250     o->writeElement(Tags::tag_userexit_operation, static_cast<string>(userexit_operation));
00251 
00252   // Write the parent class
00253   Solver::writeElement(o, tag, NOHEADER);
00254 }
00255 
00256 
00257 DECLARE_EXPORT void SolverMRP::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00258 {
00259   if (pAttr.isA(Tags::tag_constraints))
00260     setConstraints(pElement.getInt());
00261   else if (pAttr.isA(Tags::tag_maxparallel))
00262     setMaxParallel(pElement.getInt());
00263   else if (pAttr.isA(Tags::tag_autocommit))
00264     setAutocommit(pElement.getBool());
00265   else if (pAttr.isA(Tags::tag_userexit_flow))
00266     setUserExitFlow(pElement.getString());
00267   else if (pAttr.isA(Tags::tag_userexit_demand))
00268     setUserExitDemand(pElement.getString());
00269   else if (pAttr.isA(Tags::tag_userexit_buffer))
00270     setUserExitBuffer(pElement.getString());
00271   else if (pAttr.isA(Tags::tag_userexit_resource))
00272     setUserExitResource(pElement.getString());
00273   else if (pAttr.isA(Tags::tag_userexit_operation))
00274     setUserExitOperation(pElement.getString());
00275   else if (pAttr.isA(Tags::tag_plantype))
00276     setPlanType(pElement.getInt());
00277   else
00278     Solver::endElement(pIn, pAttr, pElement);
00279 }
00280 
00281 
00282 DECLARE_EXPORT PyObject* SolverMRP::getattro(const Attribute& attr)
00283 {
00284   if (attr.isA(Tags::tag_constraints))
00285     return PythonObject(getConstraints());
00286   if (attr.isA(Tags::tag_maxparallel))
00287     return PythonObject(getMaxParallel());
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_maxparallel))
00311     setMaxParallel(field.getInt());
00312   else if (attr.isA(Tags::tag_autocommit))
00313     setAutocommit(field.getBool());
00314   else if (attr.isA(Tags::tag_userexit_flow))
00315     setUserExitFlow(field);
00316   else if (attr.isA(Tags::tag_userexit_demand))
00317     setUserExitDemand(field);
00318   else if (attr.isA(Tags::tag_userexit_buffer))
00319     setUserExitBuffer(field);
00320   else if (attr.isA(Tags::tag_userexit_resource))
00321     setUserExitResource(field);
00322   else if (attr.isA(Tags::tag_userexit_operation))
00323     setUserExitOperation(field);
00324   else if (attr.isA(Tags::tag_plantype))
00325     setPlanType(field.getInt());
00326   else
00327     return Solver::setattro(attr, field);
00328   return 0;
00329 }
00330 
00331 
00332 DECLARE_EXPORT PyObject* SolverMRP::solve(PyObject *self, PyObject *args)
00333 {
00334   // Parse the argument
00335   PyObject *dem = NULL;
00336   if (args && !PyArg_ParseTuple(args, "|O:solve", &dem)) return NULL;
00337   if (dem && !PyObject_TypeCheck(dem, Demand::metadata->pythonClass))
00338     throw DataException("solver argument must be a demand");
00339 
00340   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00341   try
00342   {
00343     SolverMRP* sol = static_cast<SolverMRP*>(self);
00344     if (!dem)
00345     {
00346       // Complete replan
00347       sol->setAutocommit(true);
00348       sol->solve();
00349     }
00350     else
00351     {
00352       // Incrementally plan a single demand
00353       sol->setAutocommit(false);
00354       sol->commands.sol = sol;
00355       static_cast<Demand*>(dem)->solve(*sol, &(sol->commands));
00356     }
00357   }
00358   catch(...)
00359   {
00360     Py_BLOCK_THREADS;
00361     PythonType::evalException();
00362     return NULL;
00363   }
00364   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00365   return Py_BuildValue("");
00366 }
00367 
00368 
00369 DECLARE_EXPORT PyObject* SolverMRP::commit(PyObject *self, PyObject *args)
00370 {
00371   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00372   try
00373   {
00374     SolverMRP * me = static_cast<SolverMRP*>(self);
00375     me->scanExcess(me->commands.getFirstCommand());    
00376     me->commands.CommandList::execute();
00377   }
00378   catch(...)
00379   {
00380     Py_BLOCK_THREADS;
00381     PythonType::evalException();
00382     return NULL;
00383   }
00384   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00385   return Py_BuildValue("");
00386 }
00387 
00388 
00389 DECLARE_EXPORT PyObject* SolverMRP::undo(PyObject *self, PyObject *args)
00390 {
00391   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00392   try
00393   {
00394     static_cast<SolverMRP*>(self)->commands.undo();
00395   }
00396   catch(...)
00397   {
00398     Py_BLOCK_THREADS;
00399     PythonType::evalException();
00400     return NULL;
00401   }
00402   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00403   return Py_BuildValue("");
00404 }
00405 
00406 } // end namespace

Documentation generated for frePPLe by  doxygen