Chapter 11: Wait Rules
For each state in a given condition, the modeler should answer three questions:
What happens when an agent enters this state? That is, what does this state mean in terms of the agent’s own status or how the agent interacts with others?
How long does the agent stay in this state?
What state does the agent go to next?
The answers to these questions are expressed in rules that control how agents change during a FRED simulation. There are three major categories of rules in FRED that corresponds to the questions above:
Action rules
Wait rules, and
Transition rules.
This Chapter focuses on wait rules and how event timing occurs.
Whenever an agent enters a state, FRED determines how long the agent should wait in that state before the transition rules are applied to select the next state. The wait time for each state is defined through wait rules that have the form:
[ if (<test1> & <test2> & ... <testN>) then ] wait(<expression>)
The portion in brackets is optional. The argument to wait()
specifies a duration (in hours) for how long to wait before the
transition decisions are made. Some examples:
wait(1)
– Wait 1 hourwait(0)
– Apply transition rules immediately. Transient states with zero wait times are often useful, since they can cause effects that change the agent’s other conditions, or they can serve as decision points that separate the state trajectories of agents based on their properties.wait()
– Wait indefinitely.wait(-1)
– Wait indefinitely with any negative value.wait(1.6)
– Wait 2 hours. The value of the expression is rounded to the nearest integer.wait(0.4)
– Same aswait(0)
. The value of the expression is rounded to the nearest integer.
Any expression is permitted, so if the wait time should be drawn from a normal distribution with median 5.5 days and standard deviation of 2.0 days, you can say:
wait(24 * normal(5.5, 2.0) )
Note
State durations are often defined using statistical distributions. See chapter 7 for a list of distributions.
The until() Function
The until()
function is especially useful in wait rules. This
function returns the number of hours until the specified date and time:
until(year, month, day_of_month, hour)
For example: the following wait rule would wait until 2020-Dec-31 at 11pm.
wait(until(2020,12,31,23))
Warning
If an agent encounters a wait-until statement at a moment
greater than or equal to the date and time indicated within the
until()
function, the agent will wait forever in the state.
This situation is treated as equivalent to encountering an empty wait
statement, i.e. wait()
. A common way to account for this situation is
to add a conditonal wait prior to the wait-until statement. For example,
if (now >= sim_step(2020,12,31,23)) then wait(0)
wait(until(2020,12,31,23))
In the above example, if an agent enters the state on or after 2020-Dec-31 at 11pm, they will transition to the next state immediately.
The until function understands symbolic terms for months and hours, so the above could also be written as:
wait(until(2020, Dec, 31, 11pm))
Any argument can be an expression, for example:
wait(until( year+1, Jan, 1, 12am)) # wait until next New Year’s day
Note
An illegal argument to until()
will cause the simulation to
abort with an error message, for example wait(yr, mon, day, hr)
where hr < 0
or hr > 23
.
If only three arguments are given, the first argument must be the abbreviation for a month, such as:
until(Dec,12,3pm)
If only two arguments are given, the first argument must be the symbolic name of a month or a day of the week:
wait(until(Sat,11pm))
wait(until(Dec,31))
The latter case assumes a time of 12am
.
If a single argument is supplied of the form <number>am
or
<number>pm
, it is interpreted as a time of day on a 12-hour clock:
wait(until(2pm)) # wait until 2pm in the afternoon
If until
is given a single argument not in the format <number>am
or <number>pm
, the argument is interpreted as a timestamp or
a datestamp:
A timestamp is an expression that evaluates to a number of the form
YYYYMMDDHH
, whereYYYY
represents the year,MM
represents the month (with values01,...,12
), andHH
represents the hour on a 24-hour clock (with values00,...,23
.
You can construct a timestamp using the the formula:
my_timestamp = 1000000*year + 10000*month + 100*day_of_month + hour
A datestamp is an expression that evaluates to a number of the form
YYYYMMDD
.
The formula for a datestamp is:
my_datestamp = 10000*year + 100*month + day_of_month
For example, we might have a variable next_change
whose current
value is 20201104
(a valid datestamp). Then the following makes an
agent wait until 2020-Nov-04 at 12am:
wait(until(next_change))
If the current value of next_change
is the timestamp
2020110413
, then the following makes an agent wait until 2020-Nov-04
at 1pm:
wait(until(next_change))
If the expression does not evaluate to a valid timestamp or datestamp, a run-time error will occur and the simulation will be terminated.
The until()
function always returns a positive number. That is, it
always refers to a future time step.
Example:
state A {
wait(until(12am))
next(B)
}
If an agent enters state A
at 12am, the agent will wait 24 hours.
To have the agent go to state B
at the earliest possible midnight, use
this pattern:
state A {
if (hour == 12am) then wait(0)
wait(until(12am))
next(B)
}
Warning
the wait_until_
factors in FRED 6 are no longer supported.
Multiple Wait Rules
If a state contains multiple wait rules, the first rule that applies to the agent is used
state Symptoms {
if (age <= 10) then wait(3*24)
if (age <= 20) then wait(4*24)
wait(5*24)
next(Recovered)
}
In the example above, we assign a duration of Symptoms
that depends on
the age of the agent: 3 days for agents with age 10 or less, 4 days for
agents between 11 and 20 years old, and 5 days for agents over 20.
Note
Each state must have at least one unconditional wait rule, or a compiler error will result and the program will not execute. Given how wait rules are evaluated, any wait rule after the first unconditional wait rule will be ignored.
Zero-duration Wait Rules
If an agent executes a wait rule with zero duration, e.g. wait(0)
,
the agent immediately evaluates the transition rules for the current
state (as described in chapter 13) and
proceeds to the selected next
state. If the agent also executes a zero-duration in the next state, the
agent again evaluates that state’s transitions rules and proceeds to the
next state. This process continues until the agent reaches a state in
which it executes a non-zero duration wait rule.
This pattern is often useful when the the agent needs to make a series of decisions during a given time steps, and each zero-duration state represents a separate decision branch opportunity. A series of zero-duration states is also useful when an agent needs to execute actions in a loop, for example, to process items in a list.
However, it is also possible to create infinite loops through a series of zero-duration state transitions, for example:
state A {
...
wait(0)
next(B)
}
state B {
...
wait(0)
next(A)
}
It is not possible for the FRED compiler to detect all such situations. To protect against infinite loops, the FRED platform counts the number of zero-duration transitions performed by an agent during each step, and terminates with an error message if an agent performs more than a set maximum number of zero-duration transitions:
FRED ERROR: LOOP DETECTED AFTER 1000000 LOOPS condition <condition_name> day <sim_day> hour <hour> person <id> old_state <state_name> new_state <state_name>
The maximum number is controlled by the max_loops
configuration
parameter, with default value max_loops = 1000000
. If you need to
have an agent perform more than this number of zero-duration transition,
for example, to loop over very large lists, you can set the property in
the simulation block, for example:
simulation {
max_loops = 999999999
}