sporkforge







Simulation Engine Documentation

( <-- Back to the Simulation Engine )

Table of Contents

Introduction

Welcome to the sporkforge.com simulation engine, the free-of-charge tool that enables you to simulate a model of your complex system. To use this tool, first create a mathematical model of your system by defining iterative steps in the process you are modeling. Then, specify a set of variables which define the state of the system. The variables can be assigned equations, and can also have conditional assignments using logical branching via if-then statements. The variables retain their values across iterative steps, so you can preserve and modify state information throughout the duration of the simulation.

Once you have defined your model, you may specify the model's variables which you wish to analyze (the "variables to report"). You may also specify the number of iterative steps to execute in the simulation. Upon completion of the simulation, the mean, minimum value, maximum value, and other measurements of the variables will be reported, as well as a graph and histogram of each.

Please read this important disclaimer before using this tool.

Model structure and syntax

Models consist of a series of statements, which are either variable assignments (of the form <variable name> = <mathematical expression>;), or if-then blocks. The if-then blocks will contain statements themselves.

Each variable assignment must end with the ';' character, which allows a single equation to span multiple lines if convenient.

The model reader is case-insensitive, so the capitalization of any letters in any variable names, function names, reserved words, etc., will be ignored. Variables whose names differ only in capitalization will be interpreted as the same variable.

Whitespace (newlines & spaces) may be used freely to enhance readability. Also, comments are supported, using either '#' symbols, or c-style '/* */' notation. When a '#' is encountered in a line of text, all remaining text in that line is ignored. Also, all text enclosed within '/*' and '*/' is ignored.

To see a sample model, click on the demo link in the simulation engine page.

Mathematical expressions

Mathematical expressions are how values are calculated for use in variable assignments, the logical conditions of if-then statements, and the arguments of functions. They are composed of one or more of the following: variables, constants, operators, functions, and parentheses for associative grouping. You may nest associative groups and functions to any level. Here are some examples of mathematical expressions:

Expressions can evaluate to either a real number between -1e200 and 1e200, or to "NaN", i.e. "Not a Number". For example, both 1/x and ln[x] will evaluate to NaN for x=0. Note that the simulation will halt with an error when an expression evaluates to NaN.

Variables, constants, operators, and functions

Variables are named entities which hold a real-numbered value. Variables can be given any name that does not conflict with the names of any existing constant symbols, functions, reserved words such as "if", or predefined variables. Also, variable names must not contain any whitespace, or symbols which can be confused with arithmetic operators, etc., and may not be prefaced with '@', as that notation is reserved for predefined variables. There is no limit applied to the length of variable names.

Constants

Numbers may be specified in any expression in the usual decimal or scientific formats. Also, the following symbols represent numerical constants and may be used within expressions:

e Natural log base :  2.7182818284590452
pi Pi :  3.1415926535897932

Operators

Arithmetic operators may be used in expressions, and have the usual syntax.

List of available arithmetic operators

The following operators are supported:

-a Negation - result is negative a.
a + b Addition - result is a plus b.
a - b Subtraction - result is a minus b.
a * b Multiplication - result is a times b.
a / b Division - result is a divided by b.
a ^ b Exponent - result is a raised to the bth power.

The relative precedence of the operators is as follows, from lowest to highest:

+ , -
* , /
^
- (negation)

Functions

Functions are specified by a name, followed by square brackets, which contain zero, one, or more arguments (separated by commas), depending on the function.

List of available functions

The following functions are available, and take zero or more arguments. Arguments must be enclosed within square brackets ("[ ]"), and separated by a comma (",") when there are multiple arguments.

Note that some functions are defined over a limited range of arguments. If one or more argument values is outside the allowed range for a given function, or if one or more arguments is NaN, the function will evaluate to NaN.

Real-valued functions

ln[a] Natural logarithm, or log base e, of a. Defined for all a>0.
log2[a] Log base 2 of a. Defined for all a>0.
log10[a] Log base 10 of a. Defined for all a>0.
sqrt[a] Square root of a (equivalent to a^0.5). Defined for all a>=0.
abs[a] Absolute value of a.
sin[a] Sine of a, where a is an angle in radians.
cos[a] Cosine of a, where a is an angle in radians.
tan[a] Tangent of a, where a is an angle in radians.
arcsin[a] Arcsine, or inverse sine, of a - evaluates to the angle (in radians) whose sine is a. Defined for all a within [-1,1].
arccos[a] Arccosine, or inverse cosine, of a - evaluates to the angle (in radians) whose cosine is a. Defined for all a within [-1,1].
arctan[a] Arctangent, or inverse tangent, of a - evaluates to the angle (in radians) whose tangent is a.
sinh[a] Hyperbolic sine of a.
cosh[a] Hyperbolic cosine of a.
tanh[a] Hyperbolic tangent of a.
floor[a] Rounds a down to the nearest integer.
ceil[a] Rounds a up to the nearest integer.
mod[a , b] Remainder of a divided by b. Defined for all b!=0.
lngamma[a] Natural log of the gamma function value of a. Defined for all a>0.
sorted_val[a , b0 , b1 , b2 , ... , bn] Returns the ath sorted value if bi were sorted from lowest to highest. Defined for a in the interval [0, n-1]. If a is fractional, the sorted position returned is a rounded down to the nearest integer.
sorted_pos[a , b0 , b1 , b2 , ... , bn] Returns the position (i.e. the index of the b variable) in the argument list of the ath sorted value if bi were sorted from lowest to highest. For example, if a=3, b0=3, b1=8, b2=7, and b3=5, then the function would return 2, since b2 is third (a=3) in the sorted list of arguments b0,...,bn. Defined for a in the interval [0, n-1]. If a is fractional, the sorted position returned is a rounded down to the nearest integer.

Boolean functions

These functions evaluate to 1 if true, 0 if false.

is_eq[a , b] True if a is equal to b.
is_neq[a , b] True if a is not equal to b.
is_lt[a , b] True if a is less than b.
is_lte[a , b] True if a is less than or equal to b.
is_gt[w] True if a is greater than b.
is_gte[a , b] True if a is greater than or equal to b.
is_nan[a] True if a is NaN.

Random number generating functions

The following functions return random values from various distributions. They all use Mersenne Twister (mt19937ar) as the base random number generator. The user-specified randomization seed is used to initialize gcc's stock rand() generator, which is then used to generate an array of 256 random numbers. That array is used to initialize the MT generator. The code used to transform uniform to non-uniform random number distributions is from the GNU Scientific Library (GSL). Click here if you'd like to inspect the random number generation code.

You may not see the random number distribution you want in this list, however various other distributions are either special cases of these (for example, the erlang distribution is the gamma function with an integral shape parameter), or can be transformed mathematically in a straightforward way (such as the beta distribution).

rand_uniform[] Generates uniformly distributed random numbers on the interval [0,1).
rand_uniform_oo[] Generates uniformly distributed random numbers on the interval (0,1).
rand_uniform_cc[] Generates uniformly distributed random numbers on the interval [0,1].
rand_normal[a , b] Generates random numbers with a normal (gaussian) distribution having mean a and standard deviation b. Defined for all b>0.
rand_exponential[a] Generates random numbers with an exponential distribution having mean a. Defined for all a>0.
rand_gamma[a , b] Generates random numbers with a gamma distribution having shape parameter a and scale parameter b. Defined for all a>0 & b>0.
rand_binomial[a , b] Generates integer-valued random numbers with a binomial distribution having success probability a and number of trials b. Defined for a in the interval [0,1] and b in the interval [0,2^32). If b is fractional, the number of trials parameter used to generate the distribution will be b rounded up to the nearest integer.
rand_poisson[a] Generates integer-valued random numbers with a poisson distribution having mean a. Defined for all a>0.

Predefined variables

This is a special class of variables which are available to be used in expressions, but cannot be assigned a value in the model, as the simulator assigns their values automatically. The following predefined variables are currently available:

@numiter Total number of iteration steps in the simulation, as specified by the user.
@iter The index of the current iteration step - runs from 0 to @numiter-1.

So, for example, if you wanted to calculate the simulation's progress in your model, you could make the following assignment:

sim_progress = @iter / @numiter;

Or perhaps you want to enter an if-then statement on the last iteration step of the simulation:

if is_eq[@iter, @numiter-1] then ...(etc)

Variable assignments

A variable is declared in a model once it is assigned a value. Values are assigned as follows:

<variable name> = <mathematical expression>;

There may be multiple assignments to the same variable in the model (for example, if you want to initialize a variable to a value, then update that variable's value later in the model flow), however you may not reference a variable within an expression unless it has been previously declared via an assignment (or is being declared in that same assignment). If you want to reference a variable at a point in the model before an assignment to that variable occurs (hence using a value assigned in a previous iteration step), you can declare it using a self assignment, i.e. setting the variable equal to itself: "<variable name> = <variable name>;".

If-then statements

If-then statements allow you to branch the model flow. The structure of a basic if-then statement is as follows:

if <expression> then

   <statements>
   
endif

The <expression> between the "if" and the "then" is the statement's condition, which is interpreted as "true" if the value returned by <expression> is non-zero, and "false" if it is zero. If the condition evaluates to "true", then the statements between the "then" and the "endif" are executed. If the condition evaluates to "false", then the if-then statement's contents are skipped, and the flow resumes after the "endif".

Another form of if-then statement is:

if <expression> then

   <statements>

else
   
   <statements>

endif

In this case, if <expression> evaluates to true, then the statements between the "then" and the "else" are executed, otherwise the statements between the "else" and the "endif" are executed.

Finally, you can test multiple conditions within the same statement using "elseif":

if <expression 0> then

   <statements>

elseif <expression 1> then

   <statements>
   
...

else
   
   <statements>

endif

The "..." in the above indicates that you can put any number of "elseif" instances in between. In this case, if <expression 0> evaluates to true, then the statements within its section of the if-then block are executed, then the flow resumes after the "endif" (the statements in the other sections are skipped). If <expression 0> evaluates to false, then the statements within its section are skipped, and <expression 1> is evaluated, and so forth. If all the conditions corresponding to individual "if" and "elseif" sections evaluate to false, then if there's an "else" section at the end, the "else" section's statements are executed. If there is no "else" section (i.e. just an "endif"), then execution resumes after the "endif".

Note that "then" must be included after the condition expression for "elseif", just as it is after the condition expression for "if".

If-then statements may be nested to any level, so the statements contained within an if-then block can themselves be if-then statements.

Running the simulator

Simulation process & data flow

In a given iteration step, the simulation model is read from top-to-bottom, and the statements executed in order. If a variable has multiple assignments, and that variable is referenced in an expression, the last value assigned previous to that expression in the flow is what that variable evaluates to in the expression. For example, consider the following series of statements:

x = 1;
y = x;
x = 2;
z = x;

After these statements, x equals 2, y equals 1, and z equals 2. Also, variables may reference themselves in assignments, so the following yields exactly the same results:

x = 1;
y = x;
x = x + 1;
z = x;

If a variable references itself in its assignment (or in its first assignment in the case of multiple assignments), then the value returned by the reference is the value the variable had at the end of the previous iteration step. In other words, the data stored in variables persists throughout the simulation. Note that variables are initialized to zero at the beginning of the simulation, so at iteration step 0 the self-referenced variable will equal zero. For example, in the following model:

v = v + 1;

v equals 1 after iteration step 0, v equals 2 after iteration step 1, v equals 3 after iteration step 2, etc.

The data persistence of variables also applies in the case of conditional assignments, when a variable assignment occurs within an if-then statement. There may be some iteration steps in which this assignment is not invoked, in which case the variable simply holds the same value as when the last assignment occurred in some previous iteration step.

Normal mode

"Normal mode" is the full simulation flow. To run the simulator in normal mode, insert your model in simulation model window, and select the "Normal - run full simulation" button under the "Simulation Mode" section. Next, decide which variables you want to have data reported on, and list those variables in the "Variables to Report" window. Finally, choose the number of iterations over which to run the simulation. 1000000 is the default value, though you may select up to 2000000 iterations if necessary.

In the "Randomization Seed" entry, 0 is set as the default value. You may leave that as is, unless you want to perturb the results from a previous simulation run. Note that any integer value may be entered in this field, and that no value is "better" than another - any value results in equally random output. Also note that if the model and number of iterations are unchanged, then the same randomization seed will result in the same simulation behavior and output (that is, the sequence of random numbers returned by random number generating functions will be identical).

Once you've entered the data above, click the "Run simulation" button to start the simulation. Simulation progress updates will appear below. Please wait patiently for the simulation to finish, as it may take some time due to it being a computationally intensive task.

When the simulation is complete, the progress updates will disappear, and the simulation results will appear in its place.

Debug mode

You may wish to inspect in detail the variable values at a particular point in the simulation flow. In this case, select the "Debug - show detailed variable info..." button under the "Simulation Mode" section, then select the iteration step range from which to display variable values.

Make sure the simulation model, "Variables to Report", and number of iterations are also specified, in the same manner as in normal mode. Also, if you want to reproduce the behavior from a previous normal mode run, leave the randomization seed set to the same value.

Click the "Run simulation" button to start the simulation. When the specified iteration step range is reached, the values of all model variables at the end of each iteration step in the range will be displayed. The simulation will then halt.

Encountering errors

When a simulation is initiated, the model is checked for correct syntax, and the simulation run will halt with an error if incorrect syntax is encountered. When this happens, an error message will be printed, then the text of your model will be displayed, with the location of the problem indicated by "^^^" characters underneath the text, and "*" in the left-hand margin. When this happens, correct the problem and restart the simulation.

The simulation begins after the model has been processed and determined to be error-free. The simulation will then proceed uninterrupted, unless a variable evaluates to NaN after any iteration step. If a variable evaluates to NaN after any step in the simulation, the simulation will halt with an error, and will report the name of the variable and the iteration step (@iter) in which the NaN occurred. If you encounter such an error, inspect your model for the possibility of division by zero, taking the logarithm of a non-positive value, passing invalid argument values to functions, etc. If the problem isn't obvious, you can switch to debug mode to trace the values of the variables leading up to the iteration step at which the NaN was encountered. Make sure you use the same randomization seed to reproduce the same behavior.

Simulation results

When the simulation is complete, measurements of the values the variables held during the simulation will be displayed. At the end of each iteration step in which each "variable to report" was assigned a value, that value is recorded and used to compile a profile of the variable.

"Summary of tracked variables"

Dynamically-valued variables vs. monotonic variables

Variables whose values rise and fall throughout the course of the simulation are considered "dynamically-valued". Variables whose values never decrease or never increase are "monotonic". An example of a monotonic variable might be a one representing a cumulative tally of positive values.

These are separated into different tables since measurements that are meaningful for one type may not be meaningful for the other. For example, mean and standard deviation are useful for dynamically-valued variables, but are less useful for monotonic variables. On the other hand, the variable's final value is useful for monotonic variables, but not as useful for dynamically-valued variables.

Updates versus iterations

Each variable holds a value and can be referenced in the assignment of other variables, but not every variable is necessarily assigned a value at every iteration step, such as when it is only assigned a value within an if-then statement whose condition does not always evaluate to true. A variable is considered "updated" in an iteration step if the variable is assigned a value in that step (even if the assigned value equals the previous value).

Variable values are used in the mean and standard deviation calculations, as well in the graph and histogram, only after iteration steps in which the variable has been updated. If you want the mean and standard deviation taken over all iteration steps, then you can force a given variable to be updated at every step by a self assignment, i.e. setting the variable equal to itself: "<variable name> = <variable name>;". This self assignment will count as an update for every iteration in which it is invoked.

The reason the mean and other measurements are only taken when the variable is updated is that allows the user to define the interpretation of mean, etc, as being averaged over units other than iteration steps. For example you may want the mean of a variable in terms of time units, but you may not have modeled iteration steps as taking place over over equal units of time (as is the case in event-driven simulation).

Description of table headings

The following data are included in the variable summary tables:

Charts

Graphs and histograms of each "variable to report" will be generated and displayed.

Graphs

A graph of the updated value of each "variable to report" will be generated and displayed. The horizontal axis is the simulation progress in units of iteration steps. The vertical axis is the variable's value. Since the graph image resolution is low compared to the number of iteration steps, the graph is displayed as a range (grey vertical bars), and the mean (arithmetic average) within that range (black dots). Note that only values from updated steps are graphed, so the graph of partially updated variables may show ungraphed intervals.

Histograms

A histogram of the updated values of each "variable to report" will be generated and displayed. The horizontal axis is a range of values. The data is displayed as green bars indicating the frequency of occurrence of the variable's updated values within the range indicated by the width of the bars. The vertical axis is the proportion of the total number of iteration steps. Note that the sum of the heights of the bars will be <updated%>/100.

Numerical issues

Numerical values (as variable values, constants, intermediate values in calculations, etc) are interpreted either as numbers in the range (-1e200,1e200), or as NaN ("Not a Number"). Also, values less than 1e-13 are interpreted as zero, and numbers that differ by less than that amount are considered to be equal. Bear these limits in mind when constructing your model.

Value storage and arithmetic are done in double (64 bit) precision, so numerical precision is within 15 decimal digits. Due to the limited precision, care must be taken when adding small quantities to large numbers, or when subtracting or comparing the values of large numbers that tend to differ by a small amount.

Random numbers are generated to 32 bit precision.



( <-- Back to the Simulation Engine )











Legal Disclaimer & Privacy Policy