solverload.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/solver/solverload.cpp $
00003   version : $LastChangedRevision: 1185 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-02-21 01:00:40 +0100 (Sun, 21 Feb 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 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   // 4) Loop through all alternates or till we find a non-zero 
00094   // reply (priority search)
00095   Date min_next_date(Date::infiniteFuture);
00096   LoadPlan *lplan = data->state->q_loadplan;
00097   double bestAlternateValue = DBL_MAX;
00098   double bestAlternateQuantity = DBL_MIN;
00099   const Load* bestAlternateSelection = NULL;
00100   double beforeCost = data->state->a_cost;
00101   double beforePenalty = data->state->a_penalty;
00102   OperationPlanState originalOpplan(lplan->getOperationPlan());
00103   for (list<const Load*>::const_iterator i = thealternates.begin();
00104     i != thealternates.end();)
00105   {
00106     const Load *curload = *i;
00107     data->state->q_loadplan = lplan; // because q_loadplan can change!
00108 
00109     // 4a) Switch to this load
00110     if (lplan->getLoad() != curload) lplan->setLoad(curload);
00111     lplan->getOperationPlan()->restore(originalOpplan);
00112     data->state->q_qty = lplan->getQuantity();
00113     data->state->q_date = lplan->getDate();
00114 
00115     // 4b) Ask the resource
00116     Command* topcommand = data->getLastCommand();
00117     if (search == PRIORITY) 
00118       curload->getResource()->solve(*this,data);
00119     else
00120     {
00121       data->getSolver()->setLogLevel(0);
00122       try {curload->getResource()->solve(*this,data);}
00123       catch (...)
00124       {
00125         data->getSolver()->setLogLevel(loglevel);
00126         // Restore the planning mode
00127         data->constrainedPlanning = originalPlanningMode;
00128         throw;
00129       }
00130       data->getSolver()->setLogLevel(loglevel);
00131     }
00132 
00133     // 4c) Evaluate the result
00134     if (data->state->a_qty > ROUNDING_ERROR 
00135       && lplan->getOperationPlan()->getQuantity() > 0) 
00136     {
00137       if (search == PRIORITY)
00138       {
00139         // Priority search: accept any non-zero reply
00140         // Restore the planning mode
00141         data->constrainedPlanning = originalPlanningMode;
00142         return;
00143       }
00144       else
00145       {
00146         // Other search modes: evaluate all
00147         double deltaCost = data->state->a_cost - beforeCost;
00148         double deltaPenalty = data->state->a_penalty - beforePenalty;
00149         // Message
00150         if (loglevel>1 && search != PRIORITY)
00151           logger << indent(l->getOperation()->getLevel()) << "   Operation '" 
00152             << l->getOperation()->getName() << "' evaluates alternate '"
00153             << curload->getResource() << "': cost " << deltaCost
00154             << ", penalty " << deltaPenalty << endl;
00155         if (deltaCost < ROUNDING_ERROR && deltaPenalty < ROUNDING_ERROR)
00156         {
00157           // Zero cost and zero penalty on this alternate. It won't get any better
00158           // than this, so we accept this alternate.
00159           if (loglevel)
00160             logger << indent(l->getOperation()->getLevel()) << "   Operation '" 
00161               << l->getOperation()->getName() << "' chooses alternate '"
00162               << curload->getResource() << "' " << search << endl;
00163           // Restore the planning mode
00164           data->constrainedPlanning = originalPlanningMode;
00165           return;
00166         }
00167         data->state->a_cost = beforeCost;
00168         data->state->a_penalty = beforePenalty;
00169         double val;
00170         switch (search)
00171         {
00172           case MINCOST:
00173             val = deltaCost / lplan->getOperationPlan()->getQuantity();
00174             break;
00175           case MINPENALTY:
00176             val = deltaPenalty / lplan->getOperationPlan()->getQuantity();
00177             break;
00178           case MINCOSTPENALTY:
00179             val = (deltaCost + deltaPenalty) / lplan->getOperationPlan()->getQuantity();
00180             break;
00181           default:
00182             LogicException("Unsupported search mode for alternate load");
00183         }    
00184         if (val + ROUNDING_ERROR < bestAlternateValue 
00185           || (fabs(val - bestAlternateValue) < ROUNDING_ERROR 
00186               && lplan->getOperationPlan()->getQuantity() > bestAlternateQuantity))
00187         {
00188           // Found a better alternate
00189           bestAlternateValue = val;
00190           bestAlternateSelection = curload;
00191           bestAlternateQuantity = lplan->getOperationPlan()->getQuantity();
00192         }
00193       }
00194     }
00195     else if (loglevel>1 && search != PRIORITY)
00196       logger << indent(l->getOperation()->getLevel()) << "   Operation '" 
00197         << l->getOperation()->getName() << "' evaluates alternate '"
00198         << curload->getResource() << "': not available before " 
00199         << data->state->a_date << endl;
00200 
00201     // 4d) Undo the plan on the alternate
00202     data->undo(topcommand);
00203 
00204     // 4e) Prepare for the next alternate
00205     if (data->state->a_date < min_next_date)
00206       min_next_date = data->state->a_date;
00207     if (++i != thealternates.end() && loglevel>1 && search == PRIORITY)
00208       logger << indent(curload->getOperation()->getLevel()) << "   Alternate load switches from '"
00209             << curload->getResource()->getName() << "' to '"
00210             << (*i)->getResource()->getName() << "'" << endl;
00211   }
00212 
00213   // 5) Unconstrained plan: plan on the first alternate
00214   if (!originalPlanningMode && !(search != PRIORITY && bestAlternateSelection))
00215   {
00216     // Switch to unconstrained planning 
00217     data->constrainedPlanning = false;
00218     bestAlternateSelection = *(thealternates.begin());
00219   }
00220 
00221   // 6) Finally replan on the best alternate
00222   if (!originalPlanningMode || (search != PRIORITY && bestAlternateSelection))
00223   {
00224     // Message
00225     if (loglevel)
00226       logger << indent(l->getOperation()->getLevel()) << "   Operation '" 
00227         << l->getOperation()->getName() << "' chooses alternate '"
00228         << bestAlternateSelection->getResource() << "' " << search << endl;
00229 
00230     // Switch back
00231     data->state->q_loadplan = lplan; // because q_loadplan can change!
00232     data->state->a_cost = beforeCost;
00233     data->state->a_penalty = beforePenalty;
00234     if (lplan->getLoad() != bestAlternateSelection)
00235       lplan->setLoad(bestAlternateSelection);
00236     lplan->getOperationPlan()->restore(originalOpplan);
00237     data->state->q_qty = lplan->getQuantity();
00238     data->state->q_date = lplan->getDate();
00239     //xxxif (originalPlanningMode)
00240       bestAlternateSelection->getResource()->solve(*this,data);
00241 
00242     // Restore the planning mode
00243     data->constrainedPlanning = originalPlanningMode;
00244     return;
00245   }
00246 
00247   // 7) No alternate gave a good result
00248   data->state->a_date = min_next_date;
00249   data->state->a_qty = 0;
00250   // Restore the planning mode
00251   data->constrainedPlanning = originalPlanningMode;
00252   if (lplan->getOperationPlan()->getQuantity() != 0.0)
00253     // In case of a zero reply, we resize the operationplan to 0 right away.
00254     // This is required to make sure that the buffer inventory profile also
00255     // respects this answer.
00256     lplan->getOperationPlan()->setQuantity(0.0);
00257   if (loglevel>1)
00258     logger << indent(lplan->getOperationPlan()->getOperation()->getLevel()) <<
00259       "   Alternate load doesn't find supply on any alternate : "
00260       << data->state->a_qty << "  " << data->state->a_date << endl;
00261 }
00262 
00263 
00264 }

Generated on 21 Mar 2010 for frePPLe by  doxygen 1.6.1