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.
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.
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 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 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:
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.
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.
A Monte Carlo simulation is enabled via one of:
C++: Trick::MonteCarlo::set_enabled
C: ::mc_set_enabled
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
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)
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.
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:
"monte_master_pre"
or "monte_master_post"
while the Monte Carlo is running.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:
MONTE_
This file contains the input file lines that configured the initial state of the Monte Carlo, such as information on the
number of runs and variables. This file is also created during a dry_run.
MONTE_
This file lists the values used for each variable for each run. This file is also created during a dry_run.
MONTE_
This file contains the summary statistical information that is printed out to the screen after a Monte Carlo completes.
MONTE_
This file contains the input file commands necessary to rerun a single run as a stand alone simulation.
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
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:
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. |
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.
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.
This is the order in which jobs are executed in a MonteCarlo sim:
Program starts
For each slave
For each run sent to a slave by the master
When all runs have completed