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