6 Storybook functions

Literature has plots that are conveniently distinguished and summarized with a few words: adventure stories, coming-of-age stories, rags-to-riches, overcoming the monster, tragic fall of a heroic character, rebirth, and so on. Knowing these familiar plotlines not only gives us a vocabulary for summarizing but also helps us identify unique features of how a given story departs from the usual path.

A similar taxonomy applies to functions. Chapter 5 introduced the idea of slicing through a function—systematically varying one input while holding the other inputs constant. This Chapter introduces a small set of simple functions that characterize a surprisingly wide range of relationship types. We call this collection of functions the ☞ storybook functions ☜.

Spotted a problem?
Help us fix it!

We have constructed the storybook collection from the collective experience of modelers over many generations. We have intentionally violated convention by giving even famous functions new names that are more descriptive than the traditional names. (Note 1 points out the “official” names.)

6.1 The storybook collection

Each of the storybook functions we introduce in this Chapter takes only a single argument, that is, it has only one input going hand-in-hand with interpreting the slice plots introduced in Chapter 5.

For each storybook function, we present the story in two ways: a narrative and a graph. The action of children’s stories typically develops over time. In the language of functions, we will treat time as the input and examine the output over time. Of course, in any given modeling context, the input might be something other than time: for instance, the dose of a drug, or the speed of a vehicle.

Here is our list of storybook functions:

  • ☞ osc() ☜
  • ☞ hillside() ☜
  • ☞ hill() ☜
  • ☞ double() ☜
  • ☞ doublings() ☜
  • ☞ recip() ☜
  • ☞ steady() ☜
  • ☞ flat() ☜

To avoid unnecessary abstraction, our descriptions will assume that the function’s output is the outdoor temperature. The brief stories we will tell to introduce each function will be about how temperature changes over time.

osc()
A function that oscillates smoothly up and down, repeating itself over and over again. Such repeating functions are called ☞ periodic ☜. For example, the outdoor temperature rises and falls each day, repeating itself roughly the same way day after day. Similarly, seasonal temperatures rise and fall throughout the year, repeating each year. In these two examples, the ☞ period of oscillation ☜ is, respectively, a day and a year.

hillside()
The hillside function starts steadily, then gradually increases before leveling off. For example, hillside() might represent the temperature as a warm front moves past: a steady high temperature gradually replacing the initial low temperature.

hill()
The hill() function goes up from a baseline and then goes back down to the baseline. For instance, the hill() can describe a heat wave lasting several days before relief and the return to normal temperature.

double()
After each interval of time, the temperature doubles from what it was at the start of the interval. Think of an apocalyptic science-fiction story about climate change. The temperature is increasing over time, and as it does, various feedback mechanisms—e.g., the release of methane from melting permafrost or the increased absorption of sunlight by the ocean as an ice cap melts—strengthen.

Spotted a problem?
Help us fix it!

Eventually, of course, something has to give. The doubling pattern cannot continue indefinitely.

doublings()
Picking up on the science fiction described under double(), imagine a crusty, old meteorologist who tells how long it will take for the doubling temperature to reach any given level.

A close cousin of doublings() is magnitude(), where a 10-fold increase in the input corresponds to adding 1 to the output. Both of these are in the logarithmic family of functions. Doublings() is the logarithm with a base of 2, magnitude is the logarithm with a base of 10. The shapes of these functions are identical, so either one of them can be used to the same effect.

recip()
More science fiction, but this time about an event that triggers a new ice age. At the start, the temperature falls quickly, then more and more slowly until it steadies out. (The name recip() comes from the word “reciprocal.”)

steady()
As time increases, the temperature increases likewise. Hour to hour, the temperature increases by the same amount.

flat()
A story with no action. As time goes by, nothing changes!

Note 1: C’mon, what are the real names?

We have named the storybook functions to evoke the shape of each function. In contrast, functions in textbooks are often a mix of algebraic definitions and special names. Here is the translation from the storybook names to conventional notation.

  1. recip(x) \(\equiv 1/x\)

  2. steady(x) \(\equiv x\)

  3. flat(x) \(\equiv 1\)

  4. osc(x) \(\longrightarrow \sin(2\pi x)\).

  5. double(x) \(\equiv 2^x\) is an exponential relationship. The exponential function is \(2.718282^x\), which is similar but generally written \(e^x\).

  6. doublings(x) \(\equiv \log_2(x)\)

  7. hill(x) \(\equiv\) gaussian(x) or normal(x) distribution. The function and it’s formula are iconic, appearing even on currency notes.

Carl Gauss

Detail from the note
Figure 6. 1: The 10 Deutschmark note in 1999 graphs the Gaussian function and even gives its algebraic formula.
  1. hillside(x) is defined in terms of hill(x) using the accumulation technique described in Chapter 13. For the calculus enthusiast, it is \(\int_{-\infty}^x \text{hill}(x) dx\).

6.2 Customizing functions

We will use the storybook functions in a variety of real-world contexts. The input can be any quantity: population, age, cost, river flow, farmland area, and so on. Similarly, the output might be any quantity type that serves our modeling needs.

Spotted a problem?
Help us fix it!

How can a single function, say, hill(), work with any dimension of quantity we give it and produce as output any dimension quantity we want? There is a clever, but simple, strategem.

Input and output transformations

We have designed every storybook function to take a pure number input: with no units or dimension. Likewise, the output of all the functions is a pure number.

Each time we use a storybook function in a model, we provide an ☞ input transformation ☜ that converts the real-world quantity at hand—presumably with units and dimension—into a pure number. For the sake of illustration, suppose \(q\) is the dimension-ful quantity that we need to feed into the function, for example, miles per gallon which has dimension L / L3 (or, simplifying, L-2).

The input transformation function corresponds to a straight-line shape. Most students learn the straight-line function in the form \(m x + b\). Of course, we are calling the input \(q\), so the formula will be \(m q + b\). However, for the purpose of finding the parameter values, it is convenient to write the straight-line function in the equivalent form \(a\left(q - q_0\right)\).

The modeler selects appropriate values for the two transformation parameters, \(a\) and \(q_0\). We will discuss how to do this presently. For now, note that \(a\) and \(q_0\) are both quantities. In order for the subtraction in \(a\left(q - q_0\right)\) to make sense, the dimension of \(q_0\) must be identical to the dimension of \(q\). Or, using the notation that \([q]\) stands for “the dimension of \(q\),” we must pick \(q_0\) so that \([q_0] = [q]\).

Similarly, in order for the transformation \(a\left(q - q_0\right)\) to produce a pure number as output, we must pick parameter \(a\) to have dimension that cancels out the dimension of \((q - q_0)\). That is \([a] = 1/[q]\). Returning to the example, where the input to the model function is in miles per gallon (L-2), the quantity \(a\) must be in “gallons per mile,” that is, L2.

As an example of selecting the input transformation parameters \(a\) and \(q_0\), consider the historical data on worldwide oil production, shown in Fig 6. 2.

Figure 6. 2: World production of oil relative to the amount produced in 2023. The blue curve shows a hillside() model, described below, of how oil production changed over the 100+ years presented in the graph. The red curve shows a different model: double() with a doubling time of 10.8 years. This accords closely with the data from 1900 to the early 1960s.

Double() is an appropriate model for a phenomenon showing growth at an ever-increasing rate. For the first 60 or so years of oil production, this is an apt description. But double() provides no facility for capturing growth that starts to slow down. Instead, hillside() is the standard model for the adoption of new technology. Suppose we want to see how well the actual oil production follows the hillside() pattern. To do this, we need to pick appropriate values for \(a\) and \(q_0\) in the input transformation to hillside().

Spotted a problem?
Help us fix it!

Here is the storybook function, that is, the function that takes a pure number as input and produces a pure number as output:

Notice that hillside() returns 0.5 when the input is 0. Referring to the oil-production data (Fig 6. 2), the production index reaches half its maximum value around 1969-70. Our goal, then, is to translate the year 1969.5 to the numerical value zero. We accomplish this by setting \(q_0 = 1969.5\), so that the full input transformation expression, \(a\left(q - q_0\right)\), takes on the value zero when \(q = 1969.5\)yr.

Notice as well that hillside() reaches almost all the way to its lowest and highest levels for pure number inputs near -2 and 2. In the oil-production data, the pattern before 1969 resembles hillside(). Up through 1925, oil production was very low. With 1925 as a reference input, the value of the input transformation should be -2. In other words we want the parameter \(a\) to be such that \(a (1925 - 1969.5) \approx -2\), that is \(a = -2 / 44.5 = 0.045\) per year.

Combining hillside() with the input transformation, the model function is

\(\text{oil(year)} \equiv \text{hillside}\!\left(\strut0.045 (\text{year} - 1969.5)\right)\)

A glance at Fig 6. 2 shows serious discrepancies between the model and the oil data. Presumably, this stems from the complex history of oil use. The period up to the 1973 oil crisis was the era of cheap oil, with oil consumption increasing at an ever-faster rate. Oil became much more expensive after 1973, prompting the search for efficiency gains and for alternatives to oil altogether. In the 50 years after the oil crisis, oil utilization increased relatively slowly. Oil use may be leveling off. Alternatively, the flat pattern after 2020 may reflect the global recession during the COVID-19 pandemic.

We will look at some function-construction techniques for handling such complex dynamics in later chapters.

6.3 Input & output transformations

The hillside() function has an output near zero when the input is small, say \(x\) smaller than \(-2\) or thereabouts. For the oil production data, the output is near zero for years up to about 1910. The input transformation—\(0.045 (\text{year} - 1969.5)\)—used to align the hillside() function to the oil production data (Fig 6. 2) was selected to translate from “small in oil terms” (\(year < 1910\)) and “small in hillside() terms” (\(x < -2\)). Notice that plugging 1910 into the input transformation gives \(0.045 (1910 - 1969.5) = -2.68\).

It is also common to apply an ☞ output transformation ☜ to storybook functions. Continuing with the hillside() model of oil production, the output transformed function will look like \(A \left(\strut \text{hillside} \left(a (q - q_0)\right) - Q_0\right)\). Note that the output transformation has the same straight-line form as the input transformation! The output transformation parameters—\(A\) and \(Q_0\)—will generally have different values than the input transformation parameters \(a\) and \(q_0\).

Spotted a problem?
Help us fix it!

We set the output parameter values after selecting the appropriate input parameter values \(a\) and \(q_0\). The first trick is to find which specific input \(q\) generates an output value of zero. We will call this specific input \(q^\star\). With \(q^\star\) in hand, calculate the value of \(\text{hillside}\left(a (q^\star - q_0)\right)\) and set \(Q_0\) to this value. That way, for an input of \(q^\star\), the output-transformed value will be zero.

Finding the output transformation \(A\) parameter is done differently for the different storybook functions. We will cover this in the exercises.


New terms {