RMOL Logo  1.00.12
C++ library of Revenue Management and Optimisation classes and functions
Loading...
Searching...
No Matches
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
23
24namespace 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}
static bool isNested(const stdair::Policy &, const stdair::Policy &)
static void diffBetweenTwoPolicies(stdair::NestingNode &, const stdair::Policy &, const stdair::Policy &)
static void computeLastNode(stdair::NestingNode &, const stdair::Policy &, const stdair::SegmentCabin &)
static bool prepareDemandInput(stdair::SegmentCabin &)