sierra-research 1.3.6__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.6.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 -249
  191. sierra/core/graphs/stacked_surface_graph.py +0 -220
  192. sierra/core/graphs/summary_line_graph.py +0 -369
  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 -319
  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.6.data/data/share/man/man1/sierra-cli.1 +0 -2349
  244. sierra_research-1.3.6.data/data/share/man/man7/sierra-examples.7 +0 -488
  245. sierra_research-1.3.6.data/data/share/man/man7/sierra-exec-envs.7 +0 -331
  246. sierra_research-1.3.6.data/data/share/man/man7/sierra-glossary.7 +0 -285
  247. sierra_research-1.3.6.data/data/share/man/man7/sierra-platforms.7 +0 -358
  248. sierra_research-1.3.6.data/data/share/man/man7/sierra-usage.7 +0 -725
  249. sierra_research-1.3.6.data/data/share/man/man7/sierra.7 +0 -78
  250. sierra_research-1.3.6.dist-info/METADATA +0 -500
  251. sierra_research-1.3.6.dist-info/RECORD +0 -133
  252. sierra_research-1.3.6.dist-info/top_level.txt +0 -1
  253. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/entry_points.txt +0 -0
  254. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -2,6 +2,9 @@
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
4
  #
5
+ """
6
+ Heatmap graph generation classes for stage{4,5}.
7
+ """
5
8
 
6
9
  # Core packages
7
10
  import textwrap
@@ -12,149 +15,175 @@ import pathlib
12
15
  # 3rd party packages
13
16
  import numpy as np
14
17
  import matplotlib.pyplot as plt
15
- import mpl_toolkits.axes_grid1
16
- import pandas as pd
18
+ import holoviews as hv
19
+ import bokeh
17
20
 
18
21
  # Project packages
19
22
  from sierra.core import utils, config, storage, types
23
+ from . import pathset as _pathset
24
+
25
+ _logger = logging.getLogger(__name__)
26
+
27
+
28
+ def _ofile_ext(backend: str) -> tp.Optional[str]:
29
+ if backend == "matplotlib":
30
+ return config.kStaticImageType
31
+ elif backend == "bokeh":
32
+ return config.kInteractiveImageType
33
+
34
+ return None
35
+
36
+
37
+ def generate(
38
+ pathset: _pathset.PathSet,
39
+ input_stem: str,
40
+ output_stem: str,
41
+ medium: str,
42
+ title: str,
43
+ backend: str,
44
+ colnames: tp.Tuple[str, str, str] = ("x", "y", "z"),
45
+ xlabel: tp.Optional[str] = "",
46
+ ylabel: tp.Optional[str] = "",
47
+ zlabel: tp.Optional[str] = "",
48
+ large_text: bool = False,
49
+ xticklabels: tp.Optional[tp.List[str]] = None,
50
+ yticklabels: tp.Optional[tp.List[str]] = None,
51
+ transpose: bool = False,
52
+ ext=config.kStats["mean"].exts["mean"],
53
+ ) -> bool:
54
+ """
55
+ Generate a X vs. Y vs. Z heatmap plot of a ``.mean`` file.
20
56
 
57
+ If the necessary ``.mean`` file does not exist, the graph is not generated.
58
+ Dataframe must be constructed with {x,y,z} columns; e.g.::
21
59
 
22
- class Heatmap:
23
- """
24
- Generates a X vs. Y vs. Z heatmap plot of a ``.mean`` file.
60
+ x,y,z
61
+ 0,0,4
62
+ 0,1,5
63
+ 0,2,6
64
+ 0,3,4
65
+ 1,0,4
66
+ 0,1,4
67
+ ...
25
68
 
26
- If the necessary .mean file does not exist, the graph is not generated.
69
+ The ``x``, ``y`` columns are the indices, and the ``z`` column is the value
70
+ in that cell. The names of these columns are configurable.
27
71
 
28
72
  """
29
- @staticmethod
30
- def set_graph_size(df: pd.DataFrame, fig) -> None:
31
- """
32
- Set graph X,Y size based on dataframe dimensions.
33
- """
34
- if len(df.index) > len(df.columns):
35
- xsize = config.kGraphBaseSize
36
- ysize = xsize * float(len(df.index)) / float(len(df.columns))
37
- else:
38
- ysize = config.kGraphBaseSize
39
- xsize = ysize * float(len(df.columns)) / float(len(df.index))
40
-
41
- fig.set_size_inches(xsize, ysize)
42
-
43
- def __init__(self,
44
- input_fpath: pathlib.Path,
45
- output_fpath: pathlib.Path,
46
- title: str,
47
- xlabel: str,
48
- ylabel: str,
49
- zlabel: tp.Optional[str] = None,
50
- large_text: bool = False,
51
- xtick_labels: tp.Optional[tp.List[str]] = None,
52
- ytick_labels: tp.Optional[tp.List[str]] = None,
53
- transpose: bool = False,
54
- interpolation: tp.Optional[str] = None) -> None:
55
- # Required arguments
56
- self.input_fpath = input_fpath
57
- self.output_fpath = output_fpath
58
- self.title = '\n'.join(textwrap.wrap(title, 40))
59
- self.xlabel = xlabel if transpose else ylabel
60
- self.ylabel = ylabel if transpose else xlabel
61
-
62
- # Optional arguments
63
- if large_text:
64
- self.text_size = config.kGraphTextSizeLarge
65
- else:
66
- self.text_size = config.kGraphTextSizeSmall
67
-
68
- self.transpose = transpose
69
- self.zlabel = zlabel
70
-
71
- if interpolation:
72
- self.interpolation = interpolation
73
- else:
74
- self.interpolation = 'nearest'
75
-
76
- if self.transpose:
77
- self.xtick_labels = xtick_labels
78
- self.ytick_labels = ytick_labels
79
- else:
80
- self.xtick_labels = ytick_labels
81
- self.ytick_labels = xtick_labels
82
-
83
- self.logger = logging.getLogger(__name__)
84
-
85
- def generate(self) -> None:
86
- if not utils.path_exists(self.input_fpath):
87
- self.logger.debug(
88
- "Not generating heatmap: %s does not exist", self.input_fpath)
89
- return
90
-
91
- # Read .csv and create raw heatmap pfrom default configuration
92
- data_df = storage.DataFrameReader('storage.csv')(self.input_fpath)
93
- self._plot_df(data_df, self.output_fpath)
94
-
95
- def _plot_df(self, df: pd.DataFrame, opath: pathlib.Path) -> None:
96
- """
97
- Given a dataframe read from a file, plot it as a heatmap.
98
- """
99
- fig, ax = plt.subplots(figsize=(config.kGraphBaseSize,
100
- config.kGraphBaseSize))
101
-
102
- # Transpose if requested
103
- if self.transpose:
104
- df = df.transpose()
105
-
106
- # Plot heatmap
107
- plt.imshow(df, interpolation=self.interpolation, aspect='auto')
108
-
109
- # Add labels
110
- plt.xlabel(self.xlabel, fontsize=self.text_size['xyz_label'])
111
- plt.ylabel(self.ylabel, fontsize=self.text_size['xyz_label'])
112
-
113
- # Add X,Y ticks
114
- self._plot_ticks(ax)
115
-
116
- # Add graph title
117
- plt.title(self.title, fontsize=self.text_size['title'])
118
-
119
- # Add colorbar
120
- self._plot_colorbar(ax)
121
-
122
- # Output figure
123
- self.set_graph_size(df, fig)
124
- fig = ax.get_figure()
125
-
126
- fig.savefig(opath, bbox_inches='tight', dpi=config.kGraphDPI)
127
- # Prevent memory accumulation (fig.clf() does not close everything)
128
- plt.close(fig)
129
-
130
- def _plot_colorbar(self, ax) -> None:
131
- """
132
- Put the Z-axis colorbar on the plot.
133
- """
134
- divider = mpl_toolkits.axes_grid1.make_axes_locatable(ax)
135
- cax = divider.append_axes('right', size='5%', pad=0.05)
136
- bar = plt.colorbar(cax=cax)
137
-
138
- if self.zlabel is not None:
139
- bar.ax.set_ylabel(self.zlabel)
140
-
141
- def _plot_ticks(self, ax) -> None:
142
- """
143
- Plot X,Y ticks and their corresponding labels.
144
- """
145
- ax.tick_params(labelsize=self.text_size['tick_label'])
146
-
147
- if self.xtick_labels is not None:
148
- ax.set_xticks(np.arange(len(self.xtick_labels)))
149
- ax.set_xticklabels(self.xtick_labels, rotation='vertical')
150
-
151
- if self.ytick_labels is not None:
152
- ax.set_yticks(np.arange(len(self.ytick_labels)))
153
- ax.set_yticklabels(self.ytick_labels)
154
-
155
-
156
- class DualHeatmap:
157
- """Generates a side-by-side plot of two heataps from two CSV files.
73
+ hv.extension(backend)
74
+
75
+ input_fpath = pathset.input_root / (input_stem + ext)
76
+ output_fpath = pathset.output_root / "HM-{0}.{1}".format(
77
+ output_stem, _ofile_ext(backend)
78
+ )
79
+ if not utils.path_exists(input_fpath):
80
+ _logger.debug(
81
+ "Not generating <batchroot>/%s: <batchroot>/%s does not exist",
82
+ output_fpath.relative_to(pathset.batchroot.resolve()),
83
+ input_fpath.relative_to(pathset.batchroot.resolve()),
84
+ )
85
+ return False
86
+
87
+ title = "\n".join(textwrap.wrap(title, 40))
88
+
89
+ if large_text:
90
+ text_size = config.kGraphs["text_size_large"]
91
+ else:
92
+ text_size = config.kGraphs["text_size_small"]
93
+
94
+ # Read .csv and create raw heatmap from default configuration
95
+ df = storage.df_read(input_fpath, medium)
96
+ dataset = hv.Dataset(df, kdims=[colnames[0], colnames[1]], vdims=colnames[2])
97
+
98
+ # Transpose if requested
99
+ if transpose:
100
+ dataset.data = dataset.data.transpose()
101
+
102
+ # Plot heatmap, without showing the Z-value in each cell, which generally
103
+ # obscures things more than it helps. Plus, statistical significance isn't
104
+ # observable from a heatmap, so numerical values are kind of moot.
105
+ if backend == "matplotlib":
106
+ plot = hv.HeatMap(
107
+ dataset, kdims=[colnames[0], colnames[1]], vdims=[colnames[2]]
108
+ ).opts(show_values=False)
109
+ elif backend == "bokeh":
110
+ plot = hv.HeatMap(
111
+ dataset, kdims=[colnames[0], colnames[1]], vdims=[colnames[2]]
112
+ )
113
+
114
+ xticks = dataset.data[colnames[0]]
115
+ yticks = dataset.data[colnames[1]]
116
+
117
+ # Add X,Y ticks
118
+ if xticklabels:
119
+ plot.opts(xformatter=lambda x: xticklabels[xticks.index(x)])
120
+ if yticklabels:
121
+ plot.opts(yformatter=lambda y: yticklabels[yticks.index(y)])
122
+
123
+ # Add labels
124
+ plot.opts(xlabel=xlabel)
125
+ plot.opts(ylabel=ylabel)
126
+
127
+ # Set fontsizes
128
+ plot.opts(
129
+ fontsize={
130
+ "title": text_size["title"],
131
+ "labels": text_size["xyz_label"],
132
+ "ticks": text_size["tick_label"],
133
+ }
134
+ )
135
+
136
+ # Add title
137
+ plot.opts(title=title)
138
+
139
+ if backend == "matplotlib":
140
+ # Add colorbar.
141
+ # 2025-07-08 [JRH]: backend_opts is a mpl-specific Workaround; doing
142
+ # colorbar_opts={"label": ...} doesn't work for unknown reasons.
143
+ plot.opts(colorbar=True, backend_opts={"colorbar.label": zlabel})
144
+
145
+ _save(plot, output_fpath, backend)
146
+
147
+ _logger.debug(
148
+ "Graph written to <batchroot>/%s",
149
+ output_fpath.relative_to(pathset.batchroot),
150
+ )
151
+ return True
152
+
153
+
154
+ def _save(plot: hv.Overlay, output_fpath: pathlib.Path, backend: str) -> None:
155
+ if backend == "matplotlib":
156
+ hv.save(
157
+ plot.opts(fig_inches=config.kGraphs["base_size"]),
158
+ output_fpath,
159
+ fig=config.kStaticImageType,
160
+ dpi=config.kGraphs["dpi"],
161
+ )
162
+ plt.close("all")
163
+
164
+ elif backend == "bokeh":
165
+ fig = hv.render(plot)
166
+ fig.width = int(config.kGraphs["dpi"] * config.kGraphs["base_size"])
167
+ fig.height = int(config.kGraphs["dpi"] * config.kGraphs["base_size"])
168
+ html = bokeh.embed.file_html(fig, resources=bokeh.resources.INLINE)
169
+ with open(output_fpath, "w") as f:
170
+ f.write(html)
171
+
172
+
173
+ def generate2(
174
+ pathset: _pathset.PathSet,
175
+ ipaths: types.PathList,
176
+ output_stem: pathlib.Path,
177
+ medium: str,
178
+ title: str,
179
+ xlabel: tp.Optional[str] = None,
180
+ ylabel: tp.Optional[str] = None,
181
+ zlabel: tp.Optional[str] = None,
182
+ large_text: bool = False,
183
+ xticklabels: tp.Optional[tp.List[str]] = None,
184
+ yticklabels: tp.Optional[tp.List[str]] = None,
185
+ ) -> bool:
186
+ """Generate a side-by-side plot of two heataps from two CSV files.
158
187
 
159
188
  ``.mean`` files must be named as ``<input_stem_fpath>_X.mean``, where `X` is
160
189
  non-negative integer. Input ``.mean`` files must be 2D grids of the same
@@ -165,192 +194,84 @@ class DualHeatmap:
165
194
  If there are not exactly two file paths passed, the graph is not generated.
166
195
 
167
196
  """
168
- kCardinality = 2
169
-
170
- def __init__(self,
171
- ipaths: types.PathList,
172
- output_fpath: pathlib.Path,
173
- title: str,
174
- xlabel: tp.Optional[str] = None,
175
- ylabel: tp.Optional[str] = None,
176
- zlabel: tp.Optional[str] = None,
177
- large_text: bool = False,
178
- xtick_labels: tp.Optional[tp.List[str]] = None,
179
- ytick_labels: tp.Optional[tp.List[str]] = None,
180
- legend: tp.Optional[tp.List[str]] = None) -> None:
181
- self.ipaths = ipaths
182
- self.output_fpath = output_fpath
183
- self.title = title
184
-
185
- self.legend = legend
186
- self.xlabel = xlabel
187
- self.ylabel = ylabel
188
- self.zlabel = zlabel
189
-
190
- self.xtick_labels = xtick_labels
191
- self.ytick_labels = ytick_labels
192
-
193
- # Optional arguments
194
- if large_text:
195
- self.text_size = config.kGraphTextSizeLarge
196
- else:
197
- self.text_size = config.kGraphTextSizeSmall
198
-
199
- self.logger = logging.getLogger(__name__)
200
-
201
- def generate(self) -> None:
202
- reader = storage.DataFrameReader('storage.csv')
203
- dfs = [reader(f) for f in self.ipaths]
204
-
205
- if not dfs or len(dfs) != DualHeatmap.kCardinality:
206
- self.logger.debug(("Not generating dual heatmap: wrong # files "
207
- "(must be %s"),
208
- DualHeatmap.kCardinality)
209
- return
210
-
211
- # Scaffold graph. We can use either dataframe for setting the graph
212
- # size; we assume they have the same dimensions.
213
- #
214
- fig, axes = plt.subplots(ncols=2)
215
- Heatmap.set_graph_size(dfs[0], fig)
216
-
217
- y = np.arange(len(dfs[0].columns))
218
- x = dfs[0].index
219
- ax1, ax2 = axes
220
-
221
- # Find min, max so the shared colorbar makes sense
222
- minval = min(dfs[0].min().min(), dfs[1].min().min())
223
- maxval = max(dfs[0].max().max(), dfs[1].max().max())
224
-
225
- # Plot heatmaps
226
- im1 = ax1.matshow(dfs[0],
227
- interpolation='none',
228
- vmin=minval,
229
- vmax=maxval)
230
- im2 = ax2.matshow(dfs[1],
231
- interpolation='none',
232
- vmin=minval,
233
- vmax=maxval)
234
-
235
- # Add titles
236
- fig.suptitle(self.title, fontsize=self.text_size['title'])
237
- ax1.xaxis.set_ticks_position('bottom')
238
- ax1.yaxis.set_ticks_position('left')
239
- ax2.xaxis.set_ticks_position('bottom')
240
- ax2.yaxis.set_ticks_position('left')
241
-
242
- if self.legend is not None:
243
- ax1.set_title("\n".join(textwrap.wrap(self.legend[0], 20)),
244
- size=self.text_size['legend_label'])
245
- ax2.set_title("\n".join(textwrap.wrap(self.legend[1], 20)),
246
- size=self.text_size['legend_label'])
247
-
248
- # Add colorbar.
249
- #
250
- # Add, then remove the colorbar for the heatmap on the left so that they
251
- # both end up the same size. Not pythonic, but it works.
252
- self._plot_colorbar(fig, im1, ax1, remove=True)
253
- self._plot_colorbar(fig, im2, ax2, remove=False)
254
-
255
- # Add X,Y,Z labels:
256
- #
257
- # - X labels are needed on both heatmaps.
258
- # - Y label only needed on left heatmap.
259
- self._plot_labels(ax1, xlabel=True, ylabel=True)
260
- self._plot_labels(ax2, xlabel=True, ylabel=False)
261
-
262
- # Add X,Y ticks:
263
- #
264
- # - X tick labels needed on both heatmaps
265
- # - Y tick labels only needed on left heatmap.
266
- self._plot_ticks(ax1, x, y, xlabels=True, ylabels=True)
267
- self._plot_ticks(ax2, x, y, xlabels=True, ylabels=False)
268
-
269
- # Output figures
270
- fig.subplots_adjust(wspace=0.0, hspace=0.0)
271
- fig.savefig(self.output_fpath, bbox_inches='tight',
272
- dpi=config.kGraphDPI)
273
- # Prevent memory accumulation (fig.clf() does not close everything)
274
- plt.close(fig)
275
-
276
- def _plot_colorbar(self, fig, im, ax, remove: bool) -> None:
277
- """
278
- Plot the Z-axis color bar on the dual heatmap.
279
- """
280
- divider = mpl_toolkits.axes_grid1.make_axes_locatable(ax)
281
- cax = divider.append_axes('right', size='5%', pad=0.05)
282
-
283
- bar = fig.colorbar(im, cax=cax)
284
- if remove:
285
- fig.delaxes(fig.axes[2])
286
-
287
- # p0 = axes[0].get_position().get_points().flatten()
288
- # p1 = axes[1].get_position().get_points().flatten()
289
- # ax_cbar = fig.add_axes([p0[0], , p1[2] - p0[0], 0.05])
290
- # bar = fig.colorbar(im, cax=ax_cbar, orientation='horizontal')
291
-
292
- if self.zlabel is not None:
293
- bar.ax.set_ylabel(self.zlabel, fontsize=self.text_size['xyz_label'])
294
-
295
- def _plot_ticks(self, ax, xvals, yvals, xlabels: bool, ylabels: bool) -> None:
296
- """Plot ticks and tick labels.
297
-
298
- If the labels are numerical and the numbers are too large, force
299
- scientific notation (the ``rcParam`` way of doing this does not seem to
300
- work...)
301
-
302
- """
303
- ax.tick_params(labelsize=self.text_size['tick_label'])
304
-
305
- if xlabels:
306
- ax.set_xticks(yvals)
307
- ax.set_xticklabels(self.ytick_labels, rotation='vertical')
308
- else:
309
- ax.set_xticks([])
310
- ax.set_xticklabels([])
311
-
312
- if ylabels:
313
- ax.set_yticks(xvals)
314
- ax.set_yticklabels(self.xtick_labels, rotation='horizontal')
315
- else:
316
- ax.set_yticks([])
317
- ax.set_yticklabels([])
318
-
319
- def _plot_labels(self, ax, xlabel: bool, ylabel: bool) -> None:
320
- """
321
- Plot X,Y axis labels.
322
- """
323
- if xlabel:
324
- ax.set_xlabel(self.ylabel, fontsize=self.text_size['xyz_label'])
325
-
326
- if ylabel:
327
- ax.set_ylabel(self.xlabel, fontsize=self.text_size['xyz_label'])
328
-
329
-
330
- class HeatmapSet():
331
- """
332
- Generates a :class:`Heatmap` plot for each of the specified I/O path pairs.
333
- """
334
-
335
- def __init__(self,
336
- ipaths: types.PathList,
337
- opaths: types.PathList,
338
- titles: tp.List[str],
339
- **kwargs) -> None:
340
- self.ipaths = ipaths
341
- self.opaths = opaths
342
- self.titles = titles
343
- self.kwargs = kwargs
344
-
345
- def generate(self) -> None:
346
- for ipath, opath, title in zip(self.ipaths, self.opaths, self.titles):
347
- hm = Heatmap(input_fpath=ipath, output_fpath=opath,
348
- title=title, **self.kwargs)
349
- hm.generate()
350
-
351
-
352
- __api__ = [
353
- 'Heatmap',
354
- 'DualHeatmap',
355
- 'HeatmapSet'
356
- ]
197
+ hv.extension("matplotlib")
198
+
199
+ output_fpath = pathset.output_root / "HM-{0}.{1}".format(
200
+ output_stem, config.kStaticImageType
201
+ )
202
+ # Optional arguments
203
+ if large_text:
204
+ text_size = config.kGraphs["text_size_large"]
205
+ else:
206
+ text_size = config.kGraphs["text_size_small"]
207
+
208
+ dfs = [storage.df_read(f, medium) for f in ipaths]
209
+
210
+ if not dfs or len(dfs) != 2:
211
+ _logger.debug(
212
+ ("Not generating dual heatmap: wrong # files %s (must be 2)"), len(dfs)
213
+ )
214
+ return False
215
+
216
+ # Scaffold graph. We can use either dataframe for setting the graph
217
+ # size; we assume they have the same dimensions.
218
+ #
219
+ # fig, axes = plt.subplots(nrows=1, ncols=2)
220
+ # DualHeatmap.set_graph_size(dfs[0], fig)
221
+
222
+ yticks = np.arange(len(dfs[0].columns))
223
+ xticks = dfs[0].index
224
+
225
+ # Plot heatmaps
226
+ plot = hv.Image(dfs[0]) + hv.Image(dfs[1])
227
+
228
+ # Add X,Y ticks
229
+ if xticklabels:
230
+ plot.opts(xformatter=lambda x: xticklabels[xticks.index(x)])
231
+ if yticklabels:
232
+ plot.opts(yformatter=lambda y: yticklabels[yticks.index(y)])
233
+
234
+ # Add labels
235
+ plot.opts(xlabel=xlabel)
236
+ plot.opts(ylabel=ylabel)
237
+
238
+ # Set fontsizes
239
+ plot.opts(
240
+ fontsize={
241
+ "title": text_size["title"],
242
+ "labels": text_size["xyz_label"],
243
+ "ticks": text_size["tick_label"],
244
+ }
245
+ )
246
+ # Add title
247
+ plot.opts(title=title)
248
+
249
+ # Add colorbar.
250
+ plot.opts(
251
+ hv.opts.Layout(shared_axes=False),
252
+ hv.opts.Image(
253
+ colorbar=True,
254
+ colorbar_position="right",
255
+ backend_opts={"colorbar.label": zlabel},
256
+ ),
257
+ )
258
+
259
+ # Output figures
260
+ plot.opts(fig_inches=config.kGraphs["base_size"])
261
+
262
+ hv.save(
263
+ plot,
264
+ output_fpath,
265
+ fig=config.kStaticImageType,
266
+ dpi=config.kGraphs["dpi"],
267
+ )
268
+ plt.close("all")
269
+
270
+ _logger.debug(
271
+ "Graph written to <batchroot>/%s",
272
+ output_fpath.relative_to(pathset.batchroot),
273
+ )
274
+ return True
275
+
276
+
277
+ __all__ = ["generate", "generate2"]
@@ -0,0 +1,27 @@
1
+ #
2
+ # Copyright 2025 John Harwell, All rights reserved.
3
+ #
4
+ # SPDX-License Identifier: MIT
5
+ #
6
+
7
+ # Core packages
8
+ from dataclasses import dataclass
9
+ import pathlib
10
+ import typing as tp
11
+
12
+ # 3rd party packages
13
+
14
+ # Project packages
15
+
16
+
17
+ @dataclass
18
+ class PathSet:
19
+ """The set of paths relevant/needed when creating graphs."""
20
+
21
+ input_root: pathlib.Path
22
+ output_root: pathlib.Path
23
+ batchroot: pathlib.Path
24
+ model_root: tp.Optional[pathlib.Path]
25
+
26
+
27
+ __all__ = ["PathSet"]