mlonmcu 0.3.0.dev0__3-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 (376) hide show
  1. mlonmcu/__init__.py +26 -0
  2. mlonmcu/artifact.py +172 -0
  3. mlonmcu/cli/__init__.py +19 -0
  4. mlonmcu/cli/build.py +119 -0
  5. mlonmcu/cli/cleanup.py +75 -0
  6. mlonmcu/cli/common.py +227 -0
  7. mlonmcu/cli/compile.py +87 -0
  8. mlonmcu/cli/env.py +68 -0
  9. mlonmcu/cli/export.py +93 -0
  10. mlonmcu/cli/flow.py +67 -0
  11. mlonmcu/cli/helper/__init__.py +18 -0
  12. mlonmcu/cli/helper/filter.py +25 -0
  13. mlonmcu/cli/helper/parse.py +159 -0
  14. mlonmcu/cli/init.py +130 -0
  15. mlonmcu/cli/load.py +87 -0
  16. mlonmcu/cli/main.py +116 -0
  17. mlonmcu/cli/models.py +48 -0
  18. mlonmcu/cli/run.py +56 -0
  19. mlonmcu/cli/setup.py +136 -0
  20. mlonmcu/cli/tune.py +48 -0
  21. mlonmcu/config.py +219 -0
  22. mlonmcu/context/__init__.py +22 -0
  23. mlonmcu/context/context.py +545 -0
  24. mlonmcu/context/read_write_filelock.py +327 -0
  25. mlonmcu/environment/__init__.py +18 -0
  26. mlonmcu/environment/config.py +214 -0
  27. mlonmcu/environment/environment.py +501 -0
  28. mlonmcu/environment/init.py +191 -0
  29. mlonmcu/environment/list.py +81 -0
  30. mlonmcu/environment/loader.py +250 -0
  31. mlonmcu/environment/templates.py +69 -0
  32. mlonmcu/environment/writer.py +117 -0
  33. mlonmcu/feature/__init__.py +18 -0
  34. mlonmcu/feature/feature.py +230 -0
  35. mlonmcu/feature/features.py +1537 -0
  36. mlonmcu/feature/type.py +33 -0
  37. mlonmcu/flow/__init__.py +61 -0
  38. mlonmcu/flow/backend.py +234 -0
  39. mlonmcu/flow/framework.py +69 -0
  40. mlonmcu/flow/tflm/__init__.py +23 -0
  41. mlonmcu/flow/tflm/backend/__init__.py +23 -0
  42. mlonmcu/flow/tflm/backend/backend.py +47 -0
  43. mlonmcu/flow/tflm/backend/tflmc.py +129 -0
  44. mlonmcu/flow/tflm/backend/tflmi.py +385 -0
  45. mlonmcu/flow/tflm/framework.py +81 -0
  46. mlonmcu/flow/tvm/__init__.py +23 -0
  47. mlonmcu/flow/tvm/backend/__init__.py +26 -0
  48. mlonmcu/flow/tvm/backend/backend.py +282 -0
  49. mlonmcu/flow/tvm/backend/model_info.py +307 -0
  50. mlonmcu/flow/tvm/backend/python_utils.py +36 -0
  51. mlonmcu/flow/tvm/backend/tuner.py +41 -0
  52. mlonmcu/flow/tvm/backend/tvmaot.py +162 -0
  53. mlonmcu/flow/tvm/backend/tvmaotplus.py +51 -0
  54. mlonmcu/flow/tvm/backend/tvmc_utils.py +172 -0
  55. mlonmcu/flow/tvm/backend/tvmcg.py +102 -0
  56. mlonmcu/flow/tvm/backend/tvmllvm.py +106 -0
  57. mlonmcu/flow/tvm/backend/tvmrt.py +151 -0
  58. mlonmcu/flow/tvm/backend/wrapper.py +645 -0
  59. mlonmcu/flow/tvm/framework.py +96 -0
  60. mlonmcu/logging.py +79 -0
  61. mlonmcu/mlonmcu.py +19 -0
  62. mlonmcu/models/__init__.py +31 -0
  63. mlonmcu/models/convert_data.py +107 -0
  64. mlonmcu/models/frontend.py +571 -0
  65. mlonmcu/models/group.py +29 -0
  66. mlonmcu/models/lookup.py +345 -0
  67. mlonmcu/models/metadata.py +133 -0
  68. mlonmcu/models/model.py +104 -0
  69. mlonmcu/models/options.py +64 -0
  70. mlonmcu/models/utils.py +136 -0
  71. mlonmcu/platform/__init__.py +27 -0
  72. mlonmcu/platform/_platform.py +46 -0
  73. mlonmcu/platform/espidf/__init__.py +25 -0
  74. mlonmcu/platform/espidf/espidf.py +489 -0
  75. mlonmcu/platform/espidf/espidf_target.py +257 -0
  76. mlonmcu/platform/lookup.py +126 -0
  77. mlonmcu/platform/microtvm/__init__.py +25 -0
  78. mlonmcu/platform/microtvm/microtvm.py +580 -0
  79. mlonmcu/platform/microtvm/microtvm_arduino_target.py +68 -0
  80. mlonmcu/platform/microtvm/microtvm_backend.py +64 -0
  81. mlonmcu/platform/microtvm/microtvm_espidf_target.py +94 -0
  82. mlonmcu/platform/microtvm/microtvm_etissvp_target.py +98 -0
  83. mlonmcu/platform/microtvm/microtvm_host_target.py +46 -0
  84. mlonmcu/platform/microtvm/microtvm_spike_target.py +230 -0
  85. mlonmcu/platform/microtvm/microtvm_target.py +189 -0
  86. mlonmcu/platform/microtvm/microtvm_template_target.py +55 -0
  87. mlonmcu/platform/microtvm/microtvm_zephyr_target.py +91 -0
  88. mlonmcu/platform/mlif/__init__.py +25 -0
  89. mlonmcu/platform/mlif/mlif.py +300 -0
  90. mlonmcu/platform/mlif/mlif_target.py +86 -0
  91. mlonmcu/platform/platform.py +244 -0
  92. mlonmcu/platform/tvm/__init__.py +25 -0
  93. mlonmcu/platform/tvm/tvm.py +429 -0
  94. mlonmcu/platform/tvm/tvm_backend.py +57 -0
  95. mlonmcu/platform/tvm/tvm_target.py +165 -0
  96. mlonmcu/platform/zephyr/__init__.py +25 -0
  97. mlonmcu/platform/zephyr/zephyr.py +450 -0
  98. mlonmcu/platform/zephyr/zephyr_target.py +110 -0
  99. mlonmcu/plugins.py +26 -0
  100. mlonmcu/report.py +99 -0
  101. mlonmcu/session/__init__.py +18 -0
  102. mlonmcu/session/postprocess/__init__.py +43 -0
  103. mlonmcu/session/postprocess/postprocess.py +62 -0
  104. mlonmcu/session/postprocess/postprocesses.py +487 -0
  105. mlonmcu/session/run.py +1008 -0
  106. mlonmcu/session/session.py +373 -0
  107. mlonmcu/setup/__init__.py +18 -0
  108. mlonmcu/setup/cache.py +147 -0
  109. mlonmcu/setup/gen_requirements.py +619 -0
  110. mlonmcu/setup/setup.py +285 -0
  111. mlonmcu/setup/task.py +380 -0
  112. mlonmcu/setup/tasks/__init__.py +40 -0
  113. mlonmcu/setup/tasks/arm_gcc.py +58 -0
  114. mlonmcu/setup/tasks/cmsisnn.py +151 -0
  115. mlonmcu/setup/tasks/common.py +36 -0
  116. mlonmcu/setup/tasks/corstone300.py +64 -0
  117. mlonmcu/setup/tasks/espidf.py +87 -0
  118. mlonmcu/setup/tasks/etiss.py +147 -0
  119. mlonmcu/setup/tasks/llvm.py +67 -0
  120. mlonmcu/setup/tasks/mlif.py +49 -0
  121. mlonmcu/setup/tasks/muriscvnn.py +166 -0
  122. mlonmcu/setup/tasks/pulp.py +192 -0
  123. mlonmcu/setup/tasks/riscv_gcc.py +146 -0
  124. mlonmcu/setup/tasks/spike.py +190 -0
  125. mlonmcu/setup/tasks/tf.py +92 -0
  126. mlonmcu/setup/tasks/tflite.py +59 -0
  127. mlonmcu/setup/tasks/tflmc.py +141 -0
  128. mlonmcu/setup/tasks/tvm.py +172 -0
  129. mlonmcu/setup/tasks/utvmcg.py +96 -0
  130. mlonmcu/setup/tasks/zephyr.py +119 -0
  131. mlonmcu/setup/utils.py +374 -0
  132. mlonmcu/target/__init__.py +37 -0
  133. mlonmcu/target/_target.py +41 -0
  134. mlonmcu/target/arm/__init__.py +3 -0
  135. mlonmcu/target/arm/corstone300.py +235 -0
  136. mlonmcu/target/arm/util.py +109 -0
  137. mlonmcu/target/common.py +194 -0
  138. mlonmcu/target/elf.py +256 -0
  139. mlonmcu/target/host_x86.py +84 -0
  140. mlonmcu/target/metrics.py +75 -0
  141. mlonmcu/target/riscv/__init__.py +7 -0
  142. mlonmcu/target/riscv/etiss_pulpino.py +462 -0
  143. mlonmcu/target/riscv/gvsoc_pulp.py +215 -0
  144. mlonmcu/target/riscv/ovpsim.py +301 -0
  145. mlonmcu/target/riscv/riscv.py +185 -0
  146. mlonmcu/target/riscv/riscv_qemu.py +166 -0
  147. mlonmcu/target/riscv/spike.py +243 -0
  148. mlonmcu/target/riscv/util.py +134 -0
  149. mlonmcu/target/target.py +200 -0
  150. mlonmcu/utils.py +49 -0
  151. mlonmcu/version.py +21 -0
  152. mlonmcu-0.3.0.dev0.dist-info/LICENSE +202 -0
  153. mlonmcu-0.3.0.dev0.dist-info/METADATA +343 -0
  154. mlonmcu-0.3.0.dev0.dist-info/NOTICE +8 -0
  155. mlonmcu-0.3.0.dev0.dist-info/RECORD +376 -0
  156. mlonmcu-0.3.0.dev0.dist-info/WHEEL +5 -0
  157. mlonmcu-0.3.0.dev0.dist-info/entry_points.txt +3 -0
  158. mlonmcu-0.3.0.dev0.dist-info/top_level.txt +1 -0
  159. resources/frameworks/tvm/crt_config/README.md +7 -0
  160. resources/frameworks/tvm/crt_config/crt_config.h +53 -0
  161. resources/frameworks/tvm/tuning_records/riscv_cpu_v0.01.log.best +46 -0
  162. resources/patches/fix_esp32c3_march.patch +20 -0
  163. resources/patches/pulp_freertos_support.patch +55 -0
  164. resources/platforms/espidf/micro_speech_esp32devboard/CMakeLists.txt +29 -0
  165. resources/platforms/espidf/micro_speech_esp32devboard/README.md +70 -0
  166. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/CMakeLists.txt +78 -0
  167. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/Kconfig +76 -0
  168. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/default_data.c +26 -0
  169. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/default_model_support/process_input.c +35 -0
  170. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/default_model_support/process_output.c +45 -0
  171. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/include/exit.h +26 -0
  172. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/include/ml_interface.h +70 -0
  173. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/include/printing.h +31 -0
  174. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/ml_interface.c +74 -0
  175. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/ml_interface_tflm.cc +64 -0
  176. resources/platforms/espidf/micro_speech_esp32devboard/components/mlif2/ml_interface_tvm.c +53 -0
  177. resources/platforms/espidf/micro_speech_esp32devboard/components/tflite-lib/CMakeLists.txt +140 -0
  178. resources/platforms/espidf/micro_speech_esp32devboard/components/tflite-lib/Kconfig +8 -0
  179. resources/platforms/espidf/micro_speech_esp32devboard/components/tvm-lib/CMakeLists.txt +66 -0
  180. resources/platforms/espidf/micro_speech_esp32devboard/components/tvm-lib/Kconfig +7 -0
  181. resources/platforms/espidf/micro_speech_esp32devboard/example_test.py +38 -0
  182. resources/platforms/espidf/micro_speech_esp32devboard/info.txt +9 -0
  183. resources/platforms/espidf/micro_speech_esp32devboard/main/CMakeLists.txt +58 -0
  184. resources/platforms/espidf/micro_speech_esp32devboard/main/audio_provider.cc +193 -0
  185. resources/platforms/espidf/micro_speech_esp32devboard/main/audio_provider.h +49 -0
  186. resources/platforms/espidf/micro_speech_esp32devboard/main/command_responder.cc +53 -0
  187. resources/platforms/espidf/micro_speech_esp32devboard/main/command_responder.h +35 -0
  188. resources/platforms/espidf/micro_speech_esp32devboard/main/feature_provider.cc +132 -0
  189. resources/platforms/espidf/micro_speech_esp32devboard/main/feature_provider.cc.orig +145 -0
  190. resources/platforms/espidf/micro_speech_esp32devboard/main/feature_provider.h +55 -0
  191. resources/platforms/espidf/micro_speech_esp32devboard/main/kissfft/COPYING +11 -0
  192. resources/platforms/espidf/micro_speech_esp32devboard/main/kissfft/_kiss_fft_guts.h +168 -0
  193. resources/platforms/espidf/micro_speech_esp32devboard/main/kissfft/kiss_fft.c +408 -0
  194. resources/platforms/espidf/micro_speech_esp32devboard/main/kissfft/kiss_fft.h +124 -0
  195. resources/platforms/espidf/micro_speech_esp32devboard/main/kissfft/tools/kiss_fftr.c +159 -0
  196. resources/platforms/espidf/micro_speech_esp32devboard/main/kissfft/tools/kiss_fftr.h +46 -0
  197. resources/platforms/espidf/micro_speech_esp32devboard/main/main.cc +63 -0
  198. resources/platforms/espidf/micro_speech_esp32devboard/main/main_functions.cc +204 -0
  199. resources/platforms/espidf/micro_speech_esp32devboard/main/main_functions.h +37 -0
  200. resources/platforms/espidf/micro_speech_esp32devboard/main/micro_features_generator.cc +121 -0
  201. resources/platforms/espidf/micro_speech_esp32devboard/main/micro_features_generator.h +38 -0
  202. resources/platforms/espidf/micro_speech_esp32devboard/main/micro_model_settings.cc +23 -0
  203. resources/platforms/espidf/micro_speech_esp32devboard/main/micro_model_settings.h +43 -0
  204. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/bits.h +102 -0
  205. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/fft.cc +52 -0
  206. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/fft.h +50 -0
  207. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/fft_util.cc +69 -0
  208. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/fft_util.h +34 -0
  209. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/filterbank.c +134 -0
  210. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/filterbank.h +63 -0
  211. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/filterbank_util.c +220 -0
  212. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/filterbank_util.h +50 -0
  213. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/frontend.c +72 -0
  214. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/frontend.h +64 -0
  215. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/frontend_util.c +85 -0
  216. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/frontend_util.h +52 -0
  217. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/kiss_fft_common.h +48 -0
  218. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/kiss_fft_int16.cc +8 -0
  219. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/kiss_fft_int16.h +34 -0
  220. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/log_lut.c +30 -0
  221. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/log_lut.h +40 -0
  222. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/log_scale.c +83 -0
  223. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/log_scale.h +39 -0
  224. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/log_scale_util.c +27 -0
  225. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/log_scale_util.h +45 -0
  226. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/noise_reduction.c +51 -0
  227. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/noise_reduction.h +46 -0
  228. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/noise_reduction_util.c +45 -0
  229. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/noise_reduction_util.h +50 -0
  230. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/pcan_gain_control.c +56 -0
  231. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/pcan_gain_control.h +47 -0
  232. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/pcan_gain_control_util.c +92 -0
  233. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/pcan_gain_control_util.h +57 -0
  234. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/window.c +70 -0
  235. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/window.h +49 -0
  236. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/window_util.c +73 -0
  237. resources/platforms/espidf/micro_speech_esp32devboard/main/microfrontend/lib/window_util.h +45 -0
  238. resources/platforms/espidf/micro_speech_esp32devboard/main/pins.c +14 -0
  239. resources/platforms/espidf/micro_speech_esp32devboard/main/pins.h +30 -0
  240. resources/platforms/espidf/micro_speech_esp32devboard/main/recognize_commands.cc +150 -0
  241. resources/platforms/espidf/micro_speech_esp32devboard/main/recognize_commands.h +173 -0
  242. resources/platforms/espidf/micro_speech_esp32devboard/main/ringbuf.c +333 -0
  243. resources/platforms/espidf/micro_speech_esp32devboard/main/ringbuf.h +74 -0
  244. resources/platforms/espidf/micro_speech_esp32devboard/sdkconfig.defaults +36 -0
  245. resources/platforms/espidf/project/CMakeLists.txt +29 -0
  246. resources/platforms/espidf/project/README.md +70 -0
  247. resources/platforms/espidf/project/components/mlif/CMakeLists.txt +77 -0
  248. resources/platforms/espidf/project/components/mlif/Kconfig +76 -0
  249. resources/platforms/espidf/project/components/mlif/default_data.c +26 -0
  250. resources/platforms/espidf/project/components/mlif/default_model_support/process_input.c +35 -0
  251. resources/platforms/espidf/project/components/mlif/default_model_support/process_output.c +45 -0
  252. resources/platforms/espidf/project/components/mlif/include/exit.h +26 -0
  253. resources/platforms/espidf/project/components/mlif/include/ml_interface.h +60 -0
  254. resources/platforms/espidf/project/components/mlif/include/printing.h +31 -0
  255. resources/platforms/espidf/project/components/mlif/ml_interface.c +54 -0
  256. resources/platforms/espidf/project/components/mlif/ml_interface_tflm.cc +40 -0
  257. resources/platforms/espidf/project/components/mlif/ml_interface_tvm.c +40 -0
  258. resources/platforms/espidf/project/components/tflite-lib/CMakeLists.txt +142 -0
  259. resources/platforms/espidf/project/components/tflite-lib/Kconfig +8 -0
  260. resources/platforms/espidf/project/components/tvm-lib/CMakeLists.txt +66 -0
  261. resources/platforms/espidf/project/components/tvm-lib/Kconfig +7 -0
  262. resources/platforms/espidf/project/example_test.py +38 -0
  263. resources/platforms/espidf/project/main/CMakeLists.txt +25 -0
  264. resources/platforms/espidf/project/main/main.c +57 -0
  265. resources/platforms/espidf/project/sdkconfig.defaults +26 -0
  266. resources/platforms/zephyr/project/CMakeLists.txt +22 -0
  267. resources/platforms/zephyr/project/Kconfig +101 -0
  268. resources/platforms/zephyr/project/README.rst +33 -0
  269. resources/platforms/zephyr/project/cmake/AddAllSubdirs.cmake +21 -0
  270. resources/platforms/zephyr/project/cmake/ChooseTarget.cmake +22 -0
  271. resources/platforms/zephyr/project/cmake/LinkTFLite.cmake +223 -0
  272. resources/platforms/zephyr/project/cmake/LinkTVM.cmake +68 -0
  273. resources/platforms/zephyr/project/cmake/LookupClang.cmake +41 -0
  274. resources/platforms/zephyr/project/cmake/backends/tflmc.cmake +5 -0
  275. resources/platforms/zephyr/project/cmake/backends/tflmi.cmake +5 -0
  276. resources/platforms/zephyr/project/cmake/backends/tvmaot.cmake +10 -0
  277. resources/platforms/zephyr/project/cmake/backends/tvmaotplus.cmake +10 -0
  278. resources/platforms/zephyr/project/cmake/backends/tvmcg.cmake +10 -0
  279. resources/platforms/zephyr/project/cmake/backends/tvmllvm.cmake +8 -0
  280. resources/platforms/zephyr/project/cmake/backends/tvmrt.cmake +8 -0
  281. resources/platforms/zephyr/project/cmake/extras.cmake +9 -0
  282. resources/platforms/zephyr/project/cmake/features/auto_vectorize_gcc.cmake +65 -0
  283. resources/platforms/zephyr/project/cmake/features/auto_vectorize_llvm.cmake +20 -0
  284. resources/platforms/zephyr/project/cmake/features/cmsisnn.cmake +86 -0
  285. resources/platforms/zephyr/project/cmake/features/muriscvnn.cmake +87 -0
  286. resources/platforms/zephyr/project/cmake/frameworks/tflm.cmake +1 -0
  287. resources/platforms/zephyr/project/cmake/frameworks/tflm_mlif.cmake +8 -0
  288. resources/platforms/zephyr/project/cmake/frameworks/tvm.cmake +1 -0
  289. resources/platforms/zephyr/project/cmake/frameworks/tvm_mlif.cmake +37 -0
  290. resources/platforms/zephyr/project/lib/CMakeLists.txt +13 -0
  291. resources/platforms/zephyr/project/lib/drivers/fake_input.c +10 -0
  292. resources/platforms/zephyr/project/lib/drivers/fake_input.h +8 -0
  293. resources/platforms/zephyr/project/lib/ml_interface/CMakeLists.txt +31 -0
  294. resources/platforms/zephyr/project/lib/ml_interface/default_data.c +8 -0
  295. resources/platforms/zephyr/project/lib/ml_interface/default_model_support/CMakeLists.txt +2 -0
  296. resources/platforms/zephyr/project/lib/ml_interface/default_model_support/process_input.c +17 -0
  297. resources/platforms/zephyr/project/lib/ml_interface/default_model_support/process_output.c +27 -0
  298. resources/platforms/zephyr/project/lib/ml_interface/ml_interface.c +36 -0
  299. resources/platforms/zephyr/project/lib/ml_interface/ml_interface.h +47 -0
  300. resources/platforms/zephyr/project/lib/ml_interface/ml_interface_tflm.cc +22 -0
  301. resources/platforms/zephyr/project/lib/ml_interface/ml_interface_tvm.c +22 -0
  302. resources/platforms/zephyr/project/lib/support/CMakeLists.txt +2 -0
  303. resources/platforms/zephyr/project/lib/support/dummy.c +0 -0
  304. resources/platforms/zephyr/project/lib/support/exit.h +8 -0
  305. resources/platforms/zephyr/project/lib/support/printing.h +11 -0
  306. resources/platforms/zephyr/project/lib/support/quantize.h +23 -0
  307. resources/platforms/zephyr/project/lib/targets/corstone300.c +62 -0
  308. resources/platforms/zephyr/project/lib/targets/default.c +2 -0
  309. resources/platforms/zephyr/project/lib/targets/etiss_pulpino.c +112 -0
  310. resources/platforms/zephyr/project/lib/targets/generic_riscv.c +112 -0
  311. resources/platforms/zephyr/project/lib/targets/riscv_qemu.c +112 -0
  312. resources/platforms/zephyr/project/lib/targets/target.h +2 -0
  313. resources/platforms/zephyr/project/prj.conf +11 -0
  314. resources/platforms/zephyr/project/sample.yaml +19 -0
  315. resources/platforms/zephyr/project/src/main.c +71 -0
  316. resources/platforms/zephyr/project2/CMakeLists.txt +72 -0
  317. resources/platforms/zephyr/project2/Kconfig +101 -0
  318. resources/platforms/zephyr/project2/README.rst +33 -0
  319. resources/platforms/zephyr/project2/cmake/AddAllSubdirs.cmake +21 -0
  320. resources/platforms/zephyr/project2/cmake/ChooseTarget.cmake +22 -0
  321. resources/platforms/zephyr/project2/cmake/LinkTFLite.cmake +223 -0
  322. resources/platforms/zephyr/project2/cmake/LinkTVM.cmake +68 -0
  323. resources/platforms/zephyr/project2/cmake/LookupClang.cmake +41 -0
  324. resources/platforms/zephyr/project2/cmake/backends/tflmc.cmake +5 -0
  325. resources/platforms/zephyr/project2/cmake/backends/tflmi.cmake +5 -0
  326. resources/platforms/zephyr/project2/cmake/backends/tvmaot.cmake +10 -0
  327. resources/platforms/zephyr/project2/cmake/backends/tvmaotplus.cmake +10 -0
  328. resources/platforms/zephyr/project2/cmake/backends/tvmcg.cmake +10 -0
  329. resources/platforms/zephyr/project2/cmake/backends/tvmllvm.cmake +8 -0
  330. resources/platforms/zephyr/project2/cmake/backends/tvmrt.cmake +8 -0
  331. resources/platforms/zephyr/project2/cmake/extras.cmake +9 -0
  332. resources/platforms/zephyr/project2/cmake/features/auto_vectorize_gcc.cmake +65 -0
  333. resources/platforms/zephyr/project2/cmake/features/auto_vectorize_llvm.cmake +20 -0
  334. resources/platforms/zephyr/project2/cmake/features/cmsisnn.cmake +86 -0
  335. resources/platforms/zephyr/project2/cmake/features/muriscvnn.cmake +87 -0
  336. resources/platforms/zephyr/project2/cmake/frameworks/tflm.cmake +1 -0
  337. resources/platforms/zephyr/project2/cmake/frameworks/tflm_mlif.cmake +8 -0
  338. resources/platforms/zephyr/project2/cmake/frameworks/tvm.cmake +1 -0
  339. resources/platforms/zephyr/project2/cmake/frameworks/tvm_mlif.cmake +37 -0
  340. resources/platforms/zephyr/project2/default_data.c +8 -0
  341. resources/platforms/zephyr/project2/default_model_support/CMakeLists.txt +3 -0
  342. resources/platforms/zephyr/project2/default_model_support/process_input.c +17 -0
  343. resources/platforms/zephyr/project2/default_model_support/process_output.c +27 -0
  344. resources/platforms/zephyr/project2/lib/CMakeLists.txt +13 -0
  345. resources/platforms/zephyr/project2/lib/drivers/fake_input.c +10 -0
  346. resources/platforms/zephyr/project2/lib/drivers/fake_input.h +8 -0
  347. resources/platforms/zephyr/project2/lib/support/CMakeLists.txt +2 -0
  348. resources/platforms/zephyr/project2/lib/support/dummy.c +0 -0
  349. resources/platforms/zephyr/project2/lib/support/exit.h +8 -0
  350. resources/platforms/zephyr/project2/lib/support/printing.h +11 -0
  351. resources/platforms/zephyr/project2/lib/support/quantize.h +23 -0
  352. resources/platforms/zephyr/project2/lib/targets/corstone300.c +62 -0
  353. resources/platforms/zephyr/project2/lib/targets/default.c +2 -0
  354. resources/platforms/zephyr/project2/lib/targets/etiss_pulpino.c +112 -0
  355. resources/platforms/zephyr/project2/lib/targets/generic_riscv.c +112 -0
  356. resources/platforms/zephyr/project2/lib/targets/riscv_qemu.c +112 -0
  357. resources/platforms/zephyr/project2/lib/targets/target.h +2 -0
  358. resources/platforms/zephyr/project2/ml_interface.c +36 -0
  359. resources/platforms/zephyr/project2/ml_interface.h +47 -0
  360. resources/platforms/zephyr/project2/ml_interface_tflm.cc +22 -0
  361. resources/platforms/zephyr/project2/ml_interface_tvm.c +22 -0
  362. resources/platforms/zephyr/project2/prj.conf +13 -0
  363. resources/platforms/zephyr/project2/sample.yaml +19 -0
  364. resources/platforms/zephyr/project2/settings.json +6 -0
  365. resources/platforms/zephyr/project2/src/main.c +35 -0
  366. resources/pycharm/.run/README.md +73 -0
  367. resources/pycharm/.run/step1_ init env.run.xml +24 -0
  368. resources/pycharm/.run/step2_ setup.run.xml +25 -0
  369. resources/pycharm/.run/step3_ run resnet on pulpxpulpissimo.run.xml +25 -0
  370. resources/templates/default.yml.j2 +258 -0
  371. resources/templates/dev.yml.j2 +212 -0
  372. resources/templates/minimal.yml.j2 +234 -0
  373. resources/templates/phi.yml.j2 +282 -0
  374. resources/templates/pulp.yml.j2 +267 -0
  375. resources/templates/research.yml.j2 +237 -0
  376. resources/templates/tumeda.yml.j2 +239 -0
mlonmcu/session/run.py ADDED
@@ -0,0 +1,1008 @@
1
+ #
2
+ # Copyright (c) 2022 TUM Department of Electrical and Computer Engineering.
3
+ #
4
+ # This file is part of MLonMCU.
5
+ # See https://github.com/tum-ei-eda/mlonmcu.git for further info.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ """Definition of a MLonMCU Run which represents a single benchmark instance for a given set of options."""
20
+ import itertools
21
+ import os
22
+ import copy
23
+ import tempfile
24
+ from pathlib import Path
25
+ from enum import IntEnum
26
+
27
+ from mlonmcu.logging import get_logger
28
+ from mlonmcu.artifact import ArtifactFormat, lookup_artifacts
29
+ from mlonmcu.config import str2bool
30
+ from mlonmcu.platform.platform import CompilePlatform, TargetPlatform, BuildPlatform, TunePlatform
31
+ from mlonmcu.report import Report # TODO: move to mlonmcu.session.report
32
+ from mlonmcu.config import resolve_required_config, filter_config
33
+ from mlonmcu.models.lookup import lookup_models
34
+ from mlonmcu.feature.type import FeatureType
35
+ from mlonmcu.feature.features import get_matching_features, get_available_features
36
+ from mlonmcu.target.metrics import Metrics
37
+ from mlonmcu.models import SUPPORTED_FRONTENDS
38
+ from mlonmcu.platform import get_platforms
39
+ from mlonmcu.flow import SUPPORTED_FRAMEWORKS, SUPPORTED_BACKENDS
40
+
41
+ from .postprocess import SUPPORTED_POSTPROCESSES
42
+ from .postprocess.postprocess import RunPostprocess
43
+
44
+ logger = get_logger()
45
+
46
+
47
+ class RunStage(IntEnum):
48
+ """Type describing the stages a run can have."""
49
+
50
+ NOP = 0
51
+ LOAD = 1 # unimplemented
52
+ TUNE = 2
53
+ BUILD = 3
54
+ COMPILE = 4
55
+ RUN = 5
56
+ POSTPROCESS = 6
57
+ DONE = 7
58
+
59
+
60
+ class Run:
61
+ """A run is single model/backend/framework/target combination with a given set of features and configs."""
62
+
63
+ FEATURES = ["autotune", "target_optimized"]
64
+
65
+ DEFAULTS = {
66
+ "export_optional": False,
67
+ "tune_enabled": False,
68
+ "target_to_backend": False,
69
+ "stage_subdirs": False,
70
+ }
71
+
72
+ REQUIRED = []
73
+ OPTIONAL = []
74
+
75
+ @classmethod
76
+ def from_file(cls, path):
77
+ """Restore a run object which was written to the disk."""
78
+ raise NotImplementedError
79
+
80
+ def __init__(
81
+ self,
82
+ idx=None,
83
+ model=None,
84
+ framework=None,
85
+ frontends=None,
86
+ backend=None,
87
+ target=None,
88
+ platforms=None, # TODO: rename
89
+ features=None, # TODO: All features combined or explicit run-features -> postprocesses?
90
+ config=None, # TODO: All config combined or explicit run-config?
91
+ postprocesses=None,
92
+ archived=False,
93
+ session=None,
94
+ comment="",
95
+ ):
96
+ self.idx = idx
97
+ self.model = model # Model name, not object?
98
+ self.frontends = frontends if frontends is not None else []
99
+ self.framework = framework # ???
100
+ self.backend = backend
101
+ self.platforms = platforms if platforms is not None else []
102
+ self.artifacts_per_stage = {}
103
+ self.archived = archived
104
+ self.session = session
105
+ self.postprocesses = postprocesses if postprocesses else []
106
+ self.comment = comment
107
+ # self.stage = RunStage.NOP # max executed stage
108
+ self.completed = {stage: stage == RunStage.NOP for stage in RunStage}
109
+
110
+ self.init_directory()
111
+ self.target = target
112
+ self.cache_hints = []
113
+ self.config = config if config else {}
114
+ self.features = features if features else []
115
+ self.run_config = {}
116
+ self.run_features = self.process_features(features)
117
+ self.run_config = filter_config(self.config, "run", self.DEFAULTS, self.OPTIONAL, self.REQUIRED)
118
+ self.sub_names = []
119
+ self.result = None
120
+ self.failing = False # -> RunStatus
121
+ # self.lock = threading.Lock() # FIXME: use mutex instead of boolean
122
+ self.locked = False
123
+ self.report = None
124
+
125
+ def process_features(self, features):
126
+ """Utility which handles postprocess_features."""
127
+ if features is None:
128
+ return []
129
+ features = get_matching_features(features, FeatureType.RUN)
130
+ for feature in features:
131
+ assert feature.name in self.FEATURES, f"Incompatible feature: {feature.name}"
132
+ tmp_run_config = {f"run.{key}": value for key, value in self.run_config.items()}
133
+ feature.add_run_config(tmp_run_config)
134
+ self.run_config = filter_config(tmp_run_config, "run", self.DEFAULTS, self.OPTIONAL, self.REQUIRED)
135
+ return features
136
+
137
+ @property
138
+ def tune_enabled(self):
139
+ """Get tune_enabled property."""
140
+ value = self.run_config["tune_enabled"]
141
+ return str2bool(value) if not isinstance(value, (bool, int)) else value
142
+
143
+ @property
144
+ def target_to_backend(self):
145
+ """Get target_to_backend property."""
146
+ value = self.run_config["target_to_backend"]
147
+ return str2bool(value) if not isinstance(value, (bool, int)) else value
148
+
149
+ @property
150
+ def export_optional(self):
151
+ """Get export_optional property."""
152
+ value = self.run_config["export_optional"]
153
+ return str2bool(value) if not isinstance(value, (bool, int)) else value
154
+
155
+ @property
156
+ def stage_subdirs(self):
157
+ value = self.run_config["stage_subdirs"]
158
+ return str2bool(value) if not isinstance(value, (bool, int)) else value
159
+
160
+ @property
161
+ def build_platform(self):
162
+ """Get platform for build stage."""
163
+ if self.backend is not None and (
164
+ hasattr(self.backend, "platform") and isinstance(self.backend.platform, BuildPlatform)
165
+ ):
166
+ return self.backend.platform
167
+ for platform in self.platforms:
168
+ if isinstance(platform, BuildPlatform):
169
+ return platform
170
+ return None
171
+
172
+ @property
173
+ def tune_platform(self):
174
+ """Get platform for tune stage."""
175
+ if self.backend is not None and (
176
+ hasattr(self.backend, "platform") and isinstance(self.backend.platform, TunePlatform)
177
+ ):
178
+ return self.backend.platform
179
+ for platform in self.platforms:
180
+ if isinstance(platform, TunePlatform):
181
+ return platform
182
+ return None
183
+
184
+ @property
185
+ def compile_platform(self):
186
+ """Get platform for compile stage."""
187
+ if self.target is not None and isinstance(self.target.platform, CompilePlatform):
188
+ return self.target.platform
189
+ for platform in self.platforms:
190
+ if isinstance(platform, CompilePlatform):
191
+ return platform
192
+ return None
193
+
194
+ @property
195
+ def target_platform(self): # TODO: rename to run_stage?
196
+ """Get platform for run stage."""
197
+ if self.target:
198
+ assert isinstance(self.target.platform, TargetPlatform)
199
+ return self.target.platform
200
+ for platform in self.platforms:
201
+ if isinstance(platform, TargetPlatform):
202
+ return platform
203
+ return None
204
+
205
+ def has_stage(self, stage):
206
+ """Returns true if the given stage is available for this run."""
207
+ if stage == RunStage.NOP:
208
+ return True
209
+ if stage == RunStage.LOAD:
210
+ return self.model is not None and len(self.frontends) > 0
211
+ if stage == RunStage.TUNE:
212
+ return self.tune_enabled and self.backend is not None
213
+ if stage == RunStage.BUILD:
214
+ return self.backend is not None and self.framework is not None
215
+ if stage == RunStage.COMPILE:
216
+ return self.target is not None and len(self.platforms) > 0 and self.compile_platform is not None
217
+ if stage == RunStage.RUN:
218
+ return self.target is not None and len(self.platforms) > 0 and self.target_platform is not None
219
+ if stage == RunStage.POSTPROCESS:
220
+ return len(self.postprocesses) > 0
221
+ if stage == RunStage.DONE:
222
+ return False
223
+ return False # TODO: Throw error instead?
224
+
225
+ @property
226
+ def next_stage(self):
227
+ """Determines the next not yet completed stage. Returns RunStage.DONE if already completed."""
228
+ for stage in RunStage:
229
+ if not self.completed[stage.value] and self.has_stage(stage):
230
+ return stage
231
+ return RunStage.DONE
232
+
233
+ def lock(self):
234
+ """Aquire a mutex to lock the current run."""
235
+ # ret = self.lock.acquire(timeout=0)
236
+ ret = not self.locked
237
+ self.locked = True
238
+ if not ret:
239
+ raise RuntimeError("Parallel processing of the same run is not allowed")
240
+
241
+ def unlock(self):
242
+ """Release a mutex to unlock the current run."""
243
+ # self.lock.release()
244
+ self.locked = False
245
+
246
+ def init_directory(self):
247
+ """Initialize the temporary directory for this run."""
248
+ if self.session is None:
249
+ assert not self.archived
250
+ self.tempdir = tempfile.TemporaryDirectory()
251
+ self.dir = Path(self.tempdir.name)
252
+ else:
253
+ self.tempdir = None
254
+ self.dir = self.session.runs_dir / str(self.idx)
255
+ if not self.dir.is_dir():
256
+ os.mkdir(self.dir)
257
+ # This is not a good idea, but else we would need a mutex/lock on the shared build_dir
258
+ # A solution would be to split up the framework runtime libs from the mlif...
259
+ for platform in self.platforms: # TODO: only do this if needed! (not for every platform)
260
+ # The stage_subdirs setting is ignored here because platforms can be multi-stage!
261
+ platform.init_directory(path=Path(self.dir) / platform.name)
262
+
263
+ def copy(self):
264
+ """Create a new run based on this instance."""
265
+ new = copy.deepcopy(self)
266
+ if self.session:
267
+ new_idx = self.session.request_run_idx()
268
+ new.idx = new_idx
269
+ self.init_directory()
270
+ return new
271
+
272
+ def init_component(self, component_cls, context=None):
273
+ """Helper function to create and configure a MLonMCU component instance for this run."""
274
+ required_keys = component_cls.REQUIRED
275
+ optional_keys = component_cls.OPTIONAL
276
+ self.config.update(
277
+ resolve_required_config(
278
+ required_keys,
279
+ optional=optional_keys,
280
+ features=self.features,
281
+ config=self.config,
282
+ cache=context.cache if context else None,
283
+ default_flags=context.environment.flags if context else None,
284
+ hints=self.cache_hints,
285
+ )
286
+ )
287
+ component_config = self.config.copy() # TODOL get rid of this
288
+ return component_cls(features=self.features, config=component_config)
289
+
290
+ def add_model(self, model):
291
+ """Setter for the model instance."""
292
+ self.model = model
293
+
294
+ def add_frontend(self, frontend):
295
+ """Setter for the frontend instance."""
296
+ self.frontends = [frontend]
297
+
298
+ def add_frontends(self, frontends):
299
+ """Setter for the list of frontends."""
300
+ self.frontends = frontends
301
+
302
+ def add_backend(self, backend):
303
+ """Setter for the backend instance."""
304
+ self.backend = backend
305
+ # assert len(self.platforms) > 0, "Add at least a platform before adding a backend."
306
+ if self.model is not None:
307
+ assert self.backend.supports_model(self.model), (
308
+ "The added backend does not support the chosen model. "
309
+ "Add the backend before adding a model to find a suitable frontend."
310
+ )
311
+ for platform in self.platforms:
312
+ self.backend.add_platform_defs(platform.name, platform.definitions)
313
+
314
+ def add_framework(self, framework):
315
+ """Setter for the framework instance."""
316
+ self.framework = framework
317
+ # assert len(self.platforms) > 0, "Add at least a platform before adding a framework."
318
+ for platform in self.platforms:
319
+ self.framework.add_platform_defs(platform.name, platform.definitions)
320
+
321
+ def add_target(self, target):
322
+ """Setter for the target instance."""
323
+ self.target = target
324
+ assert self.platforms is not None, "Add at least a platform before adding a target."
325
+ for platform in self.platforms:
326
+ self.target.add_platform_defs(platform.name, platform.definitions)
327
+ self.cache_hints = [self.target.get_arch()]
328
+ # self.resolve_chache_refs()
329
+
330
+ def add_platform(self, platform):
331
+ """Setter for the platform instance."""
332
+ self.platforms = [platform]
333
+ if self.backend:
334
+ self.backend.add_platform_defs(platform.name, platform.definitions)
335
+ if self.framework:
336
+ self.framework.add_platform_defs(platform.name, platform.definitions)
337
+
338
+ def add_platforms(self, platforms):
339
+ """Setter for the list of platforms."""
340
+ self.platforms = platforms
341
+ for platform in platforms:
342
+ if self.backend:
343
+ self.backend.add_platform_defs(platform.name, platform.definitions)
344
+ if self.framework:
345
+ self.framework.add_platform_defs(platform.name, platform.definitions)
346
+
347
+ def add_postprocess(self, postprocess, append=False):
348
+ """Setter for a postprocess instance."""
349
+ if append:
350
+ self.postprocesses.append(postprocess)
351
+ else:
352
+ self.postprocesses = [postprocess]
353
+
354
+ def add_postprocesses(self, postprocesses, append=False):
355
+ """Setter for the list of postprocesses."""
356
+ if append:
357
+ self.postprocesses.extend(postprocesses)
358
+ else:
359
+ self.postprocesses = postprocesses
360
+
361
+ def add_feature(self, feature):
362
+ """Setter for a feature instance."""
363
+ self.features = [feature]
364
+ self.run_features = self.process_features(self.features)
365
+
366
+ def add_features(self, features, append=False):
367
+ """Setter for the list of features."""
368
+ self.features = features if not append else self.features + features
369
+ self.run_features = self.process_features(self.features)
370
+
371
+ def pick_model_frontend(self, model_hints, backend=None):
372
+ assert len(model_hints) > 0
373
+ if backend is None:
374
+ self.frontends = [
375
+ frontend for frontend in self.frontends if model_hints[0].formats[0] in frontend.input_formats
376
+ ]
377
+ assert len(self.frontends) > 0
378
+ return model_hints[0]
379
+ for model_hint in model_hints:
380
+ if backend.supports_model(model_hint):
381
+ self.frontends = [
382
+ frontend for frontend in self.frontends if model_hint.formats[0] in frontend.input_formats
383
+ ]
384
+ return model_hint
385
+ return None
386
+
387
+ def add_model_by_name(self, model_name, context=None):
388
+ """Helper function to initialize and configure a model by its name."""
389
+ assert context is not None, "Please supply a context"
390
+ assert len(self.frontends) > 0, "Add a frontend to the run before adding a model"
391
+ model_hints = lookup_models([model_name], frontends=self.frontends, context=context)
392
+ assert len(model_hints) > 0, f"Model with name '{model_name}' not found"
393
+ model_hint = self.pick_model_frontend(model_hints, backend=self.backend)
394
+ assert model_hint is not None, "Unable to pick a suitable model"
395
+ self.add_model(model_hint)
396
+
397
+ def add_frontend_by_name(self, frontend_name, context=None):
398
+ """Helper function to initialize and configure a frontend by its name."""
399
+ self.add_frontends_by_name([frontend_name], context=context)
400
+
401
+ def add_frontends_by_name(self, frontend_names, context=None):
402
+ """Helper function to initialize and configure frontends by their names."""
403
+ frontends = []
404
+ for name in frontend_names:
405
+ try:
406
+ assert context is not None and context.environment.has_frontend(
407
+ name
408
+ ), f"The frontend '{name}' is not enabled for this environment"
409
+ frontends.append(self.init_component(SUPPORTED_FRONTENDS[name], context=context))
410
+ except Exception:
411
+ continue
412
+ assert len(frontends) > 0, "No compatible frontend was found"
413
+ self.add_frontends(frontends)
414
+
415
+ def add_backend_by_name(self, backend_name, context=None):
416
+ """Helper function to initialize and configure a backend by its name."""
417
+ assert context is not None and context.environment.has_backend(
418
+ backend_name
419
+ ), f"The backend '{backend_name}' is not enabled for this environment"
420
+ if self.build_platform:
421
+ self.add_backend(self.init_component(self.build_platform.create_backend(backend_name), context=context))
422
+ else:
423
+ self.add_backend(self.init_component(SUPPORTED_BACKENDS[backend_name], context=context))
424
+ framework_name = self.backend.framework # TODO: does this work?
425
+ assert context.environment.has_framework(
426
+ framework_name
427
+ ), f"The framework '{framework_name}' is not enabled for this environment"
428
+ self.add_framework(self.init_component(SUPPORTED_FRAMEWORKS[framework_name], context=context))
429
+
430
+ def add_target_by_name(self, target_name, context=None):
431
+ """Helper function to initialize and configure a target by its name."""
432
+ # We can not use the following code snipped are platform targets may be resolved dynamically
433
+ # assert context is not None and context.environment.has_target(
434
+ # target_name
435
+ # ), f"The target '{target_name}' is not enabled for this environment"
436
+ assert len(self.platforms) > 0, "Please add a platform to the run before adding the target"
437
+ self.add_target(self.init_component(self.target_platform.create_target(target_name), context=context))
438
+
439
+ def add_platform_by_name(self, platform_name, context=None):
440
+ """Helper function to initialize and configure a platform by its name."""
441
+ self.add_platforms_by_name([platform_name], context=context)
442
+
443
+ def add_platforms_by_name(self, platform_names, context=None):
444
+ """Helper function to initialize and configure platforms by their names."""
445
+ platforms = []
446
+ for name in platform_names:
447
+ assert context is not None and context.environment.has_platform(
448
+ name
449
+ ), f"The platform '{name}' is not enabled for this environment"
450
+ platforms.append(self.init_component(get_platforms()[name], context=context))
451
+ self.add_platforms(platforms)
452
+
453
+ def add_postprocess_by_name(self, postprocess_name, append=True, context=None):
454
+ """Helper function to initialize and configure a postprocesses by its name."""
455
+ self.add_postprocesses_by_name([postprocess_name], append=append, context=context)
456
+
457
+ def add_postprocesses_by_name(self, postprocess_names, append=False, context=None):
458
+ """Helper function to initialize and configure postprocesses by their names."""
459
+ postprocesses = []
460
+ for name in postprocess_names:
461
+ # TODO: ?
462
+ # assert context is not None and context.environment.has_postprocess(
463
+ # postprocess_name
464
+ # ), f"The postprocess '{postprocess_name}' is not enabled for this environment"
465
+ postprocesses.append(self.init_component(SUPPORTED_POSTPROCESSES[name], context=context))
466
+ self.add_postprocesses(postprocesses, append=append)
467
+
468
+ def add_feature_by_name(self, feature_name, append=True, context=None):
469
+ """Helper function to initialize and configure a feature by its name."""
470
+ self.add_features_by_name([feature_name], context=context, append=append)
471
+
472
+ def add_features_by_name(self, feature_names, append=False, context=None):
473
+ """Helper function to initialize and configure features by their names."""
474
+ features = []
475
+ for feature_name in feature_names:
476
+ available_features = get_available_features(feature_name=feature_name)
477
+ for feature_cls in available_features:
478
+ features.append(self.init_component(feature_cls, context=context))
479
+ self.add_features(features, append=append)
480
+
481
+ def __repr__(self):
482
+ probs = []
483
+ if self.model:
484
+ probs.append(str(self.model))
485
+ if len(self.platforms) > 0:
486
+ probs.append(str(self.platforms[0] if len(self.platforms) == 1 else self.platforms))
487
+ if len(self.frontends) > 0:
488
+ probs.append(str(self.frontends[0] if len(self.frontends) == 1 else self.frontends))
489
+ if self.backend:
490
+ probs.append(str(self.backend))
491
+ if self.target:
492
+ probs.append(str(self.target))
493
+ if self.features and len(self.features) > 0:
494
+ probs.append(str(self.features))
495
+ if self.config and len(self.config) > 0:
496
+ probs.append(str(self.config))
497
+ return "Run(" + ",".join(probs) + ")"
498
+
499
+ @property
500
+ def frontend(self):
501
+ assert len(self.frontends) > 0, "Not frontend is available for this run."
502
+ return self.frontends[0]
503
+
504
+ @property
505
+ def artifacts(self):
506
+ return list(
507
+ itertools.chain([subs[subs.keys[0]] for stage, subs in self.artifacts_per_stage.items()])
508
+ ) # TODO: fix default only
509
+
510
+ def toDict(self):
511
+ """Utility not implemented yet. (TODO: remove?)"""
512
+ raise NotImplementedError
513
+
514
+ def export_stage(self, stage, optional=False):
515
+ """Export stage artifacts of this run to its directory."""
516
+ # TODO: per stage subdirs?
517
+ subdir = self.stage_subdirs
518
+ if stage in self.artifacts_per_stage:
519
+ for name in self.artifacts_per_stage[stage]:
520
+ artifacts = self.artifacts_per_stage[stage][name]
521
+ for artifact in artifacts:
522
+ if not artifact.optional or optional:
523
+ dest = self.dir
524
+ if subdir:
525
+ stage_idx = int(stage)
526
+ dest = dest / "stages" / str(stage_idx)
527
+ # TODO: stages.txt for mapping between stage idx and name
528
+ if name not in ["", "default"]:
529
+ dest = dest / "sub" / name
530
+ dest.mkdir(parents=True, exist_ok=True)
531
+ extract = artifact.fmt == ArtifactFormat.MLF
532
+ # extract = artifact.fmt == ArtifactFormat.MLF
533
+ # and not isinstance(self.platform, MicroTvmPlatform)
534
+ artifact.export(dest)
535
+ # Keep the tar as well as the extracted files
536
+ if extract:
537
+ artifact.export(dest, extract=True)
538
+
539
+ def postprocess(self):
540
+ """Postprocess the 'run'."""
541
+ logger.debug("%s Processing stage POSTPROCESS", self.prefix)
542
+ self.lock()
543
+ # assert self.completed[RunStage.RUN] # Alternative: allow to trigger previous stages recursively as a fallback
544
+
545
+ self.artifacts_per_stage[RunStage.POSTPROCESS] = []
546
+ temp_report = self.get_report()
547
+ for postprocess in self.postprocesses:
548
+ if isinstance(postprocess, RunPostprocess):
549
+ artifacts = postprocess.post_run(temp_report, self.artifacts)
550
+ if artifacts is not None:
551
+ self.artifacts_per_stage[RunStage.POSTPROCESS].extend(artifacts) # TODO: subs?
552
+ self.report = temp_report
553
+
554
+ self.completed[RunStage.POSTPROCESS] = True
555
+ self.unlock()
556
+
557
+ def run(self):
558
+ """Run the 'run' using the defined target."""
559
+ logger.debug("%s Processing stage RUN", self.prefix)
560
+ self.lock()
561
+ # Alternative: drop artifacts of higher stages when re-triggering a lower one?
562
+ self.artifacts_per_stage[RunStage.RUN] = {}
563
+ if self.has_stage(RunStage.COMPILE):
564
+ assert self.completed[RunStage.COMPILE]
565
+ self.export_stage(RunStage.COMPILE, optional=self.export_optional)
566
+ for name in self.artifacts_per_stage[RunStage.COMPILE]:
567
+ elf_artifact = self.artifacts_per_stage[RunStage.COMPILE][name][0]
568
+ self.target.generate_metrics(elf_artifact.path)
569
+ artifacts = self.target.artifacts
570
+ if isinstance(artifacts, dict):
571
+ self.artifacts_per_stage[RunStage.RUN].update(
572
+ {key if name in ["", "default"] else f"{name}_{key}": value for key, value in artifacts.items()}
573
+ )
574
+ else:
575
+ self.artifacts_per_stage[RunStage.RUN].update(
576
+ {name if name in ["", "default"] else f"{name}": artifacts}
577
+ )
578
+ else:
579
+ assert self.completed[RunStage.BUILD] # Used for tvm platform
580
+ self.export_stage(RunStage.BUILD, optional=self.export_optional)
581
+ for name in self.artifacts_per_stage[RunStage.BUILD]:
582
+ shared_object_artifact = self.artifacts_per_stage[RunStage.BUILD][name][0]
583
+ self.target.generate_metrics(shared_object_artifact.path)
584
+ artifacts = self.target.artifacts
585
+ if isinstance(artifacts, dict):
586
+ self.artifacts_per_stage[RunStage.RUN].update(
587
+ {key if name in ["", "default"] else f"{name}_{key}": value for key, value in artifacts.items()}
588
+ )
589
+ else:
590
+ self.artifacts_per_stage[RunStage.RUN].update(
591
+ {name if name in ["", "default"] else f"{name}": artifacts}
592
+ )
593
+
594
+ self.completed[RunStage.RUN] = True
595
+ self.unlock()
596
+
597
+ def compile(self):
598
+ """Compile the target software for the run."""
599
+ logger.debug("%s Processing stage COMPILE", self.prefix)
600
+ self.lock()
601
+ assert self.completed[RunStage.BUILD]
602
+
603
+ self.export_stage(RunStage.BUILD, optional=self.export_optional)
604
+ self.artifacts_per_stage[RunStage.COMPILE] = {}
605
+ for name in self.artifacts_per_stage[RunStage.BUILD]:
606
+ codegen_dir = self.dir if not self.stage_subdirs else (self.dir / "stages" / str(int(RunStage.BUILD)))
607
+ if name not in ["", "default"]:
608
+ codegen_dir = codegen_dir / "sub" / name
609
+ # TODO!
610
+ self.compile_platform.generate_elf(codegen_dir, self.target) # TODO: has to go into different dirs
611
+ artifacts = self.compile_platform.artifacts
612
+ if isinstance(artifacts, dict):
613
+ self.artifacts_per_stage[RunStage.COMPILE].update(
614
+ {key if name in ["", "default"] else f"{name}_{key}": value for key, value in artifacts.items()}
615
+ )
616
+ else:
617
+ self.artifacts_per_stage[RunStage.COMPILE].update(
618
+ {name if name in ["", "default"] else f"{name}": artifacts}
619
+ )
620
+
621
+ self.completed[RunStage.COMPILE] = True
622
+ self.unlock()
623
+
624
+ def build(self):
625
+ """Process the run using the choosen backend."""
626
+ logger.debug("%s Processing stage BUILD", self.prefix)
627
+ self.lock()
628
+ assert (not self.has_stage(RunStage.TUNE)) or self.completed[RunStage.TUNE]
629
+
630
+ if self.target_to_backend:
631
+ assert self.target is not None, "Config target_to_backend can only be used if a target was provided"
632
+ cfg = self.target.get_backend_config(self.backend.name) # Do not expect a backend prefix here
633
+ if len(cfg) > 0:
634
+ logger.debug("Updating backend config based on given target.")
635
+ self.backend.config.update(cfg)
636
+
637
+ self.export_stage(RunStage.LOAD, optional=self.export_optional) # Not required anymore?
638
+ self.artifacts_per_stage[RunStage.BUILD] = {}
639
+ for name in self.artifacts_per_stage[RunStage.LOAD]:
640
+ model_artifact = self.artifacts_per_stage[RunStage.LOAD][name][0]
641
+ if not model_artifact.exported:
642
+ model_artifact.export(self.dir)
643
+ self.backend.load_model(model=model_artifact.path)
644
+ if self.has_stage(RunStage.TUNE):
645
+ self.export_stage(RunStage.TUNE, optional=self.export_optional)
646
+ if len(self.artifacts_per_stage[RunStage.TUNE][name]) > 0: # TODO: inaccurate!
647
+ tuning_artifact = self.artifacts_per_stage[RunStage.TUNE][name][0]
648
+ if not tuning_artifact.exported:
649
+ tuning_artifact.export(self.dir)
650
+ self.backend.tuning_records = tuning_artifact.path
651
+
652
+ # TODO: allow raw data as well as filepath in backends
653
+ self.backend.generate_code()
654
+ artifacts = self.backend.artifacts
655
+ if isinstance(artifacts, dict):
656
+ self.artifacts_per_stage[RunStage.BUILD].update(
657
+ {key if name in ["", "default"] else f"{name}_{key}": value for key, value in artifacts.items()}
658
+ )
659
+ else:
660
+ self.artifacts_per_stage[RunStage.BUILD].update(
661
+ {name if name in ["", "default"] else f"{name}": artifacts}
662
+ )
663
+
664
+ self.completed[RunStage.BUILD] = True
665
+ self.unlock()
666
+
667
+ def tune(self):
668
+ """Tune the run using the choosen backend (if supported)."""
669
+ logger.debug("%s Processing stage TUNE", self.prefix)
670
+ self.lock()
671
+ assert self.completed[RunStage.LOAD]
672
+
673
+ if self.target_to_backend:
674
+ assert self.target is not None, "Config target_to_backend can only be used if a target was provided"
675
+ cfg = self.target.get_backend_config(self.backend.name) # Do not expect a backend prefix here
676
+ if len(cfg) > 0:
677
+ logger.debug("Updating backend config based on given target.")
678
+ self.backend.config.update(cfg)
679
+
680
+ self.export_stage(RunStage.LOAD, optional=self.export_optional)
681
+ self.artifacts_per_stage[RunStage.TUNE] = {}
682
+ for name in self.artifacts_per_stage[RunStage.LOAD]:
683
+ model_artifact = self.artifacts_per_stage[RunStage.LOAD][name][0]
684
+ # if not model_artifact.exported:
685
+ # model_artifact.export(self.dir)
686
+
687
+ # TODO: allow raw data as well as filepath in backends
688
+ assert self.tune_platform, "Autotuning requires a TunePlatform"
689
+ res = self.tune_platform.tune_model(model_artifact.path, self.backend, self.target)
690
+ if res:
691
+ if isinstance(res, dict):
692
+ self.artifacts_per_stage[RunStage.TUNE].update(
693
+ {key if name in ["", "default"] else f"{name}_{key}": value for key, value in res.items()}
694
+ )
695
+ else:
696
+ self.artifacts_per_stage[RunStage.TUNE].update(
697
+ {name if name in ["", "default"] else f"{name}": res}
698
+ )
699
+ else:
700
+ self.artifacts_per_stage[RunStage.TUNE][name] = []
701
+
702
+ self.completed[RunStage.TUNE] = True
703
+ self.unlock()
704
+
705
+ def load(self):
706
+ """Load the model using the given frontend."""
707
+ logger.debug("%s Processing stage LOAD", self.prefix)
708
+ self.lock()
709
+ # assert self.completed[RunStage.NOP]
710
+
711
+ self.frontend.generate_models(self.model)
712
+ # The following is very very dirty but required to update arena sizes via model metadata...
713
+ cfg_new = {}
714
+ self.frontend.process_metadata(self.model, cfg=cfg_new)
715
+ if len(cfg_new) > 0:
716
+ for key, value in cfg_new.items():
717
+ component, name = key.split(".")[:2]
718
+ if self.backend is not None and component == self.backend.name:
719
+ self.backend.config[name] = value
720
+ else:
721
+ for platform in self.platforms:
722
+ if platform is not None and component == platform.name:
723
+ platform.config[name] = value
724
+ self.config[key] = value
725
+ artifacts = self.frontend.artifacts
726
+ if isinstance(artifacts, dict):
727
+ self.artifacts_per_stage[RunStage.LOAD] = artifacts
728
+ else:
729
+ self.artifacts_per_stage[RunStage.LOAD] = {"default": artifacts}
730
+
731
+ self.completed[RunStage.LOAD] = True
732
+ self.unlock()
733
+
734
+ def process(self, until=RunStage.RUN, skip=None, export=False):
735
+ """Process the run until a given stage."""
736
+ skip = skip if skip is not None else []
737
+ if until == RunStage.DONE:
738
+ until = RunStage.DONE - 1
739
+ start = self.next_stage # self.stage hold the max finished stage
740
+ if until < start:
741
+ logger.debug("%s Nothing to do", self.prefix)
742
+ return self.get_report()
743
+
744
+ if start > RunStage.NOP:
745
+ logger.debug(
746
+ # self.prefix + "Processing run until stage %s: %s",
747
+ "%s Continuing run from stage %s until stage %s",
748
+ self.prefix,
749
+ str(RunStage(start).name),
750
+ str(RunStage(until).name),
751
+ # str(self),
752
+ )
753
+ else:
754
+ logger.debug(
755
+ # self.prefix + "Processing run until stage %s: %s",
756
+ "%s Processing run until stage %s",
757
+ self.prefix,
758
+ str(RunStage(until).name),
759
+ # str(self),
760
+ )
761
+ for stage in range(start, until + 1):
762
+ if not self.has_stage(stage):
763
+ continue
764
+ if stage in skip:
765
+ logger.debug("%s Skipping stage %s", self.prefix, str(RunStage(stage).name))
766
+ continue
767
+ if stage < RunStage.POSTPROCESS:
768
+ self.report = None # Regenerate report if earlier stages are executed
769
+ stage_funcs = {
770
+ RunStage.NOP: lambda *args, **kwargs: None, # stage already done
771
+ RunStage.LOAD: self.load,
772
+ RunStage.TUNE: self.tune,
773
+ RunStage.BUILD: self.build,
774
+ RunStage.COMPILE: self.compile,
775
+ RunStage.RUN: self.run,
776
+ RunStage.POSTPROCESS: self.postprocess,
777
+ }
778
+ func = stage_funcs[stage]
779
+ if func:
780
+ self.failing = False
781
+ try:
782
+ func()
783
+ except Exception as e:
784
+ self.failing = True
785
+ if self.locked:
786
+ self.unlock()
787
+ logger.exception(e)
788
+ run_stage = RunStage(stage).name
789
+ logger.error("%s Run failed at stage '%s', aborting...", self.prefix, run_stage)
790
+ break
791
+ # self.stage = stage # FIXME: The stage_func should update the stage intead?
792
+ report = self.get_report()
793
+ if export:
794
+ self.export(optional=self.export_optional) # TODO: set to flase?
795
+ report_file = Path(self.dir) / "report.csv"
796
+ report.export(report_file)
797
+ return report
798
+
799
+ def write_run_file(self):
800
+ """Create a run.txt file which contains information used to reconstruct the run based
801
+ on its properties at a later point in time."""
802
+ logger.debug("%s Writing run file", self.prefix)
803
+ filename = self.dir / "run.txt"
804
+ with open(filename, "w", encoding="utf-8") as handle:
805
+ handle.write(str(self))
806
+
807
+ @property
808
+ def prefix(self):
809
+ """Get prefix property."""
810
+ return (
811
+ (f"[session-{self.session.idx}] [run-{self.idx}]" if self.session else f"[run-{self.idx}]")
812
+ if self.idx is not None
813
+ else ""
814
+ )
815
+
816
+ def get_all_feature_names(self):
817
+ """Return list of feature names for this run."""
818
+ return [feature.name for feature in self.features]
819
+
820
+ def get_all_postprocess_names(self):
821
+ """Return list of postprocess names for this run."""
822
+ return [postprocess.name for postprocess in self.postprocesses]
823
+
824
+ def get_frontend_name(self):
825
+ """Return frontend name(s) for this run."""
826
+ ret = [frontend.name for frontend in self.frontends]
827
+ return ret[0] if len(ret) == 1 else ret
828
+
829
+ def get_platform_name(self):
830
+ """Return platform name(s) for this run."""
831
+ used = list(set([self.tune_platform, self.build_platform, self.compile_platform, self.target_platform]))
832
+ ret = [platform.name for platform in used if platform is not None]
833
+ return ret[0] if len(ret) == 1 else ret
834
+
835
+ def get_all_configs(self, omit_paths=False, omit_defaults=False, omit_globals=False):
836
+ """Return dict with component-specific and global configuration for this run."""
837
+
838
+ def has_prefix(key):
839
+ """Returns true if the configuration key does not have global scope."""
840
+ return "." in key
841
+
842
+ def config_helper(obj, prefix=None):
843
+ """Helper to access the configuration of a given component object."""
844
+ if prefix:
845
+ name = prefix
846
+ else:
847
+ assert hasattr(obj, "name")
848
+ name = obj.name
849
+ omit_list = []
850
+ defaults = obj.DEFAULTS
851
+ for key, value in obj.config.items():
852
+ if omit_defaults and key in defaults and defaults[key] == value:
853
+ omit_list.append(key)
854
+ ret = {
855
+ key if has_prefix(key) else f"{name}.{key}": value
856
+ for key, value in obj.config.items()
857
+ if key not in omit_list
858
+ }
859
+ if omit_paths:
860
+ ret = {
861
+ key: value
862
+ for key, value in ret.items()
863
+ if not (isinstance(value, Path) or (isinstance(value, str) and Path(value).exists()))
864
+ }
865
+ return ret
866
+
867
+ ret = {}
868
+ if not omit_globals:
869
+ ret.update(
870
+ {key: value for key, value in self.config.items() if not has_prefix(key)}
871
+ ) # Only config without a prefix!
872
+ for frontend in self.frontends:
873
+ ret.update(config_helper(frontend))
874
+ if self.backend:
875
+ ret.update(config_helper(self.backend))
876
+ if self.framework:
877
+ ret.update(config_helper(self.framework))
878
+ if self.target:
879
+ ret.update(config_helper(self.target))
880
+ for platform in self.platforms:
881
+ ret.update(config_helper(platform))
882
+ for feature in self.features:
883
+ ret.update(config_helper(feature))
884
+ for postprocess in self.postprocesses:
885
+ ret.update(config_helper(postprocess))
886
+ return ret
887
+
888
+ def get_report(self):
889
+ """Returns teh complete report of this run."""
890
+ if self.completed[RunStage.POSTPROCESS]:
891
+ if self.report is not None:
892
+ return (
893
+ self.report
894
+ ) # Use postprocessed report instead of generating a new one (TODO: find a better approach)
895
+ # TODO: config or args for stuff like (session id) and run id as well as detailed features and configs
896
+ report = Report()
897
+ pre = {}
898
+ if self.session is not None:
899
+ pre["Session"] = self.session.idx
900
+ if self.idx is not None:
901
+ pre["Run"] = self.idx
902
+ if self.model:
903
+ pre["Model"] = self.model.name
904
+ if len(self.frontends) > 0:
905
+ pre["Frontend"] = self.get_frontend_name()
906
+ if self.framework:
907
+ pre["Framework"] = self.framework.name
908
+ if self.backend:
909
+ pre["Backend"] = self.backend.name
910
+ if len(self.platforms) > 0:
911
+ pre["Platform"] = self.get_platform_name()
912
+ if self.target:
913
+ pre["Target"] = self.target.name
914
+ post = {}
915
+ post["Features"] = self.get_all_feature_names()
916
+ # post["Config"] = self.get_all_configs(omit_paths=True, omit_defaults=True, omit_globals=True)
917
+ post["Config"] = self.get_all_configs(omit_paths=True, omit_defaults=False, omit_globals=True)
918
+ post["Postprocesses"] = self.get_all_postprocess_names()
919
+ post["Comment"] = self.comment if len(self.comment) > 0 else "-"
920
+ if self.failing:
921
+ post["Failing"] = True
922
+
923
+ self.export_stage(RunStage.RUN, optional=self.export_optional)
924
+
925
+ subs = []
926
+ if RunStage.LOAD in self.artifacts_per_stage:
927
+ names = self.artifacts_per_stage[RunStage.LOAD].keys()
928
+ subs = names
929
+ if RunStage.TUNE in self.artifacts_per_stage:
930
+ names = self.artifacts_per_stage[RunStage.TUNE].keys()
931
+ subs = names
932
+ if RunStage.BUILD in self.artifacts_per_stage:
933
+ names = self.artifacts_per_stage[RunStage.BUILD].keys()
934
+ subs = names
935
+ # metrics = Metrics()
936
+ metrics_by_sub = {}
937
+ if RunStage.COMPILE in self.artifacts_per_stage:
938
+ names = self.artifacts_per_stage[RunStage.COMPILE].keys()
939
+ subs = names
940
+ for name in self.artifacts_per_stage[RunStage.COMPILE]:
941
+ metrics_by_sub[name] = Metrics()
942
+ if (
943
+ len(self.artifacts_per_stage[RunStage.COMPILE][name]) > 1
944
+ ): # TODO: look for artifact of type metrics instead
945
+ compile_metrics_artifact = lookup_artifacts(
946
+ self.artifacts_per_stage[RunStage.COMPILE][name], name="metrics.csv"
947
+ )[0]
948
+ compile_metrics = Metrics.from_csv(compile_metrics_artifact.content)
949
+ metrics_by_sub[name] = compile_metrics
950
+
951
+ if RunStage.RUN in self.artifacts_per_stage:
952
+ names = self.artifacts_per_stage[RunStage.RUN].keys()
953
+ if (
954
+ RunStage.COMPILE in self.artifacts_per_stage
955
+ and len(self.artifacts_per_stage[RunStage.COMPILE][name]) > 1
956
+ ):
957
+ if not self.failing:
958
+ assert len(names) == len(subs), "Run and Compile Stage should have the same number of subs" # TODO: fix
959
+ if not self.failing:
960
+ subs = names
961
+ for name in self.artifacts_per_stage[RunStage.RUN]:
962
+ run_metrics_artifact = lookup_artifacts(
963
+ self.artifacts_per_stage[RunStage.RUN][name], name="metrics.csv"
964
+ )[0]
965
+ run_metrics = Metrics.from_csv(run_metrics_artifact.content)
966
+ # Combine with compile metrics
967
+ if name in metrics_by_sub:
968
+ metrics_data = metrics_by_sub[name].get_data()
969
+ else:
970
+ metrics_data = {}
971
+ run_metrics_data = run_metrics.get_data()
972
+ for key, value in metrics_data.items():
973
+ if key not in run_metrics_data:
974
+ run_metrics.add(key, value)
975
+ metrics_by_sub[name] = run_metrics
976
+
977
+ pres = []
978
+ mains = []
979
+ posts = []
980
+ for sub in subs:
981
+ if sub not in ["", "default"]:
982
+ pres.append({**pre, "Sub": sub})
983
+ else:
984
+ pres.append(pre)
985
+ if sub in metrics_by_sub:
986
+ main = metrics_by_sub[sub].get_data(include_optional=self.export_optional)
987
+ else:
988
+ main = {}
989
+ mains.append(main if len(main) > 0 else {"Incomplete": [True]})
990
+ posts.append(post) # TODO: omit for subs?
991
+ report.set(pre=pres, main=mains, post=posts)
992
+ return report
993
+
994
+ def export(self, path=None, optional=False):
995
+ """Write a run configuration to a disk."""
996
+ logger.debug("%s Exporting run to disk", self.prefix)
997
+ if path is not None:
998
+ raise NotImplementedError
999
+ assert not self.locked
1000
+ for stage in range(self.next_stage):
1001
+ if not self.has_stage(stage):
1002
+ continue
1003
+ self.export_stage(stage, optional=optional)
1004
+
1005
+ self.write_run_file()
1006
+
1007
+
1008
+ # TODO: implement close()? and use closing contextlib?