Discrete-Time Dynamical Systems
Overview and Objectives
This lecture introduces simulation modeling via system-dynamics. The presentation is exercise based. A sequence of exercises guide the development of a simple discrete-time simulation model of exponential aggregate population growth. This illustrative application of simulation methods contains no explicit agents. Nevertheless, it serves as a springboard for the development of modeling, programming, and data visualization skills that are required by the agent-based models of future lectures.
The lecture exercises lead to a complete computational implementation of a simple population growth model, along with a controlling GUI and visualizations. Before attempting the exercises below, skim this lecture from start to finish to understand the overall structure. Be sure to garner a good comprehension of the conceptual model. Then work through the lecture step by step, implementing the computational model as you go. Keep your computational implementation runnable by using stubs whenever this is helpful. Verify the evolving implementation at each step by running the provided tests or by creating your own tests.
Goals and Outcomes
This lecture introduces model formulation, model implementation, and system simulation. It explores the attendant simulation issues in a tentative way, which later lectures elaborate in detail and depth. Central to this lecture is the design and implementation of a simple system-dynamics model of population growth.
Prerequisites
Each section of this lecture includes exercises are structured to foster deeper understanding through active exploration of the core material. These exercises introduce and refine programming skills that future lectures require. The exercises below presume the mastery of a small amount of preliminary material, indicated by the following prerequisites.
Modeling and Simulation
system: components + relations
model types: static and dynamic
modeling strategies: abstraction and simplification
model development: conceptual model vs computational implementation
why simulate?
Systems and Models
A system is an object or process with distinguishable components, along with the structure of their interrelations. This course uses the term model to denote any representation of a system—either an actual system, or an imaginable system. This is an extremely broad notion of what constitutes a model. Not only does it encompass normal human understandings of the quotidian world, it also applies to fictional worlds that exist only in a modeler’s imagination.
Despite this broad understanding of the fundamental nature of a model, the models considered in this course are rather narrow in scope. They are intended to elucidate some aspect of the real world, however indirectly. Even more narrowly, this course focuses on models of systems that have particular interest for social scientists. Finally, the models in this course have a pedagogical purpose: they foster key skills use in agent-based modeling and simulation.
Model Development
Model development encompasses all efforts to conceptualize, implement, or improve a model. Model development is typically driven by specific interests in a particular system, known as the target system. This lecture develops a system-dynamics model of population growth; the target system is a population undergoing exponential growth.
Some models are attempts to approximate all the salient properties of a real world system. Other models are highly simplified and abstract attempts to explore the implications of conditions that have little likelihood of occurring in the real world. In either case, model development requires simplification and abstraction: the modeler ignores real-world features that either are too costly to consider or are judged to be irrelevant to the model-development goals.
Modeling choices reflect the goals of the modeler as well as perceived constraints on model development. For the most part, the simulation models in this course are highly simplified. Their primary purpose is instructional, and the level of abstraction is very high. For example, the population growth model of this lecture treats the entire world population as a single entity, and the growth rate of this population is constant. (Later lectures address these particular simplifications.)
Model Development Process
Model development typically begins with a crude conceptual model. This is an abstract and perhaps somewhat vague representation of a system. Model development elaborates this initial conceptual model so that it becomes more precisely specified and more useful.
Sometimes model development is purely verbal or graphical. However, scientific model development often involves implementing the conceptual model as a mathematical model, computational model, or physical model. The focus of this course is computational social science, and it emphasizes turning conceptual models into computational models. For example, as a simple introduction, this lecture shows how to convert a basic conceptual understanding of exponential population growth into a computational model that produces population projections.
Relatively simple computational models can often solve problems that are mathematically challenging or even intractable [Humphreys-2002-PhilSci]. For example, as shown in subsequent lectures, computational models facilitate the characterization of complex dynamics and can provide ready answers to vexing distributional questions.
Figure f:modelingProcess provides a stylized illustration of key steps in model development and analysis. [1] Scientific model development typically involves hypothesis generation, experimentation, and data analysis. This course correspondingly shows how to run simulation experiments, to analyze the resulting data, and to report the experimental results.
Static and Dynamical Models
A model may be static or dynamical. A static model characterizes the relationships between the parts of an unchanging system. Social scientists often use static models to describe equilibrium states of a system. For example, a first course in economics typically illustrates price determination in a single market as a static equilibrium between supply and demand. In contrast, a dynamical model characterizes the evolution of a system over time. This course focuses on dynamical models.
Social scientists often use dynamical models to describe the outcomes of interactions between components of a social system. Examples include the evolution of voting patterns, the determinants of technological change, the process of population growth, or the spread of a contagion. In both static and dynamical models, the notion of time is model specific and may be only loosely related to real-world temporality.
Dynamical models can describe the behavior of a system that remains out of equilibrium or moves towards equilibrium. Although the evolution of the system over time may be deterministic, as in the current lecture, dynamical simulation models often include a stochastic component. That is, they build in some randomness, which often represents aspects of the target system about which we are ignorant or uncertain. Developing a detailed description of the behavior of a real-world stochastic dynamical system is very challenging. However, the models in this course are simple enough to be built quickly and understood fully.
Conceptual Models and Their Implementations
Model development often begins with a somewhat vague and incomplete conceptual model. This conceptual model is typically descriptive. It might be verbal, diagrammatic, or even mathematical, but it is not implemented in a programming language. This course demonstrates how to transform conceptual models into computational models. A central goal of each lecture is to implement a conceptual model as a computational model, in order to simulate the behavior of a target system.
A computational model is an algorithmic implementation of a conceptual model that is executable on some kind of computer. Modern simulations typically run on electronic digital computers. There is a natural give and take between conceptual models and computational models. Concepts drive computational modeling, and the process of computational modeling can lead to improvements in the conceptual model. In Figure f:modelingProcess, refinement of the conceptual model and of the implementation take place in parallel, making use of the results of previous refinements.
Scientists often build models in order to explore how a target system behaves. A computational implementation produces a computational model of the system. This is a simulation model: it should behave like the target system. Running the model on a computer produces a simulation.
A dynamical simulation explores the evolution of a system over time. One goal of simulation is to generate insights into the behavior of the target system. Some simulations are purely playful explorations. More often, the purpose of a simulation experiment is to gain insight into the behavior of a real world dynamical system.
Simulation has proved fruitful in elucidating many real-world systems. Simulation modeling has therefore spread widely, including applications in science, engineering, and business. An example from physical science would be simulations of the interplay of molecular forces during protein folding. An important example from medicine arises in the drug discovery process, which involves simulating the interaction between chemical compounds and biological targets. An example from engineering would be simulations of airflow over different wing designs to detect effect on lift and control. An example from business would be simulations of how supply chains affect the reliability of product distribution.
Why Use Simulation?
practicality
ethicality
The applied physical sciences often use simulation to produce speedier and cheaper investigations of target systems. Practically, simulations can be the most cost effective means to rigorously investigate the behavior of a physical system.
This course emphasizes social-science applications of modeling and simulation. Social scientists use simulations for additional reasons, both practical and ethical. Simulations are often be the only practicable means of investigation. For example, it is infeasible to design a large-scale evacuation plan by deliberately experimenting with large-scale evacuations in the real world. We may also be interested in responses of systems to variables that we cannot ethically manipulate, including environmental variables that affect human safety.
Key competitors to simulation modeling are narrative modeling and mathematical modeling. Narrative modeling is the verbal presentation and analysis of a conceptual model. Mathematical modeling is the mathematical presentation and analysis of a conceptual model. Both of these can share with simulation modeling the ability to conduct experiments that would be infeasible or unethical to attempt in the real world. Each has its strengths and weaknesses; there is no best approach to modeling. The choice must be pragmatic, considering the appropriateness of each approach to the problem at hand. As a key consideration, simulation modeling often enables the incorporation of salient details that might bedevil mathematical modelers while avoiding the lack of rigor that may bedevil narrative modelers.
System Dynamics
stock variables and flow variables
physical implementations vs software implementations
discrete-time dynamical systems
system state
evolution rule
function iteration.
A dynamical system evolves over time. A model of a dynamical system characterizes this evolution. Abstractly, a dynamical model must characterize the possible states of the system and provide an evolution rule for the system. The evolution rule describes how the system moves from one system state to another. Given an initial system state, the evolution rule determines how the system state evolves over time. It determines the trajectory of this system.
Modeling Dynamical Systems
System dynamics and agent-based modeling are two prominent yet divergent approaches to the computational modeling and simulation of evolving systems. Both approaches have proved their worth for social scientists. This course focuses on the agent-based approach, but this lecture and the next provide a brief introduction to the system-dynamics approach. This introduction emphasizes a basic structure of simulation modeling that both approaches share.
Stocks and Flows
stock variable: measurement at any point in time
flow variable: measurement refers to a period of time
The system-dynamics approach to social-science modeling and simulation gained popularity in the 1950s. In system-dynamics models, aggregate outcomes result from the dynamic interplay of aggregate quantities. The model dynamics result from the interplay of aggregative stock variables and flow variables.
A flow variable represents a rate of change. Flow variables must be defined with respect to a period of time, with reference to a specific time unit. In contrast, a stock variable is defined at any point of time, without reference to a time unit.
For example, in a model of human disease transmission, the stock variables might include the number of susceptible individuals, the number of infected and infectious individuals, and the number recovered and immune individuals. The magnitudes of these variables can be measured at any point in time without reference to the passage of time. The rates of change of these stocks are flow variables, which must reference a (real or virtual) period of time. As another example, the total population is a stock variable in a simple model of population growth, while the annual population growth is a flow variable.
A system-dynamics model characterizes how the overall behavior of a system is determined by the feedback between the stocks and flows in the system. In an infectious disease model, when the number of infectious individuals and the number of susceptible individuals are both large, then we expect a high rate of flow from the susceptible group to the infectious group. In a population growth model, there may be feedback from the level of the population to the size of its change. Dynamic interactions between the stocks and flows determine the evolution of the system.
Mathematical versus Computational Implementations
A system-dynamics model is typically implemented as a collection of computational functions that characterize these stock-flow interactions. However, other implementations are possible. For example, very simple models are amenable to exact mathematical solution. The population model of this lecture is in this class. Such models are pedagogically useful, since the exact correspondence between the mathematical model and the simulation model permits an easy comparison between the simulation results and the mathematical solution. This provides helpful guidance to simulation novices, who can readily check the validity of their computational implementation.
MONIAC
Demonstration videos:
Among economists, a particularly famous system-dynamics model is the MONIAC. The name is considered an acronym for monetary national income analogue computer. The MONIAC is an early computational implementation of a macroeconomic model, but it did not run on an electronic computer. Instead, the original implementation used a special-purpose hydro-mechanical analog computer. This physical implementation of the model is widely known as the Phillips Machine, after its creator A.W. Phillips. It is also known as the Newlyn-Phillips Machine, which adds attribution to Walter Newlyn, an associate of Phillips who did much of the construction and assisted with the simulation experiments.
The MONIAC is very early example of a system-dynamics simulation model. It is a computational implementation of a macroeconomic model of the UK that was popular in the 1940s. Water levels represent stock variables (such as the money supply), and water flow rates represent flow variables (such as the rate of government expenditure). Phillips used this model to simulate the macroeconomic effects of changes in monetary and fiscal policy. The simulations also illustrated the importance of coordinating monetary and fiscal policy. These simulations were fundamentally dynamical: hydrological interactions evolved over time, and the full effects of policy changes emerged gradually.
Methodological Differences: Aggregation
As subsequent lectures demonstrate, the macrostate in an agent-based model emerges from the interactions of many individual agents. System-dynamics models adopt a fundamentally different methodological approach: they rely heavily on aggregation. For example, the MONIAC is a purely aggregative model. It produces aggregate macroeconomic outcomes (such as GDP) through the interaction of macroeconomic aggregates (such as total consumption). Individual agents are nowhere to be found in the model implementation, even if they are implicit in the underlying conceptual model.
Implementation Differences
The most striking difference of the MONIAC from contemporary simulation methods is its physical implementation. Nowadays, system-dynamics models are typically implemented in software. A typical implementation of a system-dynamics model on a digital computer comprises a set of computational functions. The computer executes these functions in order to simulate the dynamic behavior of a system.
While this difference is practically important, it is not conceptually fundamental. Nevertheless, it is practically important, because physical models are costly and time-consuming to build (and to run). Implementing the same computational model on a modern digital computer, using a modern computer programming language, results in substantially lower costs. Indeed, for a while the Reserve Bank of New Zealand website featured a MONIAC implemented in software.
Discrete-Time Modeling and Simulation
The hydrological MONIAC implemented a continuous-time dynamical system. In this model, time moves continuously as water flows between physical chambers. In contrast, the population model of the present lecture characterizes the system only at discrete points in time. It is a discrete-time dynamical system: time progresses in discrete increments. A single increment is often called a step, model step, time step, period, or tick.
The conceptual size of a model step is the time scale of the model. The real-world temporal interpretation of a single step is model-specific; an increment can be as crude or refined the target system requires. For example, one model step may represent an hour, a day, a month, or a year. In the population growth model of this lecture, a single model step represents one year.
State Transition
Abstractly, the evolution of a computational model from its current state to the next state is governed by a function. This is the evolution rule or the state-transition function. In the discrete-time simulation models of this course, the current model state directly determines the subsequent model state. Letting \(s_t\) represent the model state at time \(t\), and letting \(f\) represent the state-transition function, this becomes \(s_{t+1} = f[s_{t}]\). [2]
Simulation models may additionally include exogenous inputs, say \(x_{t}\), that affect the evolution of the system so that \(s_{t+1} = f[s_t, x_t]\).
Later lectures consider models where the only feasible representation of the state transition function is the actual computational implementation of the entire model. However, a very simple model may imply an easily understood mathematical representation of the state-transition function. The population growth model of this lecture provides an example. Given an initial population, the model iteratively produces a sequence of population levels by applying a simple exponential-growth rule.
Introduction to Function Iteration
Iteration is the repetition of action. Function iteration is the repeated application of a function to produce a sequence of values. (See the Glossary for more details.) For example, starting from an initial state of \(s_0\), we may repeatedly apply an evolution rule \(f\) to produce the following iterates of \(f\).
As a convenient mathematical notation, write \(f^{\circ n}[s_0]\) to denote the result of iteratively applying the function \(f\) a total of \(n\) times, given the initial value \(s_0\).
State Trajectory
At time \(t\), a discrete-time dynamical system is in some state. Call it \(s_t\). For example, in a population growth model, this might be the current level of the population. By applying the evolution rule of the system, it transitions to another state, say \(s_{t+1}\). Given an initial system state, repeated application of the evolution rule produces future system states. That is, given an evolution rule (\(f\)) and an initial state (\(s_0\)), function iteration generates the implied state trajectory of this model. For example, given an annual growth rate and the current population, we can determine the population trajectory for subsequent years.
Mathematical and Computational Functions
mathematical vs computational functions (pure & impure)
command-line tests
the concept of a computational procedure
test procedures
As illustrated by Figure f:functionBlockDiagram, it is often convenient to think of a mathematical function as a rule for transforming inputs into outputs. From this perspective, a univariate real function accepts a real number as an input and produces a real number as an output. For example, a function might transform a number into its square by multiplying the number by itself. This conceptualization applies as well to computational functions, which provide a way to perform such transformations on a computer.
What is a Function?
As another example, consider transforming a number by multiplying it by \(1.01\). This is a very specific example of a univariate real function. A typical mathematical representation of this function is \(x \mapsto (1 + 0.01) * x\). (Pronounce the \(\mapsto\) arrow as maps to; the term map is a synonym for function.) This representation of the function simply states its transformation rule.
Applying this specific function to an input value of \(100.0\) produces an output value of \(101.0\). We do not even need a computer for this simple computation. Nevertheless, as shown in the present lecture, this function can represent behavior in a system of interest. Specifically, interpret this function as the yearly evolution rule for the world population. Under this interpretation, the function characterizes the evolution of the population in a world that has a fixed annual population growth rate of one percent.
Function Parameters as Bound Variables
mathematically, distinguish bound variables vs. free variables
computationally, distinguish local variables vs global variables
The expression \(x \mapsto (1 + 0.01)*x\)
represents a function by specifying its transformation rule.
To do this,
we need an abstract way to refer to any particular input the function might receive.
Here, this is the variable named x
,
called the function parameter.
In the function definition,
a parameter does not denote a particular value.
Instead, it represents any actual value that the function may be applied to.
Correspondingly, any other name will do as well, as long as all occurrences in the function definition change in the same way. For example, the expression \(p \mapsto (1 + 0.01) * p\) represents exactly the same function. In mathematical settings it is common to use a single letter to represent the possible input value, but this is just a convention. For example, the expression \(\square \mapsto (1 + 0.01) * \square\) can represent exactly the same function.
The expressions represent the same function because they describe the same relationship between inputs and outputs. Changing the parameter name does not change the meaning of the expression. The parameter is fundamentally a placeholder that makes it easier to understand the transformation of inputs into outputs. The name chosen to designate the input value has no affect how the function behaves. In fact, it has no meaning outside of this expression.
Here is another way to say the same thing: a function’s parameters are bound by the functions definition. This is true of both mathematical functions and computational functions. This means that the name chosen for a function parameter does not interact with the surrounding context, whether mathematical or computational. In fact, it has no meaning at all outside its particular function definition. So when naming a parameter, just choose a name that best aids a reader to understand the function.
From Mathematical to Computational Functions
This need to create computational functions is so common that almost all programming languages make it extremely simple to define computational functions. This allows computer programs to incorporate useful mathematical functionality by implementing mathematical functions as computational functions. For example, this section shows how to include the population growth function in a computer program.
Recall Figure f:functionBlockDiagram, which characterizes a function as a transformation of inputs into outputs. A particular input value to a computational function is called the function argument, and the corresponding output value is called the return value. Applying a computational function to an argument (of the right type) produces a return value.
Hint
As described in the Introduction to NetLogo supplement,
instead of saying that a function returns a value,
NetLogo programmers typically say that a reporter reports a value.
The term reporter procedure refers to a computational function;
the value reported is its return value.
This terminology is NetLogo specific.
It reflects the fact that a reporter procedure uses
the report
keyword to return a value.
(Earlier Logos used an output
keyword instead.)
NetLogo supports two different approaches to function definition: function literals, and reporter procedures. See the Introduction to NetLogo supplement for more details, paying special attention to the syntax for function parameters.
What is a Pure Function?
A pure function is
closed
side-effect free
A function whose output is determined solely by its explicit input is closed to other influences. The result of applying a closed function to an argument depends only on the value of the argument; it does not depend on the context in which the function is executed. Correspondingly, the definition of a pure function ensures that any given input value always produces the same output value. Roughly speaking, when a computational function is closed, knowing just the value of the argument is sufficient to predict the return value.
A function that is closed does not respond to its enclosing environment. This makes it easier to understand the function’s behavior. Additionally, programmers care about changes to that environment. Examples of environmental modification include changing the value of a nonlocal variable, writing data to a file, or communicating with a computer peripheral. A function is side-effect free if it executing it does not modify the environment in which it runs. This is often a desirable property, yet producing side effects is often desirable as well. Later sections return to this problem.
A pure function is closed and side-effect free. (See the Glossary for more details.) Many programming languages provide facilities for defining pure functions, which are the computational analogues of familiar mathematical functions.
Planning the Implementation
A typical implementation of the population growth function above produces a pure function. Good implementations sometimes require extensive planning. However, in a case as simple as this population growth function, very little planning is needed. Even so, it can be useful to sketch a basic function summary, if for no other reason than to nurture the habit.
A function summary may be extremely informal and sketchy,
or it may be quite formal and detailed.
The level of detail should reflect both
the immediate needs of the programmer
and the anticipated needs of possible future consumers of the code
(i.e., anyone who might try to understand the code).
One common to detailed description is pseudocode,
which roughly resembles an implementation in a programming language,
Instead of pseudocode,
this course sketches functions (and other subroutines)
in a simple outline format.
Here is a function sketch for the population growth function,
named nextExponential01
.
- function:
nextExponential01: Real -> Real
- function expression:
\(p \mapsto 1.01 * p\)
- parameter:
\(p\), the population
- summary:
Grow the population by 1%.
Notation in Code Sketches
Code sketches in this course freely use simple mathematical notation,
which often resembles but need not match
the notation in any particular computational implementation.
For example, it is convenient to provide a very short name to represent an arbitrary input;
this is the function parameter (here, p
).
A function sketch typically expresses
the output as an expression involving the function parameter
(here, \(1.01 * p\)).
In actual code, one may prefer a longer, more informative
parameter name (e.g., pop
or even population
).
Additionally, the function sketches in this course
usually state the data type of the valid inputs and of the return values.
The notation Real -> Real
means that
the function maps a real-valued input to a real-valued output.
This specifies the input type and output type to both be Real
,
which name the typical computational data type for representing real numbers.
(The exact meaning of this varies across programming languages.)
Hint
Unlike most other programming languages,
NetLogo includes only a single numerical data type,
which is used to represent both real numbers and integers.
Nevertheless, this course uses the
Real
and Integer
annotations to guide expectations.
Create a project named PopulationGrowth01
.
Begin this project by implementing the nextExponential01
function,
following the code outline above.
Include helpful comments in your code,
and remember to save your model after changing it.
Be sure to define a pure function. It must not depend on any other code. It must do no more than return a numerical output when applied to a numerical input. It must have no side effects. (For example, it must not print.) It must completely specify a deterministic relationship between numerical inputs and numerical outputs.
Note
In this course, functions typically have lower camel-case names (likeThis), while projects typically have upper camel-case names (LikeThis). Freely append digits to file names and function names, whenever this seems useful. Additionally, feel free to adopt other naming schemes; language-specific conventions vary widely.
Hint
A basic NetLogo model includes code, documentation,
and model-specific GUI components.
Nevertheless, a simple NetLogo project is stored in a single NetLogo model file.
Based on your reading in the Introduction to NetLogo supplement,
create a new model file and name it PopulationGrowth01.nlogo
.
(You can keep all of your NetLogo files for this course in a single folder.)
Reporter procedures can be defined in the Code
tab
but cannot be defined at the NetLogo command line.
So add the code for your nextExponential01
reporter procedure to the Code
tab of your model.
As discussed in the Introduction to NetLogo supplement,
use the to-report
and end
keywords
to begin and end the definition of a reporter procedure.
to-report nextExponential01 [#x] <put the body of your definition here> end
As discussed in the Introduction to NetLogo supplement, this course adopts the convention
of beginning the names of function parameters with an octothorpe (#
).
Be sure to add comments to your code;
recall that a semicolon begins a comment that lasts until the end of the line.
nextExponential01
The following code snippet illustrates
one possible implementation of the desired function.
It declares and defines a NetLogo reporter procedure,
so it begins with to-report
and ends with end
.
The name of this function is nextExponential01
.
The name in brackets (#x
) is the function parameter;
it is reused in the function body
to refer abstractly to any possible input to the function.
to-report nextExponential01 [#x] report 1.01 * #x end
Ad Hoc Testing of nextExponential01
Untested code should be presumed broken.
So, after creating the nextExponential01
function,
it is time to test it.
A first and simplest test is to apply this function to an
argument and then print the result,
in order to check that it behaves as expected.
The best way to do this is language dependent.
Apply the nextExponential01
function to an argument of 100
and print the result.
Confirm that the value produced equals 101
.
Apply the nextExponential01
function to an argument of 1000
and print the result.
Confirm that the value produced equals 1010
.
Hint
Once added to the Code
tab,
nextExponential01
is available globally.
This means it is available to the entire NetLogo program.
It is even available at the NetLogo command line,
which enables easy ad hoc testing that it works as expected.
For example,
at the command line you can now enter the following.
print (nextExponential01 100)
The print
command prints a value to NetLogo’s output area.
So this prints the function’s return value.
The parentheses around the function’s name and its argument
are entirely optional,
but this practice can be visually helpful.
Note that the function argument is not delimited by parentheses or brackets.
Contrast this to NetLogo’s definition syntax for reporter procedures.
When defining a reporter procedure,
the parameter names must be bracketed.
When applying a reporter procedure to an argument,
brackets around the argument are forbidden.
Simple Unit Tests Formalize Expectations
This course provides a very limited discussion of software testing, focusing on ad hoc tests and simple test procedures. Ad hoc testing by printing and then inspecting outputs is often useful, but it quickly becomes tedious. Test procedures take a small step towards more automated testing. An automated test of a single component of a program, such as a function, is often called a unit test.
What Is A Procedure?
This course distinguishes between functions and procedures as follows: a function returns a value for subsequent use, whereas a procedure does not. Both are subroutines that can repeatedly be invoked as a single unit. But since a procedure does not return a usable value, to be of interest it must have some other kind of effect. These other effects are called side effects.
The purpose of a procedure is to produce side effects. Calling the resulting changes side effects may feel a bit misleading, since producing these changes is the very purpose, but it is a standard terminology. Side-effects may be as simple and innocuous as printing some text.
Hint
NetLogo programmers typically make this distinction
by speaking of reporters and commands.
In NetLogo terminology,
a reporter procedure reports a value,
while a command procedures does not.
The definition of a command procedure
correspondingly begins with to
instead of to-report
.
Test Procedures
A test procedure formalizes some expectations about how code should behave. For example, a test procedure may check that a function, when applied to known arguments, returns the correct outputs. This is a type of unit test, where the unit is the function that is tested. A useful test must provide some kind of warning if the test fails. One big advantage of test procedures is that whenever we refactor a function, we can just rerun the existing test to ensure that our changes did not accidentally introduce new, undesirable behavior.
A pure function is particularly easy to test,
since the correct output for any specified input is completely predictable.
More concretely, consider a computational implementation of the mathematical function \(p \mapsto (1 + 0.01) * p\).
One test of this implementation might apply the computational implementation of this function
to the value 0.0
and test that the return value is 0.0
.
If the return value differs from 0.0
,
the test procedure must signal this failure in some way.
In order to better automate testing of the nextExponential01
function,
create a simple test procedure named testNextExponential01
.
This procedure should test that output is correct
for at least the following inputs:
-100.0
, 0.0
, and 100.0
.
For now, keep test creation as simple as possible:
a test failure can just raise an error,
causing the test to stop running at that point.
If you wish, print out a celebration if all tests pass.
Run your test procedure to confirm
that nextExponential01
behaves as expected.
Hint
In NetLogo, unit testing is often done with simple command procedures.
The exercise suggests the name testNextExponential
for the first test procedure.
Remember that unlike many other programming languages
NetLogo uses the equals sign (=
) for comparison,
not for assignment.
(This is also true of most spreadsheets,
Lisp dialects, and Logo dialects.)
Execute NetLogo’s error
command if a test fails,
and provide a useful error message.
Based on your reading in the Introduction to NetLogo supplement,
remember to use to
instead of to-report
when defining testNextExponential
.
After adding this test procedure to the Code
tab of your project,
run it from the NetLogo command line (in the Interface
tab).
Testing nextExponential01
If you ran into any difficulties creating this test,
copy the following definition into the Code
tab
of your PopulationGrowth01.nlogo
model.
If your nextExponential
function is correctly implemented,
this test procedure prints that the test passed.
This particular test is written so that it executes error
if a test fails.
This terminates the execution of the test procedure and displays an error message.
to testNextExponential01 type "Begin test of `nextExponential01` ... " if (0 != nextExponential01 0) [error "bad output for input of 0"] if (101 != nextExponential01 100) [error "bad output for input of 100"] if (-101 != nextExponential01 -100) [error "bad output for input of -100"] print "`nextExponential01` passed." end
Exponential Population Growth
population dynamics
rates of change vs. growth rates.
exponential growth.
application: exponential population growth
This section employs the tools developed above to produce a particularly simple forecast of future population levels. The stylized fact motivating this model is the remarkable increase in the world population since the First Agricultural Revolution [BocquetAppel-2011-Science]. Figure historicalWorldPopulationLevels illustrates world population estimates over several thousand years. Clearly, human population growth has not been a linear function of the time passed.
This section will demonstrate that a very simple model can produce such nonlinearity. As a preliminary, it presents the difference between rates of change and rates of growth, and applies this distinction to the concept of exponential growth. It then develops a simple discrete-time dynamic model of the world population. Treating the world population as a single stock implicitly aggregates across many individual attributes. For many social-science questions a less aggregative approach is advantageous, and later lectures explore such disaggregation in some detail. However, this lecture first illustrates some basic simulation tools by exploring a simple system-dynamics model at the highest level of aggregation.
Population Dynamics
This section models the world population as a discrete-time dynamical system. The current population (\(P_{t}\)) is the state of this system. The system state comprises this single stock variable. (Recall that it is a stock variable, because population can be measured at any point in time, without any reference to a period of time.) Since the world population can only assume nonnegative values, the possible states of the system correspondingly are nonnegative numbers.
The net addition to the population in a specified period is a flow variable. In a system dynamics model, flows affect stocks. The total population changes as a result of any net addition. The resulting change in the population is a change in the state of the system.
Rate of Change vs Growth Rate
If \(P_t\) is the population at time \(t\), then \(P_{t+1}-P_{t}\) is the rate of change for the period from \(t\) to \(t+1\). Define the one-period growth rate from \(t\) to \(t+1\) to be the proportional rate of change over the period. (This is sometimes called the proportional growth rate.) Let \(g_{t,t+1}\) denote this growth rate, computed as follows.
For example, Figure populationLevelsGrowthUS illustrates historical population levels in the United States, along with the associated annual growth rates. Evidently, there can be substantial short-run variation around the mean growth rate even in a large country. For example, even though it is not particularly evident in the plot of the population levels, it is very easy to detect a post-WWII baby boom in the plot of the annual population growth rates.
Exponential Growth
Real-world population growth is complicated to model and predict. Changing population growth rates affect demographic projections, but as an initial introduction to population growth, this lecture focuses entirely on the average growth rate of the population. To support the development of both conceptual understanding and modeling skills, the section starts with an extremely simple initial model: the growth rate is a constant, \(g\).
This is the evolution rule for the population. It states that there is a simple dependence of next period’s population on the current population. In this simple dynamical system, the evolution rule implies exponential growth at a constant rate \(g\).
Exponential Population Growth via Iteration
In the discrete-time population growth model,
the population \(P_{t}\) constitutes the system state at time t
.
A state trajectory is a list of populations over time.
From any given initial population,
iteratively applying the evolution rule produces future-population projections.
Concretely, suppose we are given a growth rate \(g\)
and an initial population \(P_0\).
Compute the population \(P_t\)
by iteratively (i.e., repeatedly) multiplying by \((1+g)\).
Here are the first three iterations.
Simulation Modeling vs Mathematical Modeling
This population model is so simple that computer simulation is unnecessary for its solution. For example, this model implies an interesting, useful, and particularly simple mathematical relation: \(P_{t} = (1+g)^t P_{0}\). This concisely characterizes the population at any time \(t\), and it is clear that a simple calculator can quickly produce a projection for any future population level. There is no need to simulate the evolution of the system in order to determine the population projection for any future point in time.
This means that there is no technical reason to implement a computational model of this particular dynamical system. However, many dynamical systems lack a simple mathematical solution yet are nevertheless tractable for simulation (subject to relevant limitations of the computer). Although simulating this simple population model is unnecessary, it serves to plainly illustrate a simulation process that applies to more complex systems.
Looping Constructs
Beginning with a conceptual model of a dynamical system, simulation modeling begins by implementing in code a computational model of the system. Executing the computational model—running the simulation—describes the evolution of the system over time. This typically involves iterating the evolution rule for the system. The only requirements for a first population simulation are an initial population, a particular population growth rule, and an iteration technique.
Even with a simple exponential-growth population model, iteration by hand quickly becomes tiresome. Fortunately, computers are excellent tools for repetitive tasks. The computer instructions for performing any action once provides the basis for performing the same action repeatedly. In a discrete-time dynamical system, looping constructs allow easy iteration of the evolution rule. A useful looping construct must provide a way to start and end a sequence of iterations. An oft-repeated joke describes a programmer who cannot stop shampooing, because the instructions are “lather; rinse; repeat.”
Note
This chapter presents a very imperative approach to function iteration, where the programmer explicitly describes how to repeatedly update the model state using the evolution rule. However, looping constructs vary radically across programming languages. Determine the preferred approaches to function iteration in your chosen language.
Population Projection
While modelers sometimes care only about the final state achieved by a simulation, it is common to track the evolution of the simulation over time. Tracking requires collecting simulation data at regular intervals, often at the end of each iteration. The sequence of collected data is the output trajectory for the simulation. For example, population modelers often produce annual projections for a number of decades into the future. These projections derive from the output trajectories produced by their simulations.
Imagine that you are a demographer in 2022,
with a world population of approximately \(8.1\) billion.
Given a constant population growth rate of 1% per year,
predict the world population each year for the next \(50\) years.
Use the nextEponential01
function to produce these projections.
Hint
As a first approach to this exercise,
work at the NetLogo the command line and
simply print
the population after each iteration.
(The next section develops a more structured approach.)
In the NetLogo Dictionary, review the documentation of
let
, set
, repeat
, and print
.
Attend to the difference between let
and set
.
Use the let
command to
introduce an intial population as a new local variable:
let pop 8.1
.
Change the value of the population variable with the set
command.
Consider using NetLogo’s repeat
command for iteration.
Simple Exponential Projection
Given a working nextExponential01
function in the Code
tab,
the following command-line solution prints projections
to the output area of the Command Center.
let pop 8.1 repeat 50 [set pop (nextExponential01 pop) print pop]
This simulates exponential population growth over time
and provides forecasts for the next \(50\) years.
Note that let
introduces a new local variable,
but set
changes the value of an existing variable.
Model Parameters
In order to produce population projections,
the exponential growth model requires only
an initial population and a population growth rate.
These are the model parameters;
they do not change as a simulation runs.
Our first simulation exercise relied entirely on
hardcoding these parameters.
For example,
the nextExponential01
function includes the population growth rate
of 1% literally in the function definition.
An alternative approach is to introduce model parameters as global constants.
(The next lecture considers yet another approach.)
As an example of a named model parameter,
let pop0
denote the initial population level.
Global Variables and Constants
Recall that pop0
is a model parameter,
which should not change as the simulation runs.
Some languages include features for enforcing of this constancy;
others leave it up to the care of the programmer.
In any case, running the simulation should not change its value.
Let the name pop0
be visible everywhere
in the PopulationGrowth01
model.
Any code, anywhere in the model, can access it.
Then we say that this name has has global scope.
We typically call it a global variable, even when it must remain constant.
Roughly speaking, any portion of a model’s code can refer to any global variable. Problematically, in some languages this means that any portion of a model’s code can change any global variable. One part of the model might change a global variable that another part expects to be unchanged.
Keeping track of such expectations can be difficult. For this reason, programmers should introduce global variables reluctantly and handle them carefully. Despite these serious drawbacks, the use of global variables can simplify early model development, and this course rather freely introduces global variables. This often produces easier to read example code, from which global variables can be purged by more experienced readers.
Hint
In NetLogo models,
the model parameters are commonly introduced
as global variables (or even hardcoded).
As discussed in the Introduction to NetLogo supplement,
a NetLogo program can declare global variables
at the top of the Code
tab
by means of a single use of the globals
keyword.
In contrast to some other languages,
NetLogo does not provide a simple way to freeze the value of a variable.
(It is possible but not common to replace global constants
with reporter procedures.)
Therefore, in NetLogo a global constant is no more than
a global variable that the programmer refrains from changing.
Initializing the Global Variables
A simulation requires initialization of the
model parameters before it can run.
This course typically implements this
initialization during the setup phase of the model.
In the exponential-growth population model,
initialization is trivial:
just set the value of pop0
(the initial world population).
Add pop0
as a parameter of the PopulationGrowth01
model.
Reproduce the same 50 year population projections,
but this time,
make use of the value of the pop0
model parameter.
Hint
For now, just introduce pop0
as a global variable.
As discussed in the Introduction to NetLogo supplement,
NetLogo allows the declaration of global variables
by means of the globals
keyword.
In contrast to some other languages,
NetLogo does not allow concommitant initialization of the value.
Therefore, create a setupGlobals
procedure
whose sole task is to initialize the value of pop0
.
Run this procedure as part of the setup phase of your simulation.
This procedure has an important side effect:
it changes the value of a global variable.
NetLogo does not care about the order in which
procedures and functions occur in the Code
tab.
NetLogo automatically compiles them all when it loads the model,
so they are all available always to the entire model.
Intializing and Running the Model
Make sure that globals [pop0]
occurs at the top of the model’s Code
tab
and that you have a working nextExponential01
function below that.
Add a setupGlobals
procedure to the model’s Code
tab.
to setupGlobals set pop0 8.0 ;initial population (in billions) end
The following command-line solution will print projections to the output area of the Command Center.
setupGlobals repeat 50 [set pop (nextExponential01 pop) print pop]
Exponential Population Growth: A Standardized Simulation
standardizing the simulation structure.
initializing a simulation model.
defining a simulation schedule.
create runtime data visualizations.
This section elaborates the computational implementation of the population growth model introduced above. This elaboration provides our first steps towards a standard structure for discrete-time simulation modeling. Topics include the concept of a simulation schedule and approaches to viewing simulation data.
High-Level Simulation Structure
At the highest level of abstraction, running the population growth simulation may be divided into two main phases: a setup phase, and an iteration phase. The setup phase comprises preparations to run a simulation schedule, including setting up the initial model state (e.g., the initial population). The iteration phase repeatedly executes a simulation schedule, which leads to updates of the model state (e.g., new values of the total population). Later lectures add a wrap-up phase, comprising things to do when the simulation is complete. Figure f:runsim01 outlines this typical simulation structure.
Iteration Phase
As illustrated by figure f:runsim01, a typical discrete-time simulation iterates a schedule until a stopping criterion is satisfied. This simulation schedule runs the evolution rule for the model. One execution of this schedule is called a model iteration or a simulation step.
In the population model of this section,
each simulation step updates the world population (pop
).
This involves applying the nextExponential01
function
to the current value of pop
in order to produce the new population.
This is the core of the iteration phase of the simulation.
A runsim
Procedure for the Population Model
A typical discrete-time simulation begins with a setup phase,
which may include the initialization of global variables.
Next, the simulation enters the iteration phase,
repeatedly running the simulation schedule until the simulation terminates.
The following sketch of a runsim
procedure encompasses this standard structure.
- procedure name:
runsim
- summary:
Set up the simulation, as needed.
Starting with the initial population stored in
pop0
, iterate the simulation schedule a total of50
times to produce the 50 annual population projections.
In this population model, the simulation runs \(50\) iterations of the simulation schedule. During the iteration phase, we often attempt to gather data from the simulation. There are many ways to do this, which later lectures explore in detail. For the moment, continue to simply print the simulation values at the end of each iteration.
Following the procedure sketch above,
add a runsim
procedure to the PopulationGrowth01
project.
The setup phase should fully prepare for the iteration phase.
Use this runsim
procedure to run
the same population simulation as before.
That is, during the iteration phase,
use nextExponential01
to update the population.
For this exercise,
output the projections any way you wish.
(For example, you may simply print them.)
Alternative Approach:
Create a runsim
function
that returns a sequence of projections (e.g., a list or array).
Stretch Goal:
Change runsim
so that it requires one argument:
the initial population.
Hint
For the moment,
just print each population projection when you compute it.
In this case,
the runsim
procedure is essentially just a wrapper
areound the command-line example above.
Start the set up phase by running the setupGlobals
procedure.
Introduce a local pop
variable that is initialized to pop0
.
Use a repeat
loop to update pop
50 times,
printing the updated value each time.
(In NetLogo, we will typically prefix and underscore to the name
of local variables, but this is just a convention.)
When you are done, enter runsim
at the NetLogo command line.
This should print the same forecasts as before.
Running the Population Simulation
Recall that the Code
tab of the PopulationGrowth01
project
should already contain the following.
globals [ pop0 ;Real, initial population ]
to setupGlobals set pop0 8.0 ;initial population (in billions) end
to-report nextExponential01 [#x] report 1.01 * #x end
Adding the runsim
procedure completes the simulation model.
Here is an example implementation.
After adding this to the Code
tab,
whenever you enter runsim
at the NetLogo command line,
the entire simulation runs.
to runsim ;setup phase: setupGlobals let _pop pop0 print _pop ;iteration phase: repeat 50 [ set _pop (nextExponential01 _pop) print _pop ;;primitive "export" of new datum ] end
Data Visualization
So far, our only access to the simulation trajectory has been through the examination of the printed population forecasts. This is far from adequate: the printed results provide very limited insight into the simulation outcomes. Later lectures explore a variety of tools for improving our understanding of such data.
Data visualization can provide useful insights into simulation outcomes. Data visualizations may expose patterns in the evolution of a simulation or in its final outcomes that might be missed in summary statistics. A normal human eye can process a remarkable amount of visual information very quickly, often detecting even faint patterns or small anomalies with ease.
Charts displaying the generated data can therefore promote understanding of the simulation outcomes. Of course, humans are subject to perceptual illusions and may detect patterns where there are none. Nevertheless, visual representations of simulation output often prove indispensable in its analysis. This section introduces a univariate visualization that has repeatedly proved particularly useful: the run-sequence plot or time-series plot.
Run-Sequence Plots
Creating an attractive and communicative plot can be difficult. However, programming languages and simulation toolkits often provide extensive facilities for data visualization. These plotting facilities vary widely across languages, and the ease of use of these facilities varies considerably. In some languages the builtin plotting facilities are so limited that it typically pays to export the data to a file for use by a separate plotting program. (A spreadsheet is often adequate to this task; a more advanced choice is gnuplot.)
Future lectures will discuss how to export data from simulations. However, the rest of this section assumes that the language in use provides ready access to basic plotting facilities. When a simulation model runs quickly enough, as with this population-projection model, the data may even be displayed dynamically (i.e., as the simulation runs).
Population-Projection Plots
The best approach to plotting is highly language specific. Some languages support easy GUI building that includes facilities for easy dynamic plotting. Others require additional substantial additional code even for simple plots. Regardless, plot creation generally has two components: setting up the options for the plot window, and plotting the data.
Add a run-sequence plot to your PopulationGrowth01
project.
This should display a plot of the annual population projections
produced by the population simulation.
Choose your plot options,
which may include the numerical limits of the two axes
and an informative choice of labels.
Finally, create a runsimG
procedure
that modifies your runsim
procedure by adding a plot.
Use this runsimG
procedure
to plot your annual population projections for \(50\) years.
A animated plot can create the appearance of change over time by sequentially redrawing the plot to reflect updates to the model state. In the context of simulation modeling, an animated plot is dynamic if it redraws the new model states as the simulation runs. Using an animated plot or dynamic plot for this exercise is optional. In either case, plot updates should be sequential and should add a single point to the run-sequence plot. For a dynamic plot, include plotting of the initial model state in the setup phase.
Hint
The NetLogo name denotes not just the programming language
but also the associated simulation toolkit.
The NetLogo toolkit makes it particularly easy to add GUI widgets.
It supports point-and-click GUI creation in the Interface
tab.
NetLogo does not allow the creation of GUI widgets in the Code
tab.
Instead, add widgets interactively in the Interface
tab.
Since NetLogo models are automatically endowed with an Interface
tab,
point-and-click placement of the GUI widgets is usually considered to be
a feature rather than a limitation.
NetLogo makes dynamic plotting particularly simple,
so exploit that facility while completing this exercise.
First, review the discussion of NetLogo plotting in the the Introduction to NetLogo supplement.
As discussed there,
add a plot widget via the Interface
tab.
(Adding a widget is not possible in the NetLogo Code
tab.)
To add a plot widget,
right click where you want the plot,
pick Plot
,
add a plot name (e.g., Population Projections),
set the axes limits,
and delete the example pen-update commands.
(Later lectures explore some of the other options.)
Also, set the dropdown menu under view updates
in the Interface
tab to the continuous
option.
(Later chapters discuss this option.)
Save your work.
NetLogo automatically creates a graphics window (or View) when starting up, which displays as a large black square. The models of the first few lectures make no use of this window. You may optionally edit it to make it smaller, and then cover it with the a widget to hide it altogether.
In order to reset the plot widget before running the simulation,
the setup phase of the runsimG
procedure should begin with clear-all
.
To update the plot, just use plot pop
(instead of print pop
).
After running the simulation,
if you would like to export the plot data for spreadsheet analysis,
right click on the plot and pick Export…
.
Run-Sequence Plot
;alternatively, set these options in the plot widget to setupPlotEG set-plot-x-range 0 50 set-plot-y-range pop0 (1.7 * pop0) end
to runsimG ;setup phase: clear-all setupGlobals setupPlotEG let _pop pop0 plot _pop ;iteration phase: repeat 50 [ set _pop (nextExponential01 _pop) plot _pop ;;display new datum ] end
Sample Population-Projection Plot
Figure f:exponentialGrowthPlot provides an example of a chart based on the data generated by this population model. The smooth upward trend in the population is readily apparent, as is the increasing slope of the population trajectory. The ease with which we discover these features in the data demonstrates the analytical utility of data visualizations. Plots can make it easy to detect data behaviors that are hard to detect in printed output.
Summary and Conclusions
This lecture introduces the process of modeling and simulation by discussing discrete-time system-dynamics. Core system dynamics is the interaction between aggregated stock variables and flow variables. This lecture emphasized the effect of flows on stocks, but future lectures will explore two-way interactions. System dynamics summarizes these aggregate interactions as functions, so this lecture explored the relationship between mathematical and computational functions.
In the terminology of this course,
a computational function transforms inputs into outputs.
A pure function ensures that a given input always produces the same output,
with no other side effects.
This contrast with a procedure,
which does not return a value but instead
produces changes in the program state.
In many programming languages,
simulations are controlled by procedures,
which in turn depend on functions.
This lecture illustrates this with a simple population simulation,
where runsim
procedure
iteratively applies the nextExponential01
function
in order to produce a sequence of population projections.
This lecture also explored the relationship between a conceptual model and its computational implementation. The computational model of population growth derives from an underlying conceptual model of population growth. In order to practice decomposition—breaking models into simple, reusable pieces—this lecture constructs the simulation model from extremely simple functions and procedures. In order to promote the development of correct and maintainable code, testing receives substantial emphasis.
Plots facilitate data analysis and need not be difficult to produce. The ultimate population model from this lecture produces a run-sequence plot of the population projections. Figure f:exponentialGrowthPlot provides an example of a chart based on the data generated by this simulation model.
The nonlinearity of exponential population growth is somewhat visible in this plot. It quickly becomes more visible if we extend the population projections further into the future. In fact, a 1% growth rate of population produces doubling approximately every \(70\) years. So although the \(50\) year projections may appear somewhat shocking, projections \(150\) years appear utterly impossible. The projections of this lecture assume a constant rate of population growth, but this assumption is dropped in the next lecture.
Explorations
Helpful code comments are a good coding practice. Comments can specify the meaning of each model parameter or global variable. In some languages, we may also find comments useful for expressing the intended type of each variable. For example, a comment can specify that a variable should always have an
Integer
type. This is useful when the implementation language does not support explicit typing of variables. Add such comments to thePopulationGrowth01
project.Hint
Recall from the Introduction to NetLogo supplement that NetLogo has a single numerical data type, used to represent both real numbers and integers. Although the NetLogo programming language does not support type annotations, comments still provide guidance to readers of your code.
Discrete-time simulations often include a global measure of time elapsed (i.e., the number of iterations). Conventionally this is often named
ticks
. Add aticks
variable to the population model, initializing it to0
and incrementing it near the end of each iteration. Thisticks
variable is inessential: it simply tracks virtual time in our simulation. In this population simulation, one tick will represent the passage of one year. This is the time-scale of the simulation model.Hint
As an unusual feature, NetLogo builds in a special
ticks
global variable, which must be initialized with thereset-ticks
command. Use thetick
command to increment the value ofticks
.Allow the growth rate each period to be subject to a transient random shock. How does this affect your population simulations? Allow the growth rate each period to be subject to a persistent random shock. How does this affect your population simulations?
(Advanced) Export the simulation data and use a spreadsheet to plot your population projections.
Hint
NetLogo users should export the data in multiple ways. First, right-click your plot and choose
Export
from the context menu. Second, useexport-plot
to export the data. Third, use BehaviorSpace to export the data. (See the Introduction to NetLogo supplement for an introduction to BehaviorSpace.)(Advanced) Instead of hardcoding the population growth rate, make it a model parameter (say,
gE
). Control its value with a slider in the model’s GUI.(Advanced) Change
runsim
into a function that accepts one argument, (the number of years of projections) and returns the trajectory of projections (e.g., as a list or array).Hint
Before changing your model, but after reading about NetLogo lists, explain what is printed when you run the following code at the command line of your population model.
setupGlobals let trajectory [pop0] repeat 50 [set trajectory lput (nextExponential01 last trajectory)] print trajectory
Additional Resources
Readers wanting a refresher for basic mathematical concepts may find [Feldman-2012-OxfordUP] particularly accessible and helpful. See chapter 1 for an elementary review of functions. Chapters 2 and 5 provide a brief, elementary overview of the concept of function iteration. The Wikipedia entry on iterated functions is also a useful resource.
[Frigg.Nguyen-2016-Monist] provide a probing discussion of the relationship between models and their target systems, complete with useful examples and many helpful references. Also see [Swoyer-1991-Synthese], whose broader notion of surrogative reasoning links to the suggestion in the present lecture that researchers experiment with models in order to learn about the target systems.
As a crude rule of thumb, never assume that untested code works as intended. Accordingly, computational models typically include tests, which help ensure that the code is working as intended. Some programming languages include extensive facilities for testing. Software testing is a huge topic that the present course treats in a cursory manner. Among the many excellent books and articles on how to test and maintain computer software, [Myers-2012-Wiley] and [Jorgensen-2014-CRC] provide good introductions to serious software testing.
For additional historical details about the MONIAC, see [Bissell-2007-IEEECS]. [Ng.Wright-2007-RBNZB], and Phillips’s early published description [Phillips-1950-Economica]. In addition, Part II of [Leeson-2000-CambridgeUP] is devoted to the MONIAC. [Colander-2011-EconomiaPolitica] draws lessons from the MONIAC for contemporary macromodeling efforts. [Ryder.Cavana_Cavana.etal-2021-Springer] provide a detailed exploration of how to translate knowledge of the MONIAC into a modern system-dynamics formulation.
Currently a number of web videos of the MONIAC are available on the Internet. One of them is [Making.Money.Flow-2017-RBNZ], created by the Reserve Bank of New Zealand using its still-working MONIAC. A more extended demonstration is offered by the University of Cambridge. A wonderful bit of MONIAC history is provided by Tim Harford’s lecture on Bill Phillips.
References
Bissell, Chris. (2007) The Moniac: A Hydromechanical Analog Computer of the 1950s. IEEE Control Systems 27, 69--74.
Bocquet-Appel, Jean-Pierre. (2011) When the World's Population Took Off: The Springboard of the Neolithic Demographic Transition. Science 333, 560--561. https://science.sciencemag.org/content/333/6042/560
Colander, David. (2011) The MONIAC, Modeling, and Macroeconomics. Economia Politica: Journal of Analytical and Institutional Economics , 63--82.
Feldman, David P. (2012) Chaos and Fractals: An Elementary Introduction. : Oxford University Press.
Frigg, Roman, and James Nguyen. (2016) The Fiction View of Models Reloaded. The Monist 99, 225--242. http://dx.doi.org/10.1093/monist/onw002
Humphreys, Paul. (2002) Computational Models. Philosophy of Science 69, S1--S11. www.jstor.org/stable/10.1086/341763
Jorgensen, Paul. (2014) Software Testing: A Craftsman's Approach. Boca Raton, Florida: CRC Press.
Leeson, Robert. () AWH Phillips: Collected Works in Contemporary Perspective. : Cambridge University Press.
Myers, Glenford J. (2012) The Art of Software Testing. Hoboken, N.J: John Wiley and Sons.
Ng, Tim, and Matthew Wright. (2007) Introducing the MONIAC: An Early and Innovative Economic Model. Reserve Bank Bulletin 70, 46--52. https://www.rbnz.govt.nz/research-and-publications/reserve-bank-bulletin/2007/rbb2007-70-04-04
Phillips, A. W. (1950) Mechanical Models in Economic Dynamics. Economica 17, 283--305. http://www.jstor.org/stable/2549721
Ryder, William H., and Robert Y. Cavana. (2021) A System Dynamics Translation of the Phillips Machine. In: Feedback Economics: Economic Modeling with System Dynamics, 97--134. Cham: Springer International Publishing. https://doi.org/10.1007/978-3-030-67190-7_5
Swoyer, C. (1991) Structural Representation and Surrogative Reasoning. Synthese 87, 449--508.
Reserve Bank of New Zealand,. (2017) Making Money Flow: The MONIAC.
Copyright © 2016–2023 Alan G. Isaac. All rights reserved.
- version:
2023-07-14