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: 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 
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     Command* topcommand = data->getLastCommand();
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->undo(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 }

Documentation generated for frePPLe by  doxygen