Documentation / User's manual in Python :
The TEval
abstract class defines the interface of user evaluation model. It provides an user
class basis to define a model, and allows composition class to combine models together.
A standard evaluation is a function from input parameters to output parameters. and are known
and fixed during the run, but they can now be string, vectors, or double.
These evaluations may not return a value. Generally, it is due to an inconsistent input set.
TMaster
may support (or not) this lack.
A glimpse of all the assessor classes available can be found in Figure VIII.2 that
displays the hierarchy of class starting from the inheritance of TEval
.
Figure VIII.2. Hierarchy of classes and structures for the evaluation part of the Relauncher module.
All the assessors will need to have attributes attached to them, both inputs and outputs. Input attributes might have a peculiar status, by being constant disregarding the provided stochastic law. On the other hand, output attributes might be considered temporary if, for instance, their value is of no interest for the final analysis but might be used by another assessor in composition. All this is discussed later on, in Section VIII.5 as these specification will be done directly on the master object.
The Python function, that can be used with Uranie, receives item values directly in the function parameters and return an output list (even if they are only one value). the rosenbrock example is given below.
def rosenbrock(x, y) :
d1 = (1-x)
d2 = (y-x*x)
return [d1*d1 + d2*d2,]
TThreadedRun
should now be supported and TMpiRun
works but exits badly:
the C++ destructor is not called, resulting in an incorrect MPI exit (MPI_Finalize is not call). Put the
stopSlave
method call as last instruction to avoid that evaluation processes stop before
master process.
The TPythonEval
is also able to deal with vectors and strings as inputs and outputs. If the
way to deal with string is transparent, vector are just simple plain list of real.
The class definition gives information neither about the input or output number nor about parameter order.
These information have to be added to link with TDataServer
and
TMaster
definitions. We use the addInput
and
addOutput
method with TAttribute
objects as argument to do so.
Inputs and outputs have to be added in correct order. Here is an example of how to precise inputs and outputs, from
the script in Section VIII.2:
# problem variables
x = DataServer.TAttribute("x", -3.0, 3.0)
y = DataServer.TAttribute("y", -4., 6.)
ros = DataServer.TAttribute("rose")
# user evaluation function
eval = Relauncher.TPythonEval(rosenbrock)
eval.addInput(x) # Adding attribute in the correct order
eval.addInput(y)
eval.addOutput(ros)
These classes are generaly used when you use the cling root interpreter with Uranie. So they are not detailed here.
Their documentation can be find in the C++ version of this documentation, but their uses are very similar to a
TPythonEval
class.
Using C function in python can be done with the ROOT cling interpreter and the TCIntEval
.
Suppose you have a C rosenbrock function defined in the UserFunctions.C
file as follow.
void rosenbrock(double *in, double *out)
{
double x, y, d1, d2;
x = in[0]; y = in[1];
d1 = (1-x);
d2 = (y-x*x);
out[0] = d1*d1 + d2*d2;
}
To use this function in Uranie, you need to load the script, and define the TCIntEVal
(a TCJitEval
does not work in python interpreter)
ROOT.gROOT.LoadMacro("UserFunctions.C")
fun = Relauncher.TCIntEval("rosenbrock")
It is also possible to simply use a C++ function in python, through the ROOT cling interpreter out of the Relauncher paradigm (for single-purpose estimation for instance). This discussion is already covered in Section I.3.5.3.
This class takes up the TCode
class features and make it thread safe.
Evaluation is done by an external executable, which reads one (or more) configuration file, where it finds its
inputs, and writes the single result file, where to find the output variables. TCodeEval
must
create or adapt configuration files to introduce item values, run the executable, and analyse the result file to get
its outputs. It needs to know file formats, and where to find values.
In order to be used in parallel (MPI or thread), we have to take care of file access conflicts: many processes
which modify the same file. To avoid such a thing, Uranie creates for each resource a personal directory named
URA
with then a set of numbers and letters that is more robust than the older version with just
increasing numbers. Everything is done in it: input files are created there, executable is run from it and the
output file are supposed to be found here as well. By default, these directories are created in the current
folder. You can specify another root directory, using the setWorkingDir
method. There are
two other methods that can be called to change this:
setOldTmpDir()
: This will create folder namedURANIE0
, thenURANIE1
and so on, up to the number of process chosen (for sequential job, onlyURANIE0
will be created).keepAllFolders()
: This method is meant for debugging. It creates a specific working directory for EVERY computations (warning it might overflow your home directory).
Input files are often created from template files. These files, if they are not defined with a full path, are search
in the current directory. You can specify another one using the setReference
method.
The class constructor takes a string (const char*
) as argument which is the command line used to
launch the executable. %D
jocker can be used, and will be replaced by the local directory.
With the introduction of the vectors and strings from version 3.10.0, more complex interaction with files were introduced: how to differ two iterations of a single vector and how to differentiate a double from a string. This depends highly on the nature of the input/output file under consideration, whether it is just a text file used as database (in this case it depends mostly on the way you've written the code that generate/parse it) or whether it corresponds to a more strict kind of file, for instance a piece of code (c++/python/zsh). In the latter case, strings and vectors are not written in the same way. To take this into account, a rule has been defined (commonly to both input and output files, both in the Launcher and Relauncher module). There is a method for any kind of file to define properties of vector and string objects:
setVectorProperties(string beg, string delim, string end)
: the first element is the string beginning of the vector (usually "[" for python, "(" for zsh/sh, nothing...), the second one is the delimiter between iterators (usually "," for c++/python, blank for zsh/sh...) and the last one is the end of the string (usually the opposite character of the beginning one).setStringProperties(string beg, string end)
: the first and second elements are respectively the beginning and ending character used for string (oftenly """).
Depending on the kind of chosen file, there is a default configuration chosen. This default is precised in the following two sections.
Warning
The chosen output format has to be consistent with the output parameters investigated, particularly when some are vectors which can be empty for some specific configurations. In this peculiar but still possible case, the abscence of results is indeed a result of its own and should not be taken as a failure (from an incomplete output file for instance, this specific aspect being further discussed later-on in Section VIII.5.2.1).
Most of the time this would be independent of Uranie as it would be specific to the code under investigation, and as such, it might be tricky to handle. Two use-case macro have been written to show this, so please take a look at empty vectors considered as results in Section XIV.9.9 or considered as an error in Section XIV.9.10 only because of the way the output Key-format output file is written. In a nutshell, in the former case caution has been taken to properly delimit and condensate the results so that even when the vector is empty there are sign of this, while on the other hand a simple dump is done for every instance of the vector meaning that with no content, no dumping is done leading to Uranie stating that there might be missing information in this output file (once again this is discussed in Section VIII.5.2.1).
Input file formats supported by TCodeEval
objects, include:
TFlatScript
, Input file is created from scratch. Values are given in order separated by a blank separator. The default behaviour with respect to strings and vectors for this file, is to look like the DataServer format (from the Launcher module): strings have no specific beginning/ending characters, as for the vectors whose delimiter is chosen to be a comma.TLineScript
, Input file is created from scratch. EachTAttribute
values are written on a specific line. Changing attribute means changing line. It is the equivalent of the Column format (from the Launcher module).TKeyScript
, Input file is created from an original file. EachTAttribute
is associated to a keyword. Values are substituted using a "keyword = value" pattern.TFlagScript
, Input file is created from a template file. EachTAttribute
is associated to a keyword. Each keyword is substituted directly by the current value.
TXmlScript
is not provided in this version
The addInput
method is used to declare parameters for all these file types. For
TFlatScript
and TLineScript
, it takes a single argument: a pointer to
a TAttribute
object, while in the two other cases, the same first argument is completed by a
const char * for the key. The declaration order is only significant when no key is specified (so for
the TFlatScript
and TLineScript
files).
# Input File Flat format case
finp1 = Relauncher.TFlatScript("input_rosenbrock_with_values_rows.dat")
finp1.addInput(x) # Adding attributes in the correct order, one-by-one
finp1.addInput(y)
# Or Input File Key format case
kinp1 = Relauncher.TKeyScript("input_rosenbrock_with_keys.dat")
kinp1.addInput(x, "x") # Adding attributes in the correct order, one-by-one
kinp1.addInput(y, "y")
Once done, the input files are provided to the TCodeEval
object, using the
addInputFile
method, as shown below:
# Add to the TCodeEval
code = Relauncher.TCodeEval("rosenbrock -r") # put "rosenbrock -k" instead for key
code.addInputFile(finp1) # put kinp1 instead for key
Output file formats supported by TCodeEval
include:
TFlatResult
, Output file is made up of an header characterised by # as first line character, and a line of floats separated by spaces. By default, it is constructed as the DataServer one (from Launcher module). One can consider using a flat output file written over several lines (so constructed as aTOutputFileRow
) but one needs to be very careful about the fact that all attributes might not have the same number of entries (when dealing with vectors for instance). This is discussed in the third item of Section IV.3.1.2.3 and in Section XIV.5.32.1. To do this a specific method has to be calledisMultiLine(string separ)
which says to the class that the results are written over many lines, and every field is separated by the stringsepar
.TKeyResult
, Value can be found on line composed with the key, a separator, the value and eventually a ; character. A separator is composed with space, tab, = and : characters.TLineResult
. All the values of a givenTAttribute
are written on a specific line. Changing attribute means changing line. It is the equivalent of the Column format (from the Launcher module).
TXmlResult
is not provided in this version
In a similar way of TInputFile
, one should use the addOutput
to
declare parameters, the argument being the pointer to the attribute under consideration for all these formats,
pairing with the corresponding key when dealing with a TKeyResult
object. This step can be
gathered in a single operation, as for the input file, using the setOutputs
method. Here
is an example for the ongoing use-case.
# Input File Flat format case
fout = Relauncher.TFlatResult("_output_rosenbrock_with_values_rows.dat")
fout.addOutput(ros)
# Or Input File Key format case
kout = Relauncher.TKeyResult("_output_rosenbrock_with_keys.dat")
kout.addOutput(ros, "ros")
Finally, use the addOutputFile
method of TCodeEval
to declare it:
# Add output file to the TCodeEval
code.addOutputFile(fout) # put kout instead for key
Composition offer the possibility to build an overall new kind of TEval
from the succession of
many others. It defines an ordered sequence of evaluation functions. One important thing to notice is that
composition does not deal with distribution even if it is possible. It just applies sequentially all assessors and
become really helpfull as the output of an assessor at the i-Th rank can be used as input for the next assessor, the
(i+1)-Th one. This is one by creating a TComposeEval
object as discussed briefly below.
This composer can be seen as an overall new assessor that is, usually provided to the runner (or to a master directly).
The constructor have no argument. The only important method is the addEval
one that
allows users to add evaluation functions, keeping in mind that they should be called in the correct order,
regarding what they expect (the only argument of the function being a pointer to the assessor to be stacked to
create the chain). Examples of composition can be found in Section XIV.9.12.