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.
Files changed (254) hide show
  1. sierra/__init__.py +3 -3
  2. sierra/core/__init__.py +3 -3
  3. sierra/core/batchroot.py +223 -0
  4. sierra/core/cmdline.py +681 -1057
  5. sierra/core/compare.py +11 -0
  6. sierra/core/config.py +96 -88
  7. sierra/core/engine.py +306 -0
  8. sierra/core/execenv.py +380 -0
  9. sierra/core/expdef.py +11 -0
  10. sierra/core/experiment/__init__.py +1 -0
  11. sierra/core/experiment/bindings.py +150 -101
  12. sierra/core/experiment/definition.py +414 -245
  13. sierra/core/experiment/spec.py +83 -85
  14. sierra/core/exproot.py +44 -0
  15. sierra/core/generators/__init__.py +10 -0
  16. sierra/core/generators/experiment.py +528 -0
  17. sierra/core/generators/generator_factory.py +138 -137
  18. sierra/core/graphs/__init__.py +23 -0
  19. sierra/core/graphs/bcbridge.py +94 -0
  20. sierra/core/graphs/heatmap.py +245 -324
  21. sierra/core/graphs/pathset.py +27 -0
  22. sierra/core/graphs/schema.py +77 -0
  23. sierra/core/graphs/stacked_line.py +341 -0
  24. sierra/core/graphs/summary_line.py +506 -0
  25. sierra/core/logging.py +3 -2
  26. sierra/core/models/__init__.py +3 -1
  27. sierra/core/models/info.py +19 -0
  28. sierra/core/models/interface.py +52 -122
  29. sierra/core/pipeline/__init__.py +2 -5
  30. sierra/core/pipeline/pipeline.py +228 -126
  31. sierra/core/pipeline/stage1/__init__.py +10 -0
  32. sierra/core/pipeline/stage1/pipeline_stage1.py +45 -31
  33. sierra/core/pipeline/stage2/__init__.py +10 -0
  34. sierra/core/pipeline/stage2/pipeline_stage2.py +8 -11
  35. sierra/core/pipeline/stage2/runner.py +401 -0
  36. sierra/core/pipeline/stage3/__init__.py +12 -0
  37. sierra/core/pipeline/stage3/gather.py +321 -0
  38. sierra/core/pipeline/stage3/pipeline_stage3.py +37 -84
  39. sierra/core/pipeline/stage4/__init__.py +12 -2
  40. sierra/core/pipeline/stage4/pipeline_stage4.py +36 -354
  41. sierra/core/pipeline/stage5/__init__.py +12 -0
  42. sierra/core/pipeline/stage5/pipeline_stage5.py +33 -208
  43. sierra/core/pipeline/yaml.py +48 -0
  44. sierra/core/plugin.py +529 -62
  45. sierra/core/proc.py +11 -0
  46. sierra/core/prod.py +11 -0
  47. sierra/core/ros1/__init__.py +5 -1
  48. sierra/core/ros1/callbacks.py +22 -21
  49. sierra/core/ros1/cmdline.py +59 -88
  50. sierra/core/ros1/generators.py +159 -175
  51. sierra/core/ros1/variables/__init__.py +3 -0
  52. sierra/core/ros1/variables/exp_setup.py +122 -116
  53. sierra/core/startup.py +106 -76
  54. sierra/core/stat_kernels.py +4 -5
  55. sierra/core/storage.py +13 -32
  56. sierra/core/trampoline.py +30 -0
  57. sierra/core/types.py +116 -71
  58. sierra/core/utils.py +103 -106
  59. sierra/core/variables/__init__.py +1 -1
  60. sierra/core/variables/base_variable.py +12 -17
  61. sierra/core/variables/batch_criteria.py +387 -481
  62. sierra/core/variables/builtin.py +135 -0
  63. sierra/core/variables/exp_setup.py +19 -39
  64. sierra/core/variables/population_size.py +72 -76
  65. sierra/core/variables/variable_density.py +44 -68
  66. sierra/core/vector.py +1 -1
  67. sierra/main.py +256 -88
  68. sierra/plugins/__init__.py +119 -0
  69. sierra/plugins/compare/__init__.py +14 -0
  70. sierra/plugins/compare/graphs/__init__.py +19 -0
  71. sierra/plugins/compare/graphs/cmdline.py +120 -0
  72. sierra/plugins/compare/graphs/comparator.py +291 -0
  73. sierra/plugins/compare/graphs/inter_controller.py +531 -0
  74. sierra/plugins/compare/graphs/inter_scenario.py +297 -0
  75. sierra/plugins/compare/graphs/namecalc.py +53 -0
  76. sierra/plugins/compare/graphs/outputroot.py +73 -0
  77. sierra/plugins/compare/graphs/plugin.py +147 -0
  78. sierra/plugins/compare/graphs/preprocess.py +172 -0
  79. sierra/plugins/compare/graphs/schema.py +37 -0
  80. sierra/plugins/engine/__init__.py +14 -0
  81. sierra/plugins/engine/argos/__init__.py +18 -0
  82. sierra/plugins/{platform → engine}/argos/cmdline.py +144 -151
  83. sierra/plugins/{platform/argos/variables → engine/argos/generators}/__init__.py +5 -0
  84. sierra/plugins/engine/argos/generators/engine.py +394 -0
  85. sierra/plugins/engine/argos/plugin.py +393 -0
  86. sierra/plugins/{platform/argos/generators → engine/argos/variables}/__init__.py +5 -0
  87. sierra/plugins/engine/argos/variables/arena_shape.py +183 -0
  88. sierra/plugins/engine/argos/variables/cameras.py +240 -0
  89. sierra/plugins/engine/argos/variables/constant_density.py +112 -0
  90. sierra/plugins/engine/argos/variables/exp_setup.py +82 -0
  91. sierra/plugins/{platform → engine}/argos/variables/physics_engines.py +83 -87
  92. sierra/plugins/engine/argos/variables/population_constant_density.py +178 -0
  93. sierra/plugins/engine/argos/variables/population_size.py +115 -0
  94. sierra/plugins/engine/argos/variables/population_variable_density.py +123 -0
  95. sierra/plugins/engine/argos/variables/rendering.py +108 -0
  96. sierra/plugins/engine/ros1gazebo/__init__.py +18 -0
  97. sierra/plugins/engine/ros1gazebo/cmdline.py +175 -0
  98. sierra/plugins/{platform/ros1robot → engine/ros1gazebo}/generators/__init__.py +5 -0
  99. sierra/plugins/engine/ros1gazebo/generators/engine.py +125 -0
  100. sierra/plugins/engine/ros1gazebo/plugin.py +404 -0
  101. sierra/plugins/engine/ros1gazebo/variables/__init__.py +15 -0
  102. sierra/plugins/engine/ros1gazebo/variables/population_size.py +214 -0
  103. sierra/plugins/engine/ros1robot/__init__.py +18 -0
  104. sierra/plugins/engine/ros1robot/cmdline.py +159 -0
  105. sierra/plugins/{platform/ros1gazebo → engine/ros1robot}/generators/__init__.py +4 -0
  106. sierra/plugins/engine/ros1robot/generators/engine.py +95 -0
  107. sierra/plugins/engine/ros1robot/plugin.py +410 -0
  108. sierra/plugins/{hpc/local → engine/ros1robot/variables}/__init__.py +5 -0
  109. sierra/plugins/engine/ros1robot/variables/population_size.py +146 -0
  110. sierra/plugins/execenv/__init__.py +11 -0
  111. sierra/plugins/execenv/hpc/__init__.py +18 -0
  112. sierra/plugins/execenv/hpc/adhoc/__init__.py +18 -0
  113. sierra/plugins/execenv/hpc/adhoc/cmdline.py +30 -0
  114. sierra/plugins/execenv/hpc/adhoc/plugin.py +131 -0
  115. sierra/plugins/execenv/hpc/cmdline.py +137 -0
  116. sierra/plugins/execenv/hpc/local/__init__.py +18 -0
  117. sierra/plugins/execenv/hpc/local/cmdline.py +31 -0
  118. sierra/plugins/execenv/hpc/local/plugin.py +145 -0
  119. sierra/plugins/execenv/hpc/pbs/__init__.py +18 -0
  120. sierra/plugins/execenv/hpc/pbs/cmdline.py +30 -0
  121. sierra/plugins/execenv/hpc/pbs/plugin.py +121 -0
  122. sierra/plugins/execenv/hpc/slurm/__init__.py +18 -0
  123. sierra/plugins/execenv/hpc/slurm/cmdline.py +30 -0
  124. sierra/plugins/execenv/hpc/slurm/plugin.py +133 -0
  125. sierra/plugins/execenv/prefectserver/__init__.py +18 -0
  126. sierra/plugins/execenv/prefectserver/cmdline.py +66 -0
  127. sierra/plugins/execenv/prefectserver/dockerremote/__init__.py +18 -0
  128. sierra/plugins/execenv/prefectserver/dockerremote/cmdline.py +66 -0
  129. sierra/plugins/execenv/prefectserver/dockerremote/plugin.py +132 -0
  130. sierra/plugins/execenv/prefectserver/flow.py +66 -0
  131. sierra/plugins/execenv/prefectserver/local/__init__.py +18 -0
  132. sierra/plugins/execenv/prefectserver/local/cmdline.py +29 -0
  133. sierra/plugins/execenv/prefectserver/local/plugin.py +133 -0
  134. sierra/plugins/{hpc/adhoc → execenv/robot}/__init__.py +1 -0
  135. sierra/plugins/execenv/robot/turtlebot3/__init__.py +18 -0
  136. sierra/plugins/execenv/robot/turtlebot3/plugin.py +204 -0
  137. sierra/plugins/expdef/__init__.py +14 -0
  138. sierra/plugins/expdef/json/__init__.py +14 -0
  139. sierra/plugins/expdef/json/plugin.py +504 -0
  140. sierra/plugins/expdef/xml/__init__.py +14 -0
  141. sierra/plugins/expdef/xml/plugin.py +386 -0
  142. sierra/{core/hpc → plugins/proc}/__init__.py +1 -1
  143. sierra/plugins/proc/collate/__init__.py +15 -0
  144. sierra/plugins/proc/collate/cmdline.py +47 -0
  145. sierra/plugins/proc/collate/plugin.py +271 -0
  146. sierra/plugins/proc/compress/__init__.py +18 -0
  147. sierra/plugins/proc/compress/cmdline.py +47 -0
  148. sierra/plugins/proc/compress/plugin.py +123 -0
  149. sierra/plugins/proc/decompress/__init__.py +18 -0
  150. sierra/plugins/proc/decompress/plugin.py +96 -0
  151. sierra/plugins/proc/imagize/__init__.py +15 -0
  152. sierra/plugins/proc/imagize/cmdline.py +49 -0
  153. sierra/plugins/proc/imagize/plugin.py +270 -0
  154. sierra/plugins/proc/modelrunner/__init__.py +16 -0
  155. sierra/plugins/proc/modelrunner/plugin.py +250 -0
  156. sierra/plugins/proc/statistics/__init__.py +15 -0
  157. sierra/plugins/proc/statistics/cmdline.py +64 -0
  158. sierra/plugins/proc/statistics/plugin.py +390 -0
  159. sierra/plugins/{hpc → prod}/__init__.py +1 -0
  160. sierra/plugins/prod/graphs/__init__.py +18 -0
  161. sierra/plugins/prod/graphs/cmdline.py +269 -0
  162. sierra/plugins/prod/graphs/collate.py +279 -0
  163. sierra/plugins/prod/graphs/inter/__init__.py +13 -0
  164. sierra/plugins/prod/graphs/inter/generate.py +83 -0
  165. sierra/plugins/prod/graphs/inter/heatmap.py +86 -0
  166. sierra/plugins/prod/graphs/inter/line.py +134 -0
  167. sierra/plugins/prod/graphs/intra/__init__.py +15 -0
  168. sierra/plugins/prod/graphs/intra/generate.py +202 -0
  169. sierra/plugins/prod/graphs/intra/heatmap.py +74 -0
  170. sierra/plugins/prod/graphs/intra/line.py +114 -0
  171. sierra/plugins/prod/graphs/plugin.py +103 -0
  172. sierra/plugins/prod/graphs/targets.py +63 -0
  173. sierra/plugins/prod/render/__init__.py +18 -0
  174. sierra/plugins/prod/render/cmdline.py +72 -0
  175. sierra/plugins/prod/render/plugin.py +282 -0
  176. sierra/plugins/storage/__init__.py +5 -0
  177. sierra/plugins/storage/arrow/__init__.py +18 -0
  178. sierra/plugins/storage/arrow/plugin.py +38 -0
  179. sierra/plugins/storage/csv/__init__.py +9 -0
  180. sierra/plugins/storage/csv/plugin.py +12 -5
  181. sierra/version.py +3 -2
  182. sierra_research-1.5.0.dist-info/METADATA +238 -0
  183. sierra_research-1.5.0.dist-info/RECORD +186 -0
  184. {sierra_research-1.3.11.dist-info → sierra_research-1.5.0.dist-info}/WHEEL +1 -2
  185. sierra/core/experiment/xml.py +0 -454
  186. sierra/core/generators/controller_generator_parser.py +0 -34
  187. sierra/core/generators/exp_creator.py +0 -351
  188. sierra/core/generators/exp_generators.py +0 -142
  189. sierra/core/graphs/scatterplot2D.py +0 -109
  190. sierra/core/graphs/stacked_line_graph.py +0 -251
  191. sierra/core/graphs/stacked_surface_graph.py +0 -220
  192. sierra/core/graphs/summary_line_graph.py +0 -371
  193. sierra/core/hpc/cmdline.py +0 -142
  194. sierra/core/models/graphs.py +0 -87
  195. sierra/core/pipeline/stage2/exp_runner.py +0 -286
  196. sierra/core/pipeline/stage3/imagizer.py +0 -149
  197. sierra/core/pipeline/stage3/run_collator.py +0 -317
  198. sierra/core/pipeline/stage3/statistics_calculator.py +0 -478
  199. sierra/core/pipeline/stage4/graph_collator.py +0 -320
  200. sierra/core/pipeline/stage4/inter_exp_graph_generator.py +0 -240
  201. sierra/core/pipeline/stage4/intra_exp_graph_generator.py +0 -317
  202. sierra/core/pipeline/stage4/model_runner.py +0 -168
  203. sierra/core/pipeline/stage4/rendering.py +0 -283
  204. sierra/core/pipeline/stage4/yaml_config_loader.py +0 -103
  205. sierra/core/pipeline/stage5/inter_scenario_comparator.py +0 -328
  206. sierra/core/pipeline/stage5/intra_scenario_comparator.py +0 -989
  207. sierra/core/platform.py +0 -493
  208. sierra/core/plugin_manager.py +0 -369
  209. sierra/core/root_dirpath_generator.py +0 -241
  210. sierra/plugins/hpc/adhoc/plugin.py +0 -125
  211. sierra/plugins/hpc/local/plugin.py +0 -81
  212. sierra/plugins/hpc/pbs/__init__.py +0 -9
  213. sierra/plugins/hpc/pbs/plugin.py +0 -126
  214. sierra/plugins/hpc/slurm/__init__.py +0 -9
  215. sierra/plugins/hpc/slurm/plugin.py +0 -130
  216. sierra/plugins/platform/__init__.py +0 -9
  217. sierra/plugins/platform/argos/__init__.py +0 -9
  218. sierra/plugins/platform/argos/generators/platform_generators.py +0 -383
  219. sierra/plugins/platform/argos/plugin.py +0 -337
  220. sierra/plugins/platform/argos/variables/arena_shape.py +0 -145
  221. sierra/plugins/platform/argos/variables/cameras.py +0 -243
  222. sierra/plugins/platform/argos/variables/constant_density.py +0 -136
  223. sierra/plugins/platform/argos/variables/exp_setup.py +0 -113
  224. sierra/plugins/platform/argos/variables/population_constant_density.py +0 -175
  225. sierra/plugins/platform/argos/variables/population_size.py +0 -102
  226. sierra/plugins/platform/argos/variables/population_variable_density.py +0 -132
  227. sierra/plugins/platform/argos/variables/rendering.py +0 -104
  228. sierra/plugins/platform/ros1gazebo/__init__.py +0 -9
  229. sierra/plugins/platform/ros1gazebo/cmdline.py +0 -213
  230. sierra/plugins/platform/ros1gazebo/generators/platform_generators.py +0 -137
  231. sierra/plugins/platform/ros1gazebo/plugin.py +0 -335
  232. sierra/plugins/platform/ros1gazebo/variables/__init__.py +0 -10
  233. sierra/plugins/platform/ros1gazebo/variables/population_size.py +0 -204
  234. sierra/plugins/platform/ros1robot/__init__.py +0 -9
  235. sierra/plugins/platform/ros1robot/cmdline.py +0 -175
  236. sierra/plugins/platform/ros1robot/generators/platform_generators.py +0 -112
  237. sierra/plugins/platform/ros1robot/plugin.py +0 -373
  238. sierra/plugins/platform/ros1robot/variables/__init__.py +0 -10
  239. sierra/plugins/platform/ros1robot/variables/population_size.py +0 -146
  240. sierra/plugins/robot/__init__.py +0 -9
  241. sierra/plugins/robot/turtlebot3/__init__.py +0 -9
  242. sierra/plugins/robot/turtlebot3/plugin.py +0 -194
  243. sierra_research-1.3.11.data/data/share/man/man1/sierra-cli.1 +0 -2349
  244. sierra_research-1.3.11.data/data/share/man/man7/sierra-examples.7 +0 -508
  245. sierra_research-1.3.11.data/data/share/man/man7/sierra-exec-envs.7 +0 -331
  246. sierra_research-1.3.11.data/data/share/man/man7/sierra-glossary.7 +0 -285
  247. sierra_research-1.3.11.data/data/share/man/man7/sierra-platforms.7 +0 -358
  248. sierra_research-1.3.11.data/data/share/man/man7/sierra-usage.7 +0 -729
  249. sierra_research-1.3.11.data/data/share/man/man7/sierra.7 +0 -78
  250. sierra_research-1.3.11.dist-info/METADATA +0 -492
  251. sierra_research-1.3.11.dist-info/RECORD +0 -133
  252. sierra_research-1.3.11.dist-info/top_level.txt +0 -1
  253. {sierra_research-1.3.11.dist-info → sierra_research-1.5.0.dist-info}/entry_points.txt +0 -0
  254. {sierra_research-1.3.11.dist-info → sierra_research-1.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,77 @@
1
+ #
2
+ # Copyright 2025 John Harwell, All rights reserved.
3
+ #
4
+ # SPDX-License Identifier: MIT
5
+ #
6
+ """
7
+ YAML schemas for graphs.
8
+ """
9
+ # Core packages
10
+
11
+ # 3rd party packages
12
+ import strictyaml
13
+
14
+ # Project packages
15
+
16
+
17
+ heatmap = strictyaml.Map(
18
+ {
19
+ "src_stem": strictyaml.Str(),
20
+ "dest_stem": strictyaml.Str(),
21
+ "type": strictyaml.Str(),
22
+ strictyaml.Optional("col"): strictyaml.Str(),
23
+ strictyaml.Optional("title"): strictyaml.Str(),
24
+ strictyaml.Optional("zlabel"): strictyaml.Str(),
25
+ strictyaml.Optional("xlabel"): strictyaml.Str(),
26
+ strictyaml.Optional("ylabel"): strictyaml.Str(),
27
+ strictyaml.Optional("index"): strictyaml.Int(),
28
+ strictyaml.Optional("x"): strictyaml.Str(),
29
+ strictyaml.Optional("y"): strictyaml.Str(),
30
+ strictyaml.Optional("z"): strictyaml.Str(),
31
+ strictyaml.Optional("backend"): strictyaml.Str(),
32
+ }
33
+ )
34
+ """
35
+ Schema for :func:`~sierra.core.graphs.heatmap` graphs.
36
+ """
37
+
38
+ stacked_line = strictyaml.Map(
39
+ {
40
+ "src_stem": strictyaml.Str(),
41
+ "dest_stem": strictyaml.Str(),
42
+ "type": strictyaml.Str(),
43
+ # Only optional for intra-exp, but there's not a simple way to mark it
44
+ # as such at this level.
45
+ strictyaml.Optional("cols"): strictyaml.Seq(strictyaml.Str()),
46
+ strictyaml.Optional("title"): strictyaml.Str(),
47
+ strictyaml.Optional("legend"): strictyaml.Seq(strictyaml.Str()),
48
+ strictyaml.Optional("xlabel"): strictyaml.Str(),
49
+ strictyaml.Optional("ylabel"): strictyaml.Str(),
50
+ strictyaml.Optional("points"): strictyaml.Bool(),
51
+ strictyaml.Optional("logy"): strictyaml.Bool(),
52
+ strictyaml.Optional("backend"): strictyaml.Str(),
53
+ }
54
+ )
55
+ """
56
+ Schema for :func:`~sierra.core.graphs.stacked_line` graphs.
57
+ """
58
+
59
+ summary_line = strictyaml.Map(
60
+ {
61
+ "src_stem": strictyaml.Str(),
62
+ "dest_stem": strictyaml.Str(),
63
+ "col": strictyaml.Str(),
64
+ strictyaml.Optional("legend"): strictyaml.Seq(strictyaml.Str()),
65
+ strictyaml.Optional("title"): strictyaml.Str(),
66
+ strictyaml.Optional("type"): strictyaml.Str(),
67
+ strictyaml.Optional("xlabel"): strictyaml.Str(),
68
+ strictyaml.Optional("ylabel"): strictyaml.Str(),
69
+ strictyaml.Optional("points"): strictyaml.Bool(),
70
+ strictyaml.Optional("index"): strictyaml.Int(),
71
+ strictyaml.Optional("logy"): strictyaml.Bool(),
72
+ strictyaml.Optional("backend"): strictyaml.Str(),
73
+ }
74
+ )
75
+ """
76
+ Schema for :func:`~sierra.core.graphs.summary_line` graphs.
77
+ """
@@ -0,0 +1,341 @@
1
+ # Copyright 2018 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """
6
+ Intra-experiment line graph generation classes for stage{4,5}.
7
+ """
8
+
9
+ # Core packages
10
+ import typing as tp
11
+ import logging
12
+ import pathlib
13
+
14
+ # 3rd party packages
15
+ import pandas as pd
16
+ import holoviews as hv
17
+ import matplotlib.pyplot as plt
18
+ import bokeh
19
+
20
+ # Project packages
21
+ from sierra.core import config, utils, storage, models
22
+ from . import pathset
23
+
24
+ _logger = logging.getLogger(__name__)
25
+
26
+
27
+ def _ofile_ext(backend: str) -> tp.Optional[str]:
28
+ if backend == "matplotlib":
29
+ return config.kStaticImageType
30
+ elif backend == "bokeh":
31
+ return config.kInteractiveImageType
32
+
33
+ return None
34
+
35
+
36
+ def generate(
37
+ paths: pathset.PathSet,
38
+ input_stem: str,
39
+ output_stem: str,
40
+ title: str,
41
+ medium: str,
42
+ backend: str,
43
+ xticks: tp.Optional[tp.List[float]] = None,
44
+ stats: str = "none",
45
+ xlabel: tp.Optional[str] = None,
46
+ ylabel: tp.Optional[str] = None,
47
+ points: tp.Optional[bool] = False,
48
+ large_text: bool = False,
49
+ legend: tp.Optional[tp.List[str]] = None,
50
+ xticklabels: tp.Optional[tp.List[str]] = None,
51
+ cols: tp.Optional[tp.List[str]] = None,
52
+ logyscale: bool = False,
53
+ ext: str = config.kStats["mean"].exts["mean"],
54
+ ) -> bool:
55
+ """Generate a line graph from a set of columns in a file.
56
+
57
+ If the necessary data file does not exist, the graph is not generated.
58
+
59
+ If the .stddev file that goes with the .mean does not exist, then no error
60
+ bars are plotted.
61
+
62
+ If the .model file that goes with the .mean does not exist, then no model
63
+ predictions are plotted.
64
+
65
+ Ideally, model predictions/stddev calculations would be in derived classes,
66
+ but I can't figure out a good way to easily pull that stuff out of here.
67
+ """
68
+ hv.extension(backend)
69
+
70
+ input_fpath = paths.input_root / (input_stem + ext)
71
+ output_fpath = paths.output_root / "SLN-{0}.{1}".format(
72
+ output_stem, _ofile_ext(backend)
73
+ )
74
+
75
+ if large_text:
76
+ text_size = config.kGraphs["text_size_large"]
77
+ else:
78
+ text_size = config.kGraphs["text_size_small"]
79
+
80
+ if not utils.path_exists(input_fpath):
81
+ _logger.debug(
82
+ "Not generating <batchroot>/%s: <batchroot>/%s does not exist",
83
+ output_fpath.relative_to(paths.batchroot),
84
+ input_fpath.relative_to(paths.batchroot),
85
+ )
86
+ return False
87
+
88
+ df = storage.df_read(input_fpath, medium)
89
+
90
+ # Use xticks if provided, otherwise default to using the dataframe index as
91
+ # the xticks.
92
+ dfcols = df.columns.tolist()
93
+ df["xticks"] = xticks if xticks is not None else df.index.to_list()
94
+ dataset = hv.Dataset(
95
+ # Make index a column so we can use it as kdim
96
+ data=df.reset_index(),
97
+ kdims=["index"],
98
+ vdims=cols if cols else dfcols,
99
+ )
100
+
101
+ assert len(df.index) == len(
102
+ df["xticks"]
103
+ ), "Length mismatch between xticks,# data points: {0} vs {1}".format(
104
+ len(df["xticks"]), len(df.index)
105
+ )
106
+
107
+ model = _read_models(paths.model_root, input_stem, medium)
108
+ stat_dfs = _read_stats(stats, paths.input_root, input_stem, medium)
109
+
110
+ # Plot stats if they have been computed FIRST, so they appear behind the
111
+ # actual data.
112
+ if "conf95" in stats and "stddev" in stat_dfs:
113
+ plot = _plot_stats_stddev(dataset, stat_dfs["stddev"])
114
+ plot *= _plot_selected_cols(dataset, model, legend, points, backend)
115
+ elif "bw" in stats and all(k in stat_dfs.keys() for k in config.kStats["bw"].exts):
116
+ # 2025-10-06 [JRH]: This is a limitation of hv (I think). Manually
117
+ # specifying bw plots around each datapoint on a graph can easily exceed
118
+ # the max # of things that can be in a single overlay.
119
+ _logger.warning("bw statistics not implemented for stacked_line graphs")
120
+ plot = _plot_selected_cols(dataset, model, legend, points, backend)
121
+ else:
122
+ # Plot specified columns from dataframe.
123
+ plot = _plot_selected_cols(dataset, model, legend, points, backend)
124
+
125
+ # Let the backend decide # of columns; can override with
126
+ # legend_cols=N in the future if desired.
127
+ plot.opts(legend_position="bottom")
128
+
129
+ # Add title
130
+ plot.opts(title=title)
131
+
132
+ # Add X,Y labels
133
+ if xlabel is not None:
134
+ plot.opts(xlabel=xlabel)
135
+
136
+ if ylabel is not None:
137
+ plot.opts(ylabel=ylabel)
138
+
139
+ # Set fontsizes
140
+ plot.opts(
141
+ fontsize={
142
+ "title": text_size["title"],
143
+ "labels": text_size["xyz_label"],
144
+ "ticks": text_size["tick_label"],
145
+ "legend": text_size["legend_label"],
146
+ },
147
+ )
148
+
149
+ if logyscale:
150
+ _min = min(dataset[vdim].min() for vdim in dataset.vdims)
151
+ _max = max(dataset[vdim].max() for vdim in dataset.vdims)
152
+
153
+ plot.opts(
154
+ logy=True,
155
+ ylim=(
156
+ _min * 0.9,
157
+ _max * 1.1,
158
+ ),
159
+ )
160
+
161
+ _save(plot, output_fpath, backend)
162
+ _logger.debug(
163
+ "Graph written to <batchroot>/%s",
164
+ output_fpath.relative_to(paths.batchroot),
165
+ )
166
+ return True
167
+
168
+
169
+ def _save(plot: hv.Overlay, output_fpath: pathlib.Path, backend: str) -> None:
170
+ if backend == "matplotlib":
171
+ hv.save(
172
+ plot.opts(fig_inches=config.kGraphs["base_size"]),
173
+ output_fpath,
174
+ fig=config.kStaticImageType,
175
+ dpi=config.kGraphs["dpi"],
176
+ )
177
+ plt.close("all")
178
+ elif backend == "bokeh":
179
+ fig = hv.render(plot)
180
+ fig.width = int(config.kGraphs["dpi"] * config.kGraphs["base_size"])
181
+ fig.height = int(config.kGraphs["dpi"] * config.kGraphs["base_size"])
182
+ html = bokeh.embed.file_html(fig, resources=bokeh.resources.INLINE)
183
+ with open(output_fpath, "w") as f:
184
+ f.write(html)
185
+
186
+
187
+ def _plot_selected_cols(
188
+ dataset: hv.Dataset,
189
+ model_info: models.ModelInfo,
190
+ legend: tp.List[str],
191
+ show_points: bool,
192
+ backend: str,
193
+ ) -> hv.NdOverlay:
194
+ """
195
+ Plot the selected columns in a dataframe.
196
+ """
197
+ # Always plot the data
198
+ plot = hv.Overlay(
199
+ [
200
+ hv.Curve(
201
+ dataset,
202
+ dataset.kdims[0],
203
+ vdim.name,
204
+ label=legend[dataset.vdims.index(vdim)] if legend else "",
205
+ )
206
+ for vdim in dataset.vdims
207
+ ]
208
+ )
209
+ # Plot the points for each curve if configured to do so, OR if there aren't
210
+ # that many. If you print them and there are a lot, you essentially get
211
+ # really fat lines which doesn't look good.
212
+ plot *= hv.Overlay(
213
+ [
214
+ hv.Points((dataset[dataset.kdims[0]], dataset[v]))
215
+ for v in dataset.vdims
216
+ if len(dataset[v]) <= 50 or show_points
217
+ ]
218
+ )
219
+
220
+ if backend == "matplotlib":
221
+ opts = {
222
+ "linestyle": "--",
223
+ }
224
+ elif backend == "bokeh":
225
+ opts = {"line_dash": [6, 3]}
226
+
227
+ # Plot models if they have been computed
228
+ if model_info.dataset:
229
+ plot *= hv.Overlay(
230
+ [
231
+ hv.Curve(
232
+ model_info.dataset,
233
+ model_info.dataset.kdims[0],
234
+ vdim.name,
235
+ label=model_info.legend[model_info.dataset.vdims.index(vdim)],
236
+ ).opts(**opts)
237
+ for vdim in model_info.dataset.vdims
238
+ ]
239
+ )
240
+ # Plot the points for each curve
241
+ plot *= hv.Overlay(
242
+ [
243
+ hv.Points(
244
+ (
245
+ model_info.dataset[model_info.dataset.kdims[0]],
246
+ model_info.dataset[v],
247
+ )
248
+ )
249
+ for v in model_info.dataset.vdims
250
+ if len(model_info.dataset[v]) <= 50 or show_points
251
+ ]
252
+ )
253
+
254
+ return plot
255
+
256
+
257
+ def _plot_stats_stddev(dataset: hv.Dataset, stddev_df: pd.DataFrame) -> hv.NdOverlay:
258
+ """Plot the stddev for all columns in the dataset."""
259
+ stddevs = pd.DataFrame()
260
+ for c in dataset.vdims:
261
+ stddevs[f"{c}_stddev_l"] = dataset[c] - 2 * stddev_df[c.name].abs()
262
+ stddevs[f"{c}_stddev_u"] = dataset[c] + 2 * stddev_df[c.name].abs()
263
+
264
+ # To plot area between lines, you need to add the stddev columns to the
265
+ # dataset
266
+ dataset.data = pd.concat([dataset.dframe(), stddevs], axis=1)
267
+
268
+ return hv.Overlay(
269
+ [
270
+ hv.Area(
271
+ dataset, vdims=[f"{vdim.name}_stddev_l", f"{vdim.name}_stddev_u"]
272
+ ).opts(
273
+ alpha=0.5,
274
+ )
275
+ for vdim in dataset.vdims
276
+ ]
277
+ )
278
+
279
+
280
+ def _read_stats(
281
+ setting: str, stats_root: pathlib.Path, input_stem: str, medium: str
282
+ ) -> tp.Dict[str, pd.DataFrame]:
283
+ dfs = {} # type: tp.Dict[str, pd.DataFrame]
284
+ settings = []
285
+
286
+ if setting == "none":
287
+ return dfs
288
+
289
+ if setting == "all":
290
+ settings = ["conf95", "bw"]
291
+ else:
292
+ settings = [setting]
293
+
294
+ if setting in settings:
295
+ exts = config.kStats[setting].exts
296
+
297
+ for k in exts:
298
+ ipath = stats_root / (input_stem + exts[k])
299
+ if utils.path_exists(ipath):
300
+ dfs[k] = storage.df_read(ipath, medium)
301
+ else:
302
+ _logger.warning("%s not found for '%s'", exts[k], input_stem)
303
+
304
+ return dfs
305
+
306
+
307
+ # 2024/09/13 [JRH]: The union is for compatability with type checkers in
308
+ # python {3.8,3.11}.
309
+ def _read_models(
310
+ model_root: tp.Optional[pathlib.Path], input_stem: str, medium: str
311
+ ) -> models.ModelInfo:
312
+
313
+ if model_root is None:
314
+ return models.ModelInfo()
315
+
316
+ modelf = model_root / (input_stem + config.kModelsExt["model"])
317
+ legendf = model_root / (input_stem + config.kModelsExt["legend"])
318
+
319
+ if not utils.path_exists(modelf):
320
+ _logger.trace("Model file %s missing for graph", str(modelf))
321
+ return models.ModelInfo()
322
+
323
+ info = models.ModelInfo()
324
+ df = storage.df_read(modelf, medium)
325
+ cols = df.columns.tolist()
326
+
327
+ info.dataset = hv.Dataset(data=df.reset_index(), kdims=["index"], vdims=cols)
328
+
329
+ with utils.utf8open(legendf, "r") as f:
330
+ info.legend = f.read().splitlines()
331
+
332
+ _logger.trace(
333
+ "Loaded model='%s',legend='%s'", # type: ignore
334
+ modelf.relative_to(model_root),
335
+ legendf.relative_to(model_root),
336
+ )
337
+
338
+ return info
339
+
340
+
341
+ __all__ = ["generate"]