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

Documentation generated for frePPLe by  doxygen