[ Main page | About Your System | Knowledge Bases | Rule Traces | Demand Modelling | About Fuzzy Logic | The Importance of Explainability | Commercialising

Demand Modelling

Implicit Versus Explicit Demand

In Knowledge Bases, I introduced fuzzy logic for dynamic pricing by working through six knowledge bases. They showed how pricing rules can react directly to variables such as market segment and time till use. We can call this Explicit Variable Modelling. It directly integrates diverse individual factors into the pricing model, providing granular control and insights.

This granularity comes at a cost. Imagine pricing a flight to Alpenstadt when a variety of events are going on. The rules would have to loop round these, inspecting each one for attributes such as its type, its intrinsic attractiveness, its time elasticity (how much it matters if arrival time is changed), weather forecasts, and so on. The code would become very complicated.

An alternative is what we might call Demand-Based Modelling. This calculates demand as an indicator that implicitly subsumes such factors. It offers a streamlined approach that simplifies decision-making but may obscure the impact of individual variables.

Although this moves a lot of work elsewhere, it isn't sufficient. Unexpected events can occur, and the travel consultant will need to take these into account. That is what the seventh knowledge base, Σ🗠😷😍, does: it assumes a demand model, and provides the levers to tweak it with.

One lever is health_concerns. As Covid showed, health worries about a destination can arise very quickly. The knowledge base contains rules which add this in as an independent variable influencing price.

Another variable is event_popularity_change. This allows for sudden negative or positive swings to an event's popularity, such as a suddenly-invited star entertainer, and adjusts price accordingly.

It Isn't Only the Rules

A key point I want to make is that expert systems aren't just about their rules. The inputs to the rules have to be produced, and the outputs from the rules have to be consumed. The demand models that this page presents below demonstrate this. Depending on the product or service you want to price, you may want to mention this to the inspectors. AI languages such as Prolog don't only make writing expert system rules easier, they also help with ancillary software. You, or your developers, will need to code this, and it will help to have such a language available, with the experts who can program in it.

Demand Modelling for Alpenstadt

Skiing Season

One of the simplest event models in the simulator is for skiing season, so I'll begin with that. It introduces the idea of event key points, analogous to key frames in animation.

My Prolog code for skiing-season demand is this:

skiing_season_demand( 'start'- 10,  0, "Just before early season skiing, before holiday peaks."                    ).
skiing_season_demand( 'start'    , 10, "Early season skiing, before holiday peaks."                                ).
skiing_season_demand( 'start'+ 32, 30, "Sustained skiing interest post-holidays, including school vacation peaks." ).
skiing_season_demand( 'start'+ 75, 15, "Late season and spring skiing interest."                                   ).
skiing_season_demand( 'start'+125, 15, "Late season and spring skiing interest."                                   ).
skiing_season_demand( 'start'+145,  0, "Late season and spring skiing interest finishes."                          ).

The construct is rather like a database table with three columns. The first gives a symbolic time. This represents a date relative to the start of the season, defined elsewhere as 1st December. The second column gives the expected demand increment for that date. These demand increments are percentages of baseline demand, and generally are between 0 and 200. Finally, the third column is text giving the reason for the value. This isn't used in calculations, but is handy if the system needs to justify them to the user.

As in animation, the demand for a point between two key points is got by interpolation. The outermost two key points should be zero: they join the non-zero region to its environment. The system assumes that all demand increments to the left of the first key point or the right of the last are zero.

Regarding accuracy, this is a demonstration only, so is necessarily simplified. I have arbitrarily assumed that skiing season starts on December 1st and continues till April 15th. In practice, a demand calculator would be much more nuanced, considering factors such as historical demand, typical weather, and weather forecasts. The section on trade and business fairs below hints at how to do this.

I have also given fairly small demand increments for the first part of the season, as some of the skiing demand may be subsumed by that for Christmas holidays. A real-life application would have to avoid double-counting.

Christmas

Here is my Christmas demand model.

christmas_demand( 'Christmas Eve'-16
                , 0
                , "Just before start of school holidays and early vacation departures. 
                   Increased demand as families travel for extended holiday stays."
                ).
				
christmas_demand( 'Christmas Eve'-14
                , 10
                , "Start of school holidays and early vacation departures. 
                   Increased demand as families travel for extended holiday stays."
                ).

christmas_demand( 'Christmas Eve'-2
                , 20
                , "Significant increase in demand as travellers make final journeys 
                   to their holiday destinations."
                ).

christmas_demand( 'Christmas Eve'
                , 30
                , "Peak travel day with highest demand as individuals rush to 
                   reach family and holiday destinations."
                ).

christmas_demand( 'Christmas Day'
                , 20
                , "Slightly reduced demand compared to Christmas Eve, as most 
                   travellers have already reached their destinations."
                ).

christmas_demand( 'Boxing Day'
                , 30
                , "Renewed surge in travel as individuals take advantage of 
                   the holiday for short vacations or return trips."
                ).

christmas_demand( 'New Year\'s Eve'-1
                , 40
                , "Rising demand for travel to major cities for New Year 
                   celebrations and events."
                ).

christmas_demand( 'New Year\'s Eve'
                , 50
                , "Peak demand for New Year's Eve celebrations, particularly 
                   in major cities known for public events and parties."
                ).

christmas_demand( 'New Year\'s Day'
                , 20
                , "Decreased demand as many travelers recover from New Year's 
                   Eve celebrations and begin their return journeys home."
                ).

christmas_demand( 'Epiphany'-1
                , 10
                , "Moderate increase in travel, particularly in regions 
                   where Epiphany is a significant holiday, marking the end of 
                   the extended holiday season."
                ).

christmas_demand( 'Epiphany'+1
                , 0
                , "End of extended holiday season."
                ).

This follows the same pattern, a table with three columns. The backslashes in the date names are a coding convention for embedded quotes, and don't matter here. As far as holiday behaviour is concerned, the model is more involved than for skiing, and considers school holidays, Christmas Eve and Christmas Day, New Year celebrations, and Epiphany. A real-world model would also take into account entertainments such as Weihnachtsmärkte, Christmas markets.

Easter

Easter works similarly. Once again, there are school holidays, family visits, and religious occasions. As with the other events, the date interpreter needs actual values for "Easter Sunday" and other symbolic names. I provide these by another database table, not shown.

easter_demand( 'Easter Sunday'-16
             , 0
             , "Just before start of school holidays. Slight increase in 
                travel as families plan extended stays."
             ).

easter_demand( 'Easter Sunday'-14
             , 5
             , "Start of school holidays. Slight increase in travel as 
                families plan extended stays."
             ).

easter_demand( 'Easter Sunday'-7
             , 10
             , "Increase in travel continues as more families start 
                their holiday travels."
             ).

easter_demand( 'Easter Sunday'
             , 20
             , "Peak demand on Easter Sunday for both religious and 
                non-religious travellers."
             ).

easter_demand( 'Easter Sunday'+1
             , 15
             , "Slight decrease in demand after Easter Sunday as 
                some travellers return home."
             ).

easter_demand( 'Easter Monday'
             , 10 
             , "Last peak in demand due to public holiday before a gradual 
                return to baseline."
             ).

easter_demand( 'Easter Sunday'+7
             , 2
             , "Demand starts to normalise as school holidays come to an 
                end and travellers return home."
             ).

easter_demand( 'Easter Sunday'+8
             , 0
             , "End of school holidays."
             ).

Alpenfest

Alpenfest is Alpenstadt's equivalent to Oktoberfest, and follows the same rules: starting on a mid-September Saturday, running for 16 days, then ending on the first Sunday in October. My demand model is below.

alpenfest_demand( 'Alpenfest Start'-9,  0, "Just before early arrivals and pre-festival activities." ).
alpenfest_demand( 'Alpenfest Start'-7, 10, "Early arrivals and pre-festival activities."             ).
alpenfest_demand( 'Alpenfest Start'  , 30, "Opening day of Alpenfest with peak demand."              ).
alpenfest_demand( 'Alpenfest Start'+7, 20, "Mid-festival period with sustained high demand."         ).
alpenfest_demand( 'Alpenfest End'    , 25, "Final weekend of Alpenfest with high demand."            ).
alpenfest_demand( 'Alpenfest End'+1  ,  5, "A few people fly in to meet friends, staff or stars who 
                                            were at Alpenfest."                                      ).
alpenfest_demand( 'Alpenfest End'+3  ,  0, "End of Alpenfest demand." ).

As staged events, Oktoberfest and Alpenfest have features that weren't considered above. These include the need for pre-festival setting up, and perhaps also entertainments timetabled for before the official start. I've glued a week's pre-start demand to the curve to cater for this.

Trade and Business Fairs

Alpenstadt hosts a huge variety of trade and business fairs, from Smart Home Technologien Expo and the Alpen Wein und Käse Festival to the Alpen Architektur und Design Symposium and a variety of fairs devoted to Weißwurst, pretzels, Käsespätzle and other goodies. To demonstrate how we might handle these, I've elaborated the demand model.

Anyone who has put on a folk-night or a workshop will have found themselves asking how interesting the topic is, how many people there are to watch and listen, and how many came in past years. These three factors can be formalised as TP (Theme Popularity), IS (Industry Size), and HA (Historical Attendance). The simulator stores a value for each parameter with every event in its database. If when generating a scenario, it decides to include an event in it, it looks up the associated values for these, and passes them to the first argument of fair_demand, below. This enables fair_demand to use those values under the names TP, IS, and HA when calculating the key points. Here is the code.

% Before the fair starts.
fair_demand( [_,_,_]   , 'start'-10,  0, "Just before early arrivals for setup and pre-fair meetings." ).
	
fair_demand( [TP,IS,HA], 'start'- 7, DI, "Early arrivals for setup and pre-fair meetings."             ) :-
    DI is 5 + TP*0.5 + IS*0.3 + HA*0.01.

fair_demand( [TP,IS,HA], 'start'- 3, DI, "Attendees arriving for pre-fair workshops or networking."    ) :-
    DI is 10 + TP*0.7 + IS*0.5 + HA*0.015.

fair_demand( [TP,IS,HA], 'start'   , DI, "Opening day of the fair, highest influx of attendees."       ) :-
    DI is 20 + TP + IS*0.7 + HA*0.02.

% During the fair.
fair_demand( [TP,IS,HA], 'mid'     , DI, "Mid-fair, continued high demand for flights."                ) :-
    DI is 15 + TP*0.9 + IS*0.6 + HA*0.018.

fair_demand( [TP,IS,HA], 'end'-   2, DI, "As the fair concludes, last-minute attendees and ongoing activities." ) :-
    DI is 10 + TP*0.8 + IS*0.4 + HA*0.015.

% After the fair ends.
fair_demand( [_,_,_]   , 'end'+   2,  0, "Arrivals end as the fair wraps up."                          ).

As with Alpenfest, there can be demand for people flying in to set up or meet pre-event.

Concerts

Alpenstadt is a big internationally-renowned city, so one should not be surprised to find visits from musical stars such as Coldplay, Taylor Swift, and Goran Bregović. But it also hosts German-language concerts, in genres such as Alpenrock, played by performers such as Hubert van Goisern und Die Alpinkatzen, Haindling, and the Dolomite Sextet Lienz. Because of how German speakers are bunched together in Europe, these end up as mainly regional interest. Finally, there are events of interest perhaps only within Alpenstadt itself, such as Beatles and Jethro Tull cover nights.

Corresponding to these, I've divided concerts into three levels. Level 1 is world-famous stars; level 2 is regional interest; level 3, which barely affects flight demand, is fun nights and cover nights. As concerts aren't usually annually repeated, it's easier to do this than to use the TP, IS, HA model. This is the code. As usual, the curve is bounded on each side by zero key points, and intermediate values are gotby interpolation.

world_famous_concert_demand( 'start'-9,  0, "Just before early arrivals for the concert."       ).
world_famous_concert_demand( 'start'-7, 10, "Early arrivals for the concert."                   ).
world_famous_concert_demand( 'start'-3, 20, "Increased arrivals, including international fans." ).
world_famous_concert_demand( 'start'  , 50, "Peak demand on concert day."                       ).
world_famous_concert_demand( 'end'    ,  5, "A bit of demand as visitors fly in to meet stars, 
                                             staff or friends who were there."                  ).
world_famous_concert_demand( 'end'+3  ,  0, "End of concert-related demand."                    ).

regionally_known_concert_demand( 'start'-7,  0, "Just before early arrivals, mostly local and regional fans" ).
regionally_known_concert_demand( 'start'-5,  5, "Early arrivals, mostly local and regional fans"             ).
regionally_known_concert_demand( 'start'-2, 10, "Increase in arrivals as the concert approaches"             ).
regionally_known_concert_demand( 'start'  , 30, "Peak demand on concert day"                                 ).
regionally_known_concert_demand( 'end'    ,  1, "A bit of demand as visitors fly in to meet stars, 
                                                 staff or friends who were there."                           ).
regionally_known_concert_demand( 'end'+2  ,  0, "End of concert-related demand"                              ).

fun_night_concert_demand( 'start'-1, 0, "Just before slight increase in demand."            ).
fun_night_concert_demand( 'start'  , 2, "Slight increase in demand due to local attendees." ).
fun_night_concert_demand( 'end'    , 1, "Minimal post-event demand, primarily local."       ).
fun_night_concert_demand( 'end'  +1, 0, "End of concert-related demand."                    ).

Other Cultural Events

This includes events such as the Alpenstadt Ice-Sculpture Extravaganza, Alpine Fairy Tale Reenactments, and Live Alligator Wrestling. For simplicity, I have followed the same pattern as with concerts. I therefore shan't show the code here.

Jocelyn Ireson-Paine
www.j-paine.org
www.jocelyns-cartoons.uk