Types of Primitives

Insight Maker supports the following key types of primitives that affect model operation. In addition to these, Insight Maker also supports a Text primitive and a Picture primitive that can be used to annotate or explain a model.

Stocks

In its simplest form, a stock is a bucket into which a something can accumulate or be withdrawn from.  What the bucket holds is specific to your model; it could be water, or money, or even deer. You can set an initial value or level for the stock that it will take on at the start of the simulation. The initial value may be a simple number or a more complex mathematical expression.

Stocks are flexible. For Instance, They can hold People, Water, Or Viruses.

One example of the use of a stock would be to simulate a reservoir. Water is added to the reservoir by rivers and then released from the reservoir for irrigation or consumption. Another example of a good use of a stock would be the population of a city. People migrate to the city and emigrate from the city. In this case the units for the stock would in numbers of people.

The value of a stock – or any other valued primitive for that matter – can be referenced in equations using a pair of square brackets around the primitive’s name. For instance, if you had a stock called My Lake, you could obtain twice its value in an equation using:

[My Lake]*2

The standard stock is in effect one large mixing bowl.  Materials are added directly to the bowl, completely mixed with the existing bowl contents, and then removed from the bowl. For most cases this is the desired behavior. For some cases, however, users will desire slightly different behavior. For instance, let us presume we are attempting to simulate an aging population. One way to simulate this would be to create a number of different stocks representing different age groups. For instance, we could have a Young stock, a Middle-aged stock, and an Old stock. Each time step we could transfer a fixed fraction of the young people to the middle-aged stock, and a fraction of the middle-aged people to the old person stock. We could simulate births by adding new individuals to the young stock, and deaths by removing people from the old stock. This would work pretty well if we had a population where the demographics remained roughly constant. Let’s assume, however, that we encounter a shock like the baby-boom generation in the United States. This shock would add a large number of people to the young stock instantly. In reality these individuals should age a large number of years before they are passed on to the middle-aged stock. Unfortunately, because we are moving a fixed fraction of the young people to the middle-aged stock each period, the shock is felt in the middle-age population immediately. This is not the desired behavior and will lead to incorrect demographic modeling.

We can attempt to remedy this by increasing the number of stocks. Instead of three, we could have six, or 30, or 1000. The more stocks we added, the more accurate our modeling. Creating such a large number of stocks, however, would be infeasible! Fortunately, Insight Maker allows you to convert a stock into a conveyor stocks, thereby creating a theoretically unlimited number of sub compartments within one stock.

Diagram of a Conveyor Stock With Fixed Delay

Conveyor stocks are special in that there are two types of values you can access. The regular single bracket notation gets you the value of material that has exited the converter. A new double bracket notation shows the total amount of material including that still in transit.

Exited Number In Stock = [Population]

Total Number In Stock = [[Population]]


Flows

A flow transports a material from one stock to another. As a designer, you must enter the rate of the flow. So, for instance, in our river example the magnitude of the flow could be a million gallons of water and the period over which it flows could be one day: in effect a million gallons per day. Flows may connect two stocks together. Alternatively, a flow can lack a start or an end. In this case, the unconnected end acts as an unlimited stock of material.

As with the initial value of a stock, the rate of a flow can be a simple number or a more complex equation. The equation can be based on many factors including the values of the stocks the flow is connected to and the current time. For example, if we had a stock representing the population of rabbits (called Rabbit Population), we could make the flow into the stock be the number of rabbits times a constant birthrate. This equation would result in geometric growth in the rabbit population:

[Rabbit Population]*0.01

Alternatively, if we had a flow representing rainfall, we could set the flow rate to be based on the current time of the year. Rainfall over the course of a year can be approximated using a sinusoidal function like the following:

 sin(2*pi*(Weeks()+10)/52)*30+35

Where weeks is a Insight Maker function that automatically takes on the value of the current simulation time measured in weeks, pi is the constant 3.14159…, and sin is another Insight Maker function that applies the trigonometric sine function to an angle inputted using radians.

A flow can be restricted to positive flows only.  In this case, the flow will only be applied if the calculated magnitude of the flow rate is positive. This restriction would be applicable for both our rabbit and rain examples. We know that neither of the flows should ever be negative (you cannot have negative births or negative rain) so we might want to set them to only allow positive flows just to insure that such an impossible event never happens (though it never will in any event given the equations we specified).

Every flow has two special properties that may be referenced in its equations. The first is Alpha which refers to the stock at the start of the flow. The second is Omega which refers to the stock at the end of the flow. In your equations you may use either of these instead of needing to know the actual names of a flow’s start or end stocks. For instance if you wanted 5% of the water from a lake to evaporate out of it each time period, you could use the following statement for the rate of a flow whose beginning was the lake:

[Alpha]*0.05


Variables

A variable is a pre-calculated or dynamically calculated value and is represented by an ellipse in the model.

Variables can be useful to synthesize data or supply calibration variables to your model. For instance, in a demographic model, a variable could be created to specify the birth rate. Alternatively, if you have a population model that has multiple age groups, you could dynamically calculate a variable to add up all the age groups and determine the total population size using the following equation (you would have to make sure that the Total Population variable was connected with links to the necessary stocks; see the next section for more information on links):

[Young Population]+[Middle Aged Population]+[Old Population]

Variables can also be used to create forcing functions which are based on the current time. Insight Maker supports a number of functions that return the current time in the specified units. These functions are: seconds, minutes, hours, days, weeks, and years. The following equation could be used to create a variable that returns the square of the current simulation time as measured in hours:

Hours()^2

Sometimes it is hard to decide which parts of your model should be simulated using stocks and which parts with variables. Generally stocks should only be used to model items that have inflows or outflows. If you have a stock in your model without either an inflow or an outflow, it would almost certainly be modeled better with a variable.


Links

A link makes two objects (flows, stocks, converters, or parameters) aware of each other. This allows the two primitives to reference the value of each other in their equations. A flow is automatically aware of both its Alpha and Omega and, conversely, its Alpha and Omega are aware of the flow. In any other case, if two primitives are not connected by a link, you will be unable to reference the value of the first in the equations of the second or vice versa.

For an example of the use of links, take the following model that only contains two parameters: A and B. A has some arbitrary value that is not important to us. B’s value is set to the following (the carrot sign “^” means to carry out a power operation, so the function here returns the square of the value of A):

[A]^2

We could construct our model like this:

a b.png

When we run this model though, Insight Maker will print out an error that no connection to A was found from primitive B. This is because A and B are not connected in any way so B is unaware that A exists. Therefore you cannot refer to the value of A from B. This problem is easy to fix though; just create a link joining A and B like this:

a-b.png

This time, the model runs perfectly.

Curved Links

By default, Insight Maker links are straight. Sometimes you will want to create links with bends in them. To do so, click on the link where you want the bend while holding down the "Shift" key. This will create a waypoint in the link that you can move to create bends. You can create as many waypoints in a link as you like; which allows you to define complex curves. To remove a waypoint, shift-click it. All waypoints will be removed when you change the connection end points of a link.


Converters

Often you will want to add structured input or empirical data to your model. For instance, if you were modeling a lake, you might want your model to have access to historical precipitation data that was recorded periodically. Alternatively, you might want to know the actual, surveyed surface area of the lake, given the volume of the lake.

Insight Maker makes using this data very easy with its converter primitive (similar to graphical function) which allows you to create an input-output relationship or graph. When the input source to a converter primitive takes on an input value, the converter takes on the corresponding output value which may then be used by other primitives that reference the converter. The input can be the current time (in seconds, minutes, hours, etc…) or the value of some other primitive that is linked to the converter.

For converters, if you have not specified a specific output value for a given input, the outputs closest to the input will be combined to develop an appropriate output based on an interpolation method you have specified. For illustration, take the following example input-output table for a converter whose input source is the current time in years and where linear interpolation has been selected.

At the start of the simulation (when the time is 0 years), the converter will take on the value of 0. Ten years into the simulation (when the time is 10 years), the converter will take on a value of 100..

You can enter data into the converter by hand or import a two-columned comma separated dataset (CSV file) that may be generated by Microsoft’s Excel or other spreadsheet programs.


States

A State is in effect an on/off switch. When the State is active, the model is in that state; when the State is inactive, the model is not in that state.

States are primarily useful when carrying out Agent Based Modeling. One or more sets of States may be placed in an agent to describe the current state of that agent. For instance, if you are modeling a population of individuals, you could have your agent definition contain a State representing whether or not an agent was a smoker. Similarly you could have a State representing whether or not an agent was a female. For continuous variable (such as age) a Stock should be used; but for binary or dichotomous variables, a State is a more natural primitive.

A State is configured using its initial value equation. This equation may be a simple value such as: 1 or 'true' for active, or 0 or 'false' for inactive. The equation can also be a logical equation such as:

not true # Which is false

Or a simple mathematical equation:

5*5 > 10 # Which is true

Or a function that depends on other primitives:

not ([Smoker] and [Diabetic])


Transitions

Transitions are to States as Flows are to Stocks: they move the model between States.

Imagine a simple Agent-Based disease model. In this model each agent might have two States: Healthy and Infected. The agents start in the Healthy State and then transition to the Infected State according to some infection rule. This transition is controlled by a Transition primitive that will be connect the two state. When the transition is activated, an agent that is in the Healthy State will be moved to the Infected State. A schematic of this simple agent with the two States and a Transition labeled Infection is shown below.

The key configuration option for a Transition is what triggers the Transition. There are three different types of triggers:

  • Timeout: A timeout trigger for the infection Transition would switch the healthy person to an infected person at a fixed time after the person became healthy. If all the agents in the model started in the Healthy State. They would all transition at the same time.
  • Probability: A probability trigger for the infection Transition would result in a fixed probability of transitioning every unit of time. The probability is the probability of transitioning per time unit. So if the time is specified as years and the probability is 0.5, roughly 50% of healthy agents will become infected every year.
  • Condition: The condition trigger allows the creation of a Transition that is based on logical relationships to other agents or events in the model. For instance, a trigger condition equation could be written that looked geographically at nearby agents and would cause a transition if an infected agent was within some nearby radius of susceptibility.

An example of a condition trigger would be to trigger the transition if some other state in the system occurs. For example:

Years > 20 # Transition if the time is 20 years or higher

Or:

[Health] < [Desired Health] # Transition if the desired health is greater than the current health


Actions

An Action can be used to manipulate a model during a simulation. Actions are primarily used in Agent Based Modeling.

An Action is defined by two features:

  • Trigger: What triggers the Action (a timeout, a probability, or a condition). See the Transition primitive description for more information on triggers.
  • The Action: What the Action does when triggered. This can be to move an agent, to change a primitive's value, to add a connection to an agent, or many other actions.

An example of an action for an agent might be to have the agent die off if its health falls below a threshold:

if [Health] < 0 then Self.remove() end if

Agent Populations

Agent Populations are a key ingredient in the construction of Agent Based Models. Agent Populations are used to actually store the agents during a simulation. There are two primary factors that control an Agent Population: the size of the population (the number of agents), and the agent base for the population (what type of agent will populate that population).

The population size is the number of agents in the population at the start of the simulation (the size can also be changed dynamically during the simulation by destroying agents or creating new agents).

The agent base defines what types of agents will be contained by the population. Your model can contain one or more definitions of agents (just like it can contain one or more populations of agents), each definition or Agent Base is specified by creating a Folder primitive around the portion of the model that will define that agent. Then the Extension property of the resulting Folder must be set to "Agent". After this is done, the Agent Base property of the Agent Population can be set to the Folder's name.

The following illustration shows what a resulting model might look like after this setup is completed. The Agent Population primitive is Population while the agent base Folder is labeled Person:

Accessing Individual Agents

Insight Maker includes a number of functions to access the individual agents within a population. The simplest of these is the FindAll() function. Given an agent population primitive that we'll call Population, the FindAll function returns a vector containing all the agents within that agent population:

[Population].FindAll()

If your agent population currently contained 100 agents, FindAll would return a vector with 100 elements where the first element referred to the first agent, the second element referred to the second agent, and so on. It is important to note that these elements are agent references, not numbers. So you can use a function like Reverse() on the resulting vector, but you cannot directly use functions like Mean(), as the agent references are not numerical values. We will see how to access the values for agents next.

In addition to the FindAll function, other find functions return a subset of the agents in the model. For instance, the FindState() and FindNotState() functions return, respectively, agents that either have the given state active or not active. For instance, imagine an agent-based disease model where our agents had a state primitive called Infected that represented whether the agent was currently sick. We could get a vector of the agents in our population that were currently sick using the following:

[Population].FindState([Infected])

And we could obtain a vector of the agents that were not currently infected with:

[Population].FindNotState([Infected])

Find functions can also be chained together. For instance, if we added a Male state primitive to our agents to represent whether or not the agent was a man; we could obtain a vector of all currently infected men with something like the following:

[Population].FindState([Infected]).FindState([Male])

Nesting find statements is effectively using Boolean AND logic (like you might use on a search engine: "Infected AND Male"). To perform Boolean OR logic (e.g. "Infected OR Male") and return all the agents that are either infected or a man (or both), you can use the Union function to merge two vectors:

Union([Population].FindState([Infected]), [Population].FindState([Male]))

If you wanted the agents that were either infected or men (but not both simultaneously), you could use:

Difference([Population].FindState([Infected]), [Population].FindState([Male]))

Agent Values

Once you have a vector of agents, you can extract the values of the specific primitives in those agents using the Value() and SetValue() functions.

The Value function uses two arguments: a vector of agents and the primitive for which you want the value. It returns the value of that primitive in each of the agents. For instance, let's say our agents have a primitive named Height. We could get a vector of the height of all the people in the model like so:

[Population].FindAll().Value([Height])

A vector of heights by itself is generally of not much use. Often we will want to summarize the vector of agents: converting the vector to a single number that represents some property of the population. For instance, we could determine the average height of individuals in the population. The following equation calculates the mean value of an agent's height:

Mean([Population].FindAll().Value([Height]))

In addition to determining the value of a primitive in an agent, you can also manually set the agents’ primitive values using the SetValue function. It takes the same arguments as the Value function, in addition to the value to which you want to set primitives. For instance, we could use the following to set the height of all our agents to 2.1:

[Population].FindAll().SetValue([Height], 2.1)

Creating and Removing Agents

You can programmatically create new agents in an agent population by using the add() function of the agent population. For example, the following will create a new agent that is part of the population My Population:

[My Population].add()

The add() function will return the newly created agent allowing you to configure it. For example:

newAgent <- [My Population].add()
newAgent.SetLocation({x: 100, y: 50}) # Set the location of the agent
newAgent.SetValue([Age], 72) # Set the agent's [Age] primitive value

You can use the remove() function of an agent to remove it from an agent population. This can be thought of as deleting or killing the agent.

Relationships Between Agents

Each Agent Population contains both a spatial geometry and a network geometry that may be used to relate agents.

Additional Agent Functions

Insight Maker has a large number of additional functions related to agents listed on the functions page.