HyperManual Lsd Windows Lsd FAQ's

Lsd Equations Macro Language

See the Introduction to Lsd macro language for equations. Lsd accepts also pure C++ with the actual Lsd functions expressed in pure C++ (most models are expressed in this language. Refer to the manual for the original Lsd language.).
Search the function of interest below, considering that most of them are part of a family with post-fix -L, -S, -LS. For example, the function V(...) is part of a family of function together with VL(...), VS(...) and VLS(...).
 
Return values Return Objects Modify Values Edit Model Structure Other elements
V ADDOBJ WRITE ADDOBJ v[n]
STAT RNDDRAW INCR DELETE CURRENT
SUM RNDDRAWFAIR MULT SORT p->
WHTAVE SEARCH_CND
CYCLE c->
MAX SEARCH
init_lattice(...) cur, cur1, cur2...
INCR

update_lattice(...) up->, next->, etc.
MULT


model specific objects
Random and math functions


Advanced coding (debug, memory management,
"object->hook", )
V_CHEAT


Basic C++
INTERACT





Lsd Equations
A simulation run consists in a sequence of time steps, during each of which every Variable of the model is "updated". That is, a piece of code is executed, normally returning a numerical value that is associated to the Variable for that time step. The Lsd equations are these pieces of codes.

The code for the equations are separated in blocks (see below). They are located all in a file, called the equations' file, which is then compiled together with the rest of the Lsd source code. Normally, this is doen using LMM, and therefore the user should find the equation file automatically prepared.
The only required lines in the equation file are the following:
#include "../src/fun_head.h"

MODELBEGIN

MODELEND

void close_sim(void)
{
}

The user should place the equations only after the keyword MODELBEGIN and before MODELEND . Placing the keyword DEBUG before the first equation's code (but after MODELBEGIN begin) the simulation run will write a file called "log.log" where all the Variables' equations computed are listed with some relevant information. Of course, this slows down sensibly the simulation, but it may be useful to find errors, since the last Variable computed before a crash will be the likely faulty one.

Every type of Variable has its own equation, although a model normally contains many copies of a Variable. In this cases the same piece of code is executed, as many times as many copies of the Variables, but, of course, the values used in each execution may change. In fact, Variables are located in Objects, so many copies of the Objects contains the copies of the Variables. The equations' code always refer to an Object. For example, suppose an Object Firm contains Variable Q and Variable K. If there are many copies of Objects Firm's, each equation of Q, that uses K for its computation, will refer to its own copy of K, that is, the copy of K in the same Object containing the copy of Q currently under computation.

The equations' code for all the Variables are located in the same file. The order in which the equations appear in the file is irrelevant, since the system automatically decides which Variable needs to be executed. Therefore, each equation must be thought of as a difference equation, written independenlty from one another, and computed at the generic time t:

The system analyses the lag structure and decides which computation needs to be computed first.

The code for a (type of) Variable can be expressed in two forms:

EQUATION("VarLabel")
/*
Normally here should be place a comment, specifying what the equation does
*/

RESULT(3) //This equation assigns always the value 3 to any copy of Variable VarLabel

The first line indicates that the code refer to the Variable labelled VarLabel. After the initial line normally it is placed a comment (highlighted in green). The following lines can contain any Lsd (or C++) code. The last line must be indicated with the keyword "RESULT(...)" where, in between the parenthesis, is contained the value that the Variable is assigned.
The EQUATION("VarLabel") line indicates that the Variable must be computed once and only once at every time step. Therefore, for example, if a Variable uses its values in many other equations, than it returns always the same value. That is, the number VarLabel at time t is always identical.

Lsd offers numerous functions to express the equations. The most frequently used is V("Var_or_ParLabel") that simply compute the value of another Variable or Parameter in the model. When the computations required for an equation (or a function) are slightly less than trivial, it is common to use local variables where to store intermediate results. For example, consider the following equation:

EQUATION("PROF")
/***************************

The equation computes the profit rate:
PROF(t) = P(t) * A(t-1) -C -RIM - RIN*Inn

profits per unit of capital are equal current price times lagged productivity
minus the cost for research (innovative firms spend for both type
of research) and fixed costs.

***************************/
RESULT(V("Price")*VL("A",1) - V("C") - V("RIM") - V("RIN")*V("Inn"))

It may be preferred to store the intermediate values used in the final computation in local variables, called v[0], v[1], v[2], etc.:

EQUATION("PROF")
/***************************

The equation computes the profit rate:
PROF(t) = P(t) * A(t-1) -C -RIM - RIN*Inn

profits per unit of capital are equal current price times lagged productivity
minus the cost for research (innovative firms spend for both type
of research) and fixed costs.

***************************/
v[0]=V("Price");
v[1]=VL("A",1);
v[2]=V("C");
v[3]=V("RIM");
v[4]=V("RIN");
v[5]=V("Inn");

RESULT(v[0]*v[1]-v[2]-v[3]-v[4]*v[5])

This system has several advantages. Firstly, the code generally is more readable using shorter symbols for values. Secondly, the intermediate values can be observed using the Lsd debugger, so to trace errors. Thirdly, if a value is used many times in the same equation, instead of re-calling the function providing it it is faster to store the value in a v[...] local variable and just re-use it.

Besides local variables for intermediate numerical values, it is also available a group of local "pointers": cur, cur1, cur2, etc. These are used to store objects managed in the equation. Typically, when creating new Objects one may want to initialize the Parameters of the newly created Object. This is made storing the new Object in a temporary pointer, and applying to it the Lsd functions required. For example, the following lines in an equation create a new Object of type Firm and set to 0 its K Variable (see details in the description of the Lsd functions listed above).

...
cur=ADDOBJ("Firm");
WRITELS(cur, "K",0,t);
...

Most of the Lsd functions, like WRITE or V used above, come as a family where the name of the function can be expressed with no post-fix, "L", "S", and "LS" as post-fix. The post-fix "L" stands for Lag, indicating that the operation must refer to past values. The post-fix "S" indicates a Specific object. Consider that any Lsd operation is performed at a given time step in a given equation. Therefore, the time "now" and the Object containing the Variable whose equation is executed are used as default, and no post-fix needs to be used. Otherwise, for operations that must refer to past time steps, or to Objects different from the one containing the Variable whose equation is executed, the modeller must use the post-fix version of the Lsd functions and provide the necessary details:

The equation's code can use two Objects that are available to the modeller. One is "p", which indicates the Object containing the Variable under computation. The other is "c" which refers to the Object containing the Variable that triggered the computation of the Variable under computation.


Functions

Variables associated to an EQUATION are computed once and only once at each time step. That is, the value of a variable can be used by another variable in the model, by many other variables, or by no variable at all. In any case, the system ensures that the code of the equation is computed at each time step. If, in the same time step, the value is requested more than once, then the system does not recompute the equation but returns directly the same value computed before during the same time step.

In some, rare, cases, a Variable should be recomputed every time it is requested, even in the same time step, but should not be computed if no variable request it.

This is, for example, the case of a Variable computing an identification value that must differ for any object. For example, suppose that in the model a firm can generate, with some probability, a spin-off, that is, a new firm. Moreover, each firm must have a unique IdFirm value. It is possible that during the same time step many new firms are created, or none. To generate an identification number unique for each firm it is possible to use the following code

FUNCTION("IdGenerator")
/*
Generate new values any time this variable is requested

*/

RESULT(CURRENT + 1)



Objects in the equations' code
Most Lsd functions are operated by an Object, in the case above (and almost always) by p->. The reason why (most of ) Lsd functions are operated by Objects is that the same equation code is, in general, used by many copies of the Variable it refers to. For example, consider a model with an Object Firm containing Variables Profit, Price and Quantity, and the equation for Profit is (neglecting the temporal index):

In general the model may have many copies of Object Firm, and each Variable Profit will need to use the values of Quantity and Price referring to the same instance of Object Firm. Lsd takes care automatically of identifying the correct values in the code of the equation, using the Object the Lsd function is attached to. In particular, Parent Object: p
This symbol refers to the Object containing the Variable whose equation is being executed ("p" for "parent of the Variable"). Since most of the operations in an equation concerns the values in the same Object, this Object is the most frequently used in the equations, and in most of cases the Lsd functions allow to use it by default without specifying it.

Caller Object: c
In most of cases equations computing some Variable's values are executed because other equations needed those values. This object in an equation contains the pointer to the Object that caused that equation to be computed, if any ("c" reads for "caller"). If an equation is computed only because the system requested so, and no other equation triggered the computation, then c is NULL, a conventional value.

Typically the object "c" is used in FUNCTION's, since these are not computed unless another equation requested their computation. As an example example, the following function generates a random value from a poisson random function, with the mean value taken from the parameter "mean" stored in the

Temporary Object's pointers: cur, cur1, cur2, ...
These elements are pointers to Objects, used to store temporary required Object (like  v[n] is used to stored temporay numerical values). Typically, the user applies to p->, or c-> a Lsd function returning Objects, assigning the result to cur. Then uses cur to apply a function returning values, or modifying values. See any example of the Lsd function returning Objects.

object elements: up->, next->, son->
The information in this paragraph is not necessarily relevant for writing Lsd models. But it explains the technical implementation of a Lsd model that can be used to optimize a model's code.
Every Object (for example, p, c etc.) is related to its neighbours in the model hierarchy. Every Object can therefore bring to its neighbours using the links to them. The links are determined with the following components of each Object:

In other terms, every Object can bring to another Object using the appropriate pattern along the up->, next-> and son-> fields. For example, the Object:
object->son->next->up
is nothing else that the very same initial object (assuming it has at least two descending Objects).
The best way to remember the object's components is to keep in mind a geometrical representation of Objects:

 up
 /\
 ||
 --------
|object | => next
 --------
 ||
 \/
 son

The fields above are proper Objects, and therefore can be used to run Lsd functions. For example, consider a model where you have 100,000 Objects Firm descending from a single Object Market. If a Variable in Firm uses a Parameter in Market, say Price, you can write the line
v[0]=V("Price");
but it will take a lot of time to work out, because the system will have to visit and discard all the Objects Firm before moving "up" to check in market. In fact, the function V(...) will search in Firm for a Variable or Parameter Price. If it does not find it, it will look if it can be found after the set of Firm, descending from the same Object. Which means to skip through 100,000 objects, for the first firm, 99,000 for the second and so on. Instead, using the line:

v[0]=VS(p->up,"Price");

returns directly the desired value, since the first Object searched is p->up, which is Market. Note that the modeller must be sure that objects fields use exists. If, in the example above, the equation is placed in Root, no up-> Object exists, and the simulation will issue an error.

Model specific objects
The Object pointers saw above (p->, c->, cur-> etc.) are all "local" Objects, in the sense that they represent a different content depending on the Variable in whose equation they are used. That is, they refer to Objects that, depending on the Variable whose equation is computed, change their content when the Variable computed changes: p-> refers to the Object copy whose Variable is computed; c-> to the Object that caused the Variable to be computed; cur-> and the others are assigned within the equations code.
However, the modeller can create and use "global" Object pointers, that is Object that never change the content throughout a simulation run. This is mainly done for optimization purposes, when a very large model (many Objects) contain Variables that refer frequently to one specific Object copy. The use of global Object should be avoided by unexperienced modellers, because it risks to create error difficult to be captured.
To use a global Object, declare the Object outside the scope of the equation function, on the top of the equation file before the line:

MODELBEGIN
the declaration line must be something like:

object *market;

where the name of the Object ("market" in the example) must not be one of the existing Objects (p, c, cur etc). The Object must be assigned with one of the Lsd equation functions returning Objects within an equations code. For example, there may be a Variable called init making such assignements. For example, it may be:

EQUATION("Init")
/*
Technical initialization function. It is computed only once and then it is
transformed in a parameter and never computed again.

Sets the global pointer 'market' pointing to the Object Market, so
to speed up the access to this object

*/

market=SEARCH("Market"); //assign the C++ object pointer "market" to point to the Lsd Object Market
param=1; //optional; transform "Init" in a parameter so to not compute again this equation.
RESULT(1)

The Variable Init must be computed for sure before market is used in any other equation. This can be ensured placing Init in the Object Root, since the simulation step starts always the computation from the Variables contained in the top of the model structure.
With the setting described above, the modeller can use "market" as any other Object, knowing that it refers always to the Lsd Object Market. For example, the equation for a Variable placed anywhere in the model may use the line:

v[0] = VS(market,"Price");

and be sure that the value of Variable Price is returned quickly. Note that also the conventional line

v[0] = V("Price");

would work. But it would cost a lot of time in case the Variable whose equation containing the line for Price is placed very "far" from Market.
Modellers using global Objects should be careful when assigning configurations with many copies. If, for example, the model configuration uses many copies of Objects Market, any use of the global object market would refer only to the very first copy.


V("Lab"), VL("Lab", lag), VS(obj,"Lab"), VLS(obj,"Lab",lag)
This is the most used Lsd function. It returns the value of the Variable or Parameter with label Lab. The forms VL(...) and VLS(...) permit to specify a lag, so that the values returned concern the value of Lab with lag lags. The forms VS(...) and VLS(...) specify in which Object the Parameter or Variable must be searched for. In V(...) and VL(...), where no Object is specified, the search starts from p (see available Objects).

In case the model contains many instances of Variables with the same label Var (that is, many instances of Object containing this Variable), the function returns the value of the instance "closer" to the Object where the search start from. By closer, it is meant the first instance found by searching the model using the following strategy:

1) Search in the Object object (the one specified in the function call);
2) Search in the Object(s) descending from object;
3) Search in the parent Object of object;

In each Object explored, the same strategy is applied recursively, so that every Object in the model is visited, if necessary. So, for example, if there is only one instance of Variable Varin the model, this can be found whatever Object is used to start the search.

Example
Consider the equation Qt = Kt-1 * At
The code will be as follows:

EQUATION("Q")
/*
Compute the quantity Q as the product of lagged capital K and current productivity A
*/
RESULT( VL("K",1) * V(A) )

or, equivalently:

EQUATION("Q")
/*
Compute the quantity Q as the product of lagged capital K and current productivity A
*/
v[0]=VL("K",1);
v[1]=V(A);
RESULT( v[0]* V(A) )

In the second case the two temporary variables v[0] and v[1] are used to store the intermediate results, which can be observed during a debugging session, so that modeller can trace possible unexpected results.



V_CHEAT("Lab", fake_caller), VL_CHEAT("Lab", lag, fake_caller), VS_CHEAT(obj,"Lab", fake_caller), VLS_CHEAT(obj,"Lab",lag, fake_caller)

This function is used for sophisticated coding. When the standard V(...) requests the value of a Variable, when this Variable equation is executed it can access the Object containing the caller Variable (Object c). However, in some cases, mostly for reasons of efficiency, it may be useful to "cheat" the requested Variable in believing that the Object is something else. With V_CHEAT the modeller must specify another Object (fake_caller) that will appear in the called Variable as if it were the "caller".



SUM("Lab"), SUML("Lab", lag), SUMS(obj, "Lab"), SUMLS(obj, "Lab", lag")

This function searches, with the same strategy described in V(...) (see also the different forms), an instance of the Variable Var. Then, it keeps on summing up all the values of Variables Var (with the lag lag if specified),  found in the set of Objects contiguous to the one found. Normally, it should be used to sum up the values of descending Objects. For example, if Q_TOT is contained in an Object Market, from which descend a set of Objects Firm containing Variables Q, its equation can be:

EQUATION("Q_TOT")
/*
Compute the sum of all Q's in the market*/
RESULT( SUM("Q") )
 

Note that, if your model contains many Objects Market, this equation will sum up only the Q's contained in the set of descendants of Market, and not all the Q's existing in the model.


MAX("Lab"), MAXL("Lab", lag), MAXS(obj,"Lab"), MAXLS(obj,"Lab",lag)
Same as object->sum(), but returns the maximum value instead of the sum.


WHTAVE("Lab1","Lab2"), WHTAVEL("Lab1", "Lab2", lag), WHTAVES(obj,"Lab1","Lab2"),WHTAVELS(obj,"Lab1","Lab2",lag)

Same as object->sum(), but returns the weighted average of a Variable or Parameter, that is the sum of the products between the values of Lab1 times Lab2. Of course, both Variables need to be contained in the same type of Objects.


STAT("Var"), STATS(obj,"VAR)
This function does not return a value as the ones above, but stores a set of values in the vector v. It works as the function SUM, and the like, but computes a set of descriptive statistics. Namely, it places in v the following values:

v[0]=number of elements;
v[1]=average of Var
v[2]=variance of Var
v[3]=maximum values
v[4]=mininimu values


SEARCH_CND("Lab", val), SEARCH_CNDL("Lab", val, lag), SEARCH_CNDS(obj, "Lab", val), SEARCH_CNDLS(obj, "Lab", val, lag")

This method is used to find an Object in the model that contains the Variable Lab with value val (considered the lag lag). Basically, it uses the same strategy to explore the model as described in function V(...) above. Only, it does not stop to the first instance encountered, but continues until the searched Variable is not found with the desired value. When an Object that satisfies the condition is found, it is returned to the calling equation, and can be used to activate other Lsd functions, like V(...) .

The function is used to identify a particular Object, which is neither p nor c. For example, suppose you want to extract randomly a Firm from a group of 100 instances, and that these contain a Parameter IdFirm set from 1 to 100, you can use the following code:

v[0]=rnd_integer(1,100)
cur=SEARCH_CND("IdFirm", v[0]);


SORT("LabObj", "Lab", "Direction"), SORTS(obj, "LabObj", "Lab", "Direction"), SORT2("LabObj", "Lab1", "Lab2", "Direction"), SORTS2(obj, "LabObj", "Lab1", "Lab2", "Direction"),
This function sorts (with the quick sort method) a set of Objects labeled LabObj according to the values of Variable Lab. The field Direction must be either "UP" or "DOWN". It returns an error in case the Variable Var, though defined in the model, is not contained in the Object Obj_Label. In case there are many sets of Objects with label Obj_Label, the function sorts only the first set encountered by exploring the model with the usual strategy (see V(...) ). If specified, the search for the group of Objects to sort start from obj.

Example:
SORT("Firm","Q", "DOWN");
this line, used in an equation contained in an Object from which descend many Object labelled Firm, sorts these descendants according to decreasing values of their Variable Q.
The SORT2 function is also available for ranking on two dimensions:
SORT2("AnObject","X", "Y", "UP");
In this second case the Objects are sorted according to their increasing values of X. If two or more Objects have identical values on X, then the Variable or Parameter Y is used to sort them.



ADDOBJ("Obj_Label"), ADDOBJS(obj,"Obj_Label"), ADDOBJ_EX("Obj_Label", obj_ex), ADDOBJS_EX(obj,"Obj_Label", obj_ex), ADDNOBJ("Obj_Label", num, obj_ex), ADDNOBJS(obj,"Obj_Label", num, obj_ex)
These functions adds a descendent to the Object of type Obj_Label to  obj, or in p if obj not specified, and return the newly created Object. The ADDOBJ version initializes the new Object (that is, sets the values for its Parameters and Variables, including the lagged values) according to the values defined in the initialization file for the model. In case there are many Objects of type Obj_Label, the function considers the data for the very first Object in the data file for the model. The Variables are defined as if they were already updated in the current time step. In the ADDOBJ_EX version, the function uses the example Object to initialize the newly created Object.
In case the new Object is defined as having descendants, the function builds the whole structure of its descendants. The function returns the address of the newly created Object, so that the modeller can add in the equation a customized initialization. See the function WRITE for changing the values of Variables (without using the equation) and of Parameters.

For example, you can have an equation for a Variable in the Object Market that contains the following lines:

cur=ADDOBJ("Firm");
WRITE("DateBirth",(double)t);

The lines above create a new Object Firm, which has the same initial data as the first Object in the model file. Then it modifies the Parameter DateBirth, storing there the current time step. Note that the C++ variable t, expressing the time step of the simulation, is an integer Variable, and therefore needs to be explicitly declared as double (that is, double precision floating point variable in C++), because all the numerical values in Lsd are real numbers.

As a second example, suppose that in the Market Object is contained a Variable A_MAX containing the maximum productivity. The following lines identify the Object Firm with the maximum productivity and produce a new Firm with the same initialization of this Obejcts:

v[0]=V("A_MAX");
cur=SEARCH_CND("A", v[0]);
cur=ADDOBJ_EX("Firm", cur);

The first line obtains the value of the highest productivity among the existing Firms. Then, the function SEARCH_CND(...) returns the Object that has the same productivity as the maximum one. This Object is used as example for the creation of the new Firm. Note that the same temporary variable cur is used, firstly, to store the most productive Firm and then, both as example Object and as the new Object. This may seem strange to whom is not accustomed to C++, but it is perfectly safe. In fact, the content of the temporary variable cur has already been used when the function returns the newly created Object.

If the added Object is set to save its values, these are available for post-simulation Analysis of Result. The data concerning the periods before its introduction are filled with missing values.

ADDNOBJ("Obj_Label", num, obj_ex) generates a number num of new objects, each copied from obj_ex.

Warning:
The newly created Object must be added to a parent Object (that is, object->) which is already defined as having Obj_Label type of descendants.


DELETE(obj)
This function deletes the Object obj from the model, removing it from the model and freeing the memory it was allocated for it. While its use is very simple, it should be used with care to avoid the elimination of data structure used in other parts of the model. The data produced by the Objects and saved are available for the Analysis of Result
The data stored in the deleted Objects are always available for analysis at the end of the simulation, filling with missing values the periods after the deletion.


WRITE("Lab", new_value), WRITEL("Lab", new_value, time), WRITES(obj, "Lab", new_value), WRITELS(obj, "Lab", new_value, time)
This function writes the value new_value in the Variable or Parameter Lab.  In case of Variable, it possible to make appear as if the time of latest computation for the overwriten Variable is time. That is, after to function is executed, the Variable Var will result as if its last computation had been executed at time time, and the result of the equation were new_value. If the label Var corresponds to a Parameter, the field time is ignored. The Variable must be contained in the Object obj, (or in p if obj not specified),  otherwise the functions returns an error and stops the simulation.
This function can be very useful to implement complex situations, but it should be used with extreme care because it disrupts the automatic system of controls for the execution of the equations. If the function concerns a Parameter, the field time is ignored. The most frequent use of write concerns the initialization of newly added Objects.


SEARCH("Obj_Label"), SEARCHS(obj, "Obj_Label")
This function explores one single branch of the model searching for the first instance of the Object Obj_Label. That is, it searches only within the descendants of obj, if specified, or in p if obj not specified, and their descendants. Therefore, the search is not exhaustive over all the model, unless it is started from the Root of the model. It returns the address of the found Object or NULL if no Object is found.


v[n]
The standard way to express an equation is to collect a set of data from the model and then to elaborate them to provide the desired value. Since the Lsd function to collect data may be quite long, it is good practice to store values to be used in a numerical vector. See the Introduction.
The values of v[n] are reset for each equation, and therefore cannot be used to transfer information from one equation to another.


CURRENT
This keyword contains the current value of the Variable under computation, irrespective of the time of last computation of the variable.It is the same value that can be obtained with the function VL("VarLabel",1), used in the equation for VarLabel. The result of the equation will be reported in CURRENT at the subsequent time step.

Example:
EQUATION("VarX")
/*
This function returns a progressive value each time it is requested
*/

v[0]=CURRENT;
v[1]=VL("VarX",1);

/* v[0] and v[1] are identical */
RESULT(v[0])

This can be used even when a variable is not defined to store past values, since it accesses directly the C++ memory location storing the variables' values.


INCR("Lab", value), INCRS(obj, "Lab", value)
This function works only if VarLabel is contained in obj, or in p if obj not specified, otherwise produces an error. It adds to the current value of Lab the value of value. The function returns the new value after the increment. This function should be used only with Parameters, for the same reasons explained in the functions WRITE.


MULT("Lab", value), MULTS(obj, "Lab", value)
This function works only if VarLabel is contained in obj, , or in p if obj not specified, otherwise produces an error. It multiplies the current value of VarLabel times the value of value. The function returns the new value after the product.


Math and other functions
Besides the Lsd specific function modellers can use one of the following random and mathematical functions. Of course, it is always possible to declare new functions or link to the Lsd model any C++ library.


RNDDRAW("ObjLabel", "VarLabel"), RNDDRAWL("ObjLabel", "VarLabel", lag), RNDDRAWS(obj, "ObjLabel", "VarLabel"), RNDDRAWLS(obj, "ObjLabel", "VarLabel", lag)
This function searches for a group of Objects ObjLabel, and returns the pointer to one of them chosen randomly with probability proportional to the values of VarLabel. obj can specify where the search must start from. lag can specify the lag to use for the values of VarLabel.

EXAMPLE
Consider the equation for DrawAFirm which has to return the value of Parameter IdFirm in one of the Objects Firm descending from the Object where DrawAFirm is contained in. The equation will be:
is specified the starting  then computes the values of VarLabel for each of them. It returns the address of one of the Objects of the group chosen randomly with probability linearly dependent on the value of VarLabel. The version of the function with total assumes that the latter value is equal to the sum of all VarLabel in the group, and is faster.
The following example c an equation for a Variable stored in an Object that contains a group of descending Objects Firm. The code assigns to the Parameter Prob in each Firm the square of their market shares and then draws randomly one of them, returning its identification number.
 

EQUATION("DrawAFirm"))
/***************************
Return the Id of a Firm chosen randomly with probability
equal to the square of the market shares.

***************************/

CYCLE(cur, "Firm")
{
 v[0]=V("ms");
 WRITES(cur,"Prob",v[0]*v[0]);
}

cur=RNDDRAW("Firm","Prob");
RESULT(VS(cur, "IdFirm")


RNDDRAWFAIR("ObjLabel"), RNDDRAWFAIRS(obj, "ObjLabel")
This function is identical to RNDDRAW but uses the same probability for any object. For example, the following equation returns a the parameter IdFirm from a randomly chosen object Firm.

EQUATION("DrawAFirm"))
/***************************
Return the Id of a Firm chosen randomly with identical
probabilities for each object
***************************/

cur=RNDDRAWFAIR("Firm");
RESULT(VS(cur, "IdFirm")
 
 



RNDDRAWTOT("ObjLabel", "VarLabel", total), RNDDRAWL("ObjLabel", "VarLabel", lag, , total), RNDDRAWS(obj, "ObjLabel", "VarLabel", total), RNDDRAWLS(obj, "ObjLabel", "VarLabel", lag, total)
This function is identical, but faster, to RNDDRAW but it includes also the total of the sum over all the VarLabel values.


CYCLE(cur, "ObjLabel"){  }, CYCLES(obj_from, cur, "ObjLabel"){  }
This macro scans all the Objects labelled ObjLabel descending from p or from the specified obj_from assigning cyclically the pointer cur specified. See the example for RNDDRAW.

Can be used embedded CYCLE's , provided that the pointers differ for the different cycles. For example, consider a model including Objects Market containing Objects Firm. An equation in an object containing a set of Market, for example representing a country, can use the following code:

v[0]=0; //set to v[0] to be initially 0
CYCLE(cur, "Market")
 {//for each market
  CYCLES(cur,cur1, "Firm")
   {//for each firm, contained in cur
    v[1]=VS(cur1,"Profit");
    v[0]=v[0]+v[1];
   }
 }
//from here v[0]contains the cumulated profits
The cur pointer cycles through all Market's. For each Market the pointer cur1 cycles through each Firm. For each Firm, v[1] contains the value of Profit which is cumulated in v[0].

WARNING: Never use DELETE(cur) in a CYCLE. In fact, deleting the objects pointed to by cur prevents the CYCLE from identifying the next one. Instead, use the command CYCLE_SAFE(cur, "ObjLabel"). In this case, it is possible to delete the cycling pointer cur. but it is not possible to nest different cycles.
The example below is a mistake because cur1 is deleted within a CYCLE command
ERROR - ERROR -ERROR
CYCLE(cur, "Market")
 {//for each market
  CYCLES(cur,cur1, "Firm")
   {//for each firm, contained in cur
    v[1]=VS(cur1,"Profit");
    if(v[1]<0)
      DELETE(cur1);
  }
 }
//from here there are no Firm with negative profits
ERROR - ERROR -ERROR

Instead, it should be:
CYCLE(cur, "Market")
 {//for each market
  CYCLE_SAFES(cur,cur1, "Firm")
   {//for each firm, contained in cur
    v[1]=VS(cur1,"Profit");
    if(v[1]<0)
      DELETE(cur1);
  }
 }
//from here there are no Firm with negative profits
 



init_lattice(double pix, double nrow, double ncol, char lrow[], char lcol[], char lvar[], object *p, int init_color)
This function creates a lattice graphical windows available for run time updating during a simulation run. The parameters to be passed to the function have the following meaning:
- pix=pixels used for the width of the lattice (600 should fit in typical screens)
- nrow= number of rows in the lattice
- ncol= number of columns in the lattice
- lrow= label of variable or parameter indicating the row value
- lcol= label of variable or parameter indicating the column value
- lvar= label of variable or parameter from which to read the initial color of the cell (if init_color is different from -1)
- p= pointer of the object containing the initial color of the cell (if init_color equals -1)
- init_color= indicate the type of initialization. If init_color equals -1, the function uses the lvar values reading the coordinates in each lrow and lcol to initialize the lattice. Otherwise, the lattice is homogeneously initialized to the color specified by init_color.

This function must be used only once in each simulation run and never again. Updates to the lattice colors can be applied using the update_lattice(...) function.



update_lattice(double line, double col, double val)
Modifies the color of the lattice cell in line line and column col to the value of val.



INTERACT("A commenting text", val), INTERACTS(obj,"A commenting text", val)
This function interrupts the simulation run showing a window like that of the debugger. The object shown in the debugger is the same object containing the variable under computation, or the specified object obj. Contrary to the debugger, the name of the Variable just computed and  its value, the user is shown a free text, and the value is any C++ variable, typically one of the v[i] temporary elements. The value typed in by the user will be returned by the function.

This function inserted in an equation allows the run to be modified according to the choice of an interacting user. For example, an equation may contain the following code:

 v[1]=V("MinMarketShare"); //minimum m.s. below which a firm should be removed
 CYCLE_SAFE(cur, "Firm")
  {//Cycle through each firm, allowing for the removal of the pointer.
   v[0]=VS(cur,"MarketShare"); //read the market share
   if(v[0]<v[1] )
      v[5]=INTERACTS(cur, "Remove this firm? (1=yes; 2=no)", v[4]);

   if(v[5]==1)
    {//remove if the answer was 1
     DELETE(cur);
     v[3]++;
    }
  }

This cycle removed all firms having market shares smaller than a given value and that the user permitted to kill.

Beware that such a function used in a model requires the user to interact with it.


Advanced Lsd coding

The commands listed here provide special operation rarely used. They should concern only advanced programmers.

close_sim()
At the end of each simulation run modellers can execute some specific code. Typically, this is used to free some memory allocated during the simualation run. At the end of the file for the equation is located the function close_sim() where such code can be placed. By default this function does nothing.

PARAMETER
This command within an equation transform the Variable in a Parameter, so that the equation is never computed again during the simulation run. Normally, this is used in equations meant to initialize a model at the very first time step, and does not need to be computed during a simulation run.

global variable 't'
This variable indicates the current time step of the simulation. Note that this is an integer variable, so that to assign its value to one of the Lsd function, requiring real numbers, it is necessary to use a cast. For example:
v[9]=(double)t;

DEBUG_AT(XXX)
This function placed in the equation files is used to spot the equations that causes a crash of the program. It serves to tell the modeller which equation caused the crash.
This function must placed just aftet the command line MODELBEGIN and causes the system to enter in a special running mode at time XXX (and to remain so onward). Any time a variable needs to be computed after the specified time step the system opens a file called log.log and writes the name of the variable and the time step. So, for example, if an equation file begins with the code:

#include "fun_head.h"

MODELBEGIN

DEBUG_AT(2)
...

then we will find in the log.log file all the variables computed.

obejct->hook

Any object in a Lsd model contains a special field called hook. This field is a pointer to another object, like the other pointers ->up, ->son, ->next that allow to move from one object  to other objects in the model: the containing, of "father", object, the first contained object, then next object contained in the same "father". The difference is that ->hook is never used by the system, and is left to the modellers for special purposes. Typically, it serves to speed up the access of a frequently requested object.

For example, suppose you have a model where the object consumer stores in the parameter IdUsed the id of the product currently used. Moreover, suppose that the products can break at each time step with a probability contained in the object Firm. The equation of the consumer to check if the product breaks should be the following:

EQUATION("IdUsed")
/*
Id of the product used by the consumer.

Look whether the product breaks down or not. In teh first case choose a new product.

*/
v[0]=VL("IdUsed",1); //product used by the consumer

cur=SEARCH_CND("IdFirm",v[0]); //find the object with IdFirm equal to my IdUsed
v[2]=VS(cur,"BD"); //read the probability of breaking the product
if(RND<v[2])
 { //product broken
  cur1=RNDDRAWFAIRS(cur->up,"Firm"); //choose a new firm from the father of the former firm
  v[1]=VS(cur1, "IdFirm");
 }
else
 v[1]=v[0]; //product not broken, used the same product as before
RESULT(v[1] )

This equation can slow down the simulation in case it contains thousands of producers and of consumers, since the system has to scan many Firm's to find the one required by the consumer. The same code can be written as follows:

EQUATION("IdUsed")
/*
Id of the product used by the consumer.

Look whether the product breaks down or not. In teh first case choose a new product.

p->hook is teh consumers' free pointer containing the firm used by the consumer.
*/
v[2]=VS(p->hook,"BD"); //read the probability of breaking the product
if(RND<v[2])
 { //product broken
  cur1=RNDDRAWFAIRS(cur->up,"Firm"); //choose a new firm from the father of the former firm
  p->hook=cur1;
  v[1]=VS(cur1, "IdFirm");
 }
else
 v[1]=v[0]; //product not broken, used the same product as before
RESULT(v[1] )

The code is faster because the equation do not need to scan all firms to find the desired one.

Beware that the modeller must ensure that the hook is always correctly assigned. In the example, you need to consider that at the very first time step teh hook is not assigned. At the start of a simulation run all the hook's are set to NULL. Therefore, you may change the code as follows.



EQUATION("IdUsed")
/*
Id of the product used by the consumer.

Look whether the product breaks down or not. In teh first case choose a new product.

p->hook is teh consumers' free pointer containing the firm used by the consumer.
*/
if(p->hook==NULL)
  {//executed the very first time step, when hook is not assigned
   v[0]=VL("IdUsed",1); //product used by the consumer
      cur=SEARCH_CND("IdFirm",v[0]); //find the object with IdFirm equal to my IdUsed
   v[2]=VS(cur,"BD"); //read the probability of breaking the product
   p->hook=cur;
   }
else
  v[2]=VS(p->hook,"BD"); //read the probability of breaking the product

if(RND<v[2])
 { //product broken
  cur1=RNDDRAWFAIRS(cur->up,"Firm"); //choose a new firm from the father of the former firm
  p->hook=cur1;
  v[1]=VS(cur1, "IdFirm");
 }
else
 v[1]=v[0]; //product not broken, used the same product as before
RESULT(v[1] )


Basic C++ structures

Lsd modellers need not to write programs, but just the code to compute the values of variables. The Lsd functions available allow to read the values of the model and/or to modify it. The elaboration of the variables' equation can use any C++ legal command. Here we present the most basic (and frequently used) commands. Remember that the code to compute a variable's value is executed sequentially when the system requires that variable to be updated. At the end of the equation's computation the system assign the value to the variable and then moves to compute another variable.

The code is composed by lines of commands that the computer generally executes sequentially, moving to the next line when the previous one has been completed.
Any line of code must respect the C++ rule of terminating with a semi-colon ``;'', unless the line is a multi-column Lsd command like EQUATION, or C++ command, like if(condition).

Comments

The code can include comments, that is text that is ignored by the compiler and serves to describe the code to readers. Comments in C++ comes in two forms:

/*

This is a multi line comment, continuing until
a sequence "star slash" is encountered

*/

//this is a single line comment,
//terminating at the end of the line

Assignments, arithmetic operations and increments

If a is a variable, then the programmer can assign a value to it with the command ``='':

a=4.3;

Any assignment must be terminated with a semi-colon ``;''.

It is also possible to assign values from other variables, and use the standard mathematical operations, using the parentheses to group relevant priorities:
a=b+3-d/(e+g)*(h+i);

Less obviously, it is also possible to use the same variable on both sides of the assignment:

a=a+32;

The above line assigns a with its previous value increased of 32. Therefore, if  a had the value of, say, 5, after the above line it is assigned the value of 37.

These commands incrementing the value of a variable are so common that they have also a short way to express them. For example, the command a=a+32; can be expressed also
with the command

a+=32;

saving the expression of a in the left part of the assignment. This short expression can even be used for the other arithmetical operations:

a=a+32; is equivalent to  a+=32;
a=a/32; is equivalent to  a/=32;
a=a*32; is equivalent to  a*=32;
a=a-32; is equivalent to a-=32;

A C peculiar command (++}) allows both to increase of 1 a variable and to use it as assignment. that is:

a++ is equivalent to a+=1 is equivalent to a=a+1

The command ++ (and its sister command --) works differently depending on whether it is used after or before a variable symbol. If it is used after (a++) the command firstly assign the current value, and then increases it of 1. Instead, if ++ is used before a variable (++a), it firstly increases its value, and then assigns the ersult. For example:

a=3;
b=a++;
c=++a;

At the end of the above list of commands, we will see that a equals 5 (3 and two increments in the second and third line), b equals 3 (because b=a++ firstly assigns the value of a to b, and then increments a), and c equals 5, because a increased from 4 to 5 before being assigned to c.

Note the difference that exists between the equal sign ``='' used as assignment (i.e. load the value on the left to the symbol on the right), from the logical condition of identity (are the right and left sides identical?). Computer languages must use two different symbols for the two operations. In the following paragraph on the if() conditional statement we will see the symbol used for the logical identity.

Condition if ... then ... else ...

As said before, lines of code are executed sequentially, but the programmer can jump conditionally some blocks of code. A frequently used statement is the conditional execution of different blocks of line depending on whether a condition is verified as true or not. The grammar of the conditional command is the following:


if(condition)
 {
  /*
  insert here any line you want to be executed
  if "condition" is true
  */
 }
else
 {
  /*
  insert here any line you want to be executed
  if "condition" is false
  */
 }

The curly brackets "{" and "}" can be skipped if there is only one line to be executed conditionally.

The condition is normally based on one of the following binary relations, assuming a and b to be two numerical values or variables containing numerical values:



The condition can be composed with the logical operators of negation, logical "and", logical "or":


For example, the following code distinguish three possible situations:

  1. a equals b and c does not equal d
  2. a equals b and c equals d
  3. a does not equal b (irrespective of c and d)



if(a==b && !(c==d))
 {/*
   "a" equals "b" and "c" is different from "d"
  */
 }
else
 {// if the process is here, than one of the two conditions above is not true
  if(a==b)
    { /*
      "c" equals "d"
      */
    }
   else
    { /*
      "a" is not equal to "b", but "c" may or may not equal "d"
      */
    }
 }

Conditions in C++ are just integer numbers, where ``0'' (zero) means false, and any non-zero integer means true.

Cycles for(...)

A very frequent command allows to repeat a block of lines again and again until a specific condition is satisfied. The grammar for the cycle for(...) is:

for(INIT ; CONDITION ; ENDCYCLE)
 {
  //Block of code of the cycle
  //here there can be any command
 }

 followed by the block of code to be repeated contained between curly brackets "{" and "}".

The the execution of the program reaches a line with the for(...) command, then the following operations are executed:

  1. Execute any command contained in INIT; more than one command can be placed here separated by a comma ","
  2. Control that CONDITION is true.
  3. If the CONDITION is not true exit the cycle and continue with the first line after the block of the for(...) cycle
  4. If the CONDITION is true, execute once the block of code
  5. Execute any command contained in ENDCYCLE; more than one command can be placed here separated by a comma ","
  6. Return to point 2.


Example: cycle for(...) to pepeat a block of code 5 times setting a variable from -2 to 2:

for(i=-2; i<3 ; i++)
 {
  /*
   in the execution of these lines i
   assumes values -2, -1, 0, 1 and 2
  */
 }
//here i equals 3, because it was the condition to exit the cycle