A routine are used to explain ps-i how to compute quantities specific to a particular model. Writing a routine is very much like using a calculator. In fact the name "routine" was chosen specifically to remind a person used to C or C++ that these are not functions in the sense of these programming languages.
Routines take no arguments. Rather, there is a global notion of "current agent". Before any routine is execute the "current agent" is set to point to a particular agent on a field. Thus if you routine is told to look for the value of attribute influence the particular value it obtains depends on particular current agent. There are several types of routines:
You can see the list of all functions currently known to Ps-i (this can depend upon the model loaded) by opening "Main menu->Help->Routine Browser" window.
Example:
routine 'test1' composite comment 'An example' code "[inactive]*([influence]+6)" end
Each routine has a distinct name, a type and a comment. Other directives vary depending on the type of the routine.
These require no declaration in the model
Example:
routine 'identity_poll' from 'sum_over' comment 'Computes identity count around the current agent' parameters "agent_range" "activated([cache])==$1" "agent_influence" end
This type of routine specifies from which routine it was derived. You should also specify a parameters directive. The number of parameters vary depending on which prototype routine was used. In the above example a customized routine "cache_activated" is derived from prototype routine "activated" by specifying which attribute to use. The routine "cache_activated" will thus return the activated identity in attribute "cache" (assumed to be of type repertoire). Note: due to time constraints no type checking was implemented (however the necessary hooks are in place). Though you can use attribute of any type in the above example only attributes of repertoire type will produce meaningful numbers.
Example:
routine 'test1' composite comment 'An example' code "[inactive]*([influence]+6)" end
You can use three directives, beside comment, during routine declaration.
Example:
routine 'agent_range' composite comment 'Returns range of an agent' when "agentclass==[[#basic]]" "b_range" when "agentclass==[[#entrepreneur]]" "e_range" when "agentclass==[[#innovator]]" "e_range" when "agentclass==[[#apathetic]]" "b_range" when "agentclass==[[#fanatic]]" "-1" code "0" end
(A) | value of A |
$2 | value of parameter 2. Produces recoverable error when number specified is greater than number of existing parameters. Parameters start with 1 and cannot be 0 or negative. |
A + B | add values of A and B |
A - B | subtract B from A |
A * B | multiply values of A and B |
A / B | divide A by B (division by 0 produces recoverable error) |
A mod B | remainder of division A by B (division by 0 produces recoverable error) |
A ? B : C | if value of A is non zero return value of B, otherwise return value of C |
A and B | return 1 if both A and B are non zero, return 0 otherwise |
A or B | return 1 if at least one of A and B is non zero, return 0 otherwise |
A intersect B | intersect two sets (bitwise and) |
A union B | union of two sets (bitwise or) |
A <= B | return 1 if A is less or equal to B, return 0 otherwise |
A < B | return 1 if A is strictly less than B, return 0 otherwise |
A >= B | return 1 if A is greater or equal to B, return 0 otherwise |
A > B | return 1 if A is strictly greater than B, return 0 otherwise |
A = B | return 1 if A and B have equal values |
[[aname]] | return index of attribute aname |
[[#aname]] | return index of agentclass aname |
[aname] | return value of attribute aname of the current agent |
{aname}, also aname | return value of function with name aname |
aname(a,b,c) | return value of function with name aname and parameters a, b, c |
false | same as 1 |
true | same as 0 |
Example:
routine 'test4' composite comment 'Returns square of its argument' code "$1*$1" end
Making a call "test4(rand)" will only call rand one time as it is evaluated before calling test4
Example:
routine 'my_parameter' parameter comment 'An example' code "4" end
As you can see the specification is exactly like for composite routine but with type specified as parameter. The name of the routine gets displayed as the name of the paratemer and the comment is displayed in the description field.
Note: in particular, this implies that in Ps-i you can input expressions as parameters. So, setting my_parameter to time*2 will make it dependent on time.
These routines have no user assigned name and are not visible in routine browser. They are created whenever the user specified an expression.
Example:
statistics sum 'active_agents' "1" "[inactive]==false" end
In this case two inline functions would be created one for "1" and another for "[inactive]==false".
During evaluation of expression it may happen that Ps-i will encounter an error, like, for example, division by 0. When this happens an expression is assigned a special "error" value. This value always resolves as false in boolean tests. When saving in statistics file it will be printed as "NaN". When displayed it will produce gray color. All further manipulations with an error value will produce another error value.
You can force an expression to return a recoverable error by calling builtin function error.
See also About Ps-i 2.4.