TrickLogo

Trick offers a convenient method for repeatedly running a simulation with varying inputs. We call this capability "Monte Carlo". Monte Carlo is a well-known technique where mathematical problems are solved using random numbers and probability statistics. By "Monte Carlo", we mean running the simulation repeatedly over a varying input space. How the inputs vary is up to you, the developer. How the input space is varied may not fall into the strict definition of Monte Carlo, i.e. using bell curves, linear distributions, etc.

Use this section as a reference. Refer to the tutorial for a full blown example.

Monte Carlo Tutorial

The tutorial has an example of how to use Trick-based Monte Carlo. The example shows how to use Monte Carlo to optimize the ground distance of a jet-propelled ball. The ball has a jet which yields upward force. The jet may only fire four times. The firing times are set in the input file. The Monte Carlo technique is used to run the simulation repeatedly with varying jet-firing sequences. The tutorial shows how to use predetermined (hard-coded) sequences, as well as how to use random sequences. The tutorial also shows how to use data products to analyze the multitudinous curves that a Monte Carlo simulation produces.

Structure

The curious will want to know the internal design of Monte Carlo. In the case of optimization, or when feeding new inputs to the simulation based on past results, the design becomes prerequisite. That said, here are a few brief points about the design:

The Master

The master is the command center of a Monte Carlo simulation. It does not process runs directly. Rather, it delegates them to one or more slaves which perform the actual execution. The master is responsible for spawning and managing the state of these slaves, dispatching and tracking the progress of each run, and handling the retrying of failed and timed out runs. Its life cycle consists of the following:

Slaves

Slaves are the workhorses of a Monte Carlo simulation. They are responsible for the actual execution of runs and the reporting of results back to the master. Slaves are robust enough to handle runs that fail abnormally and will continue executing until explicitly killed or disconnected. A slave's life cycle consists of the following:

Setting Up a Monte Carlo Simulation

It is important to note that the only initialization jobs the master runs are those with phase zero. As such, if one wishes to use the functions in the following discussion in user model code, a few will have effect only in phase zero initialization jobs. These jobs are explicitly noted below.

Monte Carlo Remote Shell

To start Monte Carlo slaves you must have either rsh or ssh installed. It is best to setup the remote shell so that it doesn't prompt for a password every time you run it. See tutorial for a tiny ssh test.

Activating Monte Carlo

A Monte Carlo simulation is enabled via one of:

C++: Trick::MonteCarlo::set_enabled
C: ::mc_set_enabled

Specifying the Number of Runs

This tells Monte Carlo how many simulation runs to execute. For a series of random runs, Monte Carlo will execute the simulation the number of runs specified. When MonteVarFile is specified as the input variable's type, Trick will execute the number of runs specified, not exceeding the number of values contained in the input variable's data file. For multiple MonteVarFile variables, Trick will execute the least number of runs specified, not exceeding the least number of values contained in the input variable's data file.

The number of runs to be dispatched is specified via one of:

C++: Trick::MonteCarlo::set_num_runs
C: ::mc_set_num_runs

Ranges

This optional section tells Monte Carlo which runs to execute.

A subset of runs to be dispatched can be achieved via one of:

C++: Trick::MonteCarlo::add_range
C: ::mc_add_range

All ranges will be combined, and runs falling in any of the specified ranges will be dispatched. If no ranges are specified, all runs will be dispatched. For example, the following lines in the input file result in runs 100 through 200, 250, and 300 through 500 being dispatched:

trick.mc_add_range(100, 200)
trick.mc_add_range(250)
trick.mc_add_range(300, 500)

Monte Carlo Input Variables

The following classes (which are derived from the Trick::MonteVar abstract base class) are used to specify which input variables are available for changing from run to run. The type of class tells Trick how to generate the value for the variable from run to run.

#this is a comment
0 1.00000 1.50000
1 1.50000 2.00000
2 2.00000 2.50000
3 2.50000 3.00000

After constructing such a variable, it can be added via:

C++: Trick::MonteCarlo::add_variable

C wrapper functions are not available for creating and adding variables. As such, C simulations can add variables only through the input file. To create a variable in the input file, prepend the constructor with trick.. For example:

variable0 = trick.MonteVarCalculated("ball.obj.state.input.mass")
variable0.thisown = 0 # tell Python not to free the underlying C++ class when the wrapper is garbage collected
variable1 = trick.MonteVarFile("ball.obj.state.input.position[0]", "RUN_monte/values.txt", 2)
variable1.thisown = 0
variable2 = trick.MonteVarFixed("ball.obj.state.input.position[1]", 5)
variable2.thisown = 0
variable3 = trick.MonteVarRandom("ball.obj.state.input.velocity[0]", trick.MonteVarRandom.GAUSSIAN, "", 
variable3.thisown = 0
trick.MonteVarRandom.NO_ENGINE)
variable3.set_seed(1)
variable3.set_sigma(0.6667)
variable3.set_mu(4.0)
variable3.set_min(-4.0)
variable3.set_max(4.0)
variable3.set_sigma_range(0)  # integer argument, default is 1. 0 turns limit feature off.
variable3.set_min_is_relative(True) # default true. When True, set_min value is relative to mean mu
variable3.set_max_is_relative(True) # default true. When True, set_max value is relative to mean mu

Calling a C++ function in the input file is not as simple as prepending it with trick.. To add a variable in the input file, use the following syntax:

trick_mc.mc.add_variable(variable0)
trick_mc.mc.add_variable(variable1)
trick_mc.mc.add_variable(variable2)
trick_mc.mc.add_variable(variable3)

Variables can also be added from jobs of type "monte_master_pre" or "monte_master_post" while the Monte Carlo is running. Note that new variables will effect only runs that have yet to be dispatched.

Distributed Monte Carlo

To run Monte Carlo distributed across a network, you simply need to call add_slave for each slave.

C++: Trick::MonteCarlo::add_slave
C: ::mc_add_slave

slave0 = trick.MonteSlave("WonderWoman")
slave0.thisown = 0 # tell Python not to free the underlying C++ class when the wrapper is garbage collected
trick_mc.mc.add_slave(slave0)
slave1 = trick.MonteSlave("CatWoman")
slave1.thisown = 0
trick_mc.mc.add_slave(slave1)
slave2 = trick.MonteSlave("LoisLane")
slave2.thisown = 0
trick_mc.mc.add_slave(slave2)

It is really that easy. But the following bullets need to be remembered:

Output

Data logged for each run is stored in a RUN_ directory within a MONTE_ directory on the machine that processed the run. Existing directories and files will be overwritten. These directories are not cleaned out by subsequent Monte Carlos. So, for instance, if the user runs a Monte Carlo with 1000 runs, and then reruns the same Monte Carlo with 500 runs, the first 500 RUN_* directories will contain data from the second Monte Carlo, while the last 500 will still exist and contain data from the first Monte Carlo. The following files are Monte Carlo specific:

Dry Run

A dry run generates only the monte_header and monte_runs files without actually processing any runs. It is useful for verifying input values before running a full Monte Carlo. A dry run is specified via one of:

C++: Trick::MonteCarlo::set_dry_run
C: ::mc_set_dry_run

Making Monte Carlo Less Verbose

By default, Monte Carlo is fairly verbose. If you need to suppress the messages from a Monte Carlo run:

C++: Trick::MonteCarlo::set_verbosity
C: ::mc_set_verbosity

Possible values for the verbosity argument are:

Optimization

Monte Carlo has no decision making capability. It runs a predetermined set of inputs or a random set. In order to optimize, it is usually necessary to base current inputs from past results. Intelligence must be involved for the decision making. Currently, Trick has no built-in "intelligence" for optimization. It offers a framework for you, the brains, to optimize the simulation. The framework allows on-the-fly input modification based on past simulation results.

The framework is a set of monte jobs which run at critical times for analyzing results and building new inputs. The jobs are written by the developer. The job's class determines what role the job plays (i.e. where it is run) in the optimization process. The Monte Carlo classed job is specified in the S_define like any other job.

In order to get a handle on how to plug in to the optimization framework, it helps to have a better understanding of the roles the master and slave play in the master/slave design.

The below table contains a description of the Monte Carlo specific Trick jobs:

Trick Job Description
monte_master_init
Runs once in the master before any slaves are spawned.
monte_slave_init
Runs once in each slave upon spawning.
monte_master_pre
Runs in the master before each run is dispatched. This is where you could modify inputs before they are sent to a slave.
monte_slave_pre
Runs in the slave before each dispatch received from the master is executed. Inputs are processed before this job.
monte_slave_post
Runs in the slave each time this slave completes a run. This is where you could send custom results back to the master.
monte_master_post
Runs in the master each time a slave completes a run. This is where you could receive custom results from a slave.
monte_slave_shutdown
Runs once in the slave before termination.
monte_master_shutdown
Runs once in the master after all runs have completed.

The Post-Run Connection

Post-run communication can be done via the Trick comm package in the post-run jobs. The underlying sockets are already connected at the time the post-run jobs are executed, so the user can simply use the C wrapper functions ::mc_read and ::mc_write to pass additional data between the master and slave.

Where To Put Optimization Code

Since the master is in charge of dispatching, the optimization code will reside in the master. It is debatable whether to put the actual decision making in the monte_master_pre or monte_master_post jobs. The tutorial uses monte_master_pre, which may seem less intuitive, since the monte_master_post is the one receiving the results. It turned out that the decision making for that particular algorithm was easier before the run rather than after. This is not a hard and fast rule. Wherever it makes sense for the problem at hand is where the decision making should go.

Job Order

This is the order in which jobs are executed in a MonteCarlo sim:

User Accessible Routines