forecast.h

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/modules/forecast/forecast.h $
00003   version : $LastChangedRevision: 1108 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2009-12-06 18:54:18 +0100 (Sun, 06 Dec 2009) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 by Johan De Taeye                                    *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the 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   *       - single exponential smoothing, which is applicable for
00064   *         constant demands .
00065   *       - double exponential smoothing, which is applicable for trended
00066   *         demands.
00067   *       - moving average, which is applicable when there is little demand
00068   *         history to rely on.
00069   *    The forecast method giving the smallest mean absolute deviation (aka
00070   *    "mad"-error) will be automatically picked to produce the forecast.<br>
00071   *    The algorithm will automatically tune the parameters for the
00072   *    forecasting methods (i.e. alfa for the single exponential smoothing,
00073   *    or alfa and gamma for the double exponential smoothing) to their
00074   *    optimal value. The user can specify minimum and maximum boundaries
00075   *    for the parameters and the maximum allowed number of iterations
00076   *    for the algorithm.
00077   *
00078   * The XML schema extension enabled by this module is (see mod_forecast.xsd):
00079   * <PRE>
00080   * <!-- Define the forecast type -->
00081   * <xsd:complexType name="demand_forecast">
00082   *   <xsd:complexContent>
00083   *     <xsd:extension base="demand">
00084   *       <xsd:choice minOccurs="0" maxOccurs="unbounded">
00085   *         <xsd:element name="calendar" type="calendar" />
00086   *         <xsd:element name="discrete" type="xsd:boolean" />
00087   *         <xsd:element name="buckets">
00088   *           <xsd:complexType>
00089   *             <xsd:choice minOccurs="0" maxOccurs="unbounded">
00090   *               <xsd:element name="bucket">
00091   *                 <xsd:complexType>
00092   *                   <xsd:all>
00093   *                     <xsd:element name="total" type="positiveDouble"
00094   *                       minOccurs="0" />
00095   *                     <xsd:element name="net" type="positiveDouble"
00096   *                       minOccurs="0" />
00097   *                     <xsd:element name="consumed" type="positiveDouble"
00098   *                       minOccurs="0" />
00099   *                     <xsd:element name="start" type="xsd:dateTime"
00100   *                       minOccurs="0"/>
00101   *                     <xsd:element name="end" type="xsd:dateTime"
00102   *                       minOccurs="0"/>
00103   *                   </xsd:all>
00104   *                   <xsd:attribute name="total" type="positiveDouble" />
00105   *                   <xsd:attribute name="net" type="positiveDouble" />
00106   *                   <xsd:attribute name="consumed" type="positiveDouble" />
00107   *                   <xsd:attribute name="start" type="xsd:dateTime" />
00108   *                   <xsd:attribute name="end" type="xsd:dateTime" />
00109   *                 </xsd:complexType>
00110   *               </xsd:element>
00111   *             </xsd:choice>
00112   *           </xsd:complexType>
00113   *         </xsd:element>
00114   *       </xsd:choice>
00115   *       <xsd:attribute name="discrete" type="xsd:boolean" />
00116   *     </xsd:extension>
00117   *   </xsd:complexContent>
00118   * </xsd:complexType>
00119   *
00120   * <!-- Define the netting solver. -->
00121   * <xsd:complexType name="solver_forecast">
00122   * <xsd:complexContent>
00123   *   <xsd:extension base="solver">
00124   *     <xsd:choice minOccurs="0" maxOccurs="unbounded">
00125   *       <xsd:element name="loglevel" type="loglevel" />
00126   *     </xsd:choice>
00127   *   </xsd:extension>
00128   * </xsd:complexContent>
00129   * </xsd:complexType>
00130   * </PRE>
00131   *
00132   * The module support the following configuration parameters:
00133   *
00134   *   - Net_CustomerThenItemHierarchy:<br>
00135   *     As part of the forecast netting a demand is assiociated with a certain
00136   *     forecast. When no matching forecast is found for the customer and item
00137   *     of the demand, frePPLe looks for forecast at higher level customers
00138   *     and items.<br>
00139   *     This flag allows us to control whether we first search the customer
00140   *     hierarchy and then the item hierarchy, or the other way around.<br>
00141   *     The default value is true, ie search higher customer levels before
00142   *     searching higher levels of the item.
00143   *
00144   *   - Net_MatchUsingDeliveryOperation:<br>
00145   *     Specifies whether or not a demand and a forecast require to have the
00146   *     same delivery operation to be a match.<br>
00147   *     The default value is true.
00148   *
00149   *   - Net_NetEarly:<br>
00150   *     Defines how much time before the due date of an order we are allowed
00151   *     to search for a forecast bucket to net from.<br>
00152   *     The default value is 0, meaning that we can net only from the bucket
00153   *     where the demand is due.
00154   *
00155   *   - Net_NetLate:<br>
00156   *     Defines how much time after the due date of an order we are allowed
00157   *     to search for a forecast bucket to net from.<br>
00158   *     The default value is 0, meaning that we can net only from the bucket
00159   *     where the demand is due.
00160   *
00161   *   - Forecast_Iterations:<br>
00162   *     Specifies the maximum number of iterations allowed for a forecast
00163   *     method to tune its parameters.<br>
00164   *     Only positive values are allowed and the default value is 10.<br>
00165   *     Set the parameter to 1 to disable the tuning and generate a forecast
00166   *     based on the user-supplied parameters.
00167   *
00168   *   - Forecast_madAlfa:<br>
00169   *     Specifies how the MAD forecast error is weighted for different time
00170   *     buckets. The MAD value in the most recent bucket is 1.0, and the
00171   *     weight decreases exponentially for earlier buckets.<br>
00172   *     Acceptable values are in the interval 0.5 and 1.0, and the default
00173   *     is 0.95.
00174   *
00175   *   - Forecast_Skip:<br>
00176   *     Specifies the number of time series values used to initialize the
00177   *     forecasting method. The forecast error in these bucket isn't counted.
00178   *
00179   *   - Forecast_MovingAverage.buckets<br>
00180   *     This parameter controls the number of buckets to be averaged by the
00181   *     moving average forecast method.
00182   *
00183   *   - Forecast_SingleExponential.initialAlfa,<br>
00184   *     Forecast_SingleExponential.minAlfa,<br>
00185   *     Forecast_SingleExponential.maxAlfa:<br>
00186   *     Specifies the initial value and the allowed range of the smoothing
00187   *     parameter in the single exponential forecasting method.<br>
00188   *     The allowed range is between 0 and 1. Values lower than about 0.05
00189   *     are not advisible.
00190   *
00191   *   - Forecast_DoubleExponential.initialAlfa,<br>
00192   *     Forecast_DoubleExponential.minAlfa,<br>
00193   *     Forecast_DoubleExponential.maxAlfa:<br>
00194   *     Specifies the initial value and the allowed range of the smoothing
00195   *     parameter in the double exponential forecasting method.<br>
00196   *     The allowed range is between 0 and 1. Values lower than about 0.05
00197   *     are not advisible.
00198   *
00199   *   - Forecast_DoubleExponential.initialGamma,<br>
00200   *     Forecast_DoubleExponential.minGamma,<br>
00201   *     Forecast_DoubleExponential.maxGamma:<br>
00202   *     Specifies the initial value and the allowed range of the trend
00203   *     smoothing parameter in the double exponential forecasting method.<br>
00204   *     The allowed range is between 0 and 1.
00205   */
00206 
00207 #ifndef FORECAST_H
00208 #define FORECAST_H
00209 
00210 #include "frepple.h"
00211 using namespace frepple;
00212 
00213 namespace module_forecast
00214 {
00215 
00216 
00217 /** Initialization routine for the library. */
00218 MODULE_EXPORT const char* initialize(const CommandLoadLibrary::ParameterList&);
00219 
00220 /** @brief This class represents a bucketized demand signal.
00221   *
00222   * The forecast object defines the item and priority of the demands.<br>
00223   * A calendar (of type void, double, integer or boolean) divides the time horizon
00224   * in individual time buckets. The calendar value is used to assign priorities
00225   * to the time buckets.<br>
00226   * The class basically works as an interface for a hierarchy of demands, where the
00227   * lower level demands represent forecasting time buckets.
00228   */
00229 class Forecast : public Demand
00230 {
00231     friend class ForecastSolver;
00232   public:
00233 
00234     static const Keyword tag_total;
00235     static const Keyword tag_net;
00236     static const Keyword tag_consumed;
00237 
00238     /** @brief Abstract base class for all forecasting methods. */
00239     class ForecastMethod
00240     {
00241       public:
00242         /** Forecast evaluation. */
00243         virtual double generateForecast
00244           (Forecast*, const double[], unsigned int, const double[], bool) = 0;
00245 
00246         /** This method is called when this forecast method has generated the
00247           * lowest forecast error and now needs to set the forecast values.
00248           */
00249         virtual void applyForecast
00250           (Forecast*, const Date[], unsigned int, bool) = 0;
00251 
00252         /** The name of the method. */
00253         virtual string getName() = 0;
00254     };
00255 
00256 
00257     /** @brief A class to calculate a forecast based on a moving average. */
00258     class MovingAverage : public ForecastMethod
00259     {
00260       private:
00261         /** Number of smoothed buckets. */
00262         static unsigned int defaultbuckets;
00263 
00264         /** Number of buckets to average. */
00265         unsigned int buckets;
00266 
00267         /** Calculated average.<br>
00268           * Used to carry results between the evaluation and applying of the forecast.
00269           */
00270         double avg;
00271 
00272       public:
00273         /** Constructor. */
00274         MovingAverage(int i = defaultbuckets) : buckets(i), avg(0)
00275         {
00276           if (i < 1)
00277             throw DataException("Moving average needs to smooth over at least 1 bucket");
00278         }
00279 
00280         /** Forecast evaluation. */
00281         double generateForecast(Forecast* fcst, const double history[],
00282           unsigned int count, const double madWeight[], bool debug);
00283 
00284         /** Forecast value updating. */
00285         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00286 
00287         /** Update the initial value for the alfa parameter. */
00288         static void setDefaultBuckets(int x)
00289         {
00290           if (x < 1)
00291             throw DataException("Parameter MovingAverage.buckets needs to smooth over at least 1 bucket");
00292          defaultbuckets = x;
00293         }
00294 
00295         string getName() {return "moving average";}
00296    };
00297 
00298     /** @brief A class to perform single exponential smoothing on a time series. */
00299     class SingleExponential : public ForecastMethod
00300     {
00301       private:
00302         /** Smoothing constant. */
00303         double alfa;
00304 
00305         /** Default initial alfa value.<br>
00306           * The default value is 0.2.
00307           */
00308         static double initial_alfa;
00309 
00310         /** Lower limit on the alfa parameter.<br>
00311           * The default value is 0.
00312           **/
00313         static double min_alfa;
00314 
00315         /** Upper limit on the alfa parameter.<br>
00316           * The default value is 1.
00317           **/
00318         static double max_alfa;
00319 
00320         /** Smoothed result.<br>
00321           * Used to carry results between the evaluation and applying of the forecast.
00322           */
00323         double f_i;
00324 
00325       public:
00326         /** Constructor. */
00327         SingleExponential(double a = initial_alfa) : alfa(a), f_i(0)
00328         {
00329           if (alfa < min_alfa) alfa = min_alfa;
00330           if (alfa > max_alfa) alfa = max_alfa;
00331         }
00332 
00333         /** Forecast evaluation. */
00334         double generateForecast(Forecast* fcst, const double history[],
00335           unsigned int count, const double madWeight[], bool debug);
00336 
00337         /** Forecast value updating. */
00338         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00339 
00340         /** Update the initial value for the alfa parameter. */
00341         static void setInitialAlfa(double x)
00342         {
00343           if (x<0 || x>1.0) throw DataException(
00344             "Parameter SingleExponential.initialAlfa must be between 0 and 1");
00345          initial_alfa = x;
00346         }
00347 
00348         /** Update the minimum value for the alfa parameter. */
00349         static void setMinAlfa(double x)
00350         {
00351           if (x<0 || x>1.0) throw DataException(
00352             "Parameter SingleExponential.minAlfa must be between 0 and 1");
00353           min_alfa = x;
00354         }
00355 
00356         /** Update the maximum value for the alfa parameter. */
00357         static void setMaxAlfa(double x)
00358         {
00359           if (x<0 || x>1.0) throw DataException(
00360             "Parameter SingleExponential.maxAlfa must be between 0 and 1");
00361           max_alfa = x;
00362         }
00363 
00364         string getName() {return "single exponential";}
00365     };
00366 
00367     /** @brief A class to perform double exponential smoothing on a time
00368       * series.
00369       */
00370     class DoubleExponential : public ForecastMethod
00371     {
00372       private:
00373         /** Smoothing constant. */
00374         double alfa;
00375 
00376         /** Default initial alfa value.<br>
00377           * The default value is 0.2.
00378           */
00379         static double initial_alfa;
00380 
00381         /** Lower limit on the alfa parameter.<br>
00382           * The default value is 0.
00383           **/
00384         static double min_alfa;
00385 
00386         /** Upper limit on the alfa parameter.<br>
00387           * The default value is 1.
00388           **/
00389         static double max_alfa;
00390 
00391         /** Trend smoothing constant. */
00392         double gamma;
00393 
00394         /** Default initial gamma value.<br>
00395           * The default value is 0.05.
00396           */
00397         static double initial_gamma;
00398 
00399         /** Lower limit on the gamma parameter.<br>
00400           * The default value is 0.05.
00401           **/
00402         static double min_gamma;
00403 
00404         /** Upper limit on the gamma parameter.<br>
00405           * The default value is 1.
00406           **/
00407         static double max_gamma;
00408 
00409         /** Smoothed result.<br>
00410           * Used to carry results between the evaluation and applying of the forecast.
00411           */
00412         double trend_i;
00413 
00414         /** Smoothed result.<br>
00415           * Used to carry results between the evaluation and applying of the forecast.
00416           */
00417         double constant_i;
00418 
00419       public:
00420         /** Constructor. */
00421         DoubleExponential(double a = initial_alfa, double g = initial_gamma)
00422           : alfa(a), gamma(g), trend_i(0), constant_i(0) {}
00423 
00424         /** Forecast evaluation. */
00425         double generateForecast(Forecast* fcst, const double history[],
00426           unsigned int count, const double madWeight[], bool debug);
00427 
00428         /** Forecast value updating. */
00429         void applyForecast(Forecast*, const Date[], unsigned int, bool);
00430 
00431         /** Update the initial value for the alfa parameter. */
00432         static void setInitialAlfa(double x)
00433         {
00434           if (x<0 || x>1.0) throw DataException(
00435             "Parameter DoubleExponential.initialAlfa must be between 0 and 1");
00436           initial_alfa = x;
00437         }
00438 
00439         /** Update the minimum value for the alfa parameter. */
00440         static void setMinAlfa(double x)
00441         {
00442           if (x<0 || x>1.0) throw DataException(
00443             "Parameter DoubleExponential.minAlfa must be between 0 and 1");
00444           min_alfa = x;
00445         }
00446 
00447         /** Update the maximum value for the alfa parameter. */
00448         static void setMaxAlfa(double x)
00449         {
00450           if (x<0 || x>1.0) throw DataException(
00451             "Parameter DoubleExponential.maxAlfa must be between 0 and 1");
00452           max_alfa = x;
00453         }
00454 
00455         /** Update the initial value for the alfa parameter.<br>
00456           * The default value is 0.05. <br>
00457           * Setting this parameter to too low a value can create false
00458           * positives: the double exponential method is selected for a time
00459           * series without a real trend. A single exponential is better for
00460           * such cases.
00461           */
00462         static void setInitialGamma(double x)
00463         {
00464           if (x<0 || x>1.0) throw DataException(
00465             "Parameter DoubleExponential.initialGamma must be between 0 and 1");
00466           initial_gamma = x;
00467         }
00468 
00469         /** Update the minimum value for the alfa parameter. */
00470         static void setMinGamma(double x)
00471         {
00472           if (x<0 || x>1.0) throw DataException(
00473             "Parameter DoubleExponential.minGamma must be between 0 and 1");
00474           min_gamma = x;
00475         }
00476 
00477         /** Update the maximum value for the alfa parameter. */
00478         static void setMaxGamma(double x)
00479         {
00480           if (x<0 || x>1.0) throw DataException(
00481             "Parameter DoubleExponential.maxGamma must be between 0 and 1");
00482           max_gamma = x;
00483         }
00484 
00485         string getName() {return "double exponential";}
00486     };
00487 
00488   public:
00489     /** Constructor. */
00490     explicit Forecast(const string& nm)
00491       : Demand(nm), calptr(NULL), discrete(true) {initType(metadata);}
00492 
00493     /** Destructor. */
00494     ~Forecast();
00495 
00496     /** Updates the quantity of the forecast. This method is empty. */
00497     virtual void setQuantity(double f)
00498       {throw DataException("Can't set quantity of a forecast");}
00499 
00500     /** Update the forecast quantity.<br>
00501       * The forecast quantity will be distributed equally among the buckets
00502       * available between the two dates, taking into account also the bucket
00503       * weights.<br>
00504       * The logic applied is briefly summarized as follows:
00505       *  - If the daterange has its start and end dates equal, we find the
00506       *    matching forecast bucket and update the quantity.
00507       *  - Otherwise the quantity is distributed among all intersecting
00508       *    forecast buckets. This distribution is considering the weigth of
00509       *    the bucket and the time duration of the bucket.<br>
00510       *    The bucket weight is the value specified on the calendar.<br>
00511       *    If a forecast bucket only partially overlaps with the daterange
00512       *    only the overlapping time is used as the duration.
00513       *  - If only buckets with zero weigth are found in the daterange a
00514       *    dataexception is thrown. It indicates a situation where forecast
00515       *    is specified for a date where no values are allowed.
00516       */
00517     virtual void setTotalQuantity(const DateRange& , double);
00518 
00519     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
00520     void endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement);
00521     void beginElement(XMLInput& pIn, const Attribute& pAttr);
00522     static int initialize();
00523 
00524     /** Returns whether fractional forecasts are allowed or not.<br>
00525       * The default is true.
00526       */
00527     bool getDiscrete() const {return discrete;}
00528 
00529     /** Updates forecast discreteness flag. */
00530     void setDiscrete(const bool b);
00531 
00532     /** Update the item to be planned. */
00533     virtual void setItem(Item*);
00534 
00535     /** Update the customer. */
00536     virtual void setCustomer(Customer*);
00537 
00538     /* Update the maximum allowed lateness for planning. */
00539     void setMaxLateness(TimePeriod);
00540 
00541     /* Update the minumum allowed shipment quantity for planning. */
00542     void setMinShipment(double);
00543 
00544     /** Specify a bucket calendar for the forecast. Once forecasted
00545       * quantities have been entered for the forecast, the calendar
00546       * can't be updated any more. */
00547     virtual void setCalendar(Calendar*);
00548 
00549     /** Returns a reference to the calendar used for this forecast. */
00550     Calendar* getCalendar() const {return calptr;}
00551 
00552     /** Generate a forecast value based on historical demand data.<br>
00553       * This method will call the different forecasting methods and select the
00554       * method with the lowest mad-error.<br>
00555       * It then asks the selected forecast method to generate a value for
00556       * each of the time buckets passed.
00557       */
00558     void generateFutureValues
00559       (const double[], unsigned int, const Date[], unsigned int, bool=false);
00560 
00561     /** Updates the due date of the demand. Lower numbers indicate a
00562       * higher priority level. The method also updates the priority
00563       * in all buckets.
00564       */
00565     virtual void setPriority(int);
00566 
00567     /** Updates the operation being used to plan the demands. */
00568     virtual void setOperation(Operation *);
00569 
00570     /** Updates the due date of the demand. */
00571     virtual void setDue(const Date& d)
00572     {throw DataException("Can't set due date of a forecast");}
00573 
00574     virtual const MetaClass& getType() const {return *metadata;}
00575     static const MetaClass *metadata;
00576     virtual size_t getSize() const
00577     {
00578       return sizeof(Forecast) + Demand::extrasize()
00579         + 6 * sizeof(void*); // Approx. size of an entry in forecast dictionary
00580     }
00581 
00582     /** Updates the value of the Customer_Then_Item_Hierarchy module
00583       * parameter. */
00584     static void setCustomerThenItemHierarchy(bool b)
00585       {Customer_Then_Item_Hierarchy = b;}
00586 
00587     /** Returns the value of the Customer_Then_Item_Hierarchy module
00588       * parameter. */
00589     static bool getCustomerThenItemHierarchy()
00590       {return Customer_Then_Item_Hierarchy;}
00591 
00592     /** Updates the value of the Match_Using_Delivery_Operation module
00593       * parameter. */
00594     static void setMatchUsingDeliveryOperation(bool b)
00595       {Match_Using_Delivery_Operation = b;}
00596 
00597     /** Returns the value of the Match_Using_Delivery_Operation module
00598       * parameter. */
00599     static bool getMatchUsingDeliveryOperation()
00600       {return Match_Using_Delivery_Operation;}
00601 
00602     /** Updates the value of the Net_Early module parameter. */
00603     static void setNetEarly(TimePeriod t) {Net_Early = t;}
00604 
00605     /** Returns the value of the Net_Early module parameter. */
00606     static TimePeriod getNetEarly() {return Net_Early;}
00607 
00608     /** Updates the value of the Net_Late module parameter. */
00609     static void setNetLate(TimePeriod t) {Net_Late = t;}
00610 
00611     /** Returns the value of the Net_Late module parameter. */
00612     static TimePeriod getNetLate() {return Net_Late;}
00613 
00614     /** Updates the value of the Forecast.madAlfa module parameter. */
00615     static void setForecastMadAlfa(double t)
00616     {
00617       if (t<=0.5 || t>1.0) throw DataException(
00618         "Parameter Forecast.madAlfa must be between 0.5 and 1.0"
00619         );
00620       Forecast_MadAlfa = t;
00621     }
00622 
00623     /** Returns the value of the Forecast_Iterations module parameter. */
00624     static double getForecastMadAlfa() {return Forecast_MadAlfa;}
00625 
00626     /** Updates the value of the Forecast_Iterations module parameter. */
00627     static void setForecastIterations(unsigned long t)
00628     {
00629       if (t<=0) throw DataException(
00630         "Parameter Forecast.Iterations must be bigger than 0"
00631         );
00632       Forecast_Iterations = t;
00633     }
00634 
00635     /** Returns the value of the Forecast_Iterations module parameter. */
00636     static unsigned long getForecastIterations() {return Forecast_Iterations;}
00637 
00638     /** Updates the value of the Forecast_Skip module parameter. */
00639     static void setForecastSkip(unsigned int t)
00640     {
00641       if (t<0) throw DataException(
00642         "Parameter Forecast.Skip must be bigger than or equal to 0"
00643         );
00644       Forecast_Skip = t;
00645     }
00646 
00647     /** Return the number of timeseries values used to initialize the
00648       * algorithm. The forecast error is not counted for these buckets.
00649       */
00650     static unsigned int getForecastSkip() {return Forecast_Skip;}
00651 
00652     /** A data type to maintain a dictionary of all forecasts. */
00653     typedef multimap < pair<const Item*, const Customer*>, Forecast* > MapOfForecasts;
00654 
00655     /** Callback function, used for prevent a calendar from being deleted when it
00656       * is used for an uninitialized forecast. */
00657     static bool callback(Calendar*, const Signal);
00658 
00659     /** Return a reference to a dictionary with all forecast objects. */
00660     static const MapOfForecasts& getForecasts() {return ForecastDictionary;}
00661 
00662     virtual PyObject* getattro(const Attribute&);
00663     virtual int setattro(const Attribute&, const PythonObject&);
00664     static PyObject* timeseries(PyObject *, PyObject *);
00665 
00666   private:
00667     /** Initializion of a forecast.<br>
00668       * It creates demands for each bucket of the calendar.
00669       */
00670     void instantiate();
00671 
00672     /** A void calendar to define the time buckets. */
00673     Calendar* calptr;
00674 
00675     /** Flags whether fractional forecasts are allowed. */
00676     bool discrete;
00677 
00678     /** A dictionary of all forecasts. */
00679     static MapOfForecasts ForecastDictionary;
00680 
00681     /** Controls how we search the customer and item levels when looking for a
00682       * matching forecast for a demand.
00683       */
00684     static bool Customer_Then_Item_Hierarchy;
00685 
00686     /** Controls whether or not a matching delivery operation is required
00687       * between a matching order and its forecast.
00688       */
00689     static bool Match_Using_Delivery_Operation;
00690 
00691     /** Store the maximum time difference between an order due date and a
00692       * forecast bucket to net from.<br>
00693       * The default value is 0, meaning that only netting from the due
00694       * bucket is allowed.
00695       */
00696     static TimePeriod Net_Late;
00697 
00698     /** Store the maximum time difference between an order due date and a
00699       * forecast bucket to net from.<br>
00700       * The default value is 0, meaning that only netting from the due
00701       * bucket is allowed.
00702       */
00703     static TimePeriod Net_Early;
00704 
00705     /** Specifies the maximum number of iterations allowed for a forecast
00706       * method to tune its parameters.<br>
00707       * Only positive values are allowed and the default value is 10.<br>
00708       * Set the parameter to 1 to disable the tuning and generate a
00709       * forecast based on the user-supplied parameters.
00710       */
00711     static unsigned long Forecast_Iterations;
00712 
00713     /** Specifies how the MAD forecast error is weighted for different time
00714       * buckets. The MAD value in the most recent bucket is 1.0, and the
00715       * weight decreases exponentially for earlier buckets.<br>
00716       * Acceptable values are in the interval 0.5 and 1.0, and the default
00717       * is 0.95.
00718       */
00719     static double Forecast_MadAlfa;
00720 
00721     /** Number of warmup periods.<br>
00722       * These periods are used for the initialization of the algorithm
00723       * and don't count towards measuring the forecast error.<br>
00724       * The default value is 5.
00725       */
00726     static unsigned long Forecast_Skip;
00727 };
00728 
00729 
00730 /** @brief This class represents a forecast value in a time bucket.
00731   *
00732   * A forecast bucket is never manipulated or created directly. Instead,
00733   * the owning forecast manages the buckets.
00734   */
00735 class ForecastBucket : public Demand
00736 {
00737   public:
00738     ForecastBucket(Forecast* f, Date d, Date e, double w, ForecastBucket* p)
00739       : Demand(f->getName() + " - " + string(d)), weight(w), consumed(0.0),
00740         total(0.0), timebucket(d,e), prev(p), next(NULL)
00741     {
00742       if (p) p->next = this;
00743       setOwner(f);
00744       setHidden(true);  // Avoid the subdemands show up in the output
00745       setItem(&*(f->getItem()));
00746       setDue(d);
00747       setPriority(f->getPriority());
00748       setMaxLateness(f->getMaxLateness());
00749       setMinShipment(f->getMinShipment());
00750       setOperation(&*(f->getOperation()));
00751       initType(metadata);
00752     }
00753     virtual const MetaClass& getType() const {return *metadata;}
00754     static const MetaClass *metadata;
00755     virtual size_t getSize() const
00756     {
00757       return sizeof(ForecastBucket) + Demand::extrasize();
00758     }
00759 
00760     /** Returns the relative weight of this forecast bucket when distributing
00761       * forecast over different buckets.
00762       */
00763     double getWeight() const {return weight;}
00764 
00765     /** Returns the total, gross forecast. */
00766     double getTotal() const {return total;}
00767 
00768     /** Returns the consumed forecast. */
00769     double getConsumed() const {return consumed;}
00770 
00771     /** Update the weight of this forecasting bucket. */
00772     void setWeight(double n)
00773     {
00774       if (n<0)
00775         throw DataException("Forecast bucket weight must be greater or equal to 0");
00776       weight = n;
00777     }
00778 
00779     /** Increment the total, gross forecast. */
00780     void incTotal(double n)
00781     {
00782       total += n;
00783       if (total<0) total = 0.0;
00784       setQuantity(total>consumed ? total - consumed : 0.0);
00785     }
00786 
00787     /** Update the total, gross forecast. */
00788     void setTotal(double n)
00789     {
00790       if (n<0)
00791         throw DataException("Gross forecast must be greater or equal to 0");
00792       if (total == n) return;
00793       total = n;
00794       setQuantity(total>consumed ? total - consumed : 0.0);
00795     }
00796 
00797     /** Increment the consumed forecast. */
00798     void incConsumed(double n)
00799     {
00800       consumed += n;
00801       if (consumed<0) consumed = 0.0;
00802       setQuantity(total>consumed ? total - consumed : 0.0);
00803     }
00804 
00805     /** Update the consumed forecast.<br>
00806       * This field is normally updated through the forecast netting solver, but
00807       * you can use this method to update it directly.
00808       */
00809     void setConsumed(double n)
00810     {
00811       if (n<0)
00812         throw DataException("Consumed forecast must be greater or equal to 0");
00813       if (consumed == n) return;
00814       consumed = n;
00815       setQuantity(total>consumed ? total - consumed : 0.0);
00816     }
00817 
00818     /** Return the date range for this bucket. */
00819     DateRange getDueRange() const {return timebucket;}
00820 
00821     /** Return a pointer to the next forecast bucket. */
00822     ForecastBucket* getNextBucket() const {return next;}
00823 
00824     /** Return a pointer to the previous forecast bucket. */
00825     ForecastBucket* getPreviousBucket() const {return prev;}
00826 
00827     virtual PyObject* getattro(const Attribute&);
00828     virtual int setattro(const Attribute&, const PythonObject&);
00829     static int initialize();
00830 
00831   private:
00832     double weight;
00833     double consumed;
00834     double total;
00835     DateRange timebucket;
00836     ForecastBucket* prev;
00837     ForecastBucket* next;
00838 };
00839 
00840 
00841 /** @brief Implementation of a forecast netting algorithm.
00842   *
00843   * As customer orders are being received they need to be deducted from
00844   * the forecast to avoid double-counting demand.
00845   *
00846   * The netting solver will process each order as follows:
00847   * - <b>First search for a matching forecast.</b><br>
00848   *   A matching forecast has the same item and customer as the order.<br>
00849   *   If no match is found at this level, a match is tried at higher levels
00850   *   of the customer and item.<br>
00851   *   Ultimately a match is tried with a empty customer or item field.
00852   * - <b>Next, the remaining net quantity of the forecast is decreased.</b><br>
00853   *   The forecast bucket to be reduced is the one where the order is due.<br>
00854   *   If the net quantity is already completely depleted in that bucket
00855   *   the solver will look in earlier and later buckets. The parameters
00856   *   Net_Early and Net_Late control the limits for the search in the
00857   *   time dimension.
00858   *
00859   * The logging levels have the following meaning:
00860   * - 0: Silent operation. Default logging level.
00861   * - 1: Log demands being netted and the matching forecast.
00862   * - 2: Same as 1, plus details on forecast buckets being netted.
00863   */
00864 class ForecastSolver : public Solver
00865 {
00866     friend class Forecast;
00867   public:
00868     /** Constructor. */
00869     ForecastSolver(const string& n) : Solver(n) {initType(metadata);}
00870 
00871     /** This method handles the search for a matching forecast, followed
00872       * by decreasing the net forecast.
00873       */
00874     void solve(const Demand*, void* = NULL);
00875 
00876     /** This is the main solver method that will appropriately call the other
00877       * solve methods.<br>
00878       */
00879     void solve(void *v = NULL);
00880 
00881     virtual const MetaClass& getType() const {return *metadata;}
00882     static const MetaClass *metadata;
00883     virtual size_t getSize() const {return sizeof(ForecastSolver);}
00884     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
00885     static int initialize();
00886 
00887     /** Callback function, used for netting orders against the forecast. */
00888     bool callback(Demand* l, const Signal a);
00889 
00890   private:
00891     /** Given a demand, this function will identify the forecast model it
00892       * links to.
00893       */
00894     Forecast* matchDemandToForecast(const Demand* l);
00895 
00896     /** Implements the netting of a customer order from a matching forecast
00897       * (and its delivery plan).
00898       */
00899     void netDemandFromForecast(const Demand*, Forecast*);
00900 
00901     /** Used for sorting demands during netting. */
00902     struct sorter
00903     {
00904       bool operator()(const Demand* x, const Demand* y) const
00905         {return SolverMRP::demand_comparison(x,y);}
00906     };
00907 
00908     /** Used for sorting demands during netting. */
00909     typedef multiset < Demand*, sorter > sortedDemandList;
00910 };
00911 
00912 }   // End namespace
00913 
00914 #endif
00915 
00916