RMOL Logo  1.00.0
C++ library of Revenue Management and Optimisation classes and functions
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
MarginalRevenueTransformation.cpp
Go to the documentation of this file.
1 // //////////////////////////////////////////////////////////////////////
2 // Import section
3 // //////////////////////////////////////////////////////////////////////
4 // STL
5 #include <cassert>
6 #include <sstream>
7 #include <cmath>
8 // StdAir
9 #include <stdair/basic/BasConst_General.hpp>
10 #include <stdair/basic/BasConst_Inventory.hpp>
11 #include <stdair/bom/BomManager.hpp>
12 #include <stdair/bom/SegmentCabin.hpp>
13 #include <stdair/bom/BookingClass.hpp>
14 #include <stdair/bom/SimpleNestingStructure.hpp>
15 #include <stdair/bom/NestingNode.hpp>
16 #include <stdair/bom/Policy.hpp>
17 #include <stdair/factory/FacBomManager.hpp>
18 #include <stdair/service/Logger.hpp>
19 // RMOL
21 #include <rmol/bom/Utilities.hpp>
23 
24 namespace RMOL {
25 
26  // ////////////////////////////////////////////////////////////////////
28  prepareDemandInput (stdair::SegmentCabin& ioSegmentCabin) {
29  // Build the convex hull, then adjust the yield and demand of all
30  // classes based on the hull.
31 
32  buildNestedConvexHull (ioSegmentCabin);
33  bool isSucceeded = adjustYieldAndDemand (ioSegmentCabin);
34 
35  return isSucceeded;
36  }
37 
38  // ////////////////////////////////////////////////////////////////////
39  void MarginalRevenueTransformation::
40  buildConvexHull (stdair::SegmentCabin& ioSegmentCabin) {
41  // Reset the convex hull of the segment.
42  ioSegmentCabin.resetConvexHull();
43 
44  // The first (from the left side) point of the convex hull is the "empty"
45  // policy, i.e. the one with all fare families closed.
46  const stdair::PolicyList_T& lPolicyList =
47  stdair::BomManager::getList<stdair::Policy> (ioSegmentCabin);
48 
49  // By construction, the empty policy is the first one on the list of
50  // eligible policies.
51  stdair::PolicyList_T::const_iterator itPolicy=lPolicyList.begin();
52  stdair::Policy* lEmptyPolicy_ptr = *itPolicy;
53  assert (lEmptyPolicy_ptr != NULL);
54  ioSegmentCabin.addPolicy (*lEmptyPolicy_ptr);
55 
56  // Pointer on the current policy of the convex hull.
57  stdair::Policy* lCurrentPolicy_ptr = lEmptyPolicy_ptr;
58  bool lEndOfHull = false;
59 
60  // The end of hull is reached when from the current policy, we cannot
61  // find an other one with greater demand and total revenue.
62  while (lEndOfHull == false) {
63  // Demand and total revenue of the current policy.
64  const double& lCurrentDem = lCurrentPolicy_ptr->getDemand();
65  const double lCurrentTR = lCurrentPolicy_ptr->getTotalRevenue();
66 
67  // Search for the next policy.
68  double lGradient = 0.0;
69  stdair::Policy* lNextPolicy_ptr = NULL;
70  for (stdair::PolicyList_T::const_iterator itPol = lPolicyList.begin();
71  itPol != lPolicyList.end(); ++itPol) {
72  stdair::Policy* lPolicy_ptr = *itPol;
73  assert (lPolicy_ptr != NULL);
74 
75  const double& lDem = lPolicy_ptr->getDemand();
76  const double lTR = lPolicy_ptr->getTotalRevenue();
77  if (lDem > lCurrentDem && lTR > lCurrentTR) {
78  const double lNewGradient = (lTR-lCurrentTR)/(lDem-lCurrentDem);
79  if (lNewGradient > lGradient) {
80  lGradient = lNewGradient;
81  lNextPolicy_ptr = lPolicy_ptr;
82  }
83  }
84  }
85 
86  // Check if we have found the next policy
87  if (lNextPolicy_ptr == NULL) {
88  lEndOfHull = true;
89  } else {
90  ioSegmentCabin.addPolicy (*lNextPolicy_ptr);
91  lCurrentPolicy_ptr = lNextPolicy_ptr;
92  }
93  }
94  }
95 
96  // ////////////////////////////////////////////////////////////////////
97  void MarginalRevenueTransformation::
98  buildNestedConvexHull (stdair::SegmentCabin& ioSegmentCabin) {
99  // Reset the convex hull of the segment.
100  ioSegmentCabin.resetConvexHull();
101 
102  // The first (from the left side) point of the convex hull is the "empty"
103  // policy, i.e. the one with all fare families closed.
104  const stdair::PolicyList_T& lPolicyList =
105  stdair::BomManager::getList<stdair::Policy> (ioSegmentCabin);
106 
107  // By construction, the empty policy is the first one on the list of
108  // eligible policies.
109  stdair::PolicyList_T::const_iterator itPolicy=lPolicyList.begin();
110  stdair::Policy* lEmptyPolicy_ptr = *itPolicy;
111  assert (lEmptyPolicy_ptr != NULL);
112  ioSegmentCabin.addPolicy (*lEmptyPolicy_ptr);
113 
114  // Pointer on the current policy of the convex hull.
115  stdair::Policy* lCurrentPolicy_ptr = lEmptyPolicy_ptr;
116  bool lEndOfHull = false;
117 
118  // The end of hull is reached when from the current policy, we cannot
119  // find an other one with greater demand and total revenue.
120  while (lEndOfHull == false) {
121  // Demand and total revenue of the current policy.
122  const double& lCurrentDem = lCurrentPolicy_ptr->getDemand();
123  const double lCurrentTR = lCurrentPolicy_ptr->getTotalRevenue();
124 
125  // Search for the next policy.
126  double lGradient = 0.0;
127  stdair::Policy* lNextPolicy_ptr = NULL;
128  for (stdair::PolicyList_T::const_iterator itPol = lPolicyList.begin();
129  itPol != lPolicyList.end(); ++itPol) {
130  stdair::Policy* lPolicy_ptr = *itPol;
131  assert (lPolicy_ptr != NULL);
132 
133  const double& lDem = lPolicy_ptr->getDemand();
134  const double lTR = lPolicy_ptr->getTotalRevenue();
135  if (lDem > lCurrentDem && lTR > lCurrentTR
136  && PolicyHelper::isNested (*lCurrentPolicy_ptr, *lPolicy_ptr)) {
137  const double lNewGradient = (lTR-lCurrentTR)/(lDem-lCurrentDem);
138  if (lNewGradient > lGradient) {
139  lGradient = lNewGradient;
140  lNextPolicy_ptr = lPolicy_ptr;
141  }
142  }
143  }
144 
145  // Check if we have found the next policy
146  if (lNextPolicy_ptr == NULL) {
147  lEndOfHull = true;
148  } else {
149  ioSegmentCabin.addPolicy (*lNextPolicy_ptr);
150  lCurrentPolicy_ptr = lNextPolicy_ptr;
151  }
152  }
153  }
154 
155  // ////////////////////////////////////////////////////////////////////
156  bool MarginalRevenueTransformation::
157  adjustYieldAndDemand (stdair::SegmentCabin& ioSegmentCabin) {
158  bool isSucceeded = false;
159  stdair::NbOfClasses_T lBookingClassCounter = 0;
160  // Browse the list of policies on the convex hull, compute the differences
161  // between pairs of consecutive policies.
162  const stdair::PolicyList_T& lConvexHull = ioSegmentCabin.getConvexHull();
163  stdair::PolicyList_T::const_iterator itCurrentPolicy = lConvexHull.begin();
164  assert (itCurrentPolicy != lConvexHull.end());
165  stdair::PolicyList_T::const_iterator itNextPolicy = itCurrentPolicy;
166  ++itNextPolicy;
167  // If the nesting has only one element (the empty policy),
168  // there is no optimisation and no pre-optimisation.
169  if (itNextPolicy == lConvexHull.end()) {
170  return isSucceeded;
171  }
172 
173  // Reset the yield-based nesting structure
174  stdair::FacBomManager::resetYieldBasedNestingStructure (ioSegmentCabin);
175 
176  // Retrieve the yield-based nesting structure.
177  stdair::SimpleNestingStructure& lYieldBasedNS =
178  stdair::BomManager::getObject<stdair::SimpleNestingStructure> (ioSegmentCabin, stdair::YIELD_BASED_NESTING_STRUCTURE_CODE);
179  const stdair::NestingNodeList_T& lNodeList =
180  stdair::BomManager::getList<stdair::NestingNode> (lYieldBasedNS);
181  stdair::NestingNodeList_T::const_iterator itNode = lNodeList.begin();
182 
183  for (; itNextPolicy != lConvexHull.end();
184  ++itCurrentPolicy, ++itNextPolicy, ++itNode){
185  const stdair::Policy* lCurrentPolicy_ptr = *itCurrentPolicy;
186  assert (lCurrentPolicy_ptr != NULL);
187  const stdair::Policy* lNextPolicy_ptr = *itNextPolicy;
188  assert (lNextPolicy_ptr != NULL);
189 
190  // Retrieve the node. If there isn't any node left, create new one.
191  stdair::NestingNode* lNode_ptr = NULL;
192  if (itNode == lNodeList.end()) {
193  // Create a nesting node
194  stdair::NestingNodeCode_T lNodeCode ("XXX");
195  stdair::NestingNodeKey lNodeKey (lNodeCode);
196  stdair::NestingNode& lNestingNode =
197  stdair::FacBom<stdair::NestingNode>::instance().create (lNodeKey);
198  stdair::FacBomManager::addToList (lYieldBasedNS, lNestingNode);
199  stdair::FacBomManager::linkWithParent (lYieldBasedNS, lNestingNode);
200  lNode_ptr = &lNestingNode;
201  } else {
202  lNode_ptr = *itNode;
203  }
204  assert (lNode_ptr != NULL);
205  PolicyHelper::diffBetweenTwoPolicies (*lNode_ptr, *lNextPolicy_ptr,
206  *lCurrentPolicy_ptr);
207 
208  // Compute the adjusted yield, demand mean and demand standard deviation.
209  // Note: because of the nature of the convex hull, in the adjusted
210  // standard deviation computation, we can take the difference between
211  // the squares of the standard deviations of the two policies instead of
212  // the sum of the squares.
213  const stdair::MeanValue_T lAdjustedDemMean =
214  lNextPolicy_ptr->getDemand()-lCurrentPolicy_ptr->getDemand();
215  assert (lAdjustedDemMean > 0.0);
216  const stdair::StdDevValue_T& lCurrentStdDev =
217  lCurrentPolicy_ptr->getStdDev();
218  const stdair::StdDevValue_T& lNextStdDev = lNextPolicy_ptr->getStdDev();
219  assert (lNextStdDev > lCurrentStdDev);
220  const stdair::StdDevValue_T lAdjustedDemStdDev =
221  std::sqrt (lNextStdDev*lNextStdDev - lCurrentStdDev*lCurrentStdDev);
222  const stdair::Yield_T lAdjustedYield =
223  (lNextPolicy_ptr->getTotalRevenue()-lCurrentPolicy_ptr->getTotalRevenue())/(lAdjustedDemMean);
224  assert (lAdjustedYield > 0.0);
225  lNode_ptr->setYield (lAdjustedYield);
226 
227  // Browse the list of booking classes in the node. Set the adjusted yield
228  // for each class. However, the adjusted demand forecast will be
229  // distributed only to the first class of the list.
230  const stdair::BookingClassList_T lBCList =
231  stdair::BomManager::getList<stdair::BookingClass> (*lNode_ptr);
232  stdair::BookingClassList_T::const_iterator itBC = lBCList.begin();
233  assert (itBC != lBCList.end());
234  stdair::BookingClass* lFirstClass = *itBC;
235  assert (lFirstClass != NULL);
236  lFirstClass->setMean (lAdjustedDemMean);
237  lFirstClass->setStdDev (lAdjustedDemStdDev);
238  for (; itBC != lBCList.end(); ++itBC) {
239  stdair::BookingClass* lClass = *itBC;
240  assert (lClass != NULL);
241  lClass->setAdjustedYield (lAdjustedYield);
242  ++lBookingClassCounter;
243  }
244  }
245 
246  const stdair::BookingClassList_T& lSCBookingClassList =
247  stdair::BomManager::getList<stdair::BookingClass> (ioSegmentCabin);
248  const stdair::NbOfClasses_T lNbOfBookingClass = lSCBookingClassList.size();
249  assert (lNbOfBookingClass >= lBookingClassCounter);
250  if (lBookingClassCounter < lNbOfBookingClass) {
251  // At the last node. All the classes which haven't been added to the
252  // nesting structure will be added to the next nesting node, with
253  // an adjusted yield of zero.
254  // Retrieve the node. If there isn't any node left, create new one.
255  stdair::NestingNode* lLastNode_ptr = NULL;
256  if (itNode == lNodeList.end()) {
257  // Create a nesting node
258  stdair::NestingNodeCode_T lNodeCode ("XXX");
259  stdair::NestingNodeKey lNodeKey (lNodeCode);
260  stdair::NestingNode& lNestingNode =
261  stdair::FacBom<stdair::NestingNode>::instance().create (lNodeKey);
262  stdair::FacBomManager::addToList (lYieldBasedNS, lNestingNode);
263  stdair::FacBomManager::linkWithParent (lYieldBasedNS, lNestingNode);
264  lLastNode_ptr =
265  stdair::BomManager::getObjectPtr<stdair::NestingNode>(lYieldBasedNS,
266  lNodeKey.toString());
267  } else {
268  lLastNode_ptr = *itNode;
269  }
270  assert (lLastNode_ptr != NULL);
271  const stdair::Policy* lLastPolicy_ptr = *itCurrentPolicy;
272  assert (lLastPolicy_ptr != NULL);
273  PolicyHelper::computeLastNode (*lLastNode_ptr, *lLastPolicy_ptr,
274  ioSegmentCabin);
275  }
276 
277  isSucceeded = true;
278  return isSucceeded;
279  }
280 
281 }