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
