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: 1508 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2011-09-10 10:53:11 +0200 (Sat, 10 Sep 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 /** @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 Environment::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     /** Update the gross quantity in a single forecast bucket. */
00901     virtual void setTotalQuantity(const Date , double);
00902 
00903     /** Python method to update the total quantity of one or more
00904       * forecast buckets.
00905       */
00906     static PyObject* setPythonTotalQuantity(PyObject *, PyObject *);
00907 
00908     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
00909     void endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement);
00910     void beginElement(XMLInput& pIn, const Attribute& pAttr);
00911     static int initialize();
00912 
00913     /** Returns whether fractional forecasts are allowed or not.<br>
00914       * The default is true.
00915       */
00916     bool getDiscrete() const {return discrete;}
00917 
00918     /** Updates forecast discreteness flag. */
00919     void setDiscrete(const bool b);
00920 
00921     /** Update the item to be planned. */
00922     virtual void setItem(Item*);
00923 
00924     /** Update the customer. */
00925     virtual void setCustomer(Customer*);
00926 
00927     /* Update the maximum allowed lateness for planning. */
00928     void setMaxLateness(TimePeriod);
00929 
00930     /* Update the minumum allowed shipment quantity for planning. */
00931     void setMinShipment(double);
00932 
00933     /** Specify a bucket calendar for the forecast. Once forecasted
00934       * quantities have been entered for the forecast, the calendar
00935       * can't be updated any more. */
00936     virtual void setCalendar(Calendar*);
00937 
00938     /** Returns a reference to the calendar used for this forecast. */
00939     Calendar* getCalendar() const {return calptr;}
00940 
00941     /** Generate a forecast value based on historical demand data.<br>
00942       * This method will call the different forecasting methods and select the
00943       * method with the lowest smape-error.<br>
00944       * It then asks the selected forecast method to generate a value for
00945       * each of the time buckets passed.
00946       */
00947     void generateFutureValues
00948       (const double[], unsigned int, const Date[], unsigned int, bool=false);
00949 
00950     /** Updates the due date of the demand. Lower numbers indicate a
00951       * higher priority level. The method also updates the priority
00952       * in all buckets.
00953       */
00954     virtual void setPriority(int);
00955 
00956     /** Updates the operation being used to plan the demands. */
00957     virtual void setOperation(Operation *);
00958 
00959     /** Updates the due date of the demand. */
00960     virtual void setDue(const Date& d)
00961     {throw DataException("Can't set due date of a forecast");}
00962 
00963     virtual const MetaClass& getType() const {return *metadata;}
00964     static const MetaClass *metadata;
00965     virtual size_t getSize() const
00966     {
00967       return sizeof(Forecast) + Demand::extrasize()
00968         + 6 * sizeof(void*); // Approx. size of an entry in forecast dictionary
00969     }
00970 
00971     /** Updates the value of the Customer_Then_Item_Hierarchy module
00972       * parameter. */
00973     static void setCustomerThenItemHierarchy(bool b)
00974       {Customer_Then_Item_Hierarchy = b;}
00975 
00976     /** Returns the value of the Customer_Then_Item_Hierarchy module
00977       * parameter. */
00978     static bool getCustomerThenItemHierarchy()
00979       {return Customer_Then_Item_Hierarchy;}
00980 
00981     /** Updates the value of the Match_Using_Delivery_Operation module
00982       * parameter. */
00983     static void setMatchUsingDeliveryOperation(bool b)
00984       {Match_Using_Delivery_Operation = b;}
00985 
00986     /** Returns the value of the Match_Using_Delivery_Operation module
00987       * parameter. */
00988     static bool getMatchUsingDeliveryOperation()
00989       {return Match_Using_Delivery_Operation;}
00990 
00991     /** Updates the value of the Net_Early module parameter. */
00992     static void setNetEarly(TimePeriod t) {Net_Early = t;}
00993 
00994     /** Returns the value of the Net_Early module parameter. */
00995     static TimePeriod getNetEarly() {return Net_Early;}
00996 
00997     /** Updates the value of the Net_Late module parameter. */
00998     static void setNetLate(TimePeriod t) {Net_Late = t;}
00999 
01000     /** Returns the value of the Net_Late module parameter. */
01001     static TimePeriod getNetLate() {return Net_Late;}
01002 
01003     /** Updates the value of the Forecast.smapeAlfa module parameter. */
01004     static void setForecastSmapeAlfa(double t)
01005     {
01006       if (t<=0.5 || t>1.0) throw DataException(
01007         "Parameter Forecast.smapeAlfa must be between 0.5 and 1.0"
01008         );
01009       Forecast_SmapeAlfa = t;
01010     }
01011 
01012     /** Returns the value of the Forecast_Iterations module parameter. */
01013     static double getForecastSmapeAlfa() {return Forecast_SmapeAlfa;}
01014 
01015     /** Updates the value of the Forecast_Iterations module parameter. */
01016     static void setForecastIterations(unsigned long t)
01017     {
01018       if (t<=0) throw DataException(
01019         "Parameter Forecast.Iterations must be bigger than 0"
01020         );
01021       Forecast_Iterations = t;
01022     }
01023 
01024     /** Returns the value of the Forecast_Iterations module parameter. */
01025     static unsigned long getForecastIterations() {return Forecast_Iterations;}
01026 
01027     /** Updates the value of the Forecast_Skip module parameter. */
01028     static void setForecastSkip(unsigned int t)
01029     {
01030       if (t<0) throw DataException(
01031         "Parameter Forecast.Skip must be bigger than or equal to 0"
01032         );
01033       Forecast_Skip = t;
01034     }
01035 
01036     /** Return the number of timeseries values used to initialize the
01037       * algorithm. The forecast error is not counted for these buckets.
01038       */
01039     static unsigned int getForecastSkip() {return Forecast_Skip;}
01040 
01041     /** A data type to maintain a dictionary of all forecasts. */
01042     typedef multimap < pair<const Item*, const Customer*>, Forecast* > MapOfForecasts;
01043 
01044     /** Callback function, used for prevent a calendar from being deleted when it
01045       * is used for an uninitialized forecast. */
01046     static bool callback(Calendar*, const Signal);
01047 
01048     /** Return a reference to a dictionary with all forecast objects. */
01049     static const MapOfForecasts& getForecasts() {return ForecastDictionary;}
01050 
01051     virtual PyObject* getattro(const Attribute&);
01052     virtual int setattro(const Attribute&, const PythonObject&);
01053     static PyObject* timeseries(PyObject *, PyObject *);
01054 
01055   private:
01056     /** Initializion of a forecast.<br>
01057       * It creates demands for each bucket of the calendar.
01058       */
01059     void instantiate();
01060 
01061     /** A void calendar to define the time buckets. */
01062     Calendar* calptr;
01063 
01064     /** Flags whether fractional forecasts are allowed. */
01065     bool discrete;
01066 
01067     /** A dictionary of all forecasts. */
01068     static MapOfForecasts ForecastDictionary;
01069 
01070     /** Controls how we search the customer and item levels when looking for a
01071       * matching forecast for a demand.
01072       */
01073     static bool Customer_Then_Item_Hierarchy;
01074 
01075     /** Controls whether or not a matching delivery operation is required
01076       * between a matching order and its forecast.
01077       */
01078     static bool Match_Using_Delivery_Operation;
01079 
01080     /** Store the maximum time difference between an order due date and a
01081       * forecast bucket to net from.<br>
01082       * The default value is 0, meaning that only netting from the due
01083       * bucket is allowed.
01084       */
01085     static TimePeriod Net_Late;
01086 
01087     /** Store the maximum time difference between an order due date and a
01088       * forecast bucket to net from.<br>
01089       * The default value is 0, meaning that only netting from the due
01090       * bucket is allowed.
01091       */
01092     static TimePeriod Net_Early;
01093 
01094     /** Specifies the maximum number of iterations allowed for a forecast
01095       * method to tune its parameters.<br>
01096       * Only positive values are allowed and the default value is 10.<br>
01097       * Set the parameter to 1 to disable the tuning and generate a
01098       * forecast based on the user-supplied parameters.
01099       */
01100     static unsigned long Forecast_Iterations;
01101 
01102     /** Specifies how the sMAPE forecast error is weighted for different time
01103       * buckets. The SMAPE value in the most recent bucket is 1.0, and the
01104       * weight decreases exponentially for earlier buckets.<br>
01105       * Acceptable values are in the interval 0.5 and 1.0, and the default
01106       * is 0.95.
01107       */
01108     static double Forecast_SmapeAlfa;
01109 
01110     /** Number of warmup periods.<br>
01111       * These periods are used for the initialization of the algorithm
01112       * and don't count towards measuring the forecast error.<br>
01113       * The default value is 5.
01114       */
01115     static unsigned long Forecast_Skip;
01116 };
01117 
01118 
01119 /** @brief This class represents a forecast value in a time bucket.
01120   *
01121   * A forecast bucket is never manipulated or created directly. Instead,
01122   * the owning forecast manages the buckets.
01123   */
01124 class ForecastBucket : public Demand
01125 {
01126   public:
01127     ForecastBucket(Forecast* f, Date d, Date e, double w, ForecastBucket* p)
01128       : Demand(f->getName() + " - " + string(d)), weight(w), consumed(0.0),
01129         total(0.0), timebucket(d,e), prev(p), next(NULL)
01130     {
01131       if (p) p->next = this;
01132       setOwner(f);
01133       setHidden(true);  // Avoid the subdemands show up in the output
01134       setItem(&*(f->getItem()));
01135       setDue(DueAtEndOfBucket ? e : d);
01136       setPriority(f->getPriority());
01137       setMaxLateness(f->getMaxLateness());
01138       setMinShipment(f->getMinShipment());
01139       setOperation(&*(f->getOperation()));
01140       initType(metadata);
01141     }
01142     virtual const MetaClass& getType() const {return *metadata;}
01143     static const MetaClass *metadata;
01144     virtual size_t getSize() const
01145     {
01146       return sizeof(ForecastBucket) + Demand::extrasize();
01147     }
01148 
01149     /** Returns the relative weight of this forecast bucket when distributing
01150       * forecast over different buckets.
01151       */
01152     double getWeight() const {return weight;}
01153 
01154     /** Returns the total, gross forecast. */
01155     double getTotal() const {return total;}
01156 
01157     /** Returns the consumed forecast. */
01158     double getConsumed() const {return consumed;}
01159 
01160     /** Update the weight of this forecasting bucket. */
01161     void setWeight(double n)
01162     {
01163       if (n<0)
01164         throw DataException("Forecast bucket weight must be greater or equal to 0");
01165       weight = n;
01166     }
01167 
01168     /** Increment the total, gross forecast. */
01169     void incTotal(double n)
01170     {
01171       total += n;
01172       if (total<0) total = 0.0;
01173       setQuantity(total>consumed ? total - consumed : 0.0);
01174     }
01175 
01176     /** Update the total, gross forecast. */
01177     void setTotal(double n)
01178     {
01179       if (n<0)
01180         throw DataException("Gross forecast must be greater or equal to 0");
01181       if (total == n) return;
01182       total = n;
01183       setQuantity(total>consumed ? total - consumed : 0.0);
01184     }
01185 
01186     /** Increment the consumed forecast. */
01187     void incConsumed(double n)
01188     {
01189       consumed += n;
01190       if (consumed<0) consumed = 0.0;
01191       setQuantity(total>consumed ? total - consumed : 0.0);
01192     }
01193 
01194     /** Update the consumed forecast.<br>
01195       * This field is normally updated through the forecast netting solver, but
01196       * you can use this method to update it directly.
01197       */
01198     void setConsumed(double n)
01199     {
01200       if (n<0)
01201         throw DataException("Consumed forecast must be greater or equal to 0");
01202       if (consumed == n) return;
01203       consumed = n;
01204       setQuantity(total>consumed ? total - consumed : 0.0);
01205     }
01206 
01207     /** Return the date range for this bucket. */
01208     DateRange getDueRange() const {return timebucket;}
01209 
01210     /** Return a pointer to the next forecast bucket. */
01211     ForecastBucket* getNextBucket() const {return next;}
01212 
01213     /** Return a pointer to the previous forecast bucket. */
01214     ForecastBucket* getPreviousBucket() const {return prev;}
01215 
01216     /** A flag to mark whether forecast is due at the start or at the end of a
01217       * bucket.<br>
01218       * The default is false, ie due at the start of the bucket.
01219       */
01220     static void setDueAtEndOfBucket(bool b) {DueAtEndOfBucket = b;}
01221 
01222     virtual PyObject* getattro(const Attribute&);
01223     virtual int setattro(const Attribute&, const PythonObject&);
01224     static int initialize();
01225 
01226   private:
01227     double weight;
01228     double consumed;
01229     double total;
01230     DateRange timebucket;
01231     ForecastBucket* prev;
01232     ForecastBucket* next;
01233 
01234     /** A flag to mark whether forecast is due at the start or at the end of a
01235       * bucket. */
01236     static bool DueAtEndOfBucket;
01237 };
01238 
01239 
01240 /** @brief Implementation of a forecast netting algorithm.
01241   *
01242   * As customer orders are being received they need to be deducted from
01243   * the forecast to avoid double-counting demand.
01244   *
01245   * The netting solver will process each order as follows:
01246   * - <b>First search for a matching forecast.</b><br>
01247   *   A matching forecast has the same item and customer as the order.<br>
01248   *   If no match is found at this level, a match is tried at higher levels
01249   *   of the customer and item.<br>
01250   *   Ultimately a match is tried with a empty customer or item field.
01251   * - <b>Next, the remaining net quantity of the forecast is decreased.</b><br>
01252   *   The forecast bucket to be reduced is the one where the order is due.<br>
01253   *   If the net quantity is already completely depleted in that bucket
01254   *   the solver will look in earlier and later buckets. The parameters
01255   *   Net_Early and Net_Late control the limits for the search in the
01256   *   time dimension.
01257   *
01258   * The logging levels have the following meaning:
01259   * - 0: Silent operation. Default logging level.
01260   * - 1: Log demands being netted and the matching forecast.
01261   * - 2: Same as 1, plus details on forecast buckets being netted.
01262   */
01263 class ForecastSolver : public Solver
01264 {
01265     friend class Forecast;
01266   public:
01267     /** Constructor. */
01268     ForecastSolver(const string& n) : Solver(n) {initType(metadata);}
01269 
01270     /** This method handles the search for a matching forecast, followed
01271       * by decreasing the net forecast.
01272       */
01273     void solve(const Demand*, void* = NULL);
01274 
01275     /** This is the main solver method that will appropriately call the other
01276       * solve methods.<br>
01277       */
01278     void solve(void *v = NULL);
01279 
01280     virtual const MetaClass& getType() const {return *metadata;}
01281     static const MetaClass *metadata;
01282     virtual size_t getSize() const {return sizeof(ForecastSolver);}
01283     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
01284     static int initialize();
01285 
01286     /** Callback function, used for netting orders against the forecast. */
01287     bool callback(Demand* l, const Signal a);
01288 
01289   private:
01290     /** Given a demand, this function will identify the forecast model it
01291       * links to.
01292       */
01293     Forecast* matchDemandToForecast(const Demand* l);
01294 
01295     /** Implements the netting of a customer order from a matching forecast
01296       * (and its delivery plan).
01297       */
01298     void netDemandFromForecast(const Demand*, Forecast*);
01299 
01300     /** Used for sorting demands during netting. */
01301     struct sorter
01302     {
01303       bool operator()(const Demand* x, const Demand* y) const
01304         {return SolverMRP::demand_comparison(x,y);}
01305     };
01306 
01307     /** Used for sorting demands during netting. */
01308     typedef multiset < Demand*, sorter > sortedDemandList;
01309 };
01310 
01311 }   // End namespace
01312 
01313 #endif
01314 
01315 

Documentation generated for frePPLe by  doxygen