3.2.1. sciexp2.expdef.experiments.Experiments

Methods

execute(cmd, **kwargs)

Execute one program for each experiment on the set.

generate(template_from, template_to[, ...])

Generate files from other templated files.

generate_jobs(system, template_to[, export, ...])

Generate job files for given job system.

pack(template_from, template_to[, dereference])

Copy files, accepting templates for both source and destination paths.

params(*filters, **variables)

Add new parameter permutations to experiment set.

params_append(*filters, **variables)

Same as params, but appends the new permutations.

pformat(pretty, cycle[, name])

Return pretty representation.

translate(template[, with_exps, with_unique])

Translate the given templates with each experiment.

view(*filters)

Get a view to a portion of this of this experiment set.

Attributes

dereference

Whether to dereference source symlinks during pack.

out

Output directory for all non-absolute paths.

class Experiments(experiments=None, **kwargs)

Bases: Pretty

Define and generate experiment sets.

Each experiment corresponds to a dict that maps variable names to their values, and can be accessed as if Experiments was a list.

Parameters:
experimentssequence of dict, optional

Initial experiment set.

outoptional

Initial value for the out attribute.

dereferencebool, optional

Initial value for the dereference attribute.

Attributes:
outstr

Output directory for all non-absolute paths.

dereferencebool

Whether to dereference source symlinks during pack.

See also

with_exp, with_exp_tpl

Use functions as variable values.

from_function, from_find_files

Use functions to create new variable values.

sciexp2.common.text.translate

Used to interpret template arguments in the Experiments methods, used with the variable maps of each experiment.

sciexp2.common.text.extract

Used to interpret template arguments in the Experiments methods, used to build the variable maps of new experiments.

sciexp2.common.filter.Filter

Values accepted for the filter arguments of the methods in Experiments. A list of strings will be used as an argument to sciexp2.common.filter.and_filters.

out = './out'

Output directory for all non-absolute paths.

dereference = False

Whether to dereference source symlinks during pack.

view(*filters)

Get a view to a portion of this of this experiment set.

Any changes to the experiments on a view will be reflected on the base experiment set and all its views. Adding new experiments to a view will maintain the relative order between experiments on the base experiment set.

Parameters:
filterslist of filters

Filters to select a matching subset of the experiments.

Examples

A view shows a subset of the experiments:

>>> e = Experiments()
>>> e.params(a=range(2), b=range(2))
>>> e
Experiments([{'a': 0, 'b': 0},
             {'a': 0, 'b': 1},
             {'a': 1, 'b': 0},
             {'a': 1, 'b': 1}])
>>> v = e.view("a == b")
>>> v
ExperimentsView([{'a': 0, 'b': 0}, {'a': 1, 'b': 1}])

All operations are performed only on selected elements, but apply to the underlying experiment set as if they happened “in-place”:

>>> v = e.view("a == b")
>>> v.params(c=range(2))
>>> v
ExperimentsView([{'a': 0, 'b': 0, 'c': 0},
                 {'a': 0, 'b': 0, 'c': 1},
                 {'a': 1, 'b': 1, 'c': 0},
                 {'a': 1, 'b': 1, 'c': 1}])
>>> e
Experiments([{'a': 0, 'b': 0, 'c': 0},
             {'a': 0, 'b': 0, 'c': 1},
             {'a': 0, 'b': 1},
             {'a': 1, 'b': 0},
             {'a': 1, 'b': 1, 'c': 0},
             {'a': 1, 'b': 1, 'c': 1}])

For convenience, you can also get the inverse of a view, which is a view with the negated filters:

>>> i = v.view_inverse()
>>> i
ExperimentsView([{'a': 0, 'b': 1}, {'a': 1, 'b': 0}])
>>> i.params(d=range(2))
>>> e
Experiments([{'a': 0, 'b': 0, 'c': 0},
             {'a': 0, 'b': 0, 'c': 1},
             {'a': 0, 'b': 1, 'd': 0},
             {'a': 0, 'b': 1, 'd': 1},
             {'a': 1, 'b': 0, 'd': 0},
             {'a': 1, 'b': 0, 'd': 1},
             {'a': 1, 'b': 1, 'c': 0},
             {'a': 1, 'b': 1, 'c': 1}])

Finally, you can also create new views from other views:

>>> n = v.view("c == 0")
>>> n.params(e=[10, 20])
>>> n.view_inverse().params(e=[30, 40])
>>> e
Experiments([{'a': 0, 'b': 0, 'c': 0, 'e': 10},
             {'a': 0, 'b': 0, 'c': 0, 'e': 20},
             {'a': 0, 'b': 0, 'c': 1, 'e': 30},
             {'a': 0, 'b': 0, 'c': 1, 'e': 40},
             {'a': 0, 'b': 1, 'd': 0},
             {'a': 0, 'b': 1, 'd': 1},
             {'a': 1, 'b': 0, 'd': 0},
             {'a': 1, 'b': 0, 'd': 1},
             {'a': 1, 'b': 1, 'c': 0, 'e': 10},
             {'a': 1, 'b': 1, 'c': 0, 'e': 20},
             {'a': 1, 'b': 1, 'c': 1, 'e': 30},
             {'a': 1, 'b': 1, 'c': 1, 'e': 40}])
execute(cmd, **kwargs)

Execute one program for each experiment on the set.

If cmd is a single string, it’s interpreted as a shell command, otherwise it’s interpreted as a program name and its argument list.

Arguments stdin, stdout, stderr, and all elements in cmd are interpreted as templates to translate with each experiment.

Parameters:
cmdlist of str

Command to execute.

repeatstr, optional

What to do when more than one experiment is translated into the same command. Can be "ignore", "warn" (default), or "error".

stdin, stdout, stderrfile or str, optional

Paths for the standard input/output/error for the command (defaults to None).

paralleloptional

Whether to execute commands in parallel (default is False).

See also

sciexp2.common.parallel.get_parallelism

Used to interpret argument parallel.

Notes

Command execution is internally handled by subproces.Popen and thus the stdin, stdout and stderr arguments can also have any value accepted by it.

params(*filters, **variables)

Add new parameter permutations to experiment set.

Performs the cartesian product between the current experiment set and all permutations of the given variable values (i.e., a parameter can have a sequence of values).

Parameters:
filterslist of filters, optional

Ensures new experiments match the given filters (default is to accept all).

variablesdict

A dictionary of variable name / values pairs to perform permutations.

Notes

Values in variables can be templates referencing other variables (either existing in the experiment set or provided in variables), and will thus be translated accordingly.

Providing a variable in params that already exists in the experiment set will override its value.

Examples

Can easily create value permutations that match the given filters: >>> e = Experiments() >>> e.params(“a % 2 == 0”, … a=range(4), … b=[“foo”, “bar”]) >>> e Experiments([{‘a’: 0, ‘b’: ‘foo’},

{‘a’: 0, ‘b’: ‘bar’}, {‘a’: 2, ‘b’: ‘foo’}, {‘a’: 2, ‘b’: ‘bar’}])

New variables can be appended to the experiment set:

>>> e.params(c=3)
>>> e
Experiments([{'a': 0, 'b': 'foo', 'c': 3},
             {'a': 0, 'b': 'bar', 'c': 3},
             {'a': 2, 'b': 'foo', 'c': 3},
             {'a': 2, 'b': 'bar', 'c': 3}])

Including ones with multiple values:

>>> e.params(d=[1, 2])
>>> e
Experiments([{'a': 0, 'b': 'foo', 'c': 3, 'd': 1},
             {'a': 0, 'b': 'foo', 'c': 3, 'd': 2},
             {'a': 0, 'b': 'bar', 'c': 3, 'd': 1},
             {'a': 0, 'b': 'bar', 'c': 3, 'd': 2},
             {'a': 2, 'b': 'foo', 'c': 3, 'd': 1},
             {'a': 2, 'b': 'foo', 'c': 3, 'd': 2},
             {'a': 2, 'b': 'bar', 'c': 3, 'd': 1},
             {'a': 2, 'b': 'bar', 'c': 3, 'd': 2}])

New values override old ones, even if that means eliminating some experiments:

>>> e.params(b="baz")
>>> e
Experiments([{'a': 0, 'b': 'baz', 'c': 3, 'd': 1},
             {'a': 0, 'b': 'baz', 'c': 3, 'd': 2},
             {'a': 2, 'b': 'baz', 'c': 3, 'd': 1},
             {'a': 2, 'b': 'baz', 'c': 3, 'd': 2}])
params_append(*filters, **variables)

Same as params, but appends the new permutations.

Examples

>>> e = Experiments()
>>> e.params(a=range(2))
>>> e.params_append(b=range(2))
>>> e
Experiments([{'a': 0},
             {'a': 1},
             {'b': 0},
             {'b': 1}])
pack(template_from, template_to, dereference=None)

Copy files, accepting templates for both source and destination paths.

Parameters:
template_from, template_tostr

Source/destination file templates.

dereferencebool, optional

Whether to dereference symlinks in source files (defaults to self.dereference).

Notes

The copy is performed only if the source is newer than the destination, or if the destination does not exist.

There can be cases where multiple source files correspond to a single destination file (after translating the templates). Such cases are logged by a message.

generate(template_from, template_to, parallel=True, backend='pystache', backend_options={})

Generate files from other templated files.

Like pack, the paths to source and destination files are templates. Every source file is, in addition, considered a template that will be translated with each of the experiments.

Parameters:
template_from, template_tostr

Templates for Source/destination file paths.

paralleloptional

Whether to generate files in parallel (default is True).

backend{‘pystache’, ‘jinja2’}, optional

Templating backend to process the contents of template_from.

Notes

Like pack, if multiple source files correspond to a single destination file (after translating the templates), the case is logged by a message and only the first generated file is kept.

generate_jobs(system, template_to, export=[], depends=[], submit_args=[], job_desc='jobs.jd', parallel=True)

Generate job files for given job system.

Works similar to generate, but takes a predefined source file template that is identified by system. It also creates a job descriptor file that can be later used by the launcher program.

Parameters:
systemstr

Job system name.

template_tostr

Template to path of destination job scripts.

exportlist of str, optional

List of variable names to export into the job descriptor file.

dependslist of str, optional

File path templates for job dependencies.

submit_argslist of str, optional

Templates for additional arguments to the job submission command.

job_descstr, optional

Name of the output job descriptor file.

paralleloptional

Whether to generate files in parallel (default is True).

All job systems implicitly define the *LAUNCHER* variable, which has the
path of the to the generated script to run that. All job systems also
define *DONE* and *FAIL* as file paths to identify the state of each
job.
The variables selected by `export` will be later available when using
`sciexp2.expdef.launcher` with this job descriptor. Any variable
directly or indirectly used in `depends` will be implicitly added to
`export`. Variable *LAUNCHER* is also implicitly added.
The templates specified by `depends` will be translated for each job
info file paths, and each file path will be considered a dependency of
that job. The path specified by *LAUNCHER* is implicitly added as a
dependency.
The state for a given job is decided as follows:
- Not run: Neither the *DONE* nor the *FAIL* files exist.
- Failed: The *FAIL* file exists.
- Outdated: The *DONE* file exists, but is older than any of its

dependencies.

- Done: The *DONE* file exists, and is newer than any of its

dependencies.

- Running: Some job systems can track when a job is currently executing.

Notes

None of the experiments can define the LAUNCHER variable. Some job systems also define their own set of reserved variables and default values to others (in case your experiments don’t specify them); see the job system’s documentation for more information.

translate(template, with_exps=False, with_unique=True)

Translate the given templates with each experiment.

Parameters:
templatestr

Template to translate.

with_expsbool, optional

Return the list of experiments used to get each translation.

with_uniquebool, optional

Only return unique translations.

Returns:
A list of the template translations (with_exps=False), or a list of
tuples with each translation and the corresponding experiments
(with_exps=True).

Examples

Translations are unique by default: >>> e = Experiments() >>> e.params(a=range(2), b=range(2), c=range(2)) >>> e.translate(“{{a}}-{{b}}”) [‘0-0’, ‘0-1’, ‘1-0’, ‘1-1’] >>> e.translate(“{{a}}-{{b}}”, with_unique=False) [‘0-0’, ‘0-0’, ‘0-1’, ‘0-1’, ‘1-0’, ‘1-0’, ‘1-1’, ‘1-1’]

And can be easily extended with the experiments that led to each translation: >>> e.translate(“{{a}}-{{b}}”, with_exps=True) == [ … (‘0-0’, [{‘a’: 0, ‘b’: 0, ‘c’: 0}, {‘a’: 0, ‘b’: 0, ‘c’: 1}]), … (‘0-1’, [{‘a’: 0, ‘b’: 1, ‘c’: 0}, {‘a’: 0, ‘b’: 1, ‘c’: 1}]), … (‘1-0’, [{‘a’: 1, ‘b’: 0, ‘c’: 0}, {‘a’: 1, ‘b’: 0, ‘c’: 1}]), … (‘1-1’, [{‘a’: 1, ‘b’: 1, ‘c’: 0}, {‘a’: 1, ‘b’: 1, ‘c’: 1}])] True >>> e.translate(“{{a}}-{{b}}”, with_exps=True, with_unique=False) == [ … (‘0-0’, [{‘a’: 0, ‘b’: 0, ‘c’: 0}]), … (‘0-0’, [{‘a’: 0, ‘b’: 0, ‘c’: 1}]), … (‘0-1’, [{‘a’: 0, ‘b’: 1, ‘c’: 0}]), … (‘0-1’, [{‘a’: 0, ‘b’: 1, ‘c’: 1}]), … (‘1-0’, [{‘a’: 1, ‘b’: 0, ‘c’: 0}]), … (‘1-0’, [{‘a’: 1, ‘b’: 0, ‘c’: 1}]), … (‘1-1’, [{‘a’: 1, ‘b’: 1, ‘c’: 0}]), … (‘1-1’, [{‘a’: 1, ‘b’: 1, ‘c’: 1}])] True