solverload.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/solver/solverload.cpp $ 00003 version : $LastChangedRevision: 1505 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 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 00031 namespace frepple 00032 { 00033 00034 00035 bool sortLoad(const Load* lhs, const Load* rhs) 00036 { 00037 return lhs->getPriority() < rhs->getPriority(); 00038 } 00039 00040 00041 void SolverMRP::solve(const Load* l, void* v) 00042 { 00043 // Note: This method is only called for decrease loadplans and for the leading 00044 // load of an alternate group. See SolverMRP::checkOperation 00045 SolverMRPdata* data = static_cast<SolverMRPdata*>(v); 00046 unsigned int loglevel = data->getSolver()->getLogLevel(); 00047 00048 if (data->state->q_qty >= 0.0) 00049 { 00050 // The loadplan is an increase in size, and the resource solver only needs 00051 // the decreases. 00052 // Or, it's a zero quantity loadplan. E.g. because it is not effective. 00053 data->state->a_qty = data->state->q_qty; 00054 data->state->a_date = data->state->q_date; 00055 return; 00056 } 00057 00058 if (!l->hasAlternates() && !l->getAlternate()) 00059 { 00060 // CASE I: It is not an alternate load. 00061 // Delegate the answer to the resource 00062 l->getResource()->solve(*this,v); 00063 if (data->state->a_qty == 0.0 00064 && data->state->q_operationplan->getQuantity() != 0.0) 00065 // In case of a zero reply, we resize the operationplan to 0 right away. 00066 // This is required to make sure that the buffer inventory profile also 00067 // respects this answer. 00068 data->state->q_operationplan->setQuantity(0.0); 00069 return; 00070 } 00071 00072 // CASE II: It is an alternate load. 00073 // We ask each alternate load in order of priority till we find a flow 00074 // that has a non-zero reply. 00075 00076 // 1) collect a list of alternates 00077 list<const Load*> thealternates; 00078 const Load *x = l->hasAlternates() ? l : l->getAlternate(); 00079 SearchMode search = l->getSearch(); 00080 for (Operation::loadlist::const_iterator i = l->getOperation()->getLoads().begin(); 00081 i != l->getOperation()->getLoads().end(); ++i) 00082 if ((i->getAlternate() == x || &*i == x) 00083 && i->getEffective().within(data->state->q_loadplan->getDate())) 00084 thealternates.push_back(&*i); 00085 00086 // 2) Sort the list 00087 thealternates.sort(sortLoad); // @todo cpu-intensive - better is to maintain the list in the correct order 00088 00089 // 3) Control the planning mode 00090 bool originalPlanningMode = data->constrainedPlanning; 00091 data->constrainedPlanning = true; 00092 00093 // Don't keep track of the constraints right now 00094 bool originalLogConstraints = data->logConstraints; 00095 data->logConstraints = false; 00096 00097 // 4) Loop through all alternates or till we find a non-zero 00098 // reply (priority search) 00099 Date min_next_date(Date::infiniteFuture); 00100 LoadPlan *lplan = data->state->q_loadplan; 00101 double bestAlternateValue = DBL_MAX; 00102 double bestAlternateQuantity = DBL_MIN; 00103 const Load* bestAlternateSelection = NULL; 00104 double beforeCost = data->state->a_cost; 00105 double beforePenalty = data->state->a_penalty; 00106 OperationPlanState originalOpplan(lplan->getOperationPlan()); 00107 double originalLoadplanQuantity = data->state->q_loadplan->getQuantity(); 00108 for (list<const Load*>::const_iterator i = thealternates.begin(); 00109 i != thealternates.end();) 00110 { 00111 const Load *curload = *i; 00112 data->state->q_loadplan = lplan; // because q_loadplan can change! 00113 00114 // 4a) Switch to this load 00115 if (lplan->getLoad() != curload) lplan->setLoad(curload); 00116 lplan->getOperationPlan()->restore(originalOpplan); 00117 data->state->q_qty = lplan->getQuantity(); 00118 data->state->q_date = lplan->getDate(); 00119 00120 // 4b) Ask the resource 00121 CommandManager::Bookmark* topcommand = data->setBookmark(); 00122 if (search == PRIORITY) 00123 curload->getResource()->solve(*this,data); 00124 else 00125 { 00126 data->getSolver()->setLogLevel(0); 00127 try {curload->getResource()->solve(*this,data);} 00128 catch (...) 00129 { 00130 data->getSolver()->setLogLevel(loglevel); 00131 // Restore the planning mode 00132 data->constrainedPlanning = originalPlanningMode; 00133 data->logConstraints = originalLogConstraints; 00134 throw; 00135 } 00136 data->getSolver()->setLogLevel(loglevel); 00137 } 00138 00139 // 4c) Evaluate the result 00140 if (data->state->a_qty > ROUNDING_ERROR 00141 && lplan->getOperationPlan()->getQuantity() > 0) 00142 { 00143 if (search == PRIORITY) 00144 { 00145 // Priority search: accept any non-zero reply 00146 // Restore the planning mode 00147 data->constrainedPlanning = originalPlanningMode; 00148 data->logConstraints = originalLogConstraints; 00149 return; 00150 } 00151 else 00152 { 00153 // Other search modes: evaluate all 00154 double deltaCost = data->state->a_cost - beforeCost; 00155 double deltaPenalty = data->state->a_penalty - beforePenalty; 00156 // Message 00157 if (loglevel>1 && search != PRIORITY) 00158 logger << indent(l->getOperation()->getLevel()) << " Operation '" 00159 << l->getOperation()->getName() << "' evaluates alternate '" 00160 << curload->getResource() << "': cost " << deltaCost 00161 << ", penalty " << deltaPenalty << endl; 00162 if (deltaCost < ROUNDING_ERROR && deltaPenalty < ROUNDING_ERROR) 00163 { 00164 // Zero cost and zero penalty on this alternate. It won't get any better 00165 // than this, so we accept this alternate. 00166 if (loglevel) 00167 logger << indent(l->getOperation()->getLevel()) << " Operation '" 00168 << l->getOperation()->getName() << "' chooses alternate '" 00169 << curload->getResource() << "' " << search << endl; 00170 // Restore the planning mode 00171 data->constrainedPlanning = originalPlanningMode; 00172 data->logConstraints = originalLogConstraints; 00173 return; 00174 } 00175 data->state->a_cost = beforeCost; 00176 data->state->a_penalty = beforePenalty; 00177 double val = 0.0; 00178 switch (search) 00179 { 00180 case MINCOST: 00181 val = deltaCost / lplan->getOperationPlan()->getQuantity(); 00182 break; 00183 case MINPENALTY: 00184 val = deltaPenalty / lplan->getOperationPlan()->getQuantity(); 00185 break; 00186 case MINCOSTPENALTY: 00187 val = (deltaCost + deltaPenalty) / lplan->getOperationPlan()->getQuantity(); 00188 break; 00189 default: 00190 LogicException("Unsupported search mode for alternate load"); 00191 } 00192 if (val + ROUNDING_ERROR < bestAlternateValue 00193 || (fabs(val - bestAlternateValue) < ROUNDING_ERROR 00194 && lplan->getOperationPlan()->getQuantity() > bestAlternateQuantity)) 00195 { 00196 // Found a better alternate 00197 bestAlternateValue = val; 00198 bestAlternateSelection = curload; 00199 bestAlternateQuantity = lplan->getOperationPlan()->getQuantity(); 00200 } 00201 } 00202 } 00203 else if (loglevel>1 && search != PRIORITY) 00204 logger << indent(l->getOperation()->getLevel()) << " Operation '" 00205 << l->getOperation()->getName() << "' evaluates alternate '" 00206 << curload->getResource() << "': not available before " 00207 << data->state->a_date << endl; 00208 00209 // 4d) Undo the plan on the alternate 00210 data->rollback(topcommand); 00211 00212 // 4e) Prepare for the next alternate 00213 if (data->state->a_date < min_next_date) 00214 min_next_date = data->state->a_date; 00215 if (++i != thealternates.end() && loglevel>1 && search == PRIORITY) 00216 logger << indent(curload->getOperation()->getLevel()) << " Alternate load switches from '" 00217 << curload->getResource()->getName() << "' to '" 00218 << (*i)->getResource()->getName() << "'" << endl; 00219 } 00220 00221 // 5) Unconstrained plan: plan on the first alternate 00222 if (!originalPlanningMode && !(search != PRIORITY && bestAlternateSelection)) 00223 { 00224 // Switch to unconstrained planning 00225 data->constrainedPlanning = false; 00226 bestAlternateSelection = *(thealternates.begin()); 00227 } 00228 00229 // 6) Finally replan on the best alternate 00230 if (!originalPlanningMode || (search != PRIORITY && bestAlternateSelection)) 00231 { 00232 // Message 00233 if (loglevel) 00234 logger << indent(l->getOperation()->getLevel()) << " Operation '" 00235 << l->getOperation()->getName() << "' chooses alternate '" 00236 << bestAlternateSelection->getResource() << "' " << search << endl; 00237 00238 // Switch back 00239 data->state->q_loadplan = lplan; // because q_loadplan can change! 00240 data->state->a_cost = beforeCost; 00241 data->state->a_penalty = beforePenalty; 00242 if (lplan->getLoad() != bestAlternateSelection) 00243 lplan->setLoad(bestAlternateSelection); 00244 lplan->getOperationPlan()->restore(originalOpplan); 00245 data->state->q_qty = lplan->getQuantity(); 00246 data->state->q_date = lplan->getDate(); 00247 bestAlternateSelection->getResource()->solve(*this,data); 00248 00249 // Restore the planning mode 00250 data->constrainedPlanning = originalPlanningMode; 00251 data->logConstraints = originalLogConstraints; 00252 return; 00253 } 00254 00255 // 7) No alternate gave a good result 00256 data->state->a_date = min_next_date; 00257 data->state->a_qty = 0; 00258 00259 // Restore the planning mode 00260 data->constrainedPlanning = originalPlanningMode; 00261 00262 // Maintain the constraint list 00263 if (originalLogConstraints) 00264 { 00265 const Load *primary = *(thealternates.begin()); 00266 data->planningDemand->getConstraints().push( 00267 ProblemCapacityOverload::metadata, 00268 primary->getResource(), originalOpplan.start, originalOpplan.end, 00269 -originalLoadplanQuantity); 00270 } 00271 data->logConstraints = originalLogConstraints; 00272 00273 if (lplan->getOperationPlan()->getQuantity() != 0.0) 00274 // In case of a zero reply, we resize the operationplan to 0 right away. 00275 // This is required to make sure that the buffer inventory profile also 00276 // respects this answer. 00277 lplan->getOperationPlan()->setQuantity(0.0); 00278 if (loglevel>1) 00279 logger << indent(lplan->getOperationPlan()->getOperation()->getLevel()) << 00280 " Alternate load doesn't find supply on any alternate : " 00281 << data->state->a_qty << " " << data->state->a_date << endl; 00282 } 00283 00284 00285 }