sierra-research 1.3.11__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sierra/__init__.py +3 -3
- sierra/core/__init__.py +3 -3
- sierra/core/batchroot.py +223 -0
- sierra/core/cmdline.py +681 -1057
- sierra/core/compare.py +11 -0
- sierra/core/config.py +96 -88
- sierra/core/engine.py +306 -0
- sierra/core/execenv.py +380 -0
- sierra/core/expdef.py +11 -0
- sierra/core/experiment/__init__.py +1 -0
- sierra/core/experiment/bindings.py +150 -101
- sierra/core/experiment/definition.py +414 -245
- sierra/core/experiment/spec.py +83 -85
- sierra/core/exproot.py +44 -0
- sierra/core/generators/__init__.py +10 -0
- sierra/core/generators/experiment.py +528 -0
- sierra/core/generators/generator_factory.py +138 -137
- sierra/core/graphs/__init__.py +23 -0
- sierra/core/graphs/bcbridge.py +94 -0
- sierra/core/graphs/heatmap.py +245 -324
- sierra/core/graphs/pathset.py +27 -0
- sierra/core/graphs/schema.py +77 -0
- sierra/core/graphs/stacked_line.py +341 -0
- sierra/core/graphs/summary_line.py +506 -0
- sierra/core/logging.py +3 -2
- sierra/core/models/__init__.py +3 -1
- sierra/core/models/info.py +19 -0
- sierra/core/models/interface.py +52 -122
- sierra/core/pipeline/__init__.py +2 -5
- sierra/core/pipeline/pipeline.py +228 -126
- sierra/core/pipeline/stage1/__init__.py +10 -0
- sierra/core/pipeline/stage1/pipeline_stage1.py +45 -31
- sierra/core/pipeline/stage2/__init__.py +10 -0
- sierra/core/pipeline/stage2/pipeline_stage2.py +8 -11
- sierra/core/pipeline/stage2/runner.py +401 -0
- sierra/core/pipeline/stage3/__init__.py +12 -0
- sierra/core/pipeline/stage3/gather.py +321 -0
- sierra/core/pipeline/stage3/pipeline_stage3.py +37 -84
- sierra/core/pipeline/stage4/__init__.py +12 -2
- sierra/core/pipeline/stage4/pipeline_stage4.py +36 -354
- sierra/core/pipeline/stage5/__init__.py +12 -0
- sierra/core/pipeline/stage5/pipeline_stage5.py +33 -208
- sierra/core/pipeline/yaml.py +48 -0
- sierra/core/plugin.py +529 -62
- sierra/core/proc.py +11 -0
- sierra/core/prod.py +11 -0
- sierra/core/ros1/__init__.py +5 -1
- sierra/core/ros1/callbacks.py +22 -21
- sierra/core/ros1/cmdline.py +59 -88
- sierra/core/ros1/generators.py +159 -175
- sierra/core/ros1/variables/__init__.py +3 -0
- sierra/core/ros1/variables/exp_setup.py +122 -116
- sierra/core/startup.py +106 -76
- sierra/core/stat_kernels.py +4 -5
- sierra/core/storage.py +13 -32
- sierra/core/trampoline.py +30 -0
- sierra/core/types.py +116 -71
- sierra/core/utils.py +103 -106
- sierra/core/variables/__init__.py +1 -1
- sierra/core/variables/base_variable.py +12 -17
- sierra/core/variables/batch_criteria.py +387 -481
- sierra/core/variables/builtin.py +135 -0
- sierra/core/variables/exp_setup.py +19 -39
- sierra/core/variables/population_size.py +72 -76
- sierra/core/variables/variable_density.py +44 -68
- sierra/core/vector.py +1 -1
- sierra/main.py +256 -88
- sierra/plugins/__init__.py +119 -0
- sierra/plugins/compare/__init__.py +14 -0
- sierra/plugins/compare/graphs/__init__.py +19 -0
- sierra/plugins/compare/graphs/cmdline.py +120 -0
- sierra/plugins/compare/graphs/comparator.py +291 -0
- sierra/plugins/compare/graphs/inter_controller.py +531 -0
- sierra/plugins/compare/graphs/inter_scenario.py +297 -0
- sierra/plugins/compare/graphs/namecalc.py +53 -0
- sierra/plugins/compare/graphs/outputroot.py +73 -0
- sierra/plugins/compare/graphs/plugin.py +147 -0
- sierra/plugins/compare/graphs/preprocess.py +172 -0
- sierra/plugins/compare/graphs/schema.py +37 -0
- sierra/plugins/engine/__init__.py +14 -0
- sierra/plugins/engine/argos/__init__.py +18 -0
- sierra/plugins/{platform → engine}/argos/cmdline.py +144 -151
- sierra/plugins/{platform/argos/variables → engine/argos/generators}/__init__.py +5 -0
- sierra/plugins/engine/argos/generators/engine.py +394 -0
- sierra/plugins/engine/argos/plugin.py +393 -0
- sierra/plugins/{platform/argos/generators → engine/argos/variables}/__init__.py +5 -0
- sierra/plugins/engine/argos/variables/arena_shape.py +183 -0
- sierra/plugins/engine/argos/variables/cameras.py +240 -0
- sierra/plugins/engine/argos/variables/constant_density.py +112 -0
- sierra/plugins/engine/argos/variables/exp_setup.py +82 -0
- sierra/plugins/{platform → engine}/argos/variables/physics_engines.py +83 -87
- sierra/plugins/engine/argos/variables/population_constant_density.py +178 -0
- sierra/plugins/engine/argos/variables/population_size.py +115 -0
- sierra/plugins/engine/argos/variables/population_variable_density.py +123 -0
- sierra/plugins/engine/argos/variables/rendering.py +108 -0
- sierra/plugins/engine/ros1gazebo/__init__.py +18 -0
- sierra/plugins/engine/ros1gazebo/cmdline.py +175 -0
- sierra/plugins/{platform/ros1robot → engine/ros1gazebo}/generators/__init__.py +5 -0
- sierra/plugins/engine/ros1gazebo/generators/engine.py +125 -0
- sierra/plugins/engine/ros1gazebo/plugin.py +404 -0
- sierra/plugins/engine/ros1gazebo/variables/__init__.py +15 -0
- sierra/plugins/engine/ros1gazebo/variables/population_size.py +214 -0
- sierra/plugins/engine/ros1robot/__init__.py +18 -0
- sierra/plugins/engine/ros1robot/cmdline.py +159 -0
- sierra/plugins/{platform/ros1gazebo → engine/ros1robot}/generators/__init__.py +4 -0
- sierra/plugins/engine/ros1robot/generators/engine.py +95 -0
- sierra/plugins/engine/ros1robot/plugin.py +410 -0
- sierra/plugins/{hpc/local → engine/ros1robot/variables}/__init__.py +5 -0
- sierra/plugins/engine/ros1robot/variables/population_size.py +146 -0
- sierra/plugins/execenv/__init__.py +11 -0
- sierra/plugins/execenv/hpc/__init__.py +18 -0
- sierra/plugins/execenv/hpc/adhoc/__init__.py +18 -0
- sierra/plugins/execenv/hpc/adhoc/cmdline.py +30 -0
- sierra/plugins/execenv/hpc/adhoc/plugin.py +131 -0
- sierra/plugins/execenv/hpc/cmdline.py +137 -0
- sierra/plugins/execenv/hpc/local/__init__.py +18 -0
- sierra/plugins/execenv/hpc/local/cmdline.py +31 -0
- sierra/plugins/execenv/hpc/local/plugin.py +145 -0
- sierra/plugins/execenv/hpc/pbs/__init__.py +18 -0
- sierra/plugins/execenv/hpc/pbs/cmdline.py +30 -0
- sierra/plugins/execenv/hpc/pbs/plugin.py +121 -0
- sierra/plugins/execenv/hpc/slurm/__init__.py +18 -0
- sierra/plugins/execenv/hpc/slurm/cmdline.py +30 -0
- sierra/plugins/execenv/hpc/slurm/plugin.py +133 -0
- sierra/plugins/execenv/prefectserver/__init__.py +18 -0
- sierra/plugins/execenv/prefectserver/cmdline.py +66 -0
- sierra/plugins/execenv/prefectserver/dockerremote/__init__.py +18 -0
- sierra/plugins/execenv/prefectserver/dockerremote/cmdline.py +66 -0
- sierra/plugins/execenv/prefectserver/dockerremote/plugin.py +132 -0
- sierra/plugins/execenv/prefectserver/flow.py +66 -0
- sierra/plugins/execenv/prefectserver/local/__init__.py +18 -0
- sierra/plugins/execenv/prefectserver/local/cmdline.py +29 -0
- sierra/plugins/execenv/prefectserver/local/plugin.py +133 -0
- sierra/plugins/{hpc/adhoc → execenv/robot}/__init__.py +1 -0
- sierra/plugins/execenv/robot/turtlebot3/__init__.py +18 -0
- sierra/plugins/execenv/robot/turtlebot3/plugin.py +204 -0
- sierra/plugins/expdef/__init__.py +14 -0
- sierra/plugins/expdef/json/__init__.py +14 -0
- sierra/plugins/expdef/json/plugin.py +504 -0
- sierra/plugins/expdef/xml/__init__.py +14 -0
- sierra/plugins/expdef/xml/plugin.py +386 -0
- sierra/{core/hpc → plugins/proc}/__init__.py +1 -1
- sierra/plugins/proc/collate/__init__.py +15 -0
- sierra/plugins/proc/collate/cmdline.py +47 -0
- sierra/plugins/proc/collate/plugin.py +271 -0
- sierra/plugins/proc/compress/__init__.py +18 -0
- sierra/plugins/proc/compress/cmdline.py +47 -0
- sierra/plugins/proc/compress/plugin.py +123 -0
- sierra/plugins/proc/decompress/__init__.py +18 -0
- sierra/plugins/proc/decompress/plugin.py +96 -0
- sierra/plugins/proc/imagize/__init__.py +15 -0
- sierra/plugins/proc/imagize/cmdline.py +49 -0
- sierra/plugins/proc/imagize/plugin.py +270 -0
- sierra/plugins/proc/modelrunner/__init__.py +16 -0
- sierra/plugins/proc/modelrunner/plugin.py +250 -0
- sierra/plugins/proc/statistics/__init__.py +15 -0
- sierra/plugins/proc/statistics/cmdline.py +64 -0
- sierra/plugins/proc/statistics/plugin.py +390 -0
- sierra/plugins/{hpc → prod}/__init__.py +1 -0
- sierra/plugins/prod/graphs/__init__.py +18 -0
- sierra/plugins/prod/graphs/cmdline.py +269 -0
- sierra/plugins/prod/graphs/collate.py +279 -0
- sierra/plugins/prod/graphs/inter/__init__.py +13 -0
- sierra/plugins/prod/graphs/inter/generate.py +83 -0
- sierra/plugins/prod/graphs/inter/heatmap.py +86 -0
- sierra/plugins/prod/graphs/inter/line.py +134 -0
- sierra/plugins/prod/graphs/intra/__init__.py +15 -0
- sierra/plugins/prod/graphs/intra/generate.py +202 -0
- sierra/plugins/prod/graphs/intra/heatmap.py +74 -0
- sierra/plugins/prod/graphs/intra/line.py +114 -0
- sierra/plugins/prod/graphs/plugin.py +103 -0
- sierra/plugins/prod/graphs/targets.py +63 -0
- sierra/plugins/prod/render/__init__.py +18 -0
- sierra/plugins/prod/render/cmdline.py +72 -0
- sierra/plugins/prod/render/plugin.py +282 -0
- sierra/plugins/storage/__init__.py +5 -0
- sierra/plugins/storage/arrow/__init__.py +18 -0
- sierra/plugins/storage/arrow/plugin.py +38 -0
- sierra/plugins/storage/csv/__init__.py +9 -0
- sierra/plugins/storage/csv/plugin.py +12 -5
- sierra/version.py +3 -2
- sierra_research-1.5.0.dist-info/METADATA +238 -0
- sierra_research-1.5.0.dist-info/RECORD +186 -0
- {sierra_research-1.3.11.dist-info → sierra_research-1.5.0.dist-info}/WHEEL +1 -2
- sierra/core/experiment/xml.py +0 -454
- sierra/core/generators/controller_generator_parser.py +0 -34
- sierra/core/generators/exp_creator.py +0 -351
- sierra/core/generators/exp_generators.py +0 -142
- sierra/core/graphs/scatterplot2D.py +0 -109
- sierra/core/graphs/stacked_line_graph.py +0 -251
- sierra/core/graphs/stacked_surface_graph.py +0 -220
- sierra/core/graphs/summary_line_graph.py +0 -371
- sierra/core/hpc/cmdline.py +0 -142
- sierra/core/models/graphs.py +0 -87
- sierra/core/pipeline/stage2/exp_runner.py +0 -286
- sierra/core/pipeline/stage3/imagizer.py +0 -149
- sierra/core/pipeline/stage3/run_collator.py +0 -317
- sierra/core/pipeline/stage3/statistics_calculator.py +0 -478
- sierra/core/pipeline/stage4/graph_collator.py +0 -320
- sierra/core/pipeline/stage4/inter_exp_graph_generator.py +0 -240
- sierra/core/pipeline/stage4/intra_exp_graph_generator.py +0 -317
- sierra/core/pipeline/stage4/model_runner.py +0 -168
- sierra/core/pipeline/stage4/rendering.py +0 -283
- sierra/core/pipeline/stage4/yaml_config_loader.py +0 -103
- sierra/core/pipeline/stage5/inter_scenario_comparator.py +0 -328
- sierra/core/pipeline/stage5/intra_scenario_comparator.py +0 -989
- sierra/core/platform.py +0 -493
- sierra/core/plugin_manager.py +0 -369
- sierra/core/root_dirpath_generator.py +0 -241
- sierra/plugins/hpc/adhoc/plugin.py +0 -125
- sierra/plugins/hpc/local/plugin.py +0 -81
- sierra/plugins/hpc/pbs/__init__.py +0 -9
- sierra/plugins/hpc/pbs/plugin.py +0 -126
- sierra/plugins/hpc/slurm/__init__.py +0 -9
- sierra/plugins/hpc/slurm/plugin.py +0 -130
- sierra/plugins/platform/__init__.py +0 -9
- sierra/plugins/platform/argos/__init__.py +0 -9
- sierra/plugins/platform/argos/generators/platform_generators.py +0 -383
- sierra/plugins/platform/argos/plugin.py +0 -337
- sierra/plugins/platform/argos/variables/arena_shape.py +0 -145
- sierra/plugins/platform/argos/variables/cameras.py +0 -243
- sierra/plugins/platform/argos/variables/constant_density.py +0 -136
- sierra/plugins/platform/argos/variables/exp_setup.py +0 -113
- sierra/plugins/platform/argos/variables/population_constant_density.py +0 -175
- sierra/plugins/platform/argos/variables/population_size.py +0 -102
- sierra/plugins/platform/argos/variables/population_variable_density.py +0 -132
- sierra/plugins/platform/argos/variables/rendering.py +0 -104
- sierra/plugins/platform/ros1gazebo/__init__.py +0 -9
- sierra/plugins/platform/ros1gazebo/cmdline.py +0 -213
- sierra/plugins/platform/ros1gazebo/generators/platform_generators.py +0 -137
- sierra/plugins/platform/ros1gazebo/plugin.py +0 -335
- sierra/plugins/platform/ros1gazebo/variables/__init__.py +0 -10
- sierra/plugins/platform/ros1gazebo/variables/population_size.py +0 -204
- sierra/plugins/platform/ros1robot/__init__.py +0 -9
- sierra/plugins/platform/ros1robot/cmdline.py +0 -175
- sierra/plugins/platform/ros1robot/generators/platform_generators.py +0 -112
- sierra/plugins/platform/ros1robot/plugin.py +0 -373
- sierra/plugins/platform/ros1robot/variables/__init__.py +0 -10
- sierra/plugins/platform/ros1robot/variables/population_size.py +0 -146
- sierra/plugins/robot/__init__.py +0 -9
- sierra/plugins/robot/turtlebot3/__init__.py +0 -9
- sierra/plugins/robot/turtlebot3/plugin.py +0 -194
- sierra_research-1.3.11.data/data/share/man/man1/sierra-cli.1 +0 -2349
- sierra_research-1.3.11.data/data/share/man/man7/sierra-examples.7 +0 -508
- sierra_research-1.3.11.data/data/share/man/man7/sierra-exec-envs.7 +0 -331
- sierra_research-1.3.11.data/data/share/man/man7/sierra-glossary.7 +0 -285
- sierra_research-1.3.11.data/data/share/man/man7/sierra-platforms.7 +0 -358
- sierra_research-1.3.11.data/data/share/man/man7/sierra-usage.7 +0 -729
- sierra_research-1.3.11.data/data/share/man/man7/sierra.7 +0 -78
- sierra_research-1.3.11.dist-info/METADATA +0 -492
- sierra_research-1.3.11.dist-info/RECORD +0 -133
- sierra_research-1.3.11.dist-info/top_level.txt +0 -1
- {sierra_research-1.3.11.dist-info → sierra_research-1.5.0.dist-info}/entry_points.txt +0 -0
- {sierra_research-1.3.11.dist-info → sierra_research-1.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -2,20 +2,19 @@
|
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: MIT
|
4
4
|
|
5
|
-
"""Stage 1 of the experimental pipeline: generating experimental inputs.
|
6
|
-
|
7
|
-
"""
|
5
|
+
"""Stage 1 of the experimental pipeline: generating experimental inputs."""
|
8
6
|
|
9
7
|
# Core packges
|
10
8
|
import logging
|
9
|
+
import time
|
10
|
+
import datetime
|
11
11
|
|
12
12
|
# 3rd party packages
|
13
13
|
|
14
14
|
# Project packages
|
15
|
-
from sierra.core.generators.
|
16
|
-
from sierra.core.generators.exp_creator import BatchExpCreator
|
15
|
+
from sierra.core.generators.experiment import BatchExpDefGenerator, BatchExpCreator
|
17
16
|
import sierra.core.variables.batch_criteria as bc
|
18
|
-
from sierra.core import types
|
17
|
+
from sierra.core import types, batchroot
|
19
18
|
|
20
19
|
|
21
20
|
class PipelineStage1:
|
@@ -28,15 +27,25 @@ class PipelineStage1:
|
|
28
27
|
|
29
28
|
"""
|
30
29
|
|
31
|
-
def __init__(
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
cmdopts: types.Cmdopts,
|
33
|
+
pathset: batchroot.PathSet,
|
34
|
+
controller: str,
|
35
|
+
criteria: bc.XVarBatchCriteria,
|
36
|
+
) -> None:
|
37
|
+
self.generator = BatchExpDefGenerator(
|
38
|
+
controller_name=controller,
|
39
|
+
scenario_basename=cmdopts["scenario"],
|
40
|
+
criteria=criteria,
|
41
|
+
pathset=pathset,
|
42
|
+
cmdopts=cmdopts,
|
43
|
+
)
|
44
|
+
|
45
|
+
self.creator = BatchExpCreator(
|
46
|
+
criteria=criteria, cmdopts=cmdopts, pathset=pathset
|
47
|
+
)
|
48
|
+
self.pathset = pathset
|
40
49
|
|
41
50
|
self.cmdopts = cmdopts
|
42
51
|
self.criteria = criteria
|
@@ -55,12 +64,12 @@ class PipelineStage1:
|
|
55
64
|
|
56
65
|
#. Generate changes to be applied to to the newly written per-experiment
|
57
66
|
"template" XML file for each experiment which are non-:term:`Batch
|
58
|
-
Criteria` related (e.g., :term:`
|
67
|
+
Criteria` related (e.g., :term:`Engine` changes). These changes
|
59
68
|
apply to all experiment runs in an experiment, but may differ across
|
60
|
-
experimental runs (e.g., # cores used for a simulator
|
69
|
+
experimental runs (e.g., # cores used for a simulator engine).
|
61
70
|
|
62
71
|
#. Generate per-experimental run changes for each experimental run in
|
63
|
-
the experiment, according to
|
72
|
+
the experiment, according to engine and :term:`Project`
|
64
73
|
configuration.
|
65
74
|
|
66
75
|
#. Write the input files for all experimental runs in all experiments to
|
@@ -68,17 +77,22 @@ class PipelineStage1:
|
|
68
77
|
|
69
78
|
"""
|
70
79
|
|
71
|
-
self.logger.info("Generating
|
72
|
-
self.cmdopts['batch_root'])
|
73
|
-
self.creator.create(self.generator)
|
74
|
-
|
75
|
-
n_exp_in_batch = len(self.criteria.gen_attr_changelist()) + \
|
76
|
-
len(self.criteria.gen_tag_addlist())
|
77
|
-
self.logger.info("%d input files generated in %d experiments.",
|
78
|
-
self.cmdopts['n_runs'] * n_exp_in_batch,
|
79
|
-
n_exp_in_batch)
|
80
|
+
self.logger.info("Generating batch experiment in %s...", self.pathset.root)
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
start = time.time()
|
83
|
+
self.creator.create(self.generator)
|
84
|
+
elapsed = int(time.time() - start)
|
85
|
+
sec = datetime.timedelta(seconds=elapsed)
|
86
|
+
n_exp_in_batch = len(self.criteria.gen_attr_changelist()) + len(
|
87
|
+
self.criteria.gen_element_addlist()
|
88
|
+
)
|
89
|
+
self.logger.info(
|
90
|
+
"Generation complete in %s: %d experiments, %d runs per experiment, %d runs total",
|
91
|
+
str(sec),
|
92
|
+
n_exp_in_batch,
|
93
|
+
self.cmdopts["n_runs"],
|
94
|
+
self.cmdopts["n_runs"] * n_exp_in_batch,
|
95
|
+
)
|
96
|
+
|
97
|
+
|
98
|
+
__all__ = ["PipelineStage1"]
|
@@ -2,9 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: MIT
|
4
4
|
|
5
|
-
"""Stage 2 of the experimental pipeline: running experiments.
|
6
|
-
|
7
|
-
"""
|
5
|
+
"""Stage 2 of the experimental pipeline: running experiments."""
|
8
6
|
|
9
7
|
# Core packages
|
10
8
|
import time
|
@@ -15,8 +13,8 @@ import logging
|
|
15
13
|
|
16
14
|
# Project packages
|
17
15
|
from sierra.core.variables import batch_criteria as bc
|
18
|
-
from sierra.core.pipeline.stage2.
|
19
|
-
from sierra.core import types
|
16
|
+
from sierra.core.pipeline.stage2.runner import BatchExpRunner
|
17
|
+
from sierra.core import types, batchroot
|
20
18
|
|
21
19
|
|
22
20
|
class PipelineStage2:
|
@@ -30,18 +28,17 @@ class PipelineStage2:
|
|
30
28
|
|
31
29
|
"""
|
32
30
|
|
33
|
-
def __init__(self, cmdopts: types.Cmdopts) -> None:
|
31
|
+
def __init__(self, cmdopts: types.Cmdopts, pathset: batchroot.PathSet) -> None:
|
34
32
|
self.logger = logging.getLogger(__name__)
|
35
33
|
self.cmdopts = cmdopts
|
34
|
+
self.pathset = pathset
|
36
35
|
|
37
|
-
def run(self, criteria: bc.
|
36
|
+
def run(self, criteria: bc.XVarBatchCriteria) -> None:
|
38
37
|
start = time.time()
|
39
|
-
BatchExpRunner(self.cmdopts, criteria)()
|
38
|
+
BatchExpRunner(self.cmdopts, self.pathset, criteria)()
|
40
39
|
elapsed = int(time.time() - start)
|
41
40
|
sec = datetime.timedelta(seconds=elapsed)
|
42
41
|
self.logger.info("Execution complete in %s", str(sec))
|
43
42
|
|
44
43
|
|
45
|
-
|
46
|
-
'PipelineStage2'
|
47
|
-
]
|
44
|
+
__all__ = ["PipelineStage2"]
|
@@ -0,0 +1,401 @@
|
|
1
|
+
# Copyright 2018 London Lowmanstone, John Harwell, All rights reserved.
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MIT
|
4
|
+
"""Classes for executing experiments via the specified ``--execenv``."""
|
5
|
+
|
6
|
+
# Core packages
|
7
|
+
import os
|
8
|
+
import subprocess
|
9
|
+
import time
|
10
|
+
import sys
|
11
|
+
import datetime
|
12
|
+
import logging
|
13
|
+
import pathlib
|
14
|
+
import typing as tp # noqa: F401
|
15
|
+
|
16
|
+
# 3rd party packages
|
17
|
+
|
18
|
+
# Project packages
|
19
|
+
from sierra.core.variables import batch_criteria as bc
|
20
|
+
from sierra.core import types, config, engine, utils, batchroot, execenv
|
21
|
+
import sierra.core.plugin as pm
|
22
|
+
|
23
|
+
|
24
|
+
class ExpShell:
|
25
|
+
"""Launch a shell which persists across experimental runs.
|
26
|
+
|
27
|
+
Having a persistent shell is necessary so that running pre- and post-run
|
28
|
+
shell commands have an effect on the actual commands to execute the run. If
|
29
|
+
you set an environment variable before the simulator launches (for example),
|
30
|
+
and then the shell containing that change exits, and the simulator launches
|
31
|
+
in a new shell, then the configuration has no effect. Thus, a persistent
|
32
|
+
shell.
|
33
|
+
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__(self, exec_strict: bool) -> None:
|
37
|
+
self.env = os.environ.copy()
|
38
|
+
self.logger = logging.getLogger(__name__)
|
39
|
+
self.procs = [] # type: tp.List[subprocess.Popen]
|
40
|
+
self.exec_strict = exec_strict
|
41
|
+
|
42
|
+
def run_from_spec(self, spec: types.ShellCmdSpec) -> bool:
|
43
|
+
self.logger.trace("Cmd: %s", spec.cmd) # type: ignore
|
44
|
+
|
45
|
+
# We use a special marker at the end of the cmd's output to know when
|
46
|
+
# the environment dump starts.
|
47
|
+
if spec.env:
|
48
|
+
spec.cmd += " && echo ~~~~ENV_START~~~~ && env"
|
49
|
+
|
50
|
+
proc = subprocess.Popen(
|
51
|
+
spec.cmd,
|
52
|
+
shell=spec.shell,
|
53
|
+
stdout=subprocess.PIPE,
|
54
|
+
stderr=subprocess.PIPE,
|
55
|
+
env=self.env,
|
56
|
+
)
|
57
|
+
|
58
|
+
if not spec.wait:
|
59
|
+
self.procs.append(proc)
|
60
|
+
return True
|
61
|
+
|
62
|
+
# We use communicate(), not wait() to avoid issues with IO buffers
|
63
|
+
# becoming full (e.g., you get deadlocks with wait() regularly).
|
64
|
+
stdout_raw, stderr_raw = proc.communicate()
|
65
|
+
|
66
|
+
# Update the environment for all commands
|
67
|
+
if spec.env:
|
68
|
+
self._update_env(stdout_raw)
|
69
|
+
|
70
|
+
# Only show output if the process failed (i.e., did not return 0)
|
71
|
+
if proc.returncode != 0:
|
72
|
+
self.logger.error("Cmd '%s' failed!", spec.cmd)
|
73
|
+
stdout_str = stdout_raw.decode("utf-8")
|
74
|
+
stderr_str = stderr_raw.decode("utf-8")
|
75
|
+
|
76
|
+
if spec.env:
|
77
|
+
stdout_str = stdout_str.split("~~~~ENV_START~~~~", maxsplit=1)[0]
|
78
|
+
stderr_str = stderr_str.split("~~~~ENV_START~~~~", maxsplit=1)[0]
|
79
|
+
|
80
|
+
self.logger.error(
|
81
|
+
"Cmd stdout (last 10 lines): %s",
|
82
|
+
"\n + " "\n".join(stdout_str.split("\n")[-10:]),
|
83
|
+
)
|
84
|
+
self.logger.error(
|
85
|
+
"Cmd stderr (last 10 lines): %s",
|
86
|
+
"\n" + "\n".join(stderr_str.split("\n")[-10:]),
|
87
|
+
)
|
88
|
+
if self.exec_strict:
|
89
|
+
raise RuntimeError(
|
90
|
+
("Command failed and strict checking was " "requested")
|
91
|
+
)
|
92
|
+
|
93
|
+
return False
|
94
|
+
else:
|
95
|
+
return True
|
96
|
+
|
97
|
+
def _update_env(self, stdout) -> None:
|
98
|
+
record = False
|
99
|
+
for e in stdout.decode("utf-8").split("\n"):
|
100
|
+
if record:
|
101
|
+
candidate = e.strip().split("=")
|
102
|
+
if len(candidate) != 2:
|
103
|
+
continue
|
104
|
+
|
105
|
+
key = candidate[0]
|
106
|
+
value = candidate[1]
|
107
|
+
|
108
|
+
if key not in self.env or self.env[key] != value:
|
109
|
+
self.logger.debug(
|
110
|
+
"Update experiment environment: %s=%s", key, value
|
111
|
+
)
|
112
|
+
self.env[key] = value
|
113
|
+
elif e.strip() == "~~~~ENV_START~~~~":
|
114
|
+
record = True
|
115
|
+
|
116
|
+
|
117
|
+
class BatchExpRunner:
|
118
|
+
"""Runs each :term:`Experiment` in :term:`Batch Experiment` in sequence.
|
119
|
+
|
120
|
+
Attributes:
|
121
|
+
batch_exp_root: Absolute path to the root directory for the batch
|
122
|
+
experiment inputs (i.e. experiment directories are
|
123
|
+
placed in here).
|
124
|
+
|
125
|
+
batch_stat_root: Absolute path to the root directory for statistics
|
126
|
+
which are computed in stage {3,4} (i.e. experiment
|
127
|
+
directories are placed in here).
|
128
|
+
|
129
|
+
batch_stat_exec_root: Absolute path to the root directory for statistics
|
130
|
+
which are generated as experiments run during
|
131
|
+
stage 2 (e.g., how long each experiment took).
|
132
|
+
|
133
|
+
cmdopts: Dictionary of parsed cmdline options.
|
134
|
+
|
135
|
+
criteria: Batch criteria for the experiment.
|
136
|
+
|
137
|
+
exec_exp_range: The subset of experiments in the batch to run (can be
|
138
|
+
None to run all experiments in the batch).
|
139
|
+
|
140
|
+
"""
|
141
|
+
|
142
|
+
def __init__(
|
143
|
+
self,
|
144
|
+
cmdopts: types.Cmdopts,
|
145
|
+
pathset: batchroot.PathSet,
|
146
|
+
criteria: bc.XVarBatchCriteria,
|
147
|
+
) -> None:
|
148
|
+
self.cmdopts = cmdopts
|
149
|
+
self.criteria = criteria
|
150
|
+
self.pathset = pathset
|
151
|
+
self.exec_exp_range = self.cmdopts["exp_range"]
|
152
|
+
|
153
|
+
self.logger = logging.getLogger(__name__)
|
154
|
+
|
155
|
+
utils.dir_create_checked(self.pathset.stat_exec_root, exist_ok=True)
|
156
|
+
utils.dir_create_checked(self.pathset.scratch_root, exist_ok=True)
|
157
|
+
|
158
|
+
def __call__(self) -> None:
|
159
|
+
"""
|
160
|
+
Execute experiments in the batch according to configuration.
|
161
|
+
|
162
|
+
"""
|
163
|
+
self.logger.info(
|
164
|
+
"Engine=%s, execenv=%s",
|
165
|
+
self.cmdopts["engine"],
|
166
|
+
self.cmdopts["execenv"],
|
167
|
+
)
|
168
|
+
|
169
|
+
module = pm.pipeline.get_plugin_module(self.cmdopts["engine"])
|
170
|
+
|
171
|
+
# Output some useful information before running
|
172
|
+
if hasattr(module, "pre_exp_diagnostics"):
|
173
|
+
module.pre_exp_diagnostics(self.cmdopts, self.pathset, self.logger)
|
174
|
+
|
175
|
+
exp_all = [self.pathset.input_root / d for d in self.criteria.gen_exp_names()]
|
176
|
+
|
177
|
+
exp_to_run = utils.exp_range_calc(
|
178
|
+
self.cmdopts["exp_range"],
|
179
|
+
self.pathset.input_root,
|
180
|
+
self.criteria.gen_exp_names(),
|
181
|
+
)
|
182
|
+
|
183
|
+
# Verify environment is OK before running anything
|
184
|
+
self.logger.debug("Checking --engine execution environment")
|
185
|
+
engine.execenv_check(self.cmdopts)
|
186
|
+
|
187
|
+
self.logger.debug("Checking --execenv execution environment")
|
188
|
+
execenv.execenv_check(self.cmdopts)
|
189
|
+
|
190
|
+
# Calculate path for to file for logging execution times
|
191
|
+
now = datetime.datetime.now()
|
192
|
+
exec_times_fpath = self.pathset.stat_exec_root / now.strftime("%Y-%m-%e-%H:%M")
|
193
|
+
|
194
|
+
# Start a new process for the experiment shell so pre-run commands have
|
195
|
+
# an effect (if they set environment variables, etc.).
|
196
|
+
shell = ExpShell(self.cmdopts["exec_strict"])
|
197
|
+
|
198
|
+
if self.cmdopts["exec_parallelism_paradigm"] is not None:
|
199
|
+
self.logger.warning(
|
200
|
+
"Overriding engine=%s parallelism paradigm with %s",
|
201
|
+
self.cmdopts["engine"],
|
202
|
+
self.cmdopts["exec_parallelism_paradigm"],
|
203
|
+
)
|
204
|
+
parallelism_paradigm = self.cmdopts["exec_parallelism_paradigm"]
|
205
|
+
else:
|
206
|
+
configurer = engine.ExpConfigurer(self.cmdopts)
|
207
|
+
parallelism_paradigm = configurer.parallelism_paradigm()
|
208
|
+
|
209
|
+
if parallelism_paradigm == "per-batch":
|
210
|
+
ParallelRunner(
|
211
|
+
self.pathset, self.cmdopts, exec_times_fpath, exp_all, shell
|
212
|
+
)(exp_to_run)
|
213
|
+
|
214
|
+
else:
|
215
|
+
# Run the experiment!
|
216
|
+
for exp in exp_to_run:
|
217
|
+
exp_num = exp_all.index(exp)
|
218
|
+
|
219
|
+
# Run cmds for engine-specific things to setup the experiment
|
220
|
+
# (e.g., start daemons) if needed.
|
221
|
+
engine_generator = engine.ExpShellCmdsGenerator(self.cmdopts, exp_num)
|
222
|
+
execenv_generator = execenv.ExpShellCmdsGenerator(self.cmdopts, exp_num)
|
223
|
+
|
224
|
+
for spec in execenv_generator.pre_exp_cmds():
|
225
|
+
shell.run_from_spec(spec)
|
226
|
+
|
227
|
+
for spec in engine_generator.pre_exp_cmds():
|
228
|
+
shell.run_from_spec(spec)
|
229
|
+
|
230
|
+
runner = SequentialRunner(
|
231
|
+
self.pathset,
|
232
|
+
self.cmdopts,
|
233
|
+
exec_times_fpath,
|
234
|
+
execenv_generator,
|
235
|
+
shell,
|
236
|
+
)
|
237
|
+
runner(exp.name, exp_num)
|
238
|
+
|
239
|
+
# Run cmds to cleanup {execenv, engine}-specific things now that
|
240
|
+
# the experiment is done (if needed).
|
241
|
+
for spec in execenv_generator.post_exp_cmds():
|
242
|
+
shell.run_from_spec(spec)
|
243
|
+
|
244
|
+
for spec in engine_generator.post_exp_cmds():
|
245
|
+
shell.run_from_spec(spec)
|
246
|
+
|
247
|
+
|
248
|
+
class SequentialRunner:
|
249
|
+
"""
|
250
|
+
Execute each :term:`Experimental Run` in an :term:`Experiment`.
|
251
|
+
|
252
|
+
Runs are executed parallel if the selected execution environment supports
|
253
|
+
it, otherwise sequentially. This class is meant for executing experiments
|
254
|
+
within a batch sequentially.
|
255
|
+
"""
|
256
|
+
|
257
|
+
def __init__(
|
258
|
+
self,
|
259
|
+
pathset: batchroot.PathSet,
|
260
|
+
cmdopts: types.Cmdopts,
|
261
|
+
exec_times_fpath: pathlib.Path,
|
262
|
+
generator: execenv.ExpShellCmdsGenerator,
|
263
|
+
shell: ExpShell,
|
264
|
+
) -> None:
|
265
|
+
|
266
|
+
self.exec_times_fpath = exec_times_fpath
|
267
|
+
self.shell = shell
|
268
|
+
self.generator = generator
|
269
|
+
self.cmdopts = cmdopts
|
270
|
+
self.pathset = pathset
|
271
|
+
self.logger = logging.getLogger(__name__)
|
272
|
+
|
273
|
+
def __call__(self, exp_name: str, exp_num: int) -> None:
|
274
|
+
"""Execute experimental runs for a single experiment."""
|
275
|
+
exp_input_root = self.pathset.input_root / exp_name
|
276
|
+
exp_scratch_root = self.pathset.scratch_root / exp_name
|
277
|
+
self.logger.info(
|
278
|
+
"Running exp%s in <batchroot>/%s",
|
279
|
+
exp_num,
|
280
|
+
exp_input_root.relative_to(self.pathset.root),
|
281
|
+
)
|
282
|
+
sys.stdout.flush()
|
283
|
+
|
284
|
+
start = time.time()
|
285
|
+
|
286
|
+
utils.dir_create_checked(exp_scratch_root, exist_ok=True)
|
287
|
+
|
288
|
+
# TODO: This restriction should be removed/pushed down to execution
|
289
|
+
# environments that require it.
|
290
|
+
assert (
|
291
|
+
self.cmdopts["exec_jobs_per_node"] is not None
|
292
|
+
), "# parallel jobs can't be None"
|
293
|
+
|
294
|
+
exec_opts = {
|
295
|
+
"exp_input_root": str(exp_input_root),
|
296
|
+
"work_dir": exp_input_root,
|
297
|
+
"exp_scratch_root": str(exp_scratch_root),
|
298
|
+
"cmdfile_stem_path": str(
|
299
|
+
exp_input_root / config.kGNUParallel["cmdfile_stem"]
|
300
|
+
),
|
301
|
+
"cmdfile_ext": config.kGNUParallel["cmdfile_ext"],
|
302
|
+
"exec_resume": self.cmdopts["exec_resume"],
|
303
|
+
"n_jobs": self.cmdopts["exec_jobs_per_node"],
|
304
|
+
"nodefile": self.cmdopts["nodefile"],
|
305
|
+
}
|
306
|
+
|
307
|
+
for spec in self.generator.exec_exp_cmds(exec_opts):
|
308
|
+
if not self.shell.run_from_spec(spec):
|
309
|
+
self.logger.error(
|
310
|
+
"Check outputs in %s for full details",
|
311
|
+
exec_opts["exp_scratch_root"],
|
312
|
+
)
|
313
|
+
|
314
|
+
elapsed = int(time.time() - start)
|
315
|
+
sec = datetime.timedelta(seconds=elapsed)
|
316
|
+
self.logger.info("Exp%s elapsed time: %s", exp_num, sec)
|
317
|
+
|
318
|
+
with utils.utf8open(self.exec_times_fpath, "a") as f:
|
319
|
+
f.write("exp" + str(exp_num) + ": " + str(sec) + "\n")
|
320
|
+
|
321
|
+
|
322
|
+
class ParallelRunner:
|
323
|
+
"""
|
324
|
+
Execute all runs in all :term:`Experiments <Experiment>` in parallel.
|
325
|
+
|
326
|
+
This class is meant for executing experiments within a batch concurrently.
|
327
|
+
"""
|
328
|
+
|
329
|
+
def __init__(
|
330
|
+
self,
|
331
|
+
pathset: batchroot.PathSet,
|
332
|
+
cmdopts: types.Cmdopts,
|
333
|
+
exec_times_fpath: pathlib.Path,
|
334
|
+
exp_all: tp.List[pathlib.Path],
|
335
|
+
shell: ExpShell,
|
336
|
+
) -> None:
|
337
|
+
|
338
|
+
self.exec_times_fpath = exec_times_fpath
|
339
|
+
self.shell = shell
|
340
|
+
self.exp_all = exp_all
|
341
|
+
self.cmdopts = cmdopts
|
342
|
+
self.pathset = pathset
|
343
|
+
self.logger = logging.getLogger(__name__)
|
344
|
+
|
345
|
+
def __call__(self, exp_to_run: tp.List[pathlib.Path]) -> None:
|
346
|
+
"""Execute all experimental runs for all experiments."""
|
347
|
+
|
348
|
+
self.logger.info("Kicking off all experiments in <batchroot>")
|
349
|
+
sys.stdout.flush()
|
350
|
+
|
351
|
+
start = time.time()
|
352
|
+
|
353
|
+
exp_scratch_root = self.pathset.scratch_root
|
354
|
+
utils.dir_create_checked(exp_scratch_root, exist_ok=True)
|
355
|
+
exec_opts = {
|
356
|
+
"batch_root": str(self.pathset.root),
|
357
|
+
"work_dir": str(self.pathset.root),
|
358
|
+
"batch_scratch_root": str(self.pathset.scratch_root),
|
359
|
+
"cmdfile_stem_path": str(
|
360
|
+
self.pathset.root / config.kGNUParallel["cmdfile_stem"]
|
361
|
+
),
|
362
|
+
"cmdfile_ext": config.kGNUParallel["cmdfile_ext"],
|
363
|
+
"exec_resume": self.cmdopts["exec_resume"],
|
364
|
+
"n_jobs": self.cmdopts["exec_jobs_per_node"],
|
365
|
+
}
|
366
|
+
|
367
|
+
# Run cmds for engine-specific things to setup the experiment
|
368
|
+
# (e.g., start daemons) if needed.
|
369
|
+
engine_generator = engine.BatchShellCmdsGenerator(self.cmdopts)
|
370
|
+
execenv_generator = execenv.BatchShellCmdsGenerator(self.cmdopts)
|
371
|
+
|
372
|
+
for spec in execenv_generator.pre_batch_cmds():
|
373
|
+
self.shell.run_from_spec(spec)
|
374
|
+
|
375
|
+
for spec in engine_generator.pre_batch_cmds():
|
376
|
+
self.shell.run_from_spec(spec)
|
377
|
+
|
378
|
+
for spec in execenv_generator.exec_batch_cmds(exec_opts):
|
379
|
+
if not self.shell.run_from_spec(spec):
|
380
|
+
self.logger.error(
|
381
|
+
"Check outputs in %s for full details",
|
382
|
+
exec_opts["batch_scratch_root"],
|
383
|
+
)
|
384
|
+
|
385
|
+
# Run cmds to cleanup {execenv, engine}-specific things now that
|
386
|
+
# the experiment is done (if needed).
|
387
|
+
for spec in execenv_generator.post_batch_cmds():
|
388
|
+
self.shell.run_from_spec(spec)
|
389
|
+
|
390
|
+
for spec in engine_generator.post_batch_cmds():
|
391
|
+
self.shell.run_from_spec(spec)
|
392
|
+
|
393
|
+
elapsed = int(time.time() - start)
|
394
|
+
sec = datetime.timedelta(seconds=elapsed)
|
395
|
+
self.logger.info("Elapsed time: %s", sec)
|
396
|
+
|
397
|
+
with utils.utf8open(self.exec_times_fpath, "a") as f:
|
398
|
+
f.write(": " + str(sec) + "\n")
|
399
|
+
|
400
|
+
|
401
|
+
__all__ = ["BatchExpRunner", "SequentialRunner", "ParallelRunner", "ExpShell"]
|