solverplan.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/solver/solverplan.cpp $ 00003 version : $LastChangedRevision: 1656 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 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 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 (const bad_exception&) {logger << " bad exception" << endl;} 00131 catch (const 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 (const bad_exception&) {logger << " bad exception" << endl;} 00154 catch (const 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