forecast.h

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/modules/forecast/forecast.h $
00003   version : $LastChangedRevision: 1300 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-07-06 08:08:34 +0200 (Tue, 06 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 /** @file forecast.h
00029   * @brief Header file for the module forecast.
00030   *
00031   * @namespace module_forecast
00032   * @brief Module for representing forecast.
00033   *
00034   * The forecast module provides the following functionality:
00035   *
00036   *  - A <b>new demand type</b> to model forecasts.<br>
00037   *    A forecast demand is bucketized. A demand is automatically
00038   *    created for each time bucket.<br>
00039   *    A calendar is used to define the time buckets to be used.
00040   *
00041   *  - Functionality for <b>distributing / profiling</b> forecast numbers
00042   *    into time buckets used for planning.<br>
00043   *    This functionality is typically used to translate between the time
00044   *    granularity of the sales department (which creates a sales forecast
00045   *    per e.g. calendar month) and the manufacturing department (which
00046   *    creates manufacturing and procurement plans in weekly or daily buckets
00047   *    ).<br>
00048   *    Another usage is to model a delivery date profile of the customers.
00049   *    Each bucket has a weight that is used to model situations where the
00050   *    demand is not evenly spread across buckets: e.g. when more orders are
00051   *    expected due on a monday than on a friday, or when a peak of orders is
00052   *    expected for delivery near the end of a month.
00053   *
00054   *  - A solver for <b>netting orders from the forecast</b>.<br>
00055   *    As customer orders are being received they need to be deducted from
00056   *    the forecast to avoid double-counting demand.<br>
00057   *    The netting solver will for each order search for a matching forecast
00058   *    and reduce the remaining net quantity of the forecast.
00059   *
00060   *  - A forecasting algorithm to <b>extrapolate historical demand data to
00061   *    the future</b>.<br>
00062   *    The following classical forecasting methods are implemented:
00063   *       - <b>Single exponential smoothing</b>, which is applicable for
00064   *         constant demands .
00065   *       - <b>Double exponential smoothing</b>, which is applicable for 
00066   *         trended demands.
00067   *       - <b>Holt-Winter's exponential smoothing with mutiplicative 
00068   *         seasonality</b>, which is applicable for seasonal demands. 
00069   *       - <b>Croston's method</b>, which is applicable for intermittent  
00070   *         demand (i.e. demand patterns with a lot of zero demand buckets).
00071   *       - <b>Moving average</b>, which is applicable when there is little 
00072   *         demand history to rely on.
00073   *    The forecast method giving the smallest symmetric mean percentage error (aka
00074   *    "smape"-error) will be automatically picked to produce the forecast.<br>
00075   *    The algorithm will automatically tune the parameters for the
00076   *    forecasting methods (i.e. alfa for the single exponential smoothing,
00077   *    or alfa and gamma for the double exponential smoothing) to their
00078   *    optimal value. The user can specify minimum and maximum boundaries
00079   *    for the parameters and the maximum allowed number of iterations
00080   *    for the algorithm.
00081   *
00082   * The XML schema extension enabled by this module is (see mod_forecast.xsd):
00083   * <PRE>
00084   * <!-- Define the forecast type -->
00085   * <xsd:complexType name="demand_forecast">
00086   *   <xsd:complexContent>
00087   *     <xsd:extension base="demand">
00088   *       <xsd:choice minOccurs="0" maxOccurs="unbounded">
00089   *         <xsd:element name="calendar" type="calendar" />
00090   *         <xsd:element name="discrete" type="xsd:boolean" />
00091   *         <xsd:element name="buckets">
00092   *           <xsd:complexType>
00093   *             <xsd:choice minOccurs="0" maxOccurs="unbounded">
00094   *               <xsd:element name="bucket">
00095   *                 <xsd:complexType>
00096   *                   <xsd:all>
00097   *                     <xsd:element name="total" type="positiveDouble"
00098   *                       minOccurs="0" />
00099   *                     <xsd:element name="net" type="positiveDouble"
00100   *                       minOccurs="0" />
00101   *                     <xsd:element name="consumed" type="positiveDouble"
00102   *                       minOccurs="0" />
00103   *                     <xsd:element name="start" type="xsd:dateTime"
00104   *                       minOccurs="0"/>
00105   *                     <xsd:element name="end" type="xsd:dateTime"
00106   *                       minOccurs="0"/>
00107   *                   </xsd:all>
00108   *                   <xsd:attribute name="total" type="positiveDouble" />
00109   *                   <xsd:attribute name="net" type="positiveDouble" />
00110   *                   <xsd:attribute name="consumed" type="positiveDouble" />
00111   *                   <xsd:attribute name="start" type="xsd:dateTime" />
00112   *                   <xsd:attribute name="end" type="xsd:dateTime" />
00113   *                 </xsd:complexType>
00114   *               </xsd:element>
00115   *             </xsd:choice>
00116   *           </xsd:complexType>
00117   *         </xsd:element>
00118   *       </xsd:choice>
00119   *       <xsd:attribute name="discrete" type="xsd:boolean" />
00120   *     </xsd:extension>
00121   *   </xsd:complexContent>
00122   * </xsd:complexType>
00123   *
00124   * <!-- Define the netting solver. -->
00125   * <xsd:complexType name="solver_forecast">
00126   * <xsd:complexContent>
00127   *   <xsd:extension base="solver">
00128   *     <xsd:choice minOccurs="0" maxOccurs="unbounded">
00129   *       <xsd:element name="loglevel" type="loglevel" />
00130   *     </xsd:choice>
00131   *   </xsd:extension>
00132   * </xsd:complexContent>
00133   * </xsd:complexType>
00134   * </PRE>
00135   *
00136   * The module support the following configuration parameters:
00137   *
00138   *   - DueAtEndOfBucket:<br>
00139   *     By default forecast demand is due at the start of the forecasting 
00140   *     bucket. Since the actual customer demand will come in any time in the
00141   *     bucket this is a conservative setting.<br>
00142   *     By setting this flag to true, the forecast will be due at the end of 
00143   *     the forecast bucket. 
00144   *
00145   *   - Net_CustomerThenItemHierarchy:<br>
00146   *     As part of the forecast netting a demand is assiociated with a certain
00147   *     forecast. When no matching forecast is found for the customer and item
00148   *     of the demand, frePPLe looks for forecast at higher level customers
00149   *     and items.<br>
00150   *     This flag allows us to control whether we first search the customer
00151   *     hierarchy and then the item hierarchy, or the other way around.<br>
00152   *     The default value is true, ie search higher customer levels before
00153   *     searching higher levels of the item.
00154   *
00155   *   - Net_MatchUsingDeliveryOperation:<br>
00156   *     Specifies whether or not a demand and a forecast require to have the
00157   *     same delivery operation to be a match.<br>
00158   *     The default value is true.
00159   *
00160   *   - Net_NetEarly:<br>
00161   *     Defines how much time before the due date of an order we are allowed
00162   *     to search for a forecast bucket to net from.<br>
00163   *     The default value is 0, meaning that we can net only from the bucket
00164   *     where the demand is due.
00165   *
00166   *   - Net_NetLate:<br>
00167   *     Defines how much time after the due date of an order we are allowed
00168   *     to search for a forecast bucket to net from.<br>
00169   *     The default value is 0, meaning that we can net only from the bucket
00170   *     where the demand is due.
00171   *
00172   *   - Forecast_Iterations:<br>
00173   *     Specifies the maximum number of iterations allowed for a forecast
00174   *     method to tune its parameters.<br>
00175   *     Only positive values are allowed and the default value is 10.<br>
00176   *     Set the parameter to 1 to disable the tuning and generate a forecast
00177   *     based on the user-supplied parameters.
00178   *
00179   *   - Forecast_smapeAlfa:<br>
00180   *     Specifies how the sMAPE forecast error is weighted for different time
00181   *     buckets. The sMAPE value in the most recent bucket is 1.0, and the
00182   *     weight decreases exponentially for earlier buckets.<br>
00183   *     Acceptable values are in the interval 0.5 and 1.0, and the default
00184   *     is 0.95.
00185   *
00186   *   - Forecast_Skip:<br>
00187   *     Specifies the number of time series values used to initialize the
00188   *     forecasting method. The forecast error in these bucket isn't counted.
00189   *
00190   *   - Forecast_MovingAverage.buckets<br>
00191   *     This parameter controls the number of buckets to be averaged by the
00192   *     moving average forecast method.
00193   *
00194   *   - Forecast_SingleExponential.initialAlfa,<br>
00195   *     Forecast_SingleExponential.minAlfa,<br>
00196   *     Forecast_SingleExponential.maxAlfa:<br>
00197   *     Specifies the initial value and the allowed range of the smoothing
00198   *     parameter in the single exponential forecasting method.<br>
00199   *     The allowed range is between 0 and 1. Values lower than about 0.05
00200   *     are not advisible.
00201   *
00202   *   - Forecast_DoubleExponential.initialAlfa,<br>
00203   *     Forecast_DoubleExponential.minAlfa,<br>
00204   *     Forecast_DoubleExponential.maxAlfa:<br>
00205   *     Specifies the initial value and the allowed range of the smoothing
00206   *     parameter in the double exponential forecasting method.<br>
00207   *     The allowed range is between 0 and 1. Values lower than about 0.05
00208   *     are not advisible.
00209   *
00210   *   - Forecast_DoubleExponential.initialGamma,<br>
00211   *     Forecast_DoubleExponential.minGamma,<br>
00212   *     Forecast_DoubleExponential.maxGamma:<br>
00213   *     Specifies the initial value and the allowed range of the trend
00214   *     smoothing parameter in the double exponential forecasting method.<br>
00215   *     The allowed range is between 0 and 1.
00216   *
00217   *   - Forecast_DoubleExponential_dampenTrend:<br>
00218   *     Specifies how the trend is dampened for future buckets.<br>
00219   *     The allowed range is between 0 and 1, and the default value is 0.8. 
00220   *
00221   *   - Forecast_Seasonal_initialAlfa,<br> 
00222   *     Forecast_Seasonal_minAlfa,<br> 
00223   *     Forecast_Seasonal_maxAlfa:<br>
00224   *     Specifies the initial value and the allowed range of the smoothing 
00225   *     parameter in the seasonal forecasting method.<br>
00226   *     The allowed range is between 0 and 1. Values lower than about 0.05 are
00227   *     not advisible.
00228   *
00229   *   - Forecast_Seasonal_initialBeta,<br> 
00230   *     Forecast_Seasonal_minBeta,<br> 
00231   *     Forecast_Seasonal_maxBeta:<br>
00232   *     Specifies the initial value and the allowed range of the trend 
00233   *     smoothing parameter in the seasonal forecasting method.<br>
00234   *     The allowed range is between 0 and 1.
00235   *
00236   *   - Forecast_Seasonal_initialGamma,<br> 
00237   *     Forecast_Seasonal_minGamma,<br> 
00238   *     Forecast_Seasonal_maxGamma:<br>
00239   *     Specifies the initial value and the allowed range of the seasonal 
00240   *     smoothing parameter in the seasonal forecasting method.<br>
00241   *     The allowed range is between 0 and 1.
00242   *
00243   *   - Forecast_Seasonal_minPeriod,<br> 
00244   *     Forecast_Seasonal_maxPeriod:<br>
00245   *     Specifies the periodicity of the seasonal cycles to check for.<br>
00246   *     The interval of cycles we try to detect should be broad enough. For 
00247   *     instance, if we expect to find a yearly cycle use a minimum period of 
00248   *     10 and maximum period of 14. 
00249   *
00250   *   - Forecast_Seasonal_dampenTrend<br>
00251   *     Specifies how the trend is dampened for future buckets.<br>
00252   *     The allowed range is between 0 and 1, and the default value is 0.8. 
00253   *
00254   *   - Forecast_Croston_initialAlfa,<br> 
00255   *     Forecast_Croston_minAlfa,<br> 
00256   *     Forecast_Croston_maxAlfa:<br>
00257   *     Specifies the initial value and the allowed range of the smoothing 
00258   *     parameter in the Croston forecasting method.<br>
00259   *     The allowed range is between 0 and 1. Values lower than about 0.05 
00260   *     are not advisible. 
00261   *
00262   *   - Forecast_Croston_minIntermittence:<br>
00263   *     Minimum intermittence (defined as the percentage of zero demand 
00264   *     buckets) before the Croston method is applied. When the intermittence
00265   *     exceeds this value, only Croston and moving average are considered 
00266   *     suitable forecast methods.<br>
00267   *     The default value is 0.33. 
00268   */
00269 
00270 #ifndef FORECAST_H
00271 #define FORECAST_H
00272 
00273 #include "frepple.h"
00274 using namespace frepple;
00275 
00276 namespace module_forecast
00277 {
00278 
00279 
00280 /** Initialization routine for the library. */
00281 MODULE_EXPORT const char* initialize(const CommandLoadLibrary::ParameterList&);
00282 
00283 /** @brief This class represents a bucketized demand signal.
00284   *
00285   * The forecast object defines the item and priority of the demands.<br>
00286   * A calendar (of type void, double, integer or boolean) divides the time horizon
00287   * in individual time buckets. The calendar value is used to assign priorities
00288   * to the time buckets.<br>
00289   * The class basically works as an interface for a hierarchy of demands, where the
00290   * lower level demands represent forecasting time buckets.
00291   */
00292 class Forecast : public Demand
00293 {
00294     friend class ForecastSolver;
00295   public:
00296 
00297     static const Keyword tag_total;
00298     static const Keyword tag_net;
00299     static const Keyword tag_consumed;
00300 
00301     /** @brief Abstract base class for all forecasting methods. */
00302     class ForecastMethod
00303     {
00304       public:
00305         /** Forecast evaluation. */
00306         virtual double generateForecast
00307           (Forecast*, const double[], unsigned int, const double[], bool) = 0;
00308 
00309         /** This method is called when this forecast method has generated the
00310           * lowest forecast error and now needs to set the forecast values.
00311           */
00312         virtual void applyForecast
00313           (Forecast*, const Date[], unsigned int, bool) = 0;
00314 
00315         /** The name of the method. */
00316         virtual string getName() = 0;
00317     };
00318 
00319 
00320     /** @brief A class to calculate a forecast based on a moving average. */
00321     class MovingAverage : public ForecastMethod
00322     {
00323       private:
00324         /** Number of smoothed buckets. */
00325         static unsigned int defaultbuckets;
00326 
00327         /** Number of buckets to average. */
00328         unsigned int buckets;
00329 
00330         /** Calculated average.<br>
00331           * Used to carry results between the evaluation and applying of the forecast.
00332           */
00333         double avg;
00334 
00335       public:
00336         /** Constructor. */
00337         MovingAverage(int i = defaultbuckets) : buckets(i), avg(0)
00338         {
00339           if (i < 1)
00340             throw DataException("Moving average needs to smooth over at least 1 bucket");
00341         }
00342 
00343         /** Forecast evaluation. */
00344         double generateForecast(Forecast* fcst, const double history[],
00345           unsigned int count, const double weight[], bool debug);
00346 
00347         /** Forecast value updating. */
00348         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00349 
00350         /** Update the initial value for the alfa parameter. */
00351         static void setDefaultBuckets(int x)
00352         {
00353           if (x < 1)
00354             throw DataException("Parameter MovingAverage.buckets needs to smooth over at least 1 bucket");
00355          defaultbuckets = x;
00356         }
00357 
00358         string getName() {return "moving average";}
00359    };
00360 
00361     /** @brief A class to perform single exponential smoothing on a time series. */
00362     class SingleExponential : public ForecastMethod
00363     {
00364       private:
00365         /** Smoothing constant. */
00366         double alfa;
00367 
00368         /** Default initial alfa value.<br>
00369           * The default value is 0.2.
00370           */
00371         static double initial_alfa;
00372 
00373         /** Lower limit on the alfa parameter.<br>
00374           * The default value is 0.
00375           **/
00376         static double min_alfa;
00377 
00378         /** Upper limit on the alfa parameter.<br>
00379           * The default value is 1.
00380           **/
00381         static double max_alfa;
00382 
00383         /** Smoothed result.<br>
00384           * Used to carry results between the evaluation and applying of the forecast.
00385           */
00386         double f_i;
00387 
00388       public:
00389         /** Constructor. */
00390         SingleExponential(double a = initial_alfa) : alfa(a), f_i(0)
00391         {
00392           if (alfa < min_alfa) alfa = min_alfa;
00393           if (alfa > max_alfa) alfa = max_alfa;
00394         }
00395 
00396         /** Forecast evaluation. */
00397         double generateForecast(Forecast* fcst, const double history[],
00398           unsigned int count, const double weight[], bool debug);
00399 
00400         /** Forecast value updating. */
00401         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00402 
00403         /** Update the initial value for the alfa parameter. */
00404         static void setInitialAlfa(double x)
00405         {
00406           if (x<0 || x>1.0) throw DataException(
00407             "Parameter SingleExponential.initialAlfa must be between 0 and 1");
00408          initial_alfa = x;
00409         }
00410 
00411         /** Update the minimum value for the alfa parameter. */
00412         static void setMinAlfa(double x)
00413         {
00414           if (x<0 || x>1.0) throw DataException(
00415             "Parameter SingleExponential.minAlfa must be between 0 and 1");
00416           min_alfa = x;
00417         }
00418 
00419         /** Update the maximum value for the alfa parameter. */
00420         static void setMaxAlfa(double x)
00421         {
00422           if (x<0 || x>1.0) throw DataException(
00423             "Parameter SingleExponential.maxAlfa must be between 0 and 1");
00424           max_alfa = x;
00425         }
00426 
00427         string getName() {return "single exponential";}
00428     };
00429 
00430     /** @brief A class to perform double exponential smoothing on a time
00431       * series.
00432       */
00433     class DoubleExponential : public ForecastMethod
00434     {
00435       private:
00436         /** Smoothing constant. */
00437         double alfa;
00438 
00439         /** Default initial alfa value.<br>
00440           * The default value is 0.2.
00441           */
00442         static double initial_alfa;
00443 
00444         /** Lower limit on the alfa parameter.<br>
00445           * The default value is 0.
00446           **/
00447         static double min_alfa;
00448 
00449         /** Upper limit on the alfa parameter.<br>
00450           * The default value is 1.
00451           **/
00452         static double max_alfa;
00453 
00454         /** Trend smoothing constant. */
00455         double gamma;
00456 
00457         /** Default initial gamma value.<br>
00458           * The default value is 0.05.
00459           */
00460         static double initial_gamma;
00461 
00462         /** Lower limit on the gamma parameter.<br>
00463           * The default value is 0.05.
00464           **/
00465         static double min_gamma;
00466 
00467         /** Upper limit on the gamma parameter.<br>
00468           * The default value is 1.
00469           **/
00470         static double max_gamma;
00471 
00472         /** Smoothed result.<br>
00473           * Used to carry results between the evaluation and applying of the forecast.
00474           */
00475         double trend_i;
00476 
00477         /** Smoothed result.<br>
00478           * Used to carry results between the evaluation and applying of the forecast.
00479           */
00480         double constant_i;
00481         
00482         /* Factor used to smoothen the trend in the future buckets. */
00483         static double dampenTrend;
00484 
00485       public:
00486         /** Constructor. */
00487         DoubleExponential(double a = initial_alfa, double g = initial_gamma)
00488           : alfa(a), gamma(g), trend_i(0), constant_i(0) {}
00489 
00490         /** Forecast evaluation. */
00491         double generateForecast(Forecast* fcst, const double history[],
00492           unsigned int count, const double weight[], bool debug);
00493 
00494         /** Forecast value updating. */
00495         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00496 
00497         /** Update the initial value for the alfa parameter. */
00498         static void setInitialAlfa(double x)
00499         {
00500           if (x<0 || x>1.0) throw DataException(
00501             "Parameter DoubleExponential.initialAlfa must be between 0 and 1");
00502           initial_alfa = x;
00503         }
00504 
00505         /** Update the minimum value for the alfa parameter. */
00506         static void setMinAlfa(double x)
00507         {
00508           if (x<0 || x>1.0) throw DataException(
00509             "Parameter DoubleExponential.minAlfa must be between 0 and 1");
00510           min_alfa = x;
00511         }
00512 
00513         /** Update the maximum value for the alfa parameter. */
00514         static void setMaxAlfa(double x)
00515         {
00516           if (x<0 || x>1.0) throw DataException(
00517             "Parameter DoubleExponential.maxAlfa must be between 0 and 1");
00518           max_alfa = x;
00519         }
00520 
00521         /** Update the initial value for the alfa parameter.<br>
00522           * The default value is 0.05. <br>
00523           * Setting this parameter to too low a value can create false
00524           * positives: the double exponential method is selected for a time
00525           * series without a real trend. A single exponential is better for
00526           * such cases.
00527           */
00528         static void setInitialGamma(double x)
00529         {
00530           if (x<0 || x>1.0) throw DataException(
00531             "Parameter DoubleExponential.initialGamma must be between 0 and 1");
00532           initial_gamma = x;
00533         }
00534 
00535         /** Update the minimum value for the alfa parameter. */
00536         static void setMinGamma(double x)
00537         {
00538           if (x<0 || x>1.0) throw DataException(
00539             "Parameter DoubleExponential.minGamma must be between 0 and 1");
00540           min_gamma = x;
00541         }
00542 
00543         /** Update the maximum value for the alfa parameter. */
00544         static void setMaxGamma(double x)
00545         {
00546           if (x<0 || x>1.0) throw DataException(
00547             "Parameter DoubleExponential.maxGamma must be between 0 and 1");
00548           max_gamma = x;
00549         }
00550 
00551         /** Update the dampening factor for the trend. */
00552         static void setDampenTrend(double x)
00553         {
00554           if (x<0 || x>1.0) throw DataException(
00555             "Parameter DoubleExponential.dampenTrend must be between 0 and 1");
00556           dampenTrend = x;
00557         }
00558 
00559         string getName() {return "double exponential";}
00560     };
00561 
00562     /** @brief A class to perform seasonal forecasting on a time
00563       * series.
00564       */
00565     class Seasonal : public ForecastMethod
00566     {
00567       private:
00568         /** Smoothing constant. */
00569         double alfa;
00570 
00571         /** Trend smoothing constant. */
00572         double beta;
00573 
00574         /** Seasonality smoothing constant. */
00575         double gamma;
00576 
00577         /** Default initial alfa value.<br>
00578           * The default value is 0.2.
00579           */
00580         static double initial_alfa;
00581 
00582         /** Lower limit on the alfa parameter.<br>
00583           * The default value is 0.
00584           **/
00585         static double min_alfa;
00586 
00587         /** Upper limit on the alfa parameter.<br>
00588           * The default value is 1.
00589           **/
00590         static double max_alfa;
00591 
00592         /** Default initial beta value.<br>
00593           * The default value is 0.05.
00594           */
00595         static double initial_beta;
00596 
00597         /** Lower limit on the beta parameter.<br>
00598           * The default value is 0.05.
00599           **/
00600         static double min_beta;
00601 
00602         /** Upper limit on the beta parameter.<br>
00603           * The default value is 1.
00604           **/
00605         static double max_beta;
00606 
00607         /** Default initial gamma value.<br>
00608           * The default value is 0.05.
00609           */
00610         static double initial_gamma;
00611 
00612         /** Lower limit on the gamma parameter.<br>
00613           * The default value is 0.05.
00614           **/
00615         static double min_gamma;
00616 
00617         /** Upper limit on the gamma parameter.<br>
00618           * The default value is 1.
00619           **/
00620         static double max_gamma;
00621 
00622         /** Used to dampen a trend in the future. */
00623         static double dampenTrend;
00624 
00625         /** Minimum cycle to be check for.<br>
00626           * The interval of cycles we try to detect should be broad enough.
00627           * If eg we normally expect a yearly cycle use a minimum cycle of 10.
00628           */
00629         static unsigned int min_period;
00630 
00631         /** Maximum cycle to be check for.<br>
00632           * The interval of cycles we try to detect should be broad enough.
00633           * If eg we normally expect a yearly cycle use a maximum cycle of 14.
00634           */
00635         static unsigned int max_period; 
00636 
00637         /** Period of the cycle. */
00638         unsigned short period;
00639 
00640         /** Smoothed result - constant component.<br>
00641           * Used to carry results between the evaluation and applying of the forecast.
00642           */
00643         double L_i;
00644 
00645         /** Smoothed result - trend component.<br>
00646           * Used to carry results between the evaluation and applying of the forecast.
00647           */
00648         double T_i;
00649 
00650         /** Smoothed result - seasonal component.<br>
00651           * Used to carry results between the evaluation and applying of the forecast.
00652           */
00653         double* S_i;
00654 
00655         /** Remember where in the cycle we are. */
00656         unsigned int cycleindex;
00657 
00658         /** A check for seasonality.<br>
00659           * The cycle period is returned if seasonality is detected. Zero is 
00660           * returned in case no seasonality is present. 
00661           */
00662         void detectCycle(const double[], unsigned int);
00663 
00664         /** Compute the determinant of a 3x3 matrix. */
00665         inline double determinant(const double a, const double b, const double c, 
00666           const double d, const double e, const double f, 
00667           const double g, const double h, const double i) 
00668         { return a * e * i + b * f * g + c * d * h - a * f * h - b * d * i - c * e * g; }
00669 
00670       public:
00671         /** Constructor. */
00672         Seasonal(double a = initial_alfa, double b = initial_beta, double g = initial_gamma)
00673           : alfa(a), beta(b), gamma(g), period(0), L_i(0), T_i(0), S_i(NULL) {}
00674 
00675         /** Destructor. */
00676         ~Seasonal() {if (period) delete S_i;}
00677 
00678         /** Forecast evaluation. */
00679         double generateForecast(Forecast* fcst, const double history[],
00680           unsigned int count, const double weight[], bool debug);
00681 
00682         /** Forecast value updating. */
00683         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00684  
00685         /** Update the minimum period that can be detected. */
00686         static void setMinPeriod(int x)
00687         {
00688           if (x <= 1) throw DataException(
00689             "Parameter Seasonal.minPeriod must be greater than 1");
00690           min_period = x;
00691         }
00692  
00693         /** Update the maximum period that can be detected. */
00694         static void setMaxPeriod(int x)
00695         {
00696           if (x <= 1) throw DataException(
00697             "Parameter Seasonal.maxPeriod must be greater than 1");
00698           max_period = x;
00699         }
00700 
00701         /** Update the initial value for the alfa parameter. */
00702         static void setInitialAlfa(double x)
00703         {
00704           if (x<0 || x>1.0) throw DataException(
00705             "Parameter Seasonal.initialAlfa must be between 0 and 1");
00706           initial_alfa = x;
00707         }
00708 
00709         /** Update the minimum value for the alfa parameter. */
00710         static void setMinAlfa(double x)
00711         {
00712           if (x<0 || x>1.0) throw DataException(
00713             "Parameter Seasonal.minAlfa must be between 0 and 1");
00714           min_alfa = x;
00715         }
00716 
00717         /** Update the maximum value for the alfa parameter. */
00718         static void setMaxAlfa(double x)
00719         {
00720           if (x<0 || x>1.0) throw DataException(
00721             "Parameter Seasonal.maxAlfa must be between 0 and 1");
00722           max_alfa = x;
00723         }
00724 
00725         /** Update the initial value for the beta parameter. */
00726         static void setInitialBeta(double x)
00727         {
00728           if (x<0 || x>1.0) throw DataException(
00729             "Parameter Seasonal.initialBeta must be between 0 and 1");
00730           initial_beta = x;
00731         }
00732 
00733         /** Update the minimum value for the beta parameter. */
00734         static void setMinBeta(double x)
00735         {
00736           if (x<0 || x>1.0) throw DataException(
00737             "Parameter Seasonal.minBeta must be between 0 and 1");
00738           min_beta = x;
00739         }
00740 
00741         /** Update the maximum value for the beta parameter. */
00742         static void setMaxBeta(double x)
00743         {
00744           if (x<0 || x>1.0) throw DataException(
00745             "Parameter Seasonal.maxBeta must be between 0 and 1");
00746           max_beta = x;
00747         }
00748 
00749         /** Update the initial value for the alfa parameter.<br>
00750           * The default value is 0.05. <br>
00751           */
00752         static void setInitialGamma(double x)
00753         {
00754           if (x<0 || x>1.0) throw DataException(
00755             "Parameter Seasonal.initialGamma must be between 0 and 1");
00756           initial_gamma = x;
00757         }
00758 
00759         /** Update the minimum value for the alfa parameter. */
00760         static void setMinGamma(double x)
00761         {
00762           if (x<0 || x>1.0) throw DataException(
00763             "Parameter Seasonal.minGamma must be between 0 and 1");
00764           min_gamma = x;
00765         }
00766 
00767         /** Update the maximum value for the alfa parameter. */
00768         static void setMaxGamma(double x)
00769         {
00770           if (x<0 || x>1.0) throw DataException(
00771             "Parameter Seasonal.maxGamma must be between 0 and 1");
00772           max_gamma = x;
00773         }
00774 
00775         /** Update the dampening factor for the trend. */
00776         static void setDampenTrend(double x)
00777         {
00778           if (x<0 || x>1.0) throw DataException(
00779             "Parameter Seasonal.dampenTrend must be between 0 and 1");
00780           dampenTrend = x;
00781         }
00782 
00783         string getName() {return "seasonal";}
00784     };
00785 
00786     /** @brief A class to calculate a forecast with Croston's method. */
00787     class Croston : public ForecastMethod
00788     {
00789       private:
00790         /** Smoothing constant. */
00791         double alfa;
00792 
00793         /** Default initial alfa value.<br>
00794           * The default value is 0.2.
00795           */
00796         static double initial_alfa;
00797 
00798         /** Lower limit on the alfa parameter.<br>
00799           * The default value is 0.
00800           **/
00801         static double min_alfa;
00802 
00803         /** Upper limit on the alfa parameter.<br>
00804           * The default value is 1.
00805           **/
00806         static double max_alfa;
00807 
00808         /** Minimum intermittence before this method is applicable. */
00809         static double min_intermittence;
00810 
00811         /** Smoothed forecast.<br>
00812           * Used to carry results between the evaluation and applying of the forecast.
00813           */
00814         double f_i;
00815 
00816       public:
00817         /** Constructor. */
00818         Croston(double a = initial_alfa) : alfa(a), f_i(0)
00819         {
00820           if (alfa < min_alfa) alfa = min_alfa;
00821           if (alfa > max_alfa) alfa = max_alfa;
00822         }
00823 
00824         /** Forecast evaluation. */
00825         double generateForecast(Forecast* fcst, const double history[],
00826           unsigned int count, const double weight[], bool debug);
00827 
00828         /** Forecast value updating. */
00829         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00830 
00831         /** Update the initial value for the alfa parameter. */
00832         static void setInitialAlfa(double x)
00833         {
00834           if (x<0 || x>1.0) throw DataException(
00835             "Parameter Croston.initialAlfa must be between 0 and 1");
00836          initial_alfa = x;
00837         }
00838 
00839         /** Update the minimum value for the alfa parameter. */
00840         static void setMinAlfa(double x)
00841         {
00842           if (x<0 || x>1.0) throw DataException(
00843             "Parameter Croston.minAlfa must be between 0 and 1");
00844           min_alfa = x;
00845         }
00846 
00847         /** Update the maximum value for the alfa parameter. */
00848         static void setMaxAlfa(double x)
00849         {
00850           if (x<0 || x>1.0) throw DataException(
00851             "Parameter Croston.maxAlfa must be between 0 and 1");
00852           max_alfa = x;
00853         }
00854 
00855         /** Update the minimum intermittence before applying this method. */
00856         static void setMinIntermittence(double x)
00857         {
00858           if (x<0 || x>1.0) throw DataException(
00859             "Parameter Croston.minIntermittence must be between 0 and 1");
00860           min_intermittence = x;
00861         }
00862 
00863         /** Return the minimum intermittence before applying this method. */
00864         static double getMinIntermittence() { return min_intermittence; }
00865 
00866         string getName() {return "croston";}
00867     };
00868 
00869   public:
00870     /** Constructor. */
00871     explicit Forecast(const string& nm)
00872       : Demand(nm), calptr(NULL), discrete(true) {initType(metadata);}
00873 
00874     /** Destructor. */
00875     ~Forecast();
00876 
00877     /** Updates the quantity of the forecast. This method is empty. */
00878     virtual void setQuantity(double f)
00879       {throw DataException("Can't set quantity of a forecast");}
00880 
00881     /** Update the forecast quantity.<br>
00882       * The forecast quantity will be distributed equally among the buckets
00883       * available between the two dates, taking into account also the bucket
00884       * weights.<br>
00885       * The logic applied is briefly summarized as follows:
00886       *  - If the daterange has its start and end dates equal, we find the
00887       *    matching forecast bucket and update the quantity.
00888       *  - Otherwise the quantity is distributed among all intersecting
00889       *    forecast buckets. This distribution is considering the weigth of
00890       *    the bucket and the time duration of the bucket.<br>
00891       *    The bucket weight is the value specified on the calendar.<br>
00892       *    If a forecast bucket only partially overlaps with the daterange
00893       *    only the overlapping time is used as the duration.
00894       *  - If only buckets with zero weigth are found in the daterange a
00895       *    dataexception is thrown. It indicates a situation where forecast
00896       *    is specified for a date where no values are allowed.
00897       */
00898     virtual void setTotalQuantity(const DateRange& , double);
00899 
00900     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
00901     void endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement);
00902     void beginElement(XMLInput& pIn, const Attribute& pAttr);
00903     static int initialize();
00904 
00905     /** Returns whether fractional forecasts are allowed or not.<br>
00906       * The default is true.
00907       */
00908     bool getDiscrete() const {return discrete;}
00909 
00910     /** Updates forecast discreteness flag. */
00911     void setDiscrete(const bool b);
00912 
00913     /** Update the item to be planned. */
00914     virtual void setItem(Item*);
00915 
00916     /** Update the customer. */
00917     virtual void setCustomer(Customer*);
00918 
00919     /* Update the maximum allowed lateness for planning. */
00920     void setMaxLateness(TimePeriod);
00921 
00922     /* Update the minumum allowed shipment quantity for planning. */
00923     void setMinShipment(double);
00924 
00925     /** Specify a bucket calendar for the forecast. Once forecasted
00926       * quantities have been entered for the forecast, the calendar
00927       * can't be updated any more. */
00928     virtual void setCalendar(Calendar*);
00929 
00930     /** Returns a reference to the calendar used for this forecast. */
00931     Calendar* getCalendar() const {return calptr;}
00932 
00933     /** Generate a forecast value based on historical demand data.<br>
00934       * This method will call the different forecasting methods and select the
00935       * method with the lowest smape-error.<br>
00936       * It then asks the selected forecast method to generate a value for
00937       * each of the time buckets passed.
00938       */
00939     void generateFutureValues
00940       (const double[], unsigned int, const Date[], unsigned int, bool=false);
00941 
00942     /** Updates the due date of the demand. Lower numbers indicate a
00943       * higher priority level. The method also updates the priority
00944       * in all buckets.
00945       */
00946     virtual void setPriority(int);
00947 
00948     /** Updates the operation being used to plan the demands. */
00949     virtual void setOperation(Operation *);
00950 
00951     /** Updates the due date of the demand. */
00952     virtual void setDue(const Date& d)
00953     {throw DataException("Can't set due date of a forecast");}
00954 
00955     virtual const MetaClass& getType() const {return *metadata;}
00956     static const MetaClass *metadata;
00957     virtual size_t getSize() const
00958     {
00959       return sizeof(Forecast) + Demand::extrasize()
00960         + 6 * sizeof(void*); // Approx. size of an entry in forecast dictionary
00961     }
00962 
00963     /** Updates the value of the Customer_Then_Item_Hierarchy module
00964       * parameter. */
00965     static void setCustomerThenItemHierarchy(bool b)
00966       {Customer_Then_Item_Hierarchy = b;}
00967 
00968     /** Returns the value of the Customer_Then_Item_Hierarchy module
00969       * parameter. */
00970     static bool getCustomerThenItemHierarchy()
00971       {return Customer_Then_Item_Hierarchy;}
00972 
00973     /** Updates the value of the Match_Using_Delivery_Operation module
00974       * parameter. */
00975     static void setMatchUsingDeliveryOperation(bool b)
00976       {Match_Using_Delivery_Operation = b;}
00977 
00978     /** Returns the value of the Match_Using_Delivery_Operation module
00979       * parameter. */
00980     static bool getMatchUsingDeliveryOperation()
00981       {return Match_Using_Delivery_Operation;}
00982 
00983     /** Updates the value of the Net_Early module parameter. */
00984     static void setNetEarly(TimePeriod t) {Net_Early = t;}
00985 
00986     /** Returns the value of the Net_Early module parameter. */
00987     static TimePeriod getNetEarly() {return Net_Early;}
00988 
00989     /** Updates the value of the Net_Late module parameter. */
00990     static void setNetLate(TimePeriod t) {Net_Late = t;}
00991 
00992     /** Returns the value of the Net_Late module parameter. */
00993     static TimePeriod getNetLate() {return Net_Late;}
00994 
00995     /** Updates the value of the Forecast.smapeAlfa module parameter. */
00996     static void setForecastSmapeAlfa(double t)
00997     {
00998       if (t<=0.5 || t>1.0) throw DataException(
00999         "Parameter Forecast.smapeAlfa must be between 0.5 and 1.0"
01000         );
01001       Forecast_SmapeAlfa = t;
01002     }
01003 
01004     /** Returns the value of the Forecast_Iterations module parameter. */
01005     static double getForecastSmapeAlfa() {return Forecast_SmapeAlfa;}
01006 
01007     /** Updates the value of the Forecast_Iterations module parameter. */
01008     static void setForecastIterations(unsigned long t)
01009     {
01010       if (t<=0) throw DataException(
01011         "Parameter Forecast.Iterations must be bigger than 0"
01012         );
01013       Forecast_Iterations = t;
01014     }
01015 
01016     /** Returns the value of the Forecast_Iterations module parameter. */
01017     static unsigned long getForecastIterations() {return Forecast_Iterations;}
01018 
01019     /** Updates the value of the Forecast_Skip module parameter. */
01020     static void setForecastSkip(unsigned int t)
01021     {
01022       if (t<0) throw DataException(
01023         "Parameter Forecast.Skip must be bigger than or equal to 0"
01024         );
01025       Forecast_Skip = t;
01026     }
01027 
01028     /** Return the number of timeseries values used to initialize the
01029       * algorithm. The forecast error is not counted for these buckets.
01030       */
01031     static unsigned int getForecastSkip() {return Forecast_Skip;}
01032 
01033     /** A data type to maintain a dictionary of all forecasts. */
01034     typedef multimap < pair<const Item*, const Customer*>, Forecast* > MapOfForecasts;
01035 
01036     /** Callback function, used for prevent a calendar from being deleted when it
01037       * is used for an uninitialized forecast. */
01038     static bool callback(Calendar*, const Signal);
01039 
01040     /** Return a reference to a dictionary with all forecast objects. */
01041     static const MapOfForecasts& getForecasts() {return ForecastDictionary;}
01042 
01043     virtual PyObject* getattro(const Attribute&);
01044     virtual int setattro(const Attribute&, const PythonObject&);
01045     static PyObject* timeseries(PyObject *, PyObject *);
01046 
01047   private:
01048     /** Initializion of a forecast.<br>
01049       * It creates demands for each bucket of the calendar.
01050       */
01051     void instantiate();
01052 
01053     /** A void calendar to define the time buckets. */
01054     Calendar* calptr;
01055 
01056     /** Flags whether fractional forecasts are allowed. */
01057     bool discrete;
01058 
01059     /** A dictionary of all forecasts. */
01060     static MapOfForecasts ForecastDictionary;
01061 
01062     /** Controls how we search the customer and item levels when looking for a
01063       * matching forecast for a demand.
01064       */
01065     static bool Customer_Then_Item_Hierarchy;
01066 
01067     /** Controls whether or not a matching delivery operation is required
01068       * between a matching order and its forecast.
01069       */
01070     static bool Match_Using_Delivery_Operation;
01071 
01072     /** Store the maximum time difference between an order due date and a
01073       * forecast bucket to net from.<br>
01074       * The default value is 0, meaning that only netting from the due
01075       * bucket is allowed.
01076       */
01077     static TimePeriod Net_Late;
01078 
01079     /** Store the maximum time difference between an order due date and a
01080       * forecast bucket to net from.<br>
01081       * The default value is 0, meaning that only netting from the due
01082       * bucket is allowed.
01083       */
01084     static TimePeriod Net_Early;
01085 
01086     /** Specifies the maximum number of iterations allowed for a forecast
01087       * method to tune its parameters.<br>
01088       * Only positive values are allowed and the default value is 10.<br>
01089       * Set the parameter to 1 to disable the tuning and generate a
01090       * forecast based on the user-supplied parameters.
01091       */
01092     static unsigned long Forecast_Iterations;
01093 
01094     /** Specifies how the sMAPE forecast error is weighted for different time
01095       * buckets. The SMAPE value in the most recent bucket is 1.0, and the
01096       * weight decreases exponentially for earlier buckets.<br>
01097       * Acceptable values are in the interval 0.5 and 1.0, and the default
01098       * is 0.95.
01099       */
01100     static double Forecast_SmapeAlfa;
01101 
01102     /** Number of warmup periods.<br>
01103       * These periods are used for the initialization of the algorithm
01104       * and don't count towards measuring the forecast error.<br>
01105       * The default value is 5.
01106       */
01107     static unsigned long Forecast_Skip;
01108 };
01109 
01110 
01111 /** @brief This class represents a forecast value in a time bucket.
01112   *
01113   * A forecast bucket is never manipulated or created directly. Instead,
01114   * the owning forecast manages the buckets.
01115   */
01116 class ForecastBucket : public Demand
01117 {
01118   public:
01119     ForecastBucket(Forecast* f, Date d, Date e, double w, ForecastBucket* p)
01120       : Demand(f->getName() + " - " + string(d)), weight(w), consumed(0.0),
01121         total(0.0), timebucket(d,e), prev(p), next(NULL)
01122     {
01123       if (p) p->next = this;
01124       setOwner(f);
01125       setHidden(true);  // Avoid the subdemands show up in the output
01126       setItem(&*(f->getItem()));
01127       setDue(DueAtEndOfBucket ? e : d);
01128       setPriority(f->getPriority());
01129       setMaxLateness(f->getMaxLateness());
01130       setMinShipment(f->getMinShipment());
01131       setOperation(&*(f->getOperation()));
01132       initType(metadata);
01133     }
01134     virtual const MetaClass& getType() const {return *metadata;}
01135     static const MetaClass *metadata;
01136     virtual size_t getSize() const
01137     {
01138       return sizeof(ForecastBucket) + Demand::extrasize();
01139     }
01140 
01141     /** Returns the relative weight of this forecast bucket when distributing
01142       * forecast over different buckets.
01143       */
01144     double getWeight() const {return weight;}
01145 
01146     /** Returns the total, gross forecast. */
01147     double getTotal() const {return total;}
01148 
01149     /** Returns the consumed forecast. */
01150     double getConsumed() const {return consumed;}
01151 
01152     /** Update the weight of this forecasting bucket. */
01153     void setWeight(double n)
01154     {
01155       if (n<0)
01156         throw DataException("Forecast bucket weight must be greater or equal to 0");
01157       weight = n;
01158     }
01159 
01160     /** Increment the total, gross forecast. */
01161     void incTotal(double n)
01162     {
01163       total += n;
01164       if (total<0) total = 0.0;
01165       setQuantity(total>consumed ? total - consumed : 0.0);
01166     }
01167 
01168     /** Update the total, gross forecast. */
01169     void setTotal(double n)
01170     {
01171       if (n<0)
01172         throw DataException("Gross forecast must be greater or equal to 0");
01173       if (total == n) return;
01174       total = n;
01175       setQuantity(total>consumed ? total - consumed : 0.0);
01176     }
01177 
01178     /** Increment the consumed forecast. */
01179     void incConsumed(double n)
01180     {
01181       consumed += n;
01182       if (consumed<0) consumed = 0.0;
01183       setQuantity(total>consumed ? total - consumed : 0.0);
01184     }
01185 
01186     /** Update the consumed forecast.<br>
01187       * This field is normally updated through the forecast netting solver, but
01188       * you can use this method to update it directly.
01189       */
01190     void setConsumed(double n)
01191     {
01192       if (n<0)
01193         throw DataException("Consumed forecast must be greater or equal to 0");
01194       if (consumed == n) return;
01195       consumed = n;
01196       setQuantity(total>consumed ? total - consumed : 0.0);
01197     }
01198 
01199     /** Return the date range for this bucket. */
01200     DateRange getDueRange() const {return timebucket;}
01201 
01202     /** Return a pointer to the next forecast bucket. */
01203     ForecastBucket* getNextBucket() const {return next;}
01204 
01205     /** Return a pointer to the previous forecast bucket. */
01206     ForecastBucket* getPreviousBucket() const {return prev;}
01207 
01208     /** A flag to mark whether forecast is due at the start or at the end of a 
01209       * bucket.<br>
01210       * The default is false, ie due at the start of the bucket.
01211       */
01212     static void setDueAtEndOfBucket(bool b) {DueAtEndOfBucket = b;}
01213 
01214     virtual PyObject* getattro(const Attribute&);
01215     virtual int setattro(const Attribute&, const PythonObject&);
01216     static int initialize();
01217 
01218   private:
01219     double weight;
01220     double consumed;
01221     double total;
01222     DateRange timebucket;
01223     ForecastBucket* prev;
01224     ForecastBucket* next;
01225 
01226     /** A flag to mark whether forecast is due at the start or at the end of a 
01227       * bucket. */
01228     static bool DueAtEndOfBucket;
01229 };
01230 
01231 
01232 /** @brief Implementation of a forecast netting algorithm.
01233   *
01234   * As customer orders are being received they need to be deducted from
01235   * the forecast to avoid double-counting demand.
01236   *
01237   * The netting solver will process each order as follows:
01238   * - <b>First search for a matching forecast.</b><br>
01239   *   A matching forecast has the same item and customer as the order.<br>
01240   *   If no match is found at this level, a match is tried at higher levels
01241   *   of the customer and item.<br>
01242   *   Ultimately a match is tried with a empty customer or item field.
01243   * - <b>Next, the remaining net quantity of the forecast is decreased.</b><br>
01244   *   The forecast bucket to be reduced is the one where the order is due.<br>
01245   *   If the net quantity is already completely depleted in that bucket
01246   *   the solver will look in earlier and later buckets. The parameters
01247   *   Net_Early and Net_Late control the limits for the search in the
01248   *   time dimension.
01249   *
01250   * The logging levels have the following meaning:
01251   * - 0: Silent operation. Default logging level.
01252   * - 1: Log demands being netted and the matching forecast.
01253   * - 2: Same as 1, plus details on forecast buckets being netted.
01254   */
01255 class ForecastSolver : public Solver
01256 {
01257     friend class Forecast;
01258   public:
01259     /** Constructor. */
01260     ForecastSolver(const string& n) : Solver(n) {initType(metadata);}
01261 
01262     /** This method handles the search for a matching forecast, followed
01263       * by decreasing the net forecast.
01264       */
01265     void solve(const Demand*, void* = NULL);
01266 
01267     /** This is the main solver method that will appropriately call the other
01268       * solve methods.<br>
01269       */
01270     void solve(void *v = NULL);
01271 
01272     virtual const MetaClass& getType() const {return *metadata;}
01273     static const MetaClass *metadata;
01274     virtual size_t getSize() const {return sizeof(ForecastSolver);}
01275     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
01276     static int initialize();
01277 
01278     /** Callback function, used for netting orders against the forecast. */
01279     bool callback(Demand* l, const Signal a);
01280 
01281   private:
01282     /** Given a demand, this function will identify the forecast model it
01283       * links to.
01284       */
01285     Forecast* matchDemandToForecast(const Demand* l);
01286 
01287     /** Implements the netting of a customer order from a matching forecast
01288       * (and its delivery plan).
01289       */
01290     void netDemandFromForecast(const Demand*, Forecast*);
01291 
01292     /** Used for sorting demands during netting. */
01293     struct sorter
01294     {
01295       bool operator()(const Demand* x, const Demand* y) const
01296         {return SolverMRP::demand_comparison(x,y);}
01297     };
01298 
01299     /** Used for sorting demands during netting. */
01300     typedef multiset < Demand*, sorter > sortedDemandList;
01301 };
01302 
01303 }   // End namespace
01304 
01305 #endif
01306 
01307 

Documentation generated for frePPLe by  doxygen