RMOL Logo  0.25.3
C++ library of Revenue Management and Optimisation classes and functions
 All Classes Namespaces Files Functions Variables Typedefs Friends Defines
Forecaster.cpp
Go to the documentation of this file.
00001 // //////////////////////////////////////////////////////////////////////
00002 // Import section
00003 // //////////////////////////////////////////////////////////////////////
00004 // STL
00005 #include <cassert>
00006 #include <sstream>
00007 #include <cmath>
00008 // StdAir
00009 #include <stdair/basic/BasConst_General.hpp>
00010 #include <stdair/basic/BasConst_Inventory.hpp>
00011 #include <stdair/basic/RandomGeneration.hpp>
00012 #include <stdair/bom/BomManager.hpp>
00013 #include <stdair/bom/FlightDate.hpp>
00014 #include <stdair/bom/LegDate.hpp>
00015 #include <stdair/bom/SegmentDate.hpp>
00016 #include <stdair/bom/LegCabin.hpp>
00017 #include <stdair/bom/SegmentCabin.hpp>
00018 #include <stdair/bom/GuillotineBlock.hpp>
00019 #include <stdair/bom/BookingClass.hpp>
00020 #include <stdair/service/Logger.hpp>
00021 // RMOL
00022 #include <rmol/basic/BasConst_Curves.hpp>
00023 #include <rmol/bom/Utilities.hpp>
00024 #include <rmol/bom/GuillotineBlockHelper.hpp>
00025 #include <rmol/bom/HistoricalBookingHolder.hpp>
00026 #include <rmol/bom/HistoricalBooking.hpp>
00027 #include <rmol/bom/EMDetruncator.hpp>
00028 #include <rmol/command/Forecaster.hpp>
00029 #include <rmol/command/Detruncator.hpp>
00030 
00031 namespace RMOL {
00032 
00033   // ////////////////////////////////////////////////////////////////////
00034   bool Forecaster::
00035   forecastUsingAdditivePickUp (stdair::FlightDate& ioFlightDate,
00036                                const stdair::DateTime_T& iEventTime) {
00037     // Build the offset dates.
00038     const stdair::Date_T& lEventDate = iEventTime.date();
00039     stdair::Date_T lRefDate (2012, boost::gregorian::Jan, 01);
00040     
00041     // 
00042     bool isSucceeded = true;
00043     const stdair::SegmentDateList_T& lSDList =
00044       stdair::BomManager::getList<stdair::SegmentDate> (ioFlightDate);
00045     for (stdair::SegmentDateList_T::const_iterator itSD = lSDList.begin();
00046          itSD != lSDList.end(); ++itSD) {
00047       stdair::SegmentDate* lSD_ptr = *itSD;
00048       assert (lSD_ptr != NULL);
00049 
00050       const stdair::Date_T& lBoardingDate = lSD_ptr->getBoardingDate();
00051       const stdair::DateOffset_T lSegmentDateOffset =
00052         lBoardingDate - lEventDate;
00053       const stdair::DTD_T lSegmentDTD = lSegmentDateOffset.days();
00054 
00055       // Build remaining DCP's for the segment-date.
00056       // TODO: treat the case where the segment departure is not the
00057       // same as the flight-date departure.
00058       stdair::DCPList_T lDCPList;
00059 
00060       if (lEventDate < lRefDate) {
00061         lDCPList = Utilities::buildRemainingDCPList (lSegmentDTD);
00062       } else {
00063         lDCPList = Utilities::buildRemainingDCPList2 (lSegmentDTD);
00064       }      
00065       
00066       //
00067       const stdair::SegmentCabinList_T& lSCList =
00068         stdair::BomManager::getList<stdair::SegmentCabin> (*lSD_ptr);
00069       for (stdair::SegmentCabinList_T::const_iterator itSC = lSCList.begin();
00070            itSC != lSCList.end(); ++itSC) {
00071         stdair::SegmentCabin* lSC_ptr = *itSC;
00072         assert (lSC_ptr != NULL);
00073 
00074         //
00075         // STDAIR_LOG_NOTIFICATION (ioFlightDate.getDepartureDate()
00076         //                          << ";" << lSegmentDTD);
00077         bool isForecasted = forecastUsingAdditivePickUp (*lSC_ptr, lDCPList,
00078                                                          lEventDate);
00079         if (isForecasted == false) {
00080           isSucceeded = false;
00081         }
00082       }
00083     }
00084 
00085     return isSucceeded;
00086   }
00087 
00088   // ////////////////////////////////////////////////////////////////////
00089   bool Forecaster::
00090   forecastUsingAdditivePickUp (stdair::SegmentCabin& ioSegmentCabin,
00091                                const stdair::DCPList_T& iDCPList,
00092                                const stdair::Date_T& iEventDate) {
00093     // Retrieve the number of departed similar segments.
00094     stdair::NbOfSegments_T lNbOfDepartedSegments =
00095       Utilities::getNbOfDepartedSimilarSegments (ioSegmentCabin, iEventDate);
00096     // TODO
00097     if (lNbOfDepartedSegments > 52) lNbOfDepartedSegments = 52; 
00098 
00099     // DEBUG
00100     STDAIR_LOG_DEBUG ("Nb of similar departed segments: "
00101                       << lNbOfDepartedSegments);
00102     
00103     // If the DCP list includes only DTD0 or the number of departed
00104     // segments are less than two, remaining demand for all classes
00105     // will be set to zero.
00106     stdair::DCPList_T::const_iterator itDCP = iDCPList.begin();
00107     assert (itDCP != iDCPList.end());
00108     const stdair::DCP_T& lCurrentDTD = *itDCP;
00109     if (iDCPList.size() == 1 || lNbOfDepartedSegments < 2) {
00110       setRemainingDemandForecastToZero (ioSegmentCabin);
00111       return false;
00112     } else {      
00113       // Initialise a holder for the unconstrained demand.
00114       UnconstrainedDemandVector_T lQEquivalentDemandVector (lNbOfDepartedSegments, 0.0);
00115       BookingClassUnconstrainedDemandVectorMap_T lBkgClassUncDemMap;
00116       const stdair::BookingClassList_T& lBCList =
00117         stdair::BomManager::getList<stdair::BookingClass> (ioSegmentCabin);
00118       for (stdair::BookingClassList_T::const_iterator itBC = lBCList.begin();
00119            itBC != lBCList.end(); ++itBC) {
00120         stdair::BookingClass* lBC_ptr = *itBC;
00121         assert (lBC_ptr != NULL);
00122         std::vector<stdair::NbOfRequests_T> lUncDemandVector (lNbOfDepartedSegments, 0.0);
00123         bool insertionSucceeded = lBkgClassUncDemMap.insert
00124           (BookingClassUnconstrainedDemandVectorMap_T::
00125            value_type (lBC_ptr, lUncDemandVector)).second;
00126         assert (insertionSucceeded == true);
00127       }
00128       
00129       // Build the DCP intervals and unconstrain censored booking figures for
00130       // each interval.
00131       stdair::DCPList_T::const_iterator itNextDCP = itDCP; ++itNextDCP;
00132       for (; itNextDCP != iDCPList.end(); ++itDCP, ++itNextDCP) {
00133         const stdair::DCP_T& lCurrentDCP = *itDCP;
00134         const stdair::DCP_T& lNextDCP = *itNextDCP;
00135 
00136         // DEBUG
00137         STDAIR_LOG_DEBUG ("Unconstrain demand for "
00138                           << ioSegmentCabin.describeKey()
00139                           << " and the DCP's " << lCurrentDCP << ", "
00140                           << lNextDCP);
00141         Detruncator::unconstrainUsingAdditivePickUp (ioSegmentCabin,
00142                                                      lBkgClassUncDemMap,
00143                                                      lQEquivalentDemandVector,
00144                                                      lCurrentDCP-1, lNextDCP,
00145                                                      iEventDate);
00146         STDAIR_LOG_DEBUG ("Detrucation successful");
00147       }
00148       
00149       // Retrieve the FRAT5 coefficient and compute the sell-up coef.
00150       FRAT5Curve_T::const_iterator itFRAT5 =
00151         DEFAULT_CUMULATIVE_FRAT5_CURVE.lower_bound (lCurrentDTD);
00152       assert (itFRAT5 != DEFAULT_CUMULATIVE_FRAT5_CURVE.end());
00153       const double lFRAT5Coef = itFRAT5->second;
00154       const double lSellUpCoef = -log(0.5) / (lFRAT5Coef - 1); 
00155       
00156       forecastUsingAdditivePickUp (ioSegmentCabin, lBkgClassUncDemMap,
00157                                    lQEquivalentDemandVector, lSellUpCoef);
00158       return true;
00159     }
00160   }
00161 
00162   // ////////////////////////////////////////////////////////////////////
00163   void Forecaster::
00164   forecastUsingAdditivePickUp (stdair::SegmentCabin& ioSegmentCabin,
00165                                const BookingClassUnconstrainedDemandVectorMap_T& iClassUncDemMap,
00166                                const UnconstrainedDemandVector_T& iUncDemVector,
00167                                const double& iSellUpFactor) {
00168     double lPriceOriMean; double lPriceOriStdDev;
00169     Utilities::computeDistributionParameters (iUncDemVector, lPriceOriMean,
00170                                               lPriceOriStdDev);
00171 
00172     // DEBUG
00173     //STDAIR_LOG_NOTIFICATION (lPriceOriMean << ";" << lPriceOriStdDev);
00174     
00175     // Retrieve the classes from low to high and compute the distributions of
00176     // product-oriented and price-oriented demand.
00177     // Retrieve the lowest class.
00178     const stdair::BookingClassList_T& lBCList =
00179       stdair::BomManager::getList<stdair::BookingClass> (ioSegmentCabin);
00180     stdair::BookingClassList_T::const_reverse_iterator itCurrentClass =
00181       lBCList.rbegin();
00182     assert (itCurrentClass != lBCList.rend());
00183     stdair::BookingClassList_T::const_reverse_iterator itNextClass =
00184       itCurrentClass;
00185     ++itNextClass;
00186     // If there is only one class in the cabin, the demand distribution of this
00187     // class is equal to the price-oriented demand distribution of the cabin.
00188     if (itNextClass == lBCList.rend()) {
00189       stdair::BookingClass* lLowestBC_ptr = *itCurrentClass;
00190       lLowestBC_ptr->setMean (lPriceOriMean);
00191       lLowestBC_ptr->setStdDev (lPriceOriStdDev);
00192     } else {
00193       // Compute the demand for higher class using the formula
00194       // Pro_sell_up_from_Q_to_F = e ^ ((y_F/y_Q - 1) * ln (0.5) / (FRAT5 - 1))
00195       for (; itNextClass != lBCList.rend(); ++itCurrentClass, ++itNextClass) {
00196         stdair::BookingClass* lCurrentBC_ptr = *itCurrentClass;
00197         assert (lCurrentBC_ptr != NULL);
00198         const stdair::Yield_T& lCurrentYield = lCurrentBC_ptr->getYield();
00199         stdair::BookingClass* lNextBC_ptr = *itNextClass;
00200         assert (lNextBC_ptr != NULL);
00201         const stdair::Yield_T& lNextYield = lNextBC_ptr->getYield();
00202 
00203         // Compute the part of price-oriented demand distributed to the
00204         // current class.
00205         const double lSellUp =
00206           exp ((1.0 - lNextYield/lCurrentYield) * iSellUpFactor);
00207         const double lPriceOriDemMeanFrac = lPriceOriMean * (1.0 - lSellUp);
00208         const double lPriceOriDemStdDevFrac = lPriceOriStdDev * (1.0 - lSellUp);
00209 
00210         // Compute the product-oriented demand distribution for the
00211         // current class.
00212         BookingClassUnconstrainedDemandVectorMap_T::const_iterator itBCUD =
00213           iClassUncDemMap.find (lCurrentBC_ptr);
00214         assert (itBCUD != iClassUncDemMap.end());
00215         const UnconstrainedDemandVector_T& lDemandVector = itBCUD->second;
00216         double lMean; double lStdDev;
00217         Utilities::computeDistributionParameters(lDemandVector, lMean, lStdDev);
00218         
00219         // Compute the demand distribution for the current class;
00220         lMean += lPriceOriDemMeanFrac;
00221         lStdDev = sqrt (lStdDev * lStdDev +
00222                         lPriceOriDemStdDevFrac * lPriceOriDemStdDevFrac);
00223         lCurrentBC_ptr->setMean (lMean);
00224         lCurrentBC_ptr->setStdDev (lStdDev);
00225         
00226         // DEBUG
00227         // STDAIR_LOG_NOTIFICATION ("Class " << lCurrentBC_ptr->describeKey()
00228         //                          << ", mean = " << lMean
00229         //                          << ", stddev = " << lStdDev);
00230 
00231         // Update the price-oriented demand
00232         lPriceOriMean *= lSellUp;
00233         lPriceOriStdDev *= lSellUp;
00234       }
00235 
00236       // Compute the demand distribution for the highest class (which is the
00237       // "current class")
00238       stdair::BookingClass* lCurrentBC_ptr = *itCurrentClass;
00239       assert (lCurrentBC_ptr != NULL);
00240       BookingClassUnconstrainedDemandVectorMap_T::const_iterator itBCUD =
00241         iClassUncDemMap.find (lCurrentBC_ptr);
00242       assert (itBCUD != iClassUncDemMap.end());
00243       const UnconstrainedDemandVector_T& lDemandVector = itBCUD->second;
00244       double lMean; double lStdDev;
00245       Utilities::computeDistributionParameters(lDemandVector, lMean, lStdDev);
00246         
00247       // Compute the demand distribution for the current class;
00248       lMean += lPriceOriMean;
00249       lStdDev = sqrt (lStdDev * lStdDev + lPriceOriStdDev * lPriceOriStdDev);
00250       lCurrentBC_ptr->setMean (lMean);
00251       lCurrentBC_ptr->setStdDev (lStdDev);
00252 
00253       // DEBUG
00254       // STDAIR_LOG_NOTIFICATION ("Class " << lCurrentBC_ptr->describeKey()
00255       //                          << ", mean = " << lMean
00256       //                          << ", stddev = " << lStdDev);
00257     }
00258   }
00259 
00260   // ////////////////////////////////////////////////////////////////////
00261   void Forecaster::
00262   setRemainingDemandForecastToZero (const stdair::SegmentCabin& iSegmentCabin) {
00263     // Set the demand forecast for all classes to zero.
00264     const stdair::BookingClassList_T& lBCList =
00265       stdair::BomManager::getList<stdair::BookingClass> (iSegmentCabin);
00266     for (stdair::BookingClassList_T::const_iterator itBC = lBCList.begin();
00267          itBC != lBCList.end(); ++itBC) {
00268       stdair::BookingClass* lBC_ptr = *itBC;
00269       assert (lBC_ptr != NULL);
00270       lBC_ptr->setMean (0.0);
00271     }
00272   }
00273   
00274   // ////////////////////////////////////////////////////////////////////
00275   bool Forecaster::
00276   forecastUsingMultiplicativePickUp (stdair::FlightDate& ioFlightDate,
00277                                      const stdair::DateTime_T& iEventTime) {
00278     // Build the offset dates.
00279     const stdair::Date_T& lEventDate = iEventTime.date();
00280       
00281     // 
00282     bool isSucceeded = true;
00283     const stdair::SegmentDateList_T& lSDList =
00284       stdair::BomManager::getList<stdair::SegmentDate> (ioFlightDate);
00285     for (stdair::SegmentDateList_T::const_iterator itSD = lSDList.begin();
00286          itSD != lSDList.end(); ++itSD) {
00287       stdair::SegmentDate* lSD_ptr = *itSD;
00288       assert (lSD_ptr != NULL);
00289 
00290       const stdair::Date_T& lBoardingDate = lSD_ptr->getBoardingDate();
00291       const stdair::DateOffset_T lSegmentDateOffset =
00292         lBoardingDate - lEventDate;
00293       const stdair::DTD_T lSegmentDTD = lSegmentDateOffset.days();
00294       
00295       //
00296       const stdair::SegmentCabinList_T& lSCList =
00297         stdair::BomManager::getList<stdair::SegmentCabin> (*lSD_ptr);
00298       for (stdair::SegmentCabinList_T::const_iterator itSC = lSCList.begin();
00299            itSC != lSCList.end(); ++itSC) {
00300         stdair::SegmentCabin* lSC_ptr = *itSC;
00301         assert (lSC_ptr != NULL);
00302 
00303         bool isForecasted = forecastUsingMultiplicativePickUp (*lSC_ptr,
00304                                                                lEventDate,
00305                                                                lSegmentDTD);
00306         if (isForecasted == false) {
00307           isSucceeded = false;
00308         }
00309       }
00310     }
00311     return isSucceeded;
00312   }
00313 
00314   // ////////////////////////////////////////////////////////////////////
00315   bool Forecaster::
00316   forecastUsingMultiplicativePickUp (stdair::SegmentCabin& ioSegmentCabin,
00317                                      const stdair::Date_T& iEventDate,
00318                                      const stdair::DTD_T& iSegmentDTD) {
00319     // Retrieving the number of anterior similar segments.
00320     const stdair::GuillotineBlock& lGuillotineBlock =
00321       ioSegmentCabin.getGuillotineBlock();
00322     stdair::NbOfSegments_T lNbOfAnteriorSimilarSegments =
00323       GuillotineBlockHelper::
00324       getNbOfSegmentAlreadyPassedThisDTD (lGuillotineBlock, iSegmentDTD,
00325                                           iEventDate) - 1;
00326     // Retrieve the number of departed similar segments.
00327     stdair::NbOfSegments_T lNbOfDepartedSegments =
00328       Utilities::getNbOfDepartedSimilarSegments (ioSegmentCabin, iEventDate);
00329     // TODO:
00330     if (lNbOfDepartedSegments > 52) {
00331       lNbOfAnteriorSimilarSegments =
00332         lNbOfAnteriorSimilarSegments  - lNbOfDepartedSegments + 52;
00333     }
00334       
00335     // DEBUG
00336     STDAIR_LOG_DEBUG ("Nb of anterior similar segments: "
00337                       << lNbOfAnteriorSimilarSegments);
00338 
00339     // If the iSegmentDTD is the last DCP or there is no anterior similar
00340     // segment, remaining demand for all classes will be set to zero
00341     stdair::DCPList_T::const_reverse_iterator itLastDCP =
00342       stdair::DEFAULT_DCP_LIST.rbegin();
00343     assert (itLastDCP != stdair::DEFAULT_DCP_LIST.rend());
00344     const stdair::DCP_T& lLastDCP = *itLastDCP;
00345     if (lNbOfAnteriorSimilarSegments < 1.0 || iSegmentDTD <= lLastDCP) {
00346       setRemainingDemandForecastToZero (ioSegmentCabin);
00347       return false;
00348     } else {
00349       // Retrieve the booking figures of the first DCP and consider them
00350       // as unconstrained demand figures.
00351       stdair::DCPList_T::const_iterator itDCP =stdair::DEFAULT_DCP_LIST.begin();
00352       assert (itDCP != stdair::DEFAULT_DCP_LIST.end());
00353       const stdair::DCP_T& lFirstDCP = *itDCP;
00354       
00355       // Initialise the unconstrained demand for classes.
00356       stdair::NbOfSegments_T lNbOfUsableSegments =
00357         GuillotineBlockHelper::
00358         getNbOfSegmentAlreadyPassedThisDTD (lGuillotineBlock, lFirstDCP,
00359                                             iEventDate);
00360       // TODO
00361       unsigned short lSize = lNbOfUsableSegments;
00362       if (lNbOfDepartedSegments > 52) {
00363         lSize = lNbOfUsableSegments - lNbOfDepartedSegments + 52;
00364       }
00365       
00366       STDAIR_LOG_DEBUG ("Nb of usable similar segments: "
00367                         << lNbOfUsableSegments);
00368         
00369       UnconstrainedDemandVector_T lQEquivalentDemandVector (lSize, 0.0);
00370       stdair::NbOfBookings_T lCurrentSegmentQEquivalentDemand = 0.0;
00371       BookingClassUnconstrainedDemandVectorMap_T lBkgClassUncDemVectorMap;
00372       BookingClassUnconstrainedDemandMap_T lCurrentSegmentBkgClassDemMap;
00373       const stdair::BookingClassList_T& lBCList =
00374         stdair::BomManager::getList<stdair::BookingClass> (ioSegmentCabin);
00375       for (stdair::BookingClassList_T::const_iterator itBC = lBCList.begin();
00376            itBC != lBCList.end(); ++itBC) {
00377         stdair::BookingClass* lBC_ptr = *itBC;
00378         assert (lBC_ptr != NULL);
00379 
00380         UnconstrainedDemandVector_T lUncDemandVector (lSize, 0.0);
00381         bool insertionSucceeded = lBkgClassUncDemVectorMap.
00382           insert (BookingClassUnconstrainedDemandVectorMap_T::
00383                   value_type (lBC_ptr, lUncDemandVector)).second;
00384         assert (insertionSucceeded == true);
00385         insertionSucceeded =
00386           lCurrentSegmentBkgClassDemMap.
00387           insert (BookingClassUnconstrainedDemandMap_T::
00388                   value_type (lBC_ptr, 0.0)).second;
00389         assert (insertionSucceeded == true);
00390       }
00391       Detruncator::
00392         retrieveUnconstrainedDemandForFirstDCP (ioSegmentCabin,
00393                                                 lBkgClassUncDemVectorMap,
00394                                                 lQEquivalentDemandVector,
00395                                                 lFirstDCP, lNbOfUsableSegments,
00396                                                 lSize);
00397 
00398       // Unconstrain the booking figures.
00399       stdair::DCPList_T::const_iterator itNextDCP = itDCP; ++itNextDCP;
00400       while (itNextDCP != stdair::DEFAULT_DCP_LIST.end()) {
00401         const stdair::DCP_T& lCurrentDCP = *itDCP;
00402         const stdair::DCP_T& lNextDCP = *itNextDCP;
00403         if (lCurrentDCP <= iSegmentDTD) {
00404           break;
00405         }
00406         Detruncator::
00407           unconstrainUsingMultiplicativePickUp (ioSegmentCabin,
00408                                                 lBkgClassUncDemVectorMap,
00409                                                 lQEquivalentDemandVector,
00410                                                 lCurrentDCP-1, lNextDCP,
00411                                                 iEventDate,
00412                                                 lNbOfDepartedSegments);
00413         ++itNextDCP; ++itDCP;
00414       }
00415 
00416       // Update the unconstrained demand for all the classes of the current
00417       // segment.
00418       lCurrentSegmentQEquivalentDemand =
00419         lQEquivalentDemandVector.at (lNbOfAnteriorSimilarSegments);
00420       BookingClassUnconstrainedDemandMap_T::iterator itBCUD =
00421         lCurrentSegmentBkgClassDemMap.begin();
00422       for (BookingClassUnconstrainedDemandVectorMap_T::iterator itBCUDV =
00423              lBkgClassUncDemVectorMap.begin();
00424            itBCUDV != lBkgClassUncDemVectorMap.end(); ++itBCUDV, ++itBCUD) {
00425         assert (itBCUD != lCurrentSegmentBkgClassDemMap.end());
00426         assert (itBCUD->first == itBCUDV->first);
00427         stdair::NbOfRequests_T& lUncDem = itBCUD->second;
00428         UnconstrainedDemandVector_T& lUncDemVector = itBCUDV->second;
00429         lUncDem = lUncDemVector.at (lNbOfAnteriorSimilarSegments);
00430       }
00431 
00432       // Forecast the remaining demand for all classes
00433       const stdair::DCP_T& lCurrentDTD = *itDCP;
00434       for (; itNextDCP != stdair::DEFAULT_DCP_LIST.end(); ++itNextDCP, ++itDCP){
00435         const stdair::DCP_T& lCurrentDCP = *itDCP;
00436         const stdair::DCP_T& lNextDCP = *itNextDCP;
00437         forecastUsingMultiplicativePickUp (ioSegmentCabin,
00438                                            lBkgClassUncDemVectorMap,
00439                                            lQEquivalentDemandVector,
00440                                            lCurrentDCP-1, lNextDCP, iEventDate,
00441                                            lNbOfAnteriorSimilarSegments,
00442                                            lNbOfDepartedSegments);
00443       }
00444 
00445       // Update the remaining demand for all classes
00446       lCurrentSegmentQEquivalentDemand = 
00447         lQEquivalentDemandVector.at (lNbOfAnteriorSimilarSegments)
00448         - lCurrentSegmentQEquivalentDemand;
00449       itBCUD = lCurrentSegmentBkgClassDemMap.begin();
00450       for (BookingClassUnconstrainedDemandVectorMap_T::iterator itBCUDV =
00451              lBkgClassUncDemVectorMap.begin();
00452            itBCUDV != lBkgClassUncDemVectorMap.end(); ++itBCUDV, ++itBCUD) {
00453         assert (itBCUD != lCurrentSegmentBkgClassDemMap.end());
00454         assert (itBCUD->first == itBCUDV->first);
00455         stdair::NbOfRequests_T& lUncDem = itBCUD->second;
00456         UnconstrainedDemandVector_T& lUncDemVector = itBCUDV->second;
00457         lUncDem = lUncDemVector.at (lNbOfAnteriorSimilarSegments) - lUncDem;
00458       }     
00459       
00460       // Retrieve the FRAT5 coefficient and compute the sell-up coef.
00461       FRAT5Curve_T::const_iterator itFRAT5 =
00462         DEFAULT_CUMULATIVE_FRAT5_CURVE.lower_bound (lCurrentDTD);
00463       assert (itFRAT5 != DEFAULT_CUMULATIVE_FRAT5_CURVE.end());
00464       const double lFRAT5Coef = itFRAT5->second;
00465       const double lSellUpCoef = -log(0.5) / (lFRAT5Coef - 1); 
00466       
00467       return forecastUsingMultiplicativePickUp(ioSegmentCabin,
00468                                                lCurrentSegmentBkgClassDemMap,
00469                                                lCurrentSegmentQEquivalentDemand,
00470                                                lSellUpCoef);
00471       
00472     }
00473   }
00474   
00475   // ////////////////////////////////////////////////////////////////////
00476   void Forecaster::forecastUsingMultiplicativePickUp
00477   (const stdair::SegmentCabin& iSegmentCabin,
00478    BookingClassUnconstrainedDemandVectorMap_T& ioBkgClassUncDemMap,
00479    UnconstrainedDemandVector_T& ioQEquivalentDemandVector,
00480    const stdair::DCP_T& iDCPBegin, const stdair::DCP_T& iDCPEnd,
00481    const stdair::Date_T& iCurrentDate,
00482    const stdair::NbOfSegments_T& iNbOfAnteriorSimilarSegments,
00483    const stdair::NbOfSegments_T& iNbOfDepartedSegments) {
00484 
00485     // Retrieve the guillotine block.
00486     const stdair::GuillotineBlock& lGuillotineBlock =
00487       iSegmentCabin.getGuillotineBlock();
00488 
00489     // Build the historical booking holders for the product-oriented bookings
00490     // of the casses and the Q-equivalent (price-oriented) bookings of the cabin
00491     const stdair::NbOfSegments_T lNbOfUsableSegments = GuillotineBlockHelper::
00492       getNbOfSegmentAlreadyPassedThisDTD (lGuillotineBlock, iDCPEnd,
00493                                           iCurrentDate);
00494       
00495     STDAIR_LOG_DEBUG ("Nb of usable similar segments: "
00496                       << lNbOfUsableSegments);
00497 
00498     if (lNbOfUsableSegments > 0) {
00499 
00500       // Parse the booking class list and unconstrain historical bookings.
00501       for (BookingClassUnconstrainedDemandVectorMap_T::iterator itBCUDV =
00502              ioBkgClassUncDemMap.begin(); itBCUDV != ioBkgClassUncDemMap.end();
00503            ++itBCUDV) {
00504         stdair::BookingClass* lBC_ptr = itBCUDV->first;
00505         assert (lBC_ptr != NULL);
00506         const stdair::MapKey_T& lBCKey = lBC_ptr->describeKey();
00507         const stdair::BlockIndex_T& lBlockIdx =
00508           lGuillotineBlock.getBlockIndex (lBCKey);
00509         UnconstrainedDemandVector_T& lUncDemVector = itBCUDV->second;
00510         
00511         STDAIR_LOG_DEBUG ("Unconstrain product-oriented bookings for "<<lBCKey);
00512         forecastUsingMultiplicativePickUp (lGuillotineBlock, lUncDemVector,
00513                                            iDCPBegin, iDCPEnd,
00514                                            lNbOfUsableSegments, lBlockIdx,
00515                                            iNbOfAnteriorSimilarSegments,
00516                                            iNbOfDepartedSegments);
00517       }
00518       
00519       // Unconstrain the Q-equivalent bookings.
00520       // Retrieve the block index of the segment-cabin.
00521       std::ostringstream lSCMapKey;
00522       lSCMapKey << stdair::DEFAULT_SEGMENT_CABIN_VALUE_TYPE
00523                 << iSegmentCabin.describeKey();    
00524       const stdair::BlockIndex_T& lCabinIdx =
00525         lGuillotineBlock.getBlockIndex (lSCMapKey.str());
00526       
00527       STDAIR_LOG_DEBUG ("Unconstrain price-oriented bookings");
00528       forecastUsingMultiplicativePickUp (lGuillotineBlock,
00529                                          ioQEquivalentDemandVector,
00530                                          iDCPBegin, iDCPEnd,
00531                                          lNbOfUsableSegments, lCabinIdx,
00532                                          iNbOfAnteriorSimilarSegments,
00533                                          iNbOfDepartedSegments,
00534                                          iSegmentCabin, iCurrentDate);
00535     }
00536   }
00537 
00538   // ////////////////////////////////////////////////////////////////////
00539   void Forecaster::forecastUsingMultiplicativePickUp
00540   (const stdair::GuillotineBlock& iGuillotineBlock,
00541    UnconstrainedDemandVector_T& ioUncDemVector,
00542    const stdair::DCP_T& iDCPBegin, const stdair::DCP_T& iDCPEnd,
00543    const stdair::NbOfSegments_T& iNbOfUsableSegments,
00544    const stdair::BlockIndex_T& iBlockIdx,
00545    const stdair::NbOfSegments_T& iNbOfAnteriorSimilarSegments,
00546    const stdair::NbOfSegments_T& iNbOfDepartedSegments) {
00547     // TODO
00548     stdair::NbOfSegments_T lSegBegin = 0;
00549     if (iNbOfDepartedSegments > 52) {
00550       lSegBegin = iNbOfDepartedSegments - 52;
00551     }
00552     // Retrieve the gross daily booking and availability snapshots.
00553     stdair::ConstSegmentCabinDTDRangeSnapshotView_T lBookingView =
00554       iGuillotineBlock.getConstSegmentCabinDTDRangeProductAndPriceOrientedBookingSnapshotView (lSegBegin, iNbOfUsableSegments -1, iDCPEnd, iDCPBegin);
00555     stdair::ConstSegmentCabinDTDRangeSnapshotView_T lAvlView =
00556       iGuillotineBlock.getConstSegmentCabinDTDRangeAvailabilitySnapshotView (lSegBegin, iNbOfUsableSegments -1, iDCPEnd, iDCPBegin);
00557     
00558     // Browse the list of segments and build the historical booking holder.
00559     const stdair::ValueTypeIndexMap_T& lVTIdxMap =
00560       iGuillotineBlock.getValueTypeIndexMap();
00561     const unsigned int lNbOfValueTypes = lVTIdxMap.size();
00562     HistoricalBookingHolder lHBHolder;
00563     std::vector<short> lDataIndexList;
00564     for (short i = 0; i < iNbOfUsableSegments-lSegBegin; ++i) {
00565       stdair::Flag_T lCensorshipFlag = false;
00566       stdair::NbOfBookings_T lNbOfHistoricalBkgs = 0.0;
00567       const short lNbOfDTDs = iDCPBegin - iDCPEnd + 1;
00568       
00569       // Parse the DTDs during the period
00570       for (short j = 0; j < lNbOfDTDs; ++j) {
00571         // Check if the data has been censored during this day.
00572         // STDAIR_LOG_DEBUG ("i: " << i << ", NbOfValues: " << lNbOfValueTypes
00573         //                   << ", BlockIdx: " << iBlockIdx << ", j: " << j);
00574         if (lCensorshipFlag == false) {
00575           if (lAvlView[i*lNbOfValueTypes + iBlockIdx][j] < 1.0) {
00576             lCensorshipFlag = true;
00577           }
00578         }
00579         
00580         // Get the bookings of the day.
00581         // STDAIR_LOG_DEBUG ("Bookings of the day: " << lBookingView[i*lNbOfValueTypes + iBlockIdx][j]);
00582         lNbOfHistoricalBkgs += lBookingView[i*lNbOfValueTypes + iBlockIdx][j];
00583       }
00584 
00585       // If there is no booking till now for this class and for this segment,
00586       // there will be no unconstraining process.
00587       stdair::NbOfRequests_T& lUncDemand = ioUncDemVector.at (i);
00588       if (lUncDemand < 1.0) {
00589         lUncDemand += lNbOfHistoricalBkgs;
00590       } else {
00591         double lBkgDemandFactor = lNbOfHistoricalBkgs / lUncDemand;
00592         HistoricalBooking lHistoricalBkg (lBkgDemandFactor, lCensorshipFlag);
00593         lHBHolder.addHistoricalBooking (lHistoricalBkg);
00594         lDataIndexList.push_back (i);
00595       }
00596       
00597       // DEBUG
00598       STDAIR_LOG_DEBUG ("Historical bkgs: " << lNbOfHistoricalBkgs
00599                         << ", censored: " << lCensorshipFlag);
00600     }
00601 
00602     // DEBUG
00603     STDAIR_LOG_DEBUG ("Unconstrain by multiplicative pick-up using EM");
00604     
00605     // Unconstrain the booking figures
00606     Detruncator::unconstrainUsingMultiplicativePickUp (lHBHolder);
00607 
00608     // Update the unconstrained demand vector.
00609     short i = 0;
00610     for (std::vector<short>::iterator itIdx = lDataIndexList.begin();
00611          itIdx != lDataIndexList.end(); ++itIdx, ++i) {
00612       short lIdx = *itIdx;
00613       stdair::NbOfRequests_T& lPastDemand = ioUncDemVector.at (lIdx);
00614       const stdair::NbOfRequests_T& lUncDemandFactorOfThisPeriod =
00615         lHBHolder.getUnconstrainedDemand (i);
00616       lPastDemand *= (1+lUncDemandFactorOfThisPeriod);
00617     }
00618 
00619     // Update the unconstrained demand for the current segment.
00620     if (lHBHolder.getNbOfFlights() > 0) {
00621       const stdair::NbOfRequests_T& lUncDemandFactorMean =
00622         lHBHolder.getDemandMean();
00623       stdair::NbOfRequests_T& lPastDemand =
00624         ioUncDemVector.at (iNbOfAnteriorSimilarSegments);
00625       lPastDemand *= (1+lUncDemandFactorMean);
00626     }
00627   }  
00628 
00629   // ////////////////////////////////////////////////////////////////////
00630   void Forecaster::forecastUsingMultiplicativePickUp
00631   (const stdair::GuillotineBlock& iGuillotineBlock,
00632    UnconstrainedDemandVector_T& ioUncDemVector,
00633    const stdair::DCP_T& iDCPBegin, const stdair::DCP_T& iDCPEnd,
00634    const stdair::NbOfSegments_T& iNbOfUsableSegments,
00635    const stdair::BlockIndex_T& iBlockIdx,
00636    const stdair::NbOfSegments_T& iNbOfAnteriorSimilarSegments,
00637    const stdair::NbOfSegments_T& iNbOfDepartedSegments,
00638    const stdair::SegmentCabin& iSegmentCabin,
00639    const stdair::Date_T& iCurrentDate) {
00640     // TODO
00641     stdair::NbOfSegments_T lSegBegin = 0;
00642     if (iNbOfDepartedSegments > 52) {
00643       lSegBegin = iNbOfDepartedSegments - 52;
00644     }
00645     // Retrieve the gross daily booking and availability snapshots.
00646     stdair::ConstSegmentCabinDTDRangeSnapshotView_T lBookingView =
00647       iGuillotineBlock.getConstSegmentCabinDTDRangeProductAndPriceOrientedBookingSnapshotView (lSegBegin, iNbOfUsableSegments -1, iDCPEnd, iDCPBegin);
00648     stdair::ConstSegmentCabinDTDRangeSnapshotView_T lAvlView =
00649       iGuillotineBlock.getConstSegmentCabinDTDRangeAvailabilitySnapshotView (lSegBegin, iNbOfUsableSegments -1, iDCPEnd, iDCPBegin);
00650     
00651     // Browse the list of segments and build the historical booking holder.
00652     const stdair::ValueTypeIndexMap_T& lVTIdxMap =
00653       iGuillotineBlock.getValueTypeIndexMap();
00654     const unsigned int lNbOfValueTypes = lVTIdxMap.size();
00655     HistoricalBookingHolder lHBHolder;
00656     std::vector<short> lDataIndexList;
00657     for (short i = 0; i < iNbOfUsableSegments-lSegBegin; ++i) {
00658       stdair::Flag_T lCensorshipFlag = false;
00659       stdair::NbOfBookings_T lNbOfHistoricalBkgs = 0.0;
00660       const short lNbOfDTDs = iDCPBegin - iDCPEnd + 1;
00661       
00662       // Parse the DTDs during the period
00663       for (short j = 0; j < lNbOfDTDs; ++j) {
00664         // Check if the data has been censored during this day.
00665         // STDAIR_LOG_DEBUG ("i: " << i << ", NbOfValues: " << lNbOfValueTypes
00666         //                   << ", BlockIdx: " << iBlockIdx << ", j: " << j);
00667         if (lCensorshipFlag == false) {
00668           if (lAvlView[i*lNbOfValueTypes + iBlockIdx][j] < 1.0) {
00669             lCensorshipFlag = true;
00670           }
00671         }
00672         
00673         // Get the bookings of the day.
00674         // STDAIR_LOG_DEBUG ("Bookings of the day: " << lBookingView[i*lNbOfValueTypes + iBlockIdx][j]);
00675         lNbOfHistoricalBkgs += lBookingView[i*lNbOfValueTypes + iBlockIdx][j];
00676       }
00677 
00678       // If there is no booking till now for this class and for this segment,
00679       // there will be no unconstraining process.
00680       stdair::NbOfRequests_T& lUncDemand = ioUncDemVector.at (i);
00681       if (lUncDemand < 1.0) {
00682         lUncDemand += lNbOfHistoricalBkgs;
00683       } else {
00684         double lBkgDemandFactor = lNbOfHistoricalBkgs / lUncDemand;
00685         HistoricalBooking lHistoricalBkg (lBkgDemandFactor, lCensorshipFlag);
00686         lHBHolder.addHistoricalBooking (lHistoricalBkg);
00687         lDataIndexList.push_back (i);
00688       }
00689       
00690       // DEBUG
00691       STDAIR_LOG_DEBUG ("Historical bkgs: " << lNbOfHistoricalBkgs
00692                         << ", censored: " << lCensorshipFlag);
00693     }
00694 
00695     // DEBUG
00696     STDAIR_LOG_DEBUG ("Unconstrain by multiplicative pick-up using EM");
00697     
00698     // Unconstrain the booking figures
00699     Detruncator::unconstrainUsingMultiplicativePickUp (lHBHolder);
00700 
00701     // Update the unconstrained demand vector.
00702     // LOG
00703     const stdair::SegmentDate& lSegmentDate = stdair::BomManager::
00704       getParent<stdair::SegmentDate, stdair::SegmentCabin> (iSegmentCabin);
00705     const stdair::FlightDate& lFlightDate = stdair::BomManager::
00706       getParent<stdair::FlightDate, stdair::SegmentDate> (lSegmentDate);
00707     const stdair::Date_T& lDepDate = lFlightDate.getDepartureDate();
00708     const boost::gregorian::date_duration lDD = lDepDate - iCurrentDate;
00709     const long lDTD = lDD.days();
00710     stdair::Date_T lRefDate (2012, boost::gregorian::Jan, 01);
00711     short i = 0;
00712     for (std::vector<short>::iterator itIdx = lDataIndexList.begin();
00713          itIdx != lDataIndexList.end(); ++itIdx, ++i) {
00714       short lIdx = *itIdx;
00715       stdair::NbOfRequests_T& lPastDemand = ioUncDemVector.at (lIdx);
00716       const stdair::NbOfRequests_T& lUncDemandFactorOfThisPeriod =
00717         lHBHolder.getUnconstrainedDemand (i);
00718       const double lUncDemThisPeriod =
00719         lPastDemand * lUncDemandFactorOfThisPeriod;
00720       const double lQEBkgThisPeriod =
00721         lPastDemand * lHBHolder.getHistoricalBooking (i);
00722       lPastDemand *= (1+lUncDemandFactorOfThisPeriod);
00723       if (lDepDate > lRefDate) {
00724         const stdair::DateOffset_T lDateOffset (7 *(52 - i) + 420);
00725         const stdair::Date_T lHDate = lDepDate - lDateOffset;
00726         STDAIR_LOG_NOTIFICATION (boost::gregorian::to_iso_string(lDepDate)
00727                                  << ";" << lDTD << ";" << iDCPBegin << ";"
00728                                  << iDCPEnd << ";"
00729                                  << boost::gregorian::to_iso_string (lHDate)
00730                                  << ";" << lUncDemThisPeriod);
00731         STDAIR_LOG_NOTIFICATION (boost::gregorian::to_iso_string(lDepDate)
00732                                  << ";" << lDTD << ";" << iDCPBegin << ";"
00733                                  << iDCPEnd << ";"
00734                                  << boost::gregorian::to_iso_string (lHDate)
00735                                  << ";" << lQEBkgThisPeriod);
00736       }
00737     }
00738 
00739     // Update the unconstrained demand for the current segment.
00740     if (lHBHolder.getNbOfFlights() > 0) {
00741       const stdair::NbOfRequests_T& lUncDemandFactorMean =
00742         lHBHolder.getDemandMean();
00743       stdair::NbOfRequests_T& lPastDemand =
00744         ioUncDemVector.at (iNbOfAnteriorSimilarSegments);
00745       lPastDemand *= (1+lUncDemandFactorMean);
00746     }
00747   }  
00748 
00749   // ////////////////////////////////////////////////////////////////////
00750   bool Forecaster::
00751   forecastUsingMultiplicativePickUp (stdair::SegmentCabin& ioSegmentCabin,
00752                                      const BookingClassUnconstrainedDemandMap_T& iClassUncDemMap,
00753                                      const stdair::NbOfRequests_T& iUncDem,
00754                                      const double& iSellUpFactor) {
00755     double lPriceOriMean = iUncDem;
00756     double lPriceOriStdDev = sqrt (iUncDem);
00757     
00758     // DEBUG
00759     STDAIR_LOG_DEBUG ("Price-oriented demand: mean = " << lPriceOriMean
00760                       << ", stddev = " << lPriceOriStdDev);
00761     
00762     // Retrieve the classes from low to high and compute the distributions of
00763     // product-oriented and price-oriented demand.
00764     // Retrieve the lowest class.
00765     const stdair::BookingClassList_T& lBCList =
00766       stdair::BomManager::getList<stdair::BookingClass> (ioSegmentCabin);
00767     stdair::BookingClassList_T::const_reverse_iterator itCurrentClass =
00768       lBCList.rbegin();
00769     assert (itCurrentClass != lBCList.rend());    
00770     stdair::BookingClassList_T::const_reverse_iterator itNextClass =
00771       itCurrentClass;
00772     ++itNextClass;
00773     // If there is only one class in the cabin, the demand distribution of this
00774     // class is equal to the price-oriented demand distribution of the cabin.
00775     if (itNextClass == lBCList.rend()) {
00776       stdair::BookingClass* lLowestBC_ptr = *itCurrentClass;
00777       lLowestBC_ptr->setMean (lPriceOriMean);
00778       lLowestBC_ptr->setStdDev (lPriceOriStdDev);
00779       if (lPriceOriMean > 0) {
00780         return true;
00781       } else {
00782         return false;
00783       }
00784     } else {
00785       bool isSucceeded = false;
00786       // Compute the demand for higher class using the formula
00787       // Pro_sell_up_from_Q_to_F = e ^ ((y_F/y_Q - 1) * ln (0.5) / (FRAT5 - 1))
00788       for (; itNextClass != lBCList.rend(); ++itCurrentClass, ++itNextClass) {
00789         stdair::BookingClass* lCurrentBC_ptr = *itCurrentClass;
00790         assert (lCurrentBC_ptr != NULL);
00791         const stdair::Yield_T& lCurrentYield = lCurrentBC_ptr->getYield();
00792         stdair::BookingClass* lNextBC_ptr = *itNextClass;
00793         assert (lNextBC_ptr != NULL);
00794         const stdair::Yield_T& lNextYield = lNextBC_ptr->getYield();
00795 
00796         // Compute the part of price-oriented demand distributed to the
00797         // current class.
00798         const double lSellUp =
00799           exp ((1.0 - lNextYield/lCurrentYield) * iSellUpFactor);
00800         const double lPriceOriDemMeanFrac = lPriceOriMean * (1.0 - lSellUp);
00801         const double lPriceOriDemStdDevFrac = lPriceOriStdDev * (1.0 - lSellUp);
00802 
00803         // Compute the product-oriented demand distribution for the
00804         // current class.
00805         BookingClassUnconstrainedDemandMap_T::const_iterator itBCUD =
00806           iClassUncDemMap.find (lCurrentBC_ptr);
00807         assert (itBCUD != iClassUncDemMap.end());
00808         double lMean = itBCUD->second;
00809         double lStdDev = sqrt (lMean);
00810         
00811         // Compute the demand distribution for the current class;
00812         lMean += lPriceOriDemMeanFrac;
00813         lStdDev = sqrt (lStdDev * lStdDev +
00814                         lPriceOriDemStdDevFrac * lPriceOriDemStdDevFrac);
00815         lCurrentBC_ptr->setMean (lMean);
00816         lCurrentBC_ptr->setStdDev (lStdDev);
00817 
00818         if (lMean > 0) {
00819           isSucceeded = true;
00820         }
00821         
00822         // DEBUG
00823         STDAIR_LOG_DEBUG ("Class " << lCurrentBC_ptr->describeKey()
00824                           << ", mean = " << lMean
00825                           << ", stddev = " << lStdDev);
00826 
00827         // Update the price-oriented demand
00828         lPriceOriMean *= lSellUp;
00829         lPriceOriStdDev *= lSellUp;
00830       }
00831 
00832       // Compute the demand distribution for the highest class (which is the
00833       // "current class")
00834       stdair::BookingClass* lCurrentBC_ptr = *itCurrentClass;
00835       assert (lCurrentBC_ptr != NULL);
00836       BookingClassUnconstrainedDemandMap_T::const_iterator itBCUD =
00837         iClassUncDemMap.find (lCurrentBC_ptr);
00838       assert (itBCUD != iClassUncDemMap.end());
00839       double lMean = itBCUD->second;
00840       double lStdDev = sqrt (lMean);
00841       
00842       // Compute the demand distribution for the current class;
00843       lMean += lPriceOriMean;
00844       lStdDev = sqrt (lStdDev * lStdDev + lPriceOriStdDev * lPriceOriStdDev);
00845       lCurrentBC_ptr->setMean (lMean);
00846       lCurrentBC_ptr->setStdDev (lStdDev);
00847       
00848       if (lMean > 0) {
00849         isSucceeded = true;
00850       }
00851 
00852       // DEBUG
00853       STDAIR_LOG_DEBUG ("Class " << lCurrentBC_ptr->describeKey()
00854                         << ", mean = " << lMean
00855                         << ", stddev = " << lStdDev);
00856       return isSucceeded;
00857     }
00858   }
00859 
00860 }