00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/solver/solverdemand.cpp $ 00003 version : $LastChangedRevision: 1001 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2009-07-30 18:21:45 +0200 (Thu, 30 Jul 2009) $ 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 GNUt 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA * 00024 * * 00025 ***************************************************************************/ 00026 00027 #define FREPPLE_CORE 00028 #include "frepple/solver.h" 00029 00030 00031 namespace frepple 00032 { 00033 00034 00035 DECLARE_EXPORT void SolverMRP::solve(const Demand* l, void* v) 00036 { 00037 SolverMRPdata* data = static_cast<SolverMRPdata*>(v); 00038 unsigned int loglevel = data->getSolver()->getLogLevel(); 00039 00040 // Note: This solver method does not push/pop states on the stack. 00041 // We continue to work on the top element of the stack. 00042 00043 // Message 00044 if (data->getSolver()->getLogLevel()>0) 00045 logger << "Planning demand '" << l->getName() << "' (" << l->getPriority() 00046 << ", " << l->getDue() << ", " << l->getQuantity() << ")" << endl; 00047 00048 // Unattach previous delivery operationplans. 00049 // Locked operationplans will NOT be deleted, and a part of the demand can 00050 // still remain planned. 00051 const_cast<Demand*>(l)->deleteOperationPlans(); 00052 00053 // Determine the quantity to be planned and the date for the planning loop 00054 double plan_qty = l->getQuantity() - l->getPlannedQuantity(); 00055 Date plan_date = l->getDue(); 00056 00057 // Nothing to be planned any more (e.g. all deliveries are locked...) 00058 if (plan_qty < ROUNDING_ERROR) 00059 { 00060 if (loglevel>0) logger << " Nothing to be planned." << endl; 00061 return; 00062 } 00063 00064 // Temporary values to store the 'best-reply' so far 00065 double best_q_qty = 0.0, best_a_qty = 0.0; 00066 Date best_q_date; 00067 00068 // Which operation to use? 00069 Operation* deliveryoper = l->getDeliveryOperation(); 00070 if (!deliveryoper) 00071 throw DataException("Demand '" + l->getName() + "' can't be planned"); 00072 00073 // Planning loop 00074 do 00075 { 00076 // Message 00077 if (loglevel>0) 00078 logger << "Demand '" << l << "' asks: " 00079 << plan_qty << " " << plan_date << endl; 00080 00081 // Check whether the action list is empty 00082 assert( data->empty() ); 00083 00084 // Plan the demand by asking the delivery operation to plan 00085 data->state->curBuffer = NULL; 00086 data->state->q_qty = plan_qty; 00087 data->state->q_date = plan_date; 00088 data->state->curDemand = const_cast<Demand*>(l); 00089 deliveryoper->solve(*this,v); 00090 00091 // Message 00092 if (loglevel>0) 00093 logger << "Demand '" << l << "' gets answer: " 00094 << data->state->a_qty << " " << data->state->a_date << " " 00095 << data->state->a_cost << " " << data->state->a_penalty << endl; 00096 00097 // Update the date to plan in the next loop 00098 Date copy_plan_date = plan_date; 00099 00100 // Compare the planned quantity with the minimum allowed shipment quantity 00101 // We don't accept the answer in case: 00102 // 1) Nothing is planned 00103 // 2) The planned quantity is less than the minimum shipment quantity 00104 // 3) The remaining quantity after accepting this answer is less than 00105 // the minimum quantity. 00106 if (data->state->a_qty < ROUNDING_ERROR 00107 || data->state->a_qty + ROUNDING_ERROR < l->getMinShipment() 00108 || (plan_qty - data->state->a_qty < l->getMinShipment() 00109 && plan_qty - data->state->a_qty > ROUNDING_ERROR)) 00110 { 00111 if (plan_qty - data->state->a_qty < l->getMinShipment() 00112 00113 && data->state->a_qty + ROUNDING_ERROR >= l->getMinShipment() 00114 && data->state->a_qty > best_a_qty ) 00115 { 00116 // The remaining quantity after accepting this answer is less than 00117 // the minimum quantity. Therefore, we delay accepting it now, but 00118 // still keep track of this best offer. 00119 best_a_qty = data->state->a_qty; 00120 best_q_qty = plan_qty; 00121 best_q_date = plan_date; 00122 } 00123 00124 // Delete operationplans - Undo all changes 00125 data->undo(); 00126 00127 // Set the ask date for the next pass through the loop 00128 if (data->state->a_date <= copy_plan_date) 00129 { 00130 // Oops, we didn't get a proper answer we can use for the next loop. 00131 // Print a warning and simply try one day later. 00132 if (loglevel>0) 00133 logger << "Warning: Demand '" << l << "': Lazy retry" << endl; 00134 plan_date = copy_plan_date + data->sol->getLazyDelay(); 00135 } 00136 else 00137 // Use the next-date answer from the solver 00138 plan_date = data->state->a_date; 00139 } 00140 else 00141 { 00142 // Accepting this answer 00143 if (data->state->a_qty + ROUNDING_ERROR < plan_qty) 00144 { 00145 // The demand was only partially planned. We need to do a new 00146 // 'coordinated' planning run. 00147 00148 // Delete operationplans created in the 'testing round' 00149 data->undo(); 00150 00151 // Create the correct operationplans 00152 if (loglevel>=2) logger << "Demand '" << l << "' plans coordination." << endl; 00153 data->getSolver()->setLogLevel(0); 00154 double tmpresult = data->state->a_qty; 00155 try 00156 { 00157 for(double remainder = data->state->a_qty; 00158 remainder > ROUNDING_ERROR; remainder -= data->state->a_qty) 00159 { 00160 data->state->q_qty = remainder; 00161 data->state->q_date = copy_plan_date; 00162 data->state->curDemand = const_cast<Demand*>(l); 00163 data->state->curBuffer = NULL; 00164 deliveryoper->solve(*this,v); 00165 if (data->state->a_qty < ROUNDING_ERROR) 00166 { 00167 logger << "Warning: Demand '" << l << "': Failing coordination" << endl; 00168 break; 00169 } 00170 } 00171 } 00172 catch (...) 00173 { 00174 data->getSolver()->setLogLevel(loglevel); 00175 throw; 00176 } 00177 data->getSolver()->setLogLevel(loglevel); 00178 data->state->a_qty = tmpresult; 00179 } 00180 00181 // Register the new operationplans. We need to make sure that the 00182 // correct execute method is called! 00183 data->CommandList::execute(); 00184 00185 // Update the quantity to plan in the next loop 00186 plan_qty -= data->state->a_qty; 00187 best_a_qty = 0.0; // Reset 'best-answer' remember 00188 } 00189 00190 } 00191 // Repeat while there is still a quantity left to plan and we aren't 00192 // exceeding the maximum delivery delay. 00193 while (plan_qty > ROUNDING_ERROR 00194 && plan_date < l->getDue() + l->getMaxLateness()); 00195 00196 // Accept the best possible answer. 00197 // We may have skipped it in the previous loop, awaiting a still better answer 00198 if (best_a_qty > 0.0) 00199 { 00200 if (loglevel>=2) logger << "Demand '" << l << "' accepts a best answer." << endl; 00201 data->getSolver()->setLogLevel(0); 00202 try 00203 { 00204 for(double remainder = best_q_qty; 00205 remainder > ROUNDING_ERROR; remainder -= data->state->a_qty) 00206 { 00207 data->state->q_qty = remainder; 00208 data->state->q_date = best_q_date; 00209 data->state->curDemand = const_cast<Demand*>(l); 00210 data->state->curBuffer = NULL; 00211 deliveryoper->solve(*this,v); 00212 if (data->state->a_qty < ROUNDING_ERROR) 00213 { 00214 logger << "Warning: Demand '" << l << "': Failing accepting best answer" << endl; 00215 break; 00216 } 00217 } 00218 data->CommandList::execute(); 00219 } 00220 catch (...) 00221 { 00222 data->getSolver()->setLogLevel(loglevel); 00223 throw; 00224 } 00225 data->getSolver()->setLogLevel(loglevel); 00226 } 00227 } 00228 00229 }