Chapter 10: Action Rules

Actions are one of the three types of rules found within states (see Chapter 4). Actions may be categorized by what they act upon:

by the type of agent(s) performing the action:

as well as a special set of actions which can generate or modify output from a simulation.

Note

In this section, the notation <value> denotes a single-valued expression while <values> denotes a list-valued expression.

Actions by Ordinary Agents

Most actions are available to oridinary agents with the exception of those as noted below.

Actions on Variables

Agents can modify variables that keep track of values of interest. Recall that scaler and list variables may be either either shared or agent in scope. The effect of an action on these types of variables depends on this scope. An action on a shared variable will update the value(s) stored in that variable for all agents. Conversely, an action on an agent variable will only effect the value(s) stored in the variable relative to a particular agent, usually the agent performing the action.

assignment actions

A common action is assiging a value (or values) to a variable. This action can be carried out using the assignment operator, =, or equivalently using the set() action.

  • <variable> = <value> - set the agent or shared variable to a scaler value.

  • <list_variable> = <values> - set the agent or shared list variable to a list of values.

  • set(<variable>, <value>) - set the agent or shared variable to a scaler value.

  • set(<list_variable>, <values>) - set the agent or shared list variable to a list of values.

An element in a list variable may be assigned:

  • <list_variable>[<index>] = <value> - set the value of a particular element in a list variable.

Entries in a table may be assigned:

  • <table_variable>[<key>] = <value> - set the value of a key in a table variable.

  • <table_variable>[<keys>] = <values> - set the value for each key in a list of keys in a table variable.

Finally, a key in a list-table may be assigned to a list of values:

  • <list_table_variable>[<key>] = <values> - set the value of a particular entry in a list-table variable.

on single-valued variables

  • tell(<other_agent_id>, <variable>, <expression>) - set the variable of the agent whose ID is the value of the first argument to the value of the expression in the third argument.

on list-valued variables

  • push(<list_variable>, <expression>) - appends the value of the expression to the list. If the expression is a list-valued expression, the elements in the expression are all appended to the end of the original list.

  • pop(<list_variable>) - removes the last item from the list. Throws an exception if the list is empty.

on table variables

The following actions can be performed on both table and list-table variables:

  • clear(<table>) - erase all key-value pairs in a table.

  • erase(<table>, <key_expression>) - erase a particular key-value pair in a table.

on list-table variables

  • push(<list_table_variable>[<key>], <expression>) - appends the

    value of the expression to the list associated with the key in the list_table. If the expression is a list-valued expression, the elements in the expression are all appended to the end of the original list.

  • pop(<list__table_variable>[<key>]) - removes the last item from

    the list associated with the key in the named list-table. Throws an exception if the list is empty.

  • An assignment list-table[key][pos] = value creates the list list-table[key] if it does not already exist, and assigns value to the 0-indexed position pos in the list. Any newly created items with index less than pos are assigned 0.

reading variables from a file

The following actions support reading in lists and tables from files:

  • read_table(<table_name>, <filename>, <key_column>, <value_column>) - populates a table with key-value pairs from a specified CSV file. The <table_name>> and <filename> arguments are static. The third argument is evaluated to obtain a column index (starting with 0) from which to read the keys, and the fourth argument is evaluated to obtain a column index from which to read the values. The file must be a comma-separated-value file that contains the indicated columns. The assignments of key-value pairs is sequential, so that if a key occurs more than once in the file, the final value will be associated with that key in the table.

    Note

    This function does not change any key-value pair in the table if the key does not occur in the file. That is, this function can only increase the number of key-value pairs in the table.

  • read_list_table(<list_table_name>, <filename>, <key_column>, <value_column>) - populates a list_table with key-list_value pairs from a specified CSV file. The <list_table_name>> and <filename> arguments are static. The third argument is evaluated to obtain a column index (starting with 0) from which to read the keys, and the fourth argument is evaluated to obtain a column index from which to read the values. The file must be a comma-separated-value file that contains the indicated columns. For each key-value pairs read from the file, the value is appended to the list associated with the key in list_table.

    Note

    This function overwrites any previous contents of the list_table.

Note

Lists can also be read from a file using the read() function, e.g.:

some_list = read(data_file.csv, column_index)


This function reads the indicated column from the file and stores all
the values in that column in the list variable.

Actions on Conditions

Entering a state may cause an agent to change its susceptibility or transmissibility for a condition through the following actions:

  • <condition_name>.sus = <value> - set the agent’s susceptibility to the condition to the value of the expression. Typically, a value of 1.0 is used to indicate the agent is “fully susceptible”.

    Note

    Setting <condition_name>.sus to a value less than 0.0 has the same effect as setting it to 0.0.

  • <condition_name>.trans = <value> - set the agent’s relative transmissibility for the condition to the value of the expression. The agent’s transmissibility is multiplied by the condition’s transmissibility to determine how infectious the agent is for the condition. Typically, a value of 1.0 is used to indicate “fully transmissible.”

    Note

    Setting <condition_name>.trans to a value less than 0.0 has the same effect as setting it to 0.0.

    For example, if an agent enters one of two states within a transmissible condition, COVID19, with the following actons:

     state Asymptomatic {
         COVID19.trans = 0.5
     }
    
     state Symptomatic {
        COVID19.trans = 1.0
    }
    

    The agent in the Asymptomatic state will be half as infectious as an agent that enters the Symptomatic state.

Warning

<condition_name>.sus and <condition_name>.trans may be set to values larger than 1.0. These values come into play when agent A attempts to transmit a condition to agent B. At that time a uniform random number in the range [0,1) is compared against the product the transmissibility of agent A and susceptibility of agent B. If the random number is less than the product then the attempted transmission succeeds. So the transmissibility of the the first agent can compensate for the susceptibility of the second agent, and vice versa. However, if the product is greater than or equal to 1.0, the transmission will always be successful–further increases in either quantity will have no effect.

Entering a state may cause the agent to change from one state to another state in another condition. As an example, an agent that enters a state representing receiving immunity from a vaccine may have the effect of changing from a susceptible state to a non-susceptible state for one or more disease conditions.

  • set_state(<condition_name>, <state_name1>, <state_name2>) - if the agent is currently in <condition_name>.<state_name1>, then the agent’s current state in <condition_name> becomes <state_name2>.

  • set_state(<condition_name>, <state_name>) - the agent’s current state in <condition_name> becomes <state_name>, regardless of the previous state of the agent in <condition_name>.

Examples,

state A {
    set_state(MOOD, Excited)
    set_state(VOTE, Waiting, Ready)
}

In this example, the agent entering state A changes its state in condition MOOD to the Excited state, regardless of the current state of the agent’s MOOD. The second rule changes the agent to the Ready state in the VOTE condition only if the agent is already in the state Waiting.

Actions on Groups

Individual agents interact with others in mixing groups, including places and networks. The following actions affect how the agent interacts with mixing groups:

  • join(<group_name>) - The agent will select and join a random group of the given type. If the agent already belongs to group of the given type, the action has no effect.

  • join(<group_name>, <expression>) - The agent will select and join a specific group of the given type. The expression evaluates to the ID of a specific group. If the agent already belongs to group of the given type, the action has no effect.

  • quit(<group_name>) - If the agent belongs to a group of the given type, the agent will leave that group; otherwise, the action has no effect. If the agent quits a Place, the agent also quits any partition of that Place.

  • skip(<group_name>, ..., <group_name>) - The agent temporarily stops attending any of the listed groups. The agent continues to skip until the agent performs an attend() action in this same condition. If the list is empty, the agent stops attending any group.

  • attend(<group_name>, ..., <group_name>) - This action cancels any previous skip() action for the named groups that was excuted in the current condition. If the list is empty, the agent resumes its normal attendance at group activities.

on places

Places can change locations in FRED. Movement occurs when an agent performs the move() action, with the first argument being a place name to which the agent belongs and the other arguments giving the change in x and y (in meters).

  • move(<place_name>, <x>, <y>) - move the agent’s place <x> meters East and <y> meters North.

For example:

move(Car, dx, dy)

This example assumes that model defines a place called Car and that the agent is a member of the specific car being moved.

Another action moves an agent’s place to the location of another place:

  • move_to(<place_name>, <place_name>) – move the agent’s first argument place to the location of the agent’s second argument place. For example,

    move_to(Car, Household)
    

This moves the agent’s car to the agent’s household location.

A third kind of action moves an agent’s place to a specific location specified as latitude and longitude:

  • move_to_location(<place_name>, <latitude>, <longitude>) - move the agent’s place to the indicate latitude and longitude. For example,

    move_to_location(Car, lat, lon)
    

In all the above cases, a run-time error occurs if the agent does not belong to the indicated place.

on networks

  • add_edge_to(<network_name>, <other_agent_id>) - An edge in the given network is added from the agent to the agent whose ID is the value of the second argument, an expression that is interpreted as the ID of another agent. Both agents join the network if they are not already members. If the expression does not evaluate to valid agent ID, the action has no effect. If the expression is a list-valued expression (that is, a list of agent IDs), then an edge is added to each agent in the list. For example, the followng action would create an edge in the network HH to all members of the agent household:

add_edge_to(HH, members(Household))
  • add_edge_from(<network_name>, <other_agent_id>) - An edge in the given network is added to the agent from the agent whose ID is the value of the second argument. Both agents join the network if they are not already members. If the second argument does not evaluate to valid agent ID, the action has no effect. If the second argument is a list-valued expression (that is, a list of agent IDs), then an edge is added from each agent in the list.

  • delete_edge_to(<network_name>, <other_agent_id>) - If an edge exists in the given network from the agent to the agent whose ID is the value of the second argument, then the edge is deleted. Otherwise, the action has no effect. If the second argument is a list-valued expression (that is, a list of agent IDs), then an edge to any agent in the list is deleted.

  • delete_edge_from(<network_name>, <other_agent_id>) - If an edge exists in the given network to the agent from the agent whose ID is the value of the second argument, then the edge is deleted. Otherwise, the action has no effect. If the second argument is a list-valued expression (that is, a list of agent IDs), then an edge from any agent in the list is deleted.

  • set_weight_to(<network_name>, <other_agent_id>, <pair_expression>) - If an edge exists in the given network from the agent to the agent whose ID is the value of the second argument, then the weight of the edge is set to the value of the third argument, which may include factors defined over either or both agents (see the discussion of pair context in chapter 7). Otherwise, the action has no effect. If the second argument is a list-valued expression (that is, a list of agent IDs), then any edge to any agent in the list is affected.

  • set_weight_from(<network_name>, <other_agent_id>, <pair_expression>) - If an edge exists in the given network to the agent from the agent whose ID is the value of the second argument, then the weight of the edge is set to the value of the third argument, which may include factors defined over either or both agents (see the discussion of pair context in chapter 7). Otherwise, the action has no effect. If the second argument is a list-valued expression (that is, a list of agent IDs), then any edge to any agent in the list is affected.

Actions on Other Agents

Any agent may change the state of any other agent or list of agents with the action:

  • send(<expr>, <condition_name>, <state_name>)

The expression may evaluate to either an agent ID or a list of agent IDs. Each agent in the list is immediately transitioned to the given state in the given condition.

If the condition is transmissible and the destination state is the exposed_state, then this is treated as an exposure from the executing agent. The sending agent need not be transmissible for the destination condition.

Warning

It is legal to send an agent from the Excluded state to another state.

Actions on Self

Some actions directly impact the agent preforming the action.

on location

Each agent has a specific location specified by its latitude and longitude. The agent’s location is initialized to the agent’s household and does not change unless explicitly modified by an action.

  • move(dx dy) - move the agent itself dx meters to the east and dy meters north.

  • move_to(<place>) - move the agent to a specific place to which the agent belongs.

  • move_to_location(<lat>, <lon>) - move to specific location specieifed by the lattitude and longitude coordinates.

the birth and death actions

The following actions effect the life and death of agents:

  • give_birth() - generates a new agent as offspring to the agent performing the action.

  • die() - causes an agent to die.

Actions by Group Agents

Group agents are assigned to individual mixing groups, include places and networks. The group agent can control certain aspects about their groups using the following actions.

The following actions are only permitted by group agents. A run-time error if any other agent attempts to perform these actions.

  • close() - The place associated with this group agent is temporarily closed. The schedule for the place is not changed. The temporary closure remains in effect until the group agent performs a reopen() action within the same condition as the close() action. A run-time error occurs if no such place exists for this agent.

  • reopen() - Any temporary closure for the place associated with this group agent is lifted for the current condition. If the place still has a temporary closure due to another condition, the place remains closed. A run-time error occurs if no such place exists for this agent.

  • adjust_contacts(<multiplier>) - The contact_rate for the group associated with this group agent is multiplied by the value of the given expression. The effect is to change the number of contacts among the agents attending the group. This can change the rate of transmissions that occur within the group. This change remains in effect until the group agent adjusts the contacts again. To return to the initial contact rates, the agent should do adjust_contacts(1.0).

A group agent associated with a specific place may alter the times at which that place is open for business using the following actions. If any other agent attempts to perform these action, a run-time error occurs.

  • add_to_schedule(<day-of-week>, <start-hour>, <start-minute>, <end-hour>, <end-minute>) - the group agent’s place is scheduled to be open on the given day of the week, starting at the start-hour and start-minute, and ending just prior to the end-hour and end-minute. The <day-of_week> argument may be an expression that evaluates to a value in the range 0..6 where 0 represents Sunday. The <day-of_week> argument may also be one of the following symbolic terms: Sun, Mon, …, Sat (meaning the corresponding of the days of the week), Weekdays (meaning Monday through Friday), Weekends (meaning Saturday and Sunday), or Everyday. The hours must evaluate to a value in the range 0..23. Symbolic aliases for hours are recognized, such as 2pm for 14. The minutes must evaluate to a value in the range 0..59.

  • remove_from_schedule(<day-of-week>, <start-hour>, <start-minute>, <end-hour>, <end-minute>) - the indicated period is removed from the schedule for the group agent’s place. See add_to_schedule() for a description of the arguments.

  • clear_schedule() - The place associated with the group agent has no scheduled hours of operation.

  • reset_schedule() - removes any site-specific schedule from the group agent’s place, reverting its schedule to that of its place type.

Actions by the Meta Agent

Some actions can only be performed by the Meta agent.

reading agent file

The Meta agent can add new ordinary agents to the population, or add new features to existing agents with the following action:

  • read_agent_file(<filename>) - reads a user data file using the same methods used to read an agent_file from the synthetic population. The fields are specified by a header line in the file. The field ID if it exists gives the agent ID. A missing ID field or a value of 0 in a specific row results in a new agent generated with a unique ID. The other fields in the file are interpreted as agent variable values if the column header corresponds to the name of a declared agent variable. read_agent_file(filename) can also be used as a list-expression in which case it returns the list of IDs of the agents read from the file.

reading place file

The Meta agent can add new place locations to the population, or add new features to existing places with the following action:

  • read_place_file(<filename>,<place_type>) - reads a user data file using the same methods used to read a place_file from the synthetic population. Both arguments are static. The fields in the file are specified by a header line in the file. The field ID if it exists gives the place ID. A missing ID field or a value of 0 in a specific row results in a new place being generated with a unique ID. The other fields in the file are interpreted as agent variable values for the group agent associated with the place, if the column header corresponds to the name of a declared agent variable. read_place_file(<filename>,<place_type>) can also be used as a list-expression in which case it returns the list of IDs of the places read from the file.

on transmissibility

  • <condition_name>.trans = <value> - Set the transmissibility of the condition to the value of the expression. This change remains in effect until further modification by the Meta agent.

on places

The Meta agent may create a new site for any place type. If any other agent attempts to perform this action, a run-time error occurs.

  • add_site(<place_name>, <sp_id>, <latitude>, <longitude>, <elevation>) - Adds a new site to the named place with sp_id, latitude, longitude, and elevation set to be the values of the last four expressions in the argument list.

The Meta agent may alter the times at which places are open for business using the following actions. If any other agent attempts to perform these actions, a run-time error occurs.

  • add_to_schedule(<place>, <day-of-week>, <start-hour>, <start-minute>, <end-hour>, <end-minute>) - the place is scheduled to be open on the given day of the week, starting at the start-hour and start-minute, and ending just prior to the end-hour and end-minute. If the <place>> argument is the name of a place type, the changes apply to all sites if that place type that do not have site-specific schedules. If <place> is an expression that evaluates to a place ID, then the change applies only to that specific site. The <day-of_week> argument may be an expression that evaluates to a value in the range 0..6 where 0 represents Sunday. The <day-of_week> argument may also be one of the following symbolic terms: Sun, Mon, …, Sat (meaning the corresponding of the days of the week), Weekdays (meaning Monday through Friday), Weekends (meaning Saturday and Sunday), or Everyday. The hours must evaluate to a value in the range 0..23. Symbolic aliases for hours are recognized, such as 2pm for 14. The minutes must evaluate to a value in the range 0..59.

  • remove_from_schedule(<place-type-name>, <day-of-week>, <start-hour>, <start-minute>, <end-hour>, <end-minute>) - the indicated period is removed from the schedule for the indicated place. See add_to_schedule() for a description of the arguments.

  • clear_schedule(<place>) - If the expression is a place-type name, this action clears the schedule for the named place type. This means that the place type has no scheduled hours of operation. If the expression evaluates to an individual place ID, the schedule is cleared just for that site.

  • reset_schedule(<place-ID>) - removes any site-specific schedule from the given site, reverting its schedule to that of its place type.

  • read_schedule_file(<filename>) - reads in a file which each line has the format: ID,DAYS,OPEN,CLOSE where ID is the ID of a specific place site; DAYS has a format like Mon for Monday or Mon-Wed for Monday and Wednesday or Weekdays (meaning Monday through Friday), Weekends (meaning Saturday and Sunday), or Everyday; and OPEN and CLOSE have the format HH:MM for hour and minute using a 24-hour clock. The specific place schedule is altered to add the indicate times and days to the schedule.

on networks

The Meta agent can alter the edges in a network to create networks with specific degree distributions:

  • randomize_network(<network_name>, <mean_degree>, <max_degree>) - The network is converted to a random network. The new mean degree of the network is determined by the value of the second argument, and the maximum degree allowed is determined by the value of the third argument.

  • preferential_attachment_network(<network_name>, <trials>, <samples_per_trial>) - The network is converted to a preferential attachment network. The process is to repeatedly select a source node in the network and one outward edge from the source node (one source node per trial), and then to pick a number of sample nodes as possible destinations for the original edge. The original edge is replaced by an edge from the source node to the sample node with the largest in-degree, if the in-degree of the sample if larger than the in-degree of the original destination node.

    Note

    This “rewiring” procedure has been shown by Barabasi et al. (1999) to result in scale-free networks. The number of trials is determined by the value of the second argument, and the number of samples per trials is determined by the value of the third argument. The larger the number of trials, the more the degree distribution tends to a power-law distribution. this action leaves the mean degree of the network unchanged; that is, the total number of edges in the network does not change.

Actions Generating Output

Various actions can be used to alter the kinds of output generated by agents.

printing to file

Any agent may perform the following action to produce output:

  • open_csv(<filename>,...) - opens a csv (comma-seperated vales) file with each additional argument being a quoted string serving as a column header. The first argument <filename> is static. All further output to this file must have the same number of fields as the column header.

It is a run-time error to open the same file more than once. There is a predicate is_file_open(<filename>) which returns true if the file has been opened, and false otherwise. It is common practice to have the Meta agent open all output files in the startup block:

startup {
   open_csv(file1.csv, "ID", "VALUE")
   open_csv(file2.csv, "DAY", "HOUR", "REPORT")
}
  • print_csv(<filename>,...) - appends one line to the previously opened csv file <filename>. The first argument <filename> is static. There must be one additional argument for each column in the file. Each argument can be either a single value or a quoted string. The output is comma-separated.

  • print_file(<filename>,...) - appends one line to the named file. The first argument <filename> is static. Each additional argument can be either a single value, a list expression, or a quoted string. In a list argument occurs, the values in the list are printed separated by spaces. There is no separation between fields in the output, so use ” ” as additional arguments where needed.

  • print(...) - behaves like print_file() except the output is directed to the terminal at the end of the run. Each argument can be either a single value, a list expression, or a quoted string. In a list argument occurs, the values in the list are printed separated by spaces. There is no separation between fields in the output, so use ” ” as additional arguments where needed.

Note

All output files are written to the directory <path_to_results>/JOB/<id>/OUT/RUN<run_number>/CSV/<filename>.

Note

An output file can be retrieved by the command fred_get_file -k <key> -f <csv_file> -r <run>. If -r is omitted, then the files for all runs are retrieved. File are printed to standard output, with a header of the form:

RUN<run> =====================

Consider the following sample test program called test_print.fred:

simulation {
    locations = Jefferson_County_PA
    start_date = 2020-Jan-01
    end_date = 2020-Jan-04
}

startup {
    open_csv(out.csv, "ID", "SIM_DAY", "HOUR", "REAL_AGE", "SIM_RUN")
}

condition FOO {
    start_state = Start

    state Start {
        wait(0)
        if (age > 99) then next(A)
        default(Excluded)
    }

    state A {
        print_csv(out.csv, id, sim_day, hour, real_age, sim_run)
        wait(1)
        next(A)
    }
}

This population happens to have only two agents over the age of 99, so we should see output from each of these two agents during each time step they enter state A.

Suppose we run the program in a job with 2 runs:

$ fred_job -k test_print -p test_print.fred -n 2 -m 2
fred_job: starting job test_print at Tue Feb  2 15:36:50 EST 2021

fred_compile -p test_print.fred
No errors found.
No warnings.

fred_job: running job test_print id 154 run 1 ...
fred_job: running job test_print id 154 run 2 ...
run_set 0 completed at Tue Feb  2 15:36:51 EST 2021

fred_job: finished job test_print 154 at Tue Feb  2 15:36:51 EST 2021

Now let’s retrieve the output file from run 2:

$ fred_get_file -k test_print -f out.csv --run 2

ID,SIM_DAY,HOUR,REAL_AGE,SIM_RUN
185882382,0,0,102.932,2
354736951,0,0,106.464,2
185882382,0,1,102.932,2
354736951,0,1,106.464,2
185882382,0,2,102.932,2
354736951,0,2,106.464,2
185882382,0,3,102.932,2
354736951,0,3,106.464,2
185882382,0,4,102.932,2
354736951,0,4,106.464,2
185882382,0,5,102.932,2
354736951,0,5,106.464,2
185882382,0,6,102.932,2
354736951,0,6,106.464,2
185882382,0,7,102.932,2
354736951,0,7,106.464,2
...

printing an event message

It is often useful to print messages associated with an agent’s current state. This is mainly useful when developing or debugging a model, but it might also be used whenever it is necessary to trace the activity of agents. The following actions address this need:

  • print_event_file(<filename>, <arg_list>) - print one line of text to the named file. The line begins with the simulation date, the simulation time, the simulation day, the agent id and the current condition and state. This is then followed by the information in the arg list in the same way as print_file().

  • print_event(<arg_list>) - behaves the same as print_event_file() except that the output is directed to stdout.

For example:

simulation {
   locations = Jefferson_County_PA
       start_date = 2020-Jan-01
       end_date = 2020-Jan-01
}

condition TEST {
   start_state = Start
       state Start {
           if (age > 96) then print_event(age)
               wait()
               default()
}

produces the output:

===================================
output from RUN1:
2020-01-01 00:00:00 day 0 agent 221220472 in TEST.Start 98
2020-01-01 00:00:00 day 0 agent 335367245 in TEST.Start 99
2020-01-01 00:00:00 day 0 agent 365122652 in TEST.Start 98
2020-01-01 00:00:00 day 0 agent 376295212 in TEST.Start 98
2020-01-01 00:00:00 day 0 agent 402760673 in TEST.Start 98
2020-01-01 00:00:00 day 0 agent 133928247 in TEST.Start 99
2020-01-01 00:00:00 day 0 agent 190108957 in TEST.Start 97
2020-01-01 00:00:00 day 0 agent 236048890 in TEST.Start 98
2020-01-01 00:00:00 day 0 agent 343232988 in TEST.Start 97
2020-01-01 00:00:00 day 0 agent 939614941 in TEST.Start 104
2020-01-01 00:00:00 day 0 agent 939614910 in TEST.Start 102
===================================

printing an abort message

The following action will terminate a FRED run under program control:

  • abort() - causes FRED to terminate with an error message:

FRED ERROR: Agent <id> aborts in condition <cond_name> state <state_name> on sim day <N> sim date <YYYY-MM-DD>:
<rule_containing_abort>

Example:

condition FOO {
      ...
    state B {
        if (age < 18) then abort()
        ...
    }
}

If an agent under 18 enters state B above, FRED exits with an error message like:

FRED terminated with errors:
===================================
RUN1: FRED ERROR: Agent 164581176 aborts in condition FOO state B on sim day 10 sim date 2020-01-11:
if(age<18) then abort()

Note

The abort() function provides functionality normally associated with an

assert() operator since FRED prints the rule that causes an abort.

Control Structures for Actions

FRED includes several control structures that determine which actions are performed by an agent.

if-then-else

Any action may be preceded by an if-then test. The action is performed only if the test is true for the agent executing the statement.

For example,

if (age < 10) then print("agent ", id, " is younger than 10")

In this example, the agent executing the statement prints a message if the agent’s age is less than 10.

The test may contain any number of clauses separated by &, each of which must be true for the action to be performed.

For example,

if (age < 10 & size(Household) < 5) then <some_action>

Here, the action is performed if the agent is less than 10 and belongs to a Household with fewer than 5 members.

To perform more than one action if the test is true, put the actions into a code block:

if (<test_1> & <test_2> & ... <test_I>) then {
   <action_1>
   <action_2>
   ...
   <action_N>
}

To perform some actions if the test is true and some other actions if the test is false, use an if-then-else statement:

if (<test_1> & <test_2> & ... <test_I>) then {
   <action_if_true_1>
   <action_if_true_2>
   ...
   <action_if_true_N>
}
else {
   <action_if_false_1>
   <action_if_false_2>
   ...
   <action_if_false_M>
}

Example:

if (age < 10) then {
   count_under_10 = count_under_10 + 1
   print("agent ", id, " is younger than 10")
}
else {
   count_10_or_older = count_10_or_older + 1
   print("agent ", id, " is at least 10")
}

Note

If a statement includes an else part, then brackets are required for both the then block and the else block, even if these block contain only one action.

The spacing in an if-then-else is ignored, so it is acceptable to have an entire statement on a single line, for example:

if (age < 10) then {x = 0} else {x = 1}

Note that brackets are required here because the statement includes an else part.

for-loops

A for-loop is used for iterating over a list in a manner similar to the for-loop in Python. A set of actions is performed by the agent, once for each item in the list.

for (<shared_var>, <list-expression>) do {
   <action_1>
   <action_2>
   ...
   <action_N>
}

The for statement has two arguments. The first argument must be the name of a shared numeric variable that serves as a loop variable. The second argument is an expression that evaluates to a list of values.

The loop variable is assigned to each value in the list and the block of actions is performed for each value of the loop variable.

Examples:

my_list = list(2,6,5,8)
for (x, my_list) do {
   if (x > 5) then print(x)
}

The above example would print out 6, and 8, one value per line.

To loop through a set of actions a specified number of times, use the range() function. The range() function returns a list of numbers, starting with 0 by default, and increments by 1 (by default), and ends at a specified number.

With a single argument, range(N) returns the list 0..(N-1). For example,

for (x, range(6)) do {
    print(x)
}

The above code would print of the values 0 through 5, one value per line.

while-loops

A while-loop is used for iterating over a set of actions as long as a given test is true.

while (<test>) do {
   <action_1>
   <action_2>
   ...
   <action_N>
}

For example:

i = 0
while (i < 6) do {
   print(i)
   i = i + 1
}

The above code would print of the values 0 through 5, one value per line.

Note

Remember to add 1 to i in the above example, or the statement would run forever.

nested statements

It is acceptable to nest control statements in FRED. For example, you can use nested for-loops or use if-then-else statements inside of while-loops.