Chapter 11: Wait Rules

For each state in a given condition, the modeler should answer three questions:

  1. 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?

  2. How long does the agent stay in this state?

  3. 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:

  1. Action rules

  2. Wait rules, and

  3. 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 hour

  • wait(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 as wait(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, where YYYY represents the year, MM represents the month (with values 01,...,12), and HH represents the hour on a 24-hour clock (with values 00,...,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
}