ProBP Optimization¶
In this example, we will look at the benefits of using a path-based RM system. We will use PassengerSim to simulate the same demo network as the previous example, except we will have one of the carriers using ProBP instead of EMSR-B.
import passengersim as pax
pax.versions()
passengersim 0.59.dev9+ga3f6c1b98 passengersim.core 0.59.dev1+g671876c7d
Just as in the previous example, we begin by loading the network configuration from YAML input files.
cfg = pax.Config.from_yaml(pax.demo_network("3MKT/DEMO"))
For this simulation, however, we don't want to just use the
all the inputs exactly as loaded from our demo config files.
In order to change the RM system for AL1, we can assign a new
named system to the rm_system attribute for that carrier:
cfg.carriers.AL1.rm_system = "P"
The ProBP RM system is one of the standard RM systems available for use in PassengerSim, so this is all that's needed to use it. But for this example, we're also going to explore some of the "options" that can be set on many of the pre-configured RM systems. For the ProBP system, one of the options is the use of a bid price vector, instead of a fixed daily bid price. For our first look at this system, we'll turn off the bid price vector, like this:
cfg.carriers.AL1.rm_system_options = {"bid_price_vector": False}
Now that we've finished modifying our configuration, we can use it to create and run a simulation
sim = pax.MultiSimulation(cfg)
summary = sim.run()
We can then review some of the same reports as the previous example, to get an idea of the benefit of using the path-based ProBP system instead of the leg-based EMSR-B.
summary.fig_carrier_revenues()
Since the 3MKT network is perfectly symmetric (both AL1 and AL2 have identical flight legs with identical schedules and capacities in every possible market) when both carriers were running the "E" system we saw average revenues that were neck-and-neck at $90.75k. Now with AL1 running "P", we see average revenues on that carrier have gone up almost 1%, to $91.52.
summary.fig_carrier_load_factors()
We also see a change in load factors, with AL1 achieving those improved revenue numbers while also having a slightly lower average load factor.
summary.fig_carrier_rasm()
The revenue per available seat mile follows the same pattern as the top line revenue number (as it must -- we have not changed the "ASM" denominator).
summary.fig_fare_class_mix()
In the fare class mix, we can see that AL1 is achieving these results by cutting back on seats sold to the lower fare classes, leaving more room for the higher revenue fare classes who tend to book closer to departure.
summary.fig_bookings_by_timeframe(by_class=True, by_carrier="AL1")
The head-to-head revenue figure can give us a little more insight into why the path-based algorithm performs better. When we looked at this figure for the E vs E simulation, the cloud of simulation results was clearly symmetric around the red "even" line: on busy days or slow days, the two carriers tended to do better or worse together, with the likelihood of AL1 or AL2 coming out ahead being more or less a coin flip no matter what the overall demand looked like on any sampled day.
summary.fig_carrier_head_to_head_revenue("AL1", "AL2")
The story is different in the figure above: there is a distinct bias visible in the point cloud relative to the red "balanced" line. On slow days, both carriers tend to end up with less revenue than average but AL2 is typically takes a smaller hit than AL1. But on busier days, which in this simulation are more frequent than slow days, AL1 tends to benefit more.
Using the Bid Price Vector¶
Earlier, we turned off the bid price vector option for ProBP. Let's turn it back on and see what happens.
cfg.carriers.AL1.rm_system_options = {"bid_price_vector": True}
sim2 = pax.MultiSimulation(cfg)
summary2 = sim2.run()
We can compare two simulation runs using PassengerSim's Contrast tools.
from passengersim.contrast import Contrast
comp = Contrast({"w/o BP Vector": summary, "with BP Vector": summary2})
Variants of many of the visualizations we've already seen are
available for the Contrast class, including for comparing carrier
summary statistics like revenue and load factor.
comp.fig_carrier_revenues()
It appears that AL1 is able to squeeze out even more revenue by using the bid price vector.
comp.fig_carrier_load_factors()
Unlike the switch from E to P, which resulted in more revenue but lower load factors, the addition of the bid price vector now results in more revenue and higher load factors.
To understand what is happening a little bit more, we can examine the bid price history figure.
comp.fig_bid_price_history(by_carrier="AL1")
This figure shows that when we turn on the bid price vector, the average bid prices for legs operated by AL1 tend to run a little bit lower across most of the booking horizon, but run much higher in the final few days before departure.