Lsd Equations' Language
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.
Modellers 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. Alternatively, users can also use a simplified
macro language (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, 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 |
Lsd Equations
FOR NEW USERS: To write Lsd equations it is also available a macro
language which simplifies the equations' coding. If you are not locked
in C++ you may prefer to learn this language (the two languages are perfectly
equivalent, and a model may even contain equations in both expressions).
See here the equations references for the
macro language.
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
independenlty from one another, and computed at the generic time t:
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, modellers 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 semplify 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 modeller 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):
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 to use 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 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.
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->,
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:
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 Marke, 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. 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]=p->up->cal("Price",0);
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:
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 assignements. 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 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] = 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. 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.
object->cal("Var", lag),
or
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 Varible 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 modellers can use as many
temporary variables as necessary.
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 another 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 modellers to determine the priority of computations
for the equations by simply setting appropriately the lag notation in the
equations. But it allows also to discriminate the behaviour 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, an erroneous call 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 modeller 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 modeller 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 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.
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.
object->overall_max("Var", lag)
Same as object->sum(), but returns the maximum value instead of the sum.
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 Objects.
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]=mininimu 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.
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 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 cal(...).
The function is used to identify a particular Object, which is neitherp
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 to overcome 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.
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.
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
declared as 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 to initialize 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 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.
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.
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.
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.
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 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.
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.
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.
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.
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.
lastupdate
This is a field of Variable. It contains the simulation date of the
latest time the Variable has executed its equation to update its value.
Normally modellers should not fiddle with this value, since they risk to
disrupt 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 modeller 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.
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.
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 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.
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;
}
Advanced Lsd coding
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.
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;
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 of 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: