LSD Function-based Equations

Equations are elaborations of present and past values of elements in the model used to produce a numerical value to be assigned to a variable. Modelers write the Equations for LSD models as chunks of C++ code, whose use is described here. See here for an introduction to LSD Equations coding. Usually users are encouraged to use the simplified LSD macro language for creating new models and explore the latest LSD features (see here for the macro language manual).

An Equation can contain any legal C++ code and can exploit a set of LSD specific functions to access the model. These functions can: return numerical values, return Objects (entities of the model), modify model values, and modify the structure of the model. The functions are operated by Objects, some of these available directly in the Equations, while other can be requested and selected with the LSD functions.

Return values

Return Objects

Modify Values

Edit Model Structure

Model Elements

cal

add_an_object

write

add_an_object

lastupdate

stat

draw_rnd

increment

delete_obj

v[n]

sum

go_brother

multiply

lsdqsort

Objects in Equations

whg_av

search_var_cond

p->

overall_max

search

c->

increment

cur, cur1, cur2...

multiply

up->, next->, etc.

Random and math functions

model specific objects

Advanced tricks

Basic C++ tricks

FOR NEW MODELS: The preferred way too write LSD Equations for new models is using the LSD macro language which simplifies the Equations' coding and offer new capabilities for modelers. If you are not locked-in older model versions you may prefer to learn the macro language. For the basic functions, the two ways for writing LSD Equations are equivalent. A model may even contain Equations in both formats if the basic command subset is used.

WARNING: In case of the usage of pure or mixed old-style function-based Equations, the modeler should include only ‘#include fun_head.h’ (instead of ‘#include fun_head_fast.h’) in the equation file. Failure to do so will trigger compilation errors.

WARNING: LSD no longer allows an element defined as a Variable in the LSD Browser to operate as a Function, irrespective of the code in the equation file (like --last_update). Model configurations created for old versions of LSD depending on this behavior must be updated to properly set Functions as such in the LSD Browser.

▲Top

LSD Equations

The LSD Equations are written as pieces of C++ code, to be included in a specific C++ source file. Model writers must compile such file and link it to the rest of C++ source code. Each model has one specific equation file, containing the Equations for that model.

Each Equation must be thought of as a difference equation, written independently from one another, and computed at the generic time t:

Xt = f(Yt-lag, Zt-lag, ...)

The values used in the Equations can indifferently be Parameters or Variables, stored in any Object in the model. For Variables' values the user must specify the time lag to consider (for Parameters this field is ignored).

The functional expression can be any legal C++ code, including the LSD specific functions. By default the Equation for a Variable is compute once and only once at every time step. But, as many LSD automatic mechanism, modelers can overrule such default and force an Equation to be computed many times during the same time step (see lastupdate)

The Equations are represented as independent blocks of code with the following structure (the order of the blocks in the file is not relevant):

if(!strcmp(label, "X") )
{
//This code is executed only by variable X
//place here any legal C++ code or LSD function
 
res = 1 ; //value used for the Variable (now X is always equal to 1)
goto end;
}

if(!strcmp(label, "Y") )
{
//This code is executed only by variable Y
//place here any legal C++ code or LSD function

res = 2 ; //value used for the Variable (Y i always equal to 2)
goto end;
}

For example, in order to have Variable X computed as the sum of Y and Z the corresponding code is:

if(!strcmp(label, "X") )
{
/*****
This equation is the LSD equivalent of:

X[t]=Y[t]+Z[t]

*****/

res = p->cal("Y",0) + p->cal("Z",0);
goto end;
}

The command cal("Y",0) is a LSD function, that is described here in detail  (it just returns the value of Y with 0 lag, or at time t). Normally, the intermediate operations are assigned to temporary C++ variables, called v[0], v[1], v[2], etc., so to simplify the reading, like:

if(!strcmp(label, "X") )
{
/*****
This equation is the LSD equivalent of:

X[t]=Y[t]+Z[t]

*****/
v[0]=p->cal("Y",0);
v[1]=p->cal("Z",0);

res = v[0] + v[1];
goto end;
}

Therefore, writing the Equations for LSD models is reduced to implement some C++ code for each Variable independently. The modeler must just write the Equations keeping in mind that many copies of the same Variable will make use of the same code. To differentiate between the different copies of the Variables the code uses the Objects referred to the Variable, much like the indexes are used in models using vectors.

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):

Profit = Price x Quantity

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,

·         p-> is the same Object containing the Variable whose Equation is computed;

·         c-> is the Object that requested the computation of the Equation;

·         cur, cur1, cur2, etc. are C++ temporary "pointers", which can store addresses of Objects obtained by other LSD functions.

Parent Object: p->

The Object most used to fetch values in an Equation is the Object containing the Variable to which the Equation refers to. Using this Object, if there are many instances of its type, each Equation for its Variables will use the values contained in the same Object. In the code the Object is referred to using p-> (for "parent of the Variable") followed by the type of function that is necessary to use. Typically, model writers will use the function cal("Label", lag) providing the value of Variable (or Parameter) with name Label and with lag lag (where the lag is always 0 for Parameters). See more on cal(...).

Caller Object: c->

Another frequently used Object is called c->, for caller. If a Variable requests the value of another Variable, the latter's Equation can necessitate using the Object containing the "caller" Variable, and for this its Equation can use c->. Note that if a Variable is not requested by another Variable (but it is simply computed because of its normal updating), the value of c-> is NULL: any LSD function operated by a NULL Object will cause an error.

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 temporary numerical values). Typically, the user applies to p->, or c-> a LSD function returning Objects, assigning the result to cur. Then, use cur to apply a function returning values, or modifying values. See any example of a LSD function returning Objects.

Most LSD functions (like, for example, cal(...) ) do not require the Object to which they attached to be able to satisfy the function required, but try to make "smart" inductions. In the example above, imagine that Price is not a Variable contained in Firm, but defined in the Object Market, placed, in the model structure, "up" to the Object Firm, that is containing Firms. The Equation code like:

if(!strcmp(label, "Profit") )
{
/*****
This equation is the LSD equivalent of:

Profit[t] = Price[t] x Quantity[t]

*****/
v[0]=p->cal("Price",0);
v[1]=p->cal("Quantity",0);

res = v[0] * v[1];
goto end;
}

keeps on working in the same way both in the case Price is in Firm, or Market. In fact, for the LSD function cal(...) starts searching the Variable Price in p->. But, if it is not found there, then keeps on searching in other Objects of the model.

Object elements: up->, next->

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 neighbors in the model hierarchy. Every Object can therefore bring to its neighbors using the links to them. The links are determined with the following components of each Object:

·         object->up-> is the Object containing the Object object. The top Object in the model hierarchy, Root, has the field up set to NULL.

·         object->next-> is the Object copy following the Object object in the list of descendants from the parent of object. Note that object->next-> can be of different type from object. All the Object instances in the chain next will have the same Object up, since all of them descend from the same parent Object. The last Object in the chain has the field next equal to NULL.

In other terms, every Object can bring to another Object using the appropriate pattern along the up-> and next-> fields.

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]=p->cal("Price",0);

but it will take a lot of time to work out. In fact, the function cal(...) 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. This 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]=p->up->cal("Price",0);

returns directly the desired value, since the first Object searched is p->up, which is Market. Note that the modeler 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 modeler can create and use a “global” Object pointer, that is, an Object that never change the content throughout a simulation run. This is mainly done for optimization purposes, when a very large model (many Objects) contains Variables that refer frequently to one specific Object copy. The use of global Object should be avoided by unexperienced modelers, because it risks creating errors 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:

double variable::fun(object *caller)

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 assignment. For example, it may be:

if(!strcmp(label, "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=p->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.
res=1;
goto end;
}

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 modeler 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] = market->cal("Price",0);

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

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

would work too. 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. Modelers 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.

▲Top

object->cal("Var", lag)
object->cal(caller, "Var", lag)

This is the most used LSD function. It provides the value of the Variable whose label is Var with the lag lag, expressed as an integer value. The second form of the LSD function is rarely used. It tells to the Equation computing Var that the object that requested its value is caller, instead of the default system passing the actual Object that requested Var.

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 object. 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 Var in the model, this can be found whatever Object is used to start the search.

It is clear that, in the general case, where many Variables Var exist in the model (that is, there are many instances of the Object containing such Variable), it is crucial to choose properly the Object from which the search has to start. In fact, this determines which instance's value is returned.

The Object normally used to apply a LSD function is the same Object containing the Variable whose Equation is computed. This Object is referred to by the name p, which is a pointer. So, for example, consider the Equation for the Variable Q (quantity), to be computed as the product between the Variable K (capital) and the Variable A (productivity), both with lagged values. Consider also that the Variable Q is contained in an Object containing also the Variables K and A to be used in the Equation.

The code will be as follows:

if(!strcmp(label, "Q"))
{
v[0]=p->cal("K", 1);
//Store in v[0] the value of K, with lag 1
v[1]=p->cal("A", 1);
//Store in v[1] the value of A, with lag 1
res=(v[0]*v[1]);  
//Assign the result
goto end;         
//Declare terminated the equation
}

The values returned by the two calls to the function cal() are stored in two temporary variables, v[0] and v[1]. The modelers can use as many temporary variables as necessary (from 0 to 999).

Besides the Object p, by default the function for the Equations has also another Object that can be used to activate a function. In fact, the computation of the Equations for the Variables can be activated because of two reasons:

1)    The system automatically requests the updated value for each Variable at each step of the simulation, and

2)    The value can be requested by the Equation of another Variable.

The second case is due to the fact that the system cannot know in advance the exact order it should update the Variables. In order to avoid that, the value of a Variable is used in an Equation before its own Equation (that is, if the requested value is the most recent one), each Variable stores the number of the time step it has been lastly updated. If, say, the Equation for a Variable X requests the most updated value of a Variable Y, but this has not been computed yet, the system stops the execution of the Equation for X, computes the Equation for Y, and then continues the remaining code of the Equation for X, using the just computed value for Y. In technical terms, the procedure for the Equation for X is said to be placed on the stack, while the Equation for Y is computed. Of course, the Equation for Y can trigger the Equation for other Variable Z etc.

The Equation for a Variable is never computed twice in the same time step, by default. In fact, in our example, when the system will try to compute the most updated value of Y as part of the normal updating procedure, it will find that its last computation corresponds to the current time step, and will skip its Equation. This system allows modelers to determine the priority of computations for the Equations by simply setting appropriately the lag notation in the Equations. But it allows also discriminating the behavior of an Equation according to the position of the Variable which requested the computation.

For example, consider the Equation for the innovation via imitation A_IM in the Nelson and Winter example model. It has been placed apart (that is, not as part of Object Firm) because it makes a computation common to every firm but does not provide a value that is worth on its own to be stored in Firm. Therefore, there is one single Variable A_IM which needs to be computed more than once every time (i.e., once for every Firm) and that needs to read the Variables of the instance of Firm that requested its computation.

The Equation is the following:

if(!strcmp(label, "A_IM"))
{
last_update--;

/*************************************************
The line above forces the equation to be computed any time it is requested.
Without this line, the equation would be computed only once for each time step, thus providing the same value to every firm requesting it.

**************************************************/
/**************************
The value of the system variable "c" corresponds to the Object that requested value for this Variable.
If "c" is NULL, it means that actually it is not a Firm
requesting its value, but the system, and hence the equation is not relevant.
**************************/

if(c==NULL)
{res=0;
 goto end;
}

/*******************************
"c" is used to activate the "cal" function, so the values K and RIM
are the ones of the calling Object, Firms in this case.
*********************************/
v[0]=c->cal("K", 0);
v[1]=c->cal("RIM", 0);
v[2]=c->cal("A_MAX", 1);
if(RND<v[0]*v[1]*1.25)
 res=v[2];
else
 res=0;

goto end;
}

As you have seen, the function cal()is called by the Object c rather than the Object p. In fact, the Object that contains A_IM does not have any relation with the Objects containing Firm's. Therefore, erroneous calls like

p->cal("K",0);

in the Equation for A_IM would return always the same instance of K, notably the one contained in the first instance of Object Firm descending from Market, which is not what the modeler wants. The Object c instead reports the Object which originated the computation for A_IM, that is, the instance of Firm which is computing its instance of Variable A necessitating the value for A_IM.

It is crucial to use the proper Object to activate the function. In the large majority of cases p is the right choice. A good rule of thumb to solve the dubious cases is to use the Object c only if:

1)    the Equation you are writing is supposed to be computed more than once during each time step (which needs to be explicitly stated by using the line with last_update--), and

2)    the Variable value needs to be used by other Variables that are not hierarchically related with the Object containing the Variable.

Apart the two Objects p and c, the modeler can use any other Object in the model, but it is necessary to manually find the Object. In order to find a specific instance of an Object, it is necessary to know the value of a Variable contained in the Object that needs to be used. See, for example, the functions search_var_cond(), go_brother(), search() and, in general, the LSD functions returning Objects.

The second form of the LSD function:

object->cal(caller, "Var", lag)

is identical to the previous one, but it gives you more flexibility. It says to the Object object to search for the element Var. If Var is an Equation computed because of this very call, then the "caller" object appearing to Var is caller instead of object.

▲Top

object->sum("Var", lag)

This function searches, with the same strategy described above, an instance of the Variable Var. Then, it keeps on summing up all the values of Variables Var (with the lag lag) 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:

if(!strcmp(label, "Q_TOT"))
{
res=p->sum("Q", 0);
goto end;
}

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.

▲Top

object->overall_max("Var", lag)

Same as object->sum(), but returns the maximum value instead of the sum.

▲Top

object->whg_av("Var1", "Var2", lag)

Same as object->sum(), but returns the sum of the products between the values of Var1 times Var2. Of course, both Variables need to be contained in the same type of Object.

▲Top

object->stat("Var", vector)

This function does not return a value as the ones above, but stores a set of values in the vector vector. It works as the function sum, and the like, but computes a set of descriptive statistics. Namely, it places in vector the following values:

·         vector[0] = number of elements

·         vector[1] = average of Var

·         vector[2] = variance of Var

·         vector[3] = maximum values

·         vector[4] = minimum values

It is used with lines like:

p->stat("Q", v);

so that the temporary variables v[0], v[1], etc. contain the statistics above described.

▲Top

object->search_var_cond("Var", value, lag)

This method is used to find an Object in the model that contains the Variable Var with value value (considered the lag lag). Basically, it uses the same strategy to explore the model as described in function cal() above. Only, it does not stop at 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 cal(...).

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, and make some computation on its Variables. After you have drawn randomly a number (say an integer between 1 and the maximum number of Firm in the model, stored in the temporary variable v[0]), you can use a line like the following:

cur=p->search_var_cond("IdFirm", v[0], 0);

Cur is a temporary variable used to store the address of Objects. The line above returns in cur the Object whose Variable IdFirm has the value stored in v[0]. Now you can use lines like:

v[1]=cur-cal("Q",0);
v[2]=cur-cal("K",0);

to obtain information from this Object. This method allows overcoming the default system in LSD, which would return the values of Variables which have a special relation with the Object containing the Variable whose Equation is computed (same Object, "nearby" Object, calling Object).

Note that the temporary variable to store the Object cur may or may not be present in the file containing the code for the Equation of your model. You can always add new temporary variables, when needed, given that this is a normal file for a C++ function. To declare that you want to use a temporary variable to store Object (actually, Objects' addresses), you need to add lines like:

object *cur;
object *cur1;

at the very beginning of the function variable::fun, that is, before the actual list of Variables' Equations' blocks.

▲Top

object->lsdqsort("Obj_Label", "Var", "Direction")

This function sorts (with the quick sort method) a set of Objects labeled Obj_Label according to the values of Variable Var. 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 cal(...)) starting from object.

Example:

p->lsdqsort("Firm", "Q", "DOWN");

if the line is used in an Equation contained in an Object from which descend many Object labelled Firm, it sorts these descendants according to decreasing values of their Variable Q.

The function is also available for ranking on two dimensions:

p->lsdqsort("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.

▲Top

object->add_an_object("Obj_Label")
object->add_an_object("Obj_Label", example)

These functions add a descendent to the Object object. The new descendant is of type Obj_Label, and is returned by the function. The first type 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 second case, 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. For each descendant type, the function creates one single instance, and initializes their values in the same way from the data file.

The function returns the address of the newly created Object, so that the modeler 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=p->add_an_object("Firm");
cur->write("DateBirth",(double)t,0);

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 (not present in the actual example model), 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 converted to double (that is, double precision floating point variable in C++), because all the numerical values in LSD are real numbers.

If, instead, you want to create new firms from the Equation of an Object which is not Market, from which the new firm has to descend, then you need to use the following two lines:

cur=p->search ("Market");
cur=cur->add_an_object("Firm");

The use of the second type of function allows initializing the values of the new Object equal to some example Object. For example, if you want to add a new Object in the Market identical to the highest productivity one you may write:

v[0]=p->cal("A_MAX",0);
cur=p->search_var_cond("A", v[0], 0);
cur=p->add_an_object("Firm", cur);

The first line obtains the value of the highest productivity among the existing firms. Then, the function search_var_cond(…) 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 who 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.

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.

▲Top

object->delete_obj()

This function deletes the Object object 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. For example, an Equation must never use a line like the following:

p->delete_obj(); // Serious error !!!

In fact, eliminating the Object p, the function deletes also the Variable whose Equation is being executed, resulting in unpredictable errors.

Normally, its use should be done as follows:

for(cur=p->search("Firm"); cur!=NULL; )
{
v[0]=cur->cal("ToDie", 0);
cur1=go_brother(cur);
if(v[0]==1)
 cur->delete_obj();
cur=cur1;
}

The cycle above scans all Object labelled Firms and delete all the instances whose Variable ToDie is set to 1. It starts by assigning to the temporary Object cur the first element of type Firm, which is supposed to descend from the Object containing the Variable under computation. The cycle controls that the value of the Variable ToDie: if this is set to 1, then the Object is deleted. Note that the subsequent element of cur is stored in another temporary Object because, if cur is deleted, it cannot any longer provide the information on the following element of the list of descendants.

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.

▲Top

object->write("Var", new_value, time)

This function writes the value new_value in the Variable Var and sets the time of last update at 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 object, 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.

▲Top

object->search("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 object and their descendants. Therefore, the search is not exhaustive all over 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.

▲Top

go_brother(object)

This function is not defined as a member of class object. It considers the type of the Object object passed as parameter and returns the following element in the list whose object is part of, only if this following element is of the same type of object. It returns NULL in case a different object follows object or in case object is the last of the list.

This function is very useful when the Equation has to treat sequentially a set of object of the same type. This same function is used, for example, to implement the function sum(…) described above. The function sum could in fact be written in an Equation as follows:

for(v[0]=0, cur=p->search("Obj"); cur!=NULL; cur=go_brother(cur) )
v[0]=v[0]+cur->cal("VarToSum",0);

where Obj is the name of the Object containing the Variables to be summarized.

▲Top

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:

if(!strcmp(label, "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]=p->cal("Price", 0);
v[1]=p->cal("A", 1);
v[2]=p->cal("RIM", 0);
v[3]=p->cal("RIN", 0);
v[4]=p->cal("Inn",0);
v[5]=p->cal("C",0);

res=(v[0]*v[1] - v[5] - v[2] - v[3]*v[4]);
goto end;
}

The values of v[n] are reset for each Equation, and therefore cannot be used to transfer information from one Equation to another.

▲Top

object->increment("VarLabel", value)

This function works only if VarLabel is contained in object->, otherwise produces an error. It adds to the current value of VarLabel 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(…).

▲Top

object->multiply("VarLabel", value)

This function works only if VarLabel is contained in object->, 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.

▲Top

lastupdate

This is a field of Variable. It contains the simulation data about the latest time the Variable has executed its Equation to update its value. Normally modelers should not fiddle with this value, since they risk disrupting the automatic scheduling system implemented in LSD. However, there is one case when this is necessary. In fact, by default in LSD a Variable computes its value (and therefore executes its Equation code) only once at each time step.

But in some cases the modeler needs to have one Equation repeated many times during the same time step. This is the case, for example, when a Variable provides some particular random value. In this case it is likely that the Variable value is requested many times during the same time step. And in each case the Equation code must draw a different value. The example below shows the Equation for a Variable that returns a uniform random function. Suppose that the model contains an Object with the two Parameters UpperLimit and LowerLimit, besides a Variable that requests the value for the Uniform:

if(!strcmp(label, "Uniform"))
{
/***************************
Return a Uniform value
***************************/
last_update--;
//repeat the computation any time is requested
if(c==NULL)  
//Avoids to be computed when the system activates the equation
{
  res=-1;
  goto end;
}
v[0]=c->cal("UpperLimit", 0);
v[1]=c->cal("LowerLimit", 0);
res=v[0]+RND*(v[1] - v[0]);
goto end;
}

Note that the Equation returns default value when it is not requested by any other Variable, that is, when the Object c-> is NULL.

▲Top

Math and other functions

Besides the LSD specific function modelers 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.

·         abs(a): return the absolute value of a;

·         min(a, b): return the minimum between a and b;

·         max(a, b): return the maximum between a and b;

·         round(a): return the integer closest to the real value a;

·         exp(a): return the exponential of a;

·         log(a): return the natural log of a;

·         sqrt(a): return the square root of a;

·         pow(a, b): return the power b of a;

·         RND: This function (is actually a macro) produces a random uniform value in the interval [0,1].

·         rnd_integer(min, max): return a random integer value in the interval [min, max] with uniform probability

·         norm(mean, dev): return a random value drawn from a normal random function with mean mean and deviation dev;

·         poisson(mean): return a draw from a poisson random function with mean mean;

·         gamma(mean): return a draw from a gamma random function with mean mean and order 1.

▲Top

object->draw_rnd("ObjLabel", "VarLabel", lag)
object->draw_rnd("ObjLabel", "VarLabel", lag, total)

This function searches for a group of Objects ObjLabel, 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 is 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.

if(!strcmp(label, "DrawAFirm"))
{
/***************************
Return the Id of a Firm chosen randomly with probability
equal to the square of the market shares.
***************************/
for(cur=p->search("Firm"); cur!=NULL; cur=go_brother(cur);)
{
 v[0]=cur->cal("ms", 0);
 cur->write("Prob", v[0]*v[0],0);
}
cur=p->draw_rnd("Firm","Prob",0);
res=cur->cal("IdFirm",0);
goto end;
}

▲Top

Advanced LSD coding

close_sim()

At the end of each simulation run modelers can execute some specific code. Typically, this is used to free some memory allocated during the simulation 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.

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 may be necessary to use a cast. For example:

v[9]=(double)t;

▲Top

Basic C++ structures

Any C++ expression can be embedded in a LSD Equations. In the following are listed the most frequently used.

if ( CONDITION ) { commands1 } else { command2 }

This is the basic conditional statement. CONDITION can be: >, <, == (equal), != (different). commands1 and commands2 can be any set of commands, with the first executed only if CONDITION is true and commands2 executed only if it is false. If there is only one line of commands, the brackets can be avoided.

for( commands_init; CONDITION; commands_end } { commands }

Execute cyclically commands until CONDITION is true. Before the beginning of the cycle commands_init are executed. At the end of every cycle commands_end are executed. When the program encounters this line the program executes the following steps:

1)    execute commands_init

2)    if CONDITION is true execute commands, otherwise end the cycle and continue the program after commands

3)    execute commands_end

4)    return to step 2.

▲Top