Chapter 5: Variables

Variables Types

There are four types of variables that can be defined in a FRED model. Each type is characterized by the kind of data that it is able to store.

  • scalar variables represent a single numerical value.

  • list variables represent a list of numerical values.

  • table variables store key-value pairs with numerical values.

  • list-table variables store key-value pairs with list values.

Note

All variables store either real numbers or lists of real numbers.

All variables can be set using the assignment operator, =. For example, a scalar variable, x, can be set by any agent with an action rule, e.g.

x = 42

The expression on the right side must evaluate to a single real number.

List variables can store a list-valued expression. For example, a list variable, primes, can be set by any agent with an action rule, e.g.

primes = list(2, 3, 5, 7, 11, 13)

Similarly, table and list-table variables store real numbers and lists of real numbers respectively; however, setting and accessing these variables requires the use of a key. The keys to tables are real numbers. Expressions that evaluate to a real number may be used. For example, a table could be used to store the factorial of selected integers, e.g.

factorial[1] = 1
factorial[2] = 1 * 2
factorial[3] = 1 * 2 * 3
factorial[4] = 1 * 2 * 3 * 4

where the keys are integers, and the values are the factorial of that integer.

Warning

Variables are stored as signed 64-bit doubles in FRED, so there is a limit to largest value that can be stored. In the above example, the largest factorial that could be stored is \(18!\).

A list-table, factors, could be used to store the positive factors for the associated integer key, e.g.

factors[1] = list(1)
factors[2] = list(1, 2)
factors[3] = list(1, 3)
factors[4] = list(1, 2, 4)
factors[42] = list(1, 2, 3, 6, 7, 14, 21, 42)

where the keys integers, and the values are the list of positive factors associated with each integer.

All variables must be declared within a variables block. For example, the variables used in the examples above could be declared as:

variables {
   shared numeric x
   shared list primes
   shared table factorial
   shared list_table factors
}

Shared & Agent Variables

Variables can also be categorized by their scope:

  • shared: all agents share the same value

  • agent: individual agents maintain independent values

All agents “see” the same value for shared variables. If any agent changes the value of a shared variable, all agents will see the new value. Conversely, each agent may set and maintain a different value for agent variables. If a particular agent changes the value of an agent variable, only that agent will have access to that new value. A model may contain any number of shared and agent variables.

Shared variables include single-valued, list-valued, table, and list-table variables. These must be declared in a variables block as:

variables {
   shared numeric <variable-name>
   shared list <list-variable-name>
   shared table <table-name>
   shared list_table <list-table-name>
}

Agent variables only include single-valued and list-valued variables. These must also be declared in a variables block as:

variables {
   agent numeric <variable-name>
   agent list <list-variable-name>
}

Note

A common convention is to prepend agent variable names with my_ in order to make it easy to distinguish variable types within a FRED program. For example, an agent variable called my_temperature might be used to track the internal body temperature of agents.

The Variables Block

All variables are declared in a variables block.

variables {
   ...
}

Each new variable defined in a model must be declared by type in a variables block:

variables {
   shared x
   <x = 0>
   <x.output = 0>

   shared list x_list
   <x_list = list()>
   <x_list.output = 0>

   shared table t
   <t.output = 0>
   <t.default_value = none>

   shared list_table l_t

   agent numeric y
   <y = 0>

   agent list y_list
   <y_list = list()>
}

The statements in brackets, <> are optional with the default value indicated. For scalar-valued and list-valued variables, an constant initial value can be specified using the assignment operator, =.

Table variables may be initialized with a default value by setting <table-name>.default_value = <value>. The default value will be returned for a key that has not been set. List-tables are always initialized to empty in a variables block. Only an agent may update this variable type within a state block.

Warning

Accessing a table with no default value or a list-table using a key that has not been set will cause a simulation to abort with an appropriate error message.

Output for shared variables may be “turned on” by setting <variable-name>.output = 1. For these variables, a time series of the values will be generated and saved to the results. This type of output is not available for agent variables.

More than one variable of a given type may be declared on a single line within a variables block. For example,

variables {
   shared numeric x y z
}

Warning

When declaring more than one variable on a single line, separate variable names by spaces only. Using commas will result in a compilation error.

Note

If any variable is declared multiple times, only one instance of the variable is created.

A FRED program may contain any number of variables blocks. Variables blocks are combined according to the order in which they appear in the model. For example,

variables {
   shared numeric x
   x = 0
}
variables {
   shared numeric y
   y = 0
}

is equivalent to:

variables {
   shared numeric x
   x = 0
   shaed numeric y
   y = 0
}

List-variables are initialized before single-valued variables. Within these categories, variables are initialized in the order in which they are declared. These rules imply that care should be taken when initializing one shared variable based upon another shared variable, since the second variable may not yet have a value when the first variable is set. If you want to initialize one variable based on the initial value of another variable, it may be better to use a startup block, as described next.

Finally, the initial values of all shared variables are stored in the results associated with each simulation run.

Startup Blocks

Prior to a simulation beginning, shared variables may be updated by the Meta agent in a startup block:

startup {
   ...
}

The assignments in a startup block are evaluated after shared variables are initialized in a variables block. Unlike variables blocks, the assignment statements in startup blocks may contain arbitrary expressions and are executed by the Meta agent in the order they appear within the program.

For example, if a model parameter is not well constrained, a modeler may set its value stochastically in order to capture the uncertainty in its initial value on the outcome of a model, e.g.

variables {
   shared numeric x
}

startup {
   x = normal(0, 1)
}

After the Meta agent executes the statements in the startup block, all group agents execute the statements in the group_startup block. For example, if a place Hospital has group agents, we could initialize the number of beds associaterd with hospitals as follows:

variables {
   agent numeric beds
}

group_startup {
   if (is_group_agent(Hospital)) then beds = uniform(100, 500)
}

After the group_startup block is completed, any statements in the agent_startup block are evaluated for each agent. For example:

variables {
   agent numeric temperature
}

agent_startup {
   temperature = normal(98.6, 1.0)
}

will assign to each agent a distinct temperature drawn from a normal distribution with a mean of 98.6 and a standard deviation of 1.0.

Local Variables

Condition can include one or more variables blocks that can declare any types of variable described apreviously in this chapter, including both shared and agent variables. Variables that are declared in a variables nested within a condition block are called local variables. Consider this example:

condition COND {

   variables {
      shared numeric x
   }
...
}

Declaring a variable called x within condition COND creates a variable with name COND.x. Within the declaring condition, local variables can be referenced use the short name, e.g. x will refer to the variable with the full name COND.x.

Local variables provide a condition-based name space, but they are still accessible outside the declaring condition. In the example above. any statement outside the condition COND may refer to that variable using the full name COND.x.

If there’s a variable x defined in a non-condition variables block, then any statement may refer to that variable using the notation FRED.x. This notation can be used to access the non-local variable x in a condition that has also declared x in its local variables block.