frost-gnn 0.3.1__tar.gz

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 (273) hide show
  1. frost_gnn-0.3.1/.env.example +5 -0
  2. frost_gnn-0.3.1/.github/scripts/build_pypi_release.py +128 -0
  3. frost_gnn-0.3.1/.github/scripts/update_coverage_badge.py +40 -0
  4. frost_gnn-0.3.1/.github/scripts/verify_pypi_release.py +95 -0
  5. frost_gnn-0.3.1/.github/workflows/coverage.yml +88 -0
  6. frost_gnn-0.3.1/.github/workflows/publish-pypi.yml +71 -0
  7. frost_gnn-0.3.1/.gitignore +109 -0
  8. frost_gnn-0.3.1/.python-version +1 -0
  9. frost_gnn-0.3.1/.vscode/settings.json +119 -0
  10. frost_gnn-0.3.1/CITATION.cff +38 -0
  11. frost_gnn-0.3.1/CMakeLists.txt +35 -0
  12. frost_gnn-0.3.1/LICENSE +21 -0
  13. frost_gnn-0.3.1/PKG-INFO +175 -0
  14. frost_gnn-0.3.1/README.md +133 -0
  15. frost_gnn-0.3.1/assets/frost-banner.png +0 -0
  16. frost_gnn-0.3.1/assets/frost-banner.prompt +9 -0
  17. frost_gnn-0.3.1/configs/config.yaml +65 -0
  18. frost_gnn-0.3.1/configs/model/apan.yaml +20 -0
  19. frost_gnn-0.3.1/configs/model/cawn.yaml +19 -0
  20. frost_gnn-0.3.1/configs/model/dygformer.yaml +25 -0
  21. frost_gnn-0.3.1/configs/model/graph_mixer.yaml +23 -0
  22. frost_gnn-0.3.1/configs/model/jodie.yaml +19 -0
  23. frost_gnn-0.3.1/configs/model/tcl.yaml +20 -0
  24. frost_gnn-0.3.1/configs/model/tgat.yaml +21 -0
  25. frost_gnn-0.3.1/configs/model/tgn.yaml +28 -0
  26. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_fixed.yaml +8 -0
  27. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_fixed_all.yaml +9 -0
  28. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_hist.yaml +7 -0
  29. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_post_train.yaml +8 -0
  30. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_random.yaml +10 -0
  31. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_tgb.yaml +11 -0
  32. frost_gnn-0.3.1/configs/neg_sampler/rnd_dst_then_tgb_wiki.yaml +4 -0
  33. frost_gnn-0.3.1/configs/ngh_sampler/recent.yaml +2 -0
  34. frost_gnn-0.3.1/configs/ngh_sampler/time_interval_aware.yaml +3 -0
  35. frost_gnn-0.3.1/configs/ngh_sampler/uniform.yaml +2 -0
  36. frost_gnn-0.3.1/configs/predict.yaml +13 -0
  37. frost_gnn-0.3.1/docs/model_training.md +229 -0
  38. frost_gnn-0.3.1/pyproject.toml +88 -0
  39. frost_gnn-0.3.1/run.py +7 -0
  40. frost_gnn-0.3.1/src/__init__.py +0 -0
  41. frost_gnn-0.3.1/src/frost/__init__.py +0 -0
  42. frost_gnn-0.3.1/src/frost/__main__.py +7 -0
  43. frost_gnn-0.3.1/src/frost/cli.py +94 -0
  44. frost_gnn-0.3.1/src/frost/data/__init__.py +35 -0
  45. frost_gnn-0.3.1/src/frost/data/catalog.py +86 -0
  46. frost_gnn-0.3.1/src/frost/data/cli.py +299 -0
  47. frost_gnn-0.3.1/src/frost/data/dgb.py +278 -0
  48. frost_gnn-0.3.1/src/frost/data/graph_policy.py +111 -0
  49. frost_gnn-0.3.1/src/frost/data/paths.py +167 -0
  50. frost_gnn-0.3.1/src/frost/data/preparation.py +690 -0
  51. frost_gnn-0.3.1/src/frost/data/sources.py +27 -0
  52. frost_gnn-0.3.1/src/frost/data/tgb.py +242 -0
  53. frost_gnn-0.3.1/src/frost/data/tgb_download.py +209 -0
  54. frost_gnn-0.3.1/src/frost/data/tgb_protocol.py +21 -0
  55. frost_gnn-0.3.1/src/frost/datatypes/__init__.py +0 -0
  56. frost_gnn-0.3.1/src/frost/datatypes/env.py +73 -0
  57. frost_gnn-0.3.1/src/frost/datatypes/graph.py +231 -0
  58. frost_gnn-0.3.1/src/frost/datatypes/mem_mail_box/__init__.py +0 -0
  59. frost_gnn-0.3.1/src/frost/datatypes/mem_mail_box/_data_ops.py +188 -0
  60. frost_gnn-0.3.1/src/frost/datatypes/mem_mail_box/box.py +64 -0
  61. frost_gnn-0.3.1/src/frost/datatypes/mem_mail_box/mail.py +47 -0
  62. frost_gnn-0.3.1/src/frost/datatypes/mem_mail_box/mem.py +53 -0
  63. frost_gnn-0.3.1/src/frost/datatypes/meta.py +56 -0
  64. frost_gnn-0.3.1/src/frost/datatypes/n_feat_nid.py +94 -0
  65. frost_gnn-0.3.1/src/frost/datatypes/plan.py +137 -0
  66. frost_gnn-0.3.1/src/frost/datatypes/result.py +28 -0
  67. frost_gnn-0.3.1/src/frost/datatypes/sampled_layout.py +124 -0
  68. frost_gnn-0.3.1/src/frost/datatypes/tasks.py +138 -0
  69. frost_gnn-0.3.1/src/frost/launch/__init__.py +1 -0
  70. frost_gnn-0.3.1/src/frost/launch/affinity.py +593 -0
  71. frost_gnn-0.3.1/src/frost/launch/config.py +93 -0
  72. frost_gnn-0.3.1/src/frost/launch/dispatch.py +62 -0
  73. frost_gnn-0.3.1/src/frost/launch/distributed.py +70 -0
  74. frost_gnn-0.3.1/src/frost/launch/factory.py +76 -0
  75. frost_gnn-0.3.1/src/frost/launch/gpu_topology.py +235 -0
  76. frost_gnn-0.3.1/src/frost/launch/plan.py +67 -0
  77. frost_gnn-0.3.1/src/frost/launch/single_gpu_resident.py +127 -0
  78. frost_gnn-0.3.1/src/frost/launch/storage.py +200 -0
  79. frost_gnn-0.3.1/src/frost/launcher.py +575 -0
  80. frost_gnn-0.3.1/src/frost/metrics.py +53 -0
  81. frost_gnn-0.3.1/src/frost/models/__init__.py +0 -0
  82. frost_gnn-0.3.1/src/frost/models/contracts.py +95 -0
  83. frost_gnn-0.3.1/src/frost/models/encoder/__init__.py +0 -0
  84. frost_gnn-0.3.1/src/frost/models/encoder/apan.py +42 -0
  85. frost_gnn-0.3.1/src/frost/models/encoder/cawn.py +495 -0
  86. frost_gnn-0.3.1/src/frost/models/encoder/dygformer.py +563 -0
  87. frost_gnn-0.3.1/src/frost/models/encoder/graph_mixer.py +328 -0
  88. frost_gnn-0.3.1/src/frost/models/encoder/jodie.py +104 -0
  89. frost_gnn-0.3.1/src/frost/models/encoder/tcl.py +292 -0
  90. frost_gnn-0.3.1/src/frost/models/encoder/tgat.py +305 -0
  91. frost_gnn-0.3.1/src/frost/models/encoder/tgn.py +172 -0
  92. frost_gnn-0.3.1/src/frost/models/header.py +44 -0
  93. frost_gnn-0.3.1/src/frost/models/mem_updater.py +266 -0
  94. frost_gnn-0.3.1/src/frost/models/modules/__init__.py +0 -0
  95. frost_gnn-0.3.1/src/frost/models/modules/temporal_graph_attention_layer.py +247 -0
  96. frost_gnn-0.3.1/src/frost/models/modules/time_encoder.py +37 -0
  97. frost_gnn-0.3.1/src/frost/models/outputs.py +79 -0
  98. frost_gnn-0.3.1/src/frost/models/protocols.py +25 -0
  99. frost_gnn-0.3.1/src/frost/models/recipes.py +183 -0
  100. frost_gnn-0.3.1/src/frost/models/temporal_link_model.py +67 -0
  101. frost_gnn-0.3.1/src/frost/neg_sampler/__init__.py +25 -0
  102. frost_gnn-0.3.1/src/frost/neg_sampler/_contextual.py +42 -0
  103. frost_gnn-0.3.1/src/frost/neg_sampler/_utils.py +335 -0
  104. frost_gnn-0.3.1/src/frost/neg_sampler/historical.py +151 -0
  105. frost_gnn-0.3.1/src/frost/neg_sampler/inductive.py +167 -0
  106. frost_gnn-0.3.1/src/frost/neg_sampler/precomputed.py +144 -0
  107. frost_gnn-0.3.1/src/frost/neg_sampler/protocols.py +33 -0
  108. frost_gnn-0.3.1/src/frost/neg_sampler/random.py +239 -0
  109. frost_gnn-0.3.1/src/frost/neg_sampler/tgb_eval.py +238 -0
  110. frost_gnn-0.3.1/src/frost/predict/__init__.py +1 -0
  111. frost_gnn-0.3.1/src/frost/predict/cli.py +215 -0
  112. frost_gnn-0.3.1/src/frost/predict/config.py +67 -0
  113. frost_gnn-0.3.1/src/frost/predict/data.py +179 -0
  114. frost_gnn-0.3.1/src/frost/predict/model.py +437 -0
  115. frost_gnn-0.3.1/src/frost/py.typed +0 -0
  116. frost_gnn-0.3.1/src/frost/runtime/_early_stopping.py +20 -0
  117. frost_gnn-0.3.1/src/frost/runtime/_thread_base.py +201 -0
  118. frost_gnn-0.3.1/src/frost/runtime/control_plane.py +273 -0
  119. frost_gnn-0.3.1/src/frost/runtime/dynamic_state_policy.py +98 -0
  120. frost_gnn-0.3.1/src/frost/runtime/host/__init__.py +9 -0
  121. frost_gnn-0.3.1/src/frost/runtime/host/_batch_balance.py +70 -0
  122. frost_gnn-0.3.1/src/frost/runtime/host/_config.py +52 -0
  123. frost_gnn-0.3.1/src/frost/runtime/host/_engine.py +821 -0
  124. frost_gnn-0.3.1/src/frost/runtime/host/_plan_router.py +338 -0
  125. frost_gnn-0.3.1/src/frost/runtime/host/_scatter.py +242 -0
  126. frost_gnn-0.3.1/src/frost/runtime/host/analyzer.py +303 -0
  127. frost_gnn-0.3.1/src/frost/runtime/host/p2p_planner/__init__.py +0 -0
  128. frost_gnn-0.3.1/src/frost/runtime/host/p2p_planner/_dynamic_planner.py +851 -0
  129. frost_gnn-0.3.1/src/frost/runtime/host/p2p_planner/_static_planner.py +237 -0
  130. frost_gnn-0.3.1/src/frost/runtime/host/protocols.py +59 -0
  131. frost_gnn-0.3.1/src/frost/runtime/host/types.py +39 -0
  132. frost_gnn-0.3.1/src/frost/runtime/protocols.py +11 -0
  133. frost_gnn-0.3.1/src/frost/runtime/worker/__init__.py +0 -0
  134. frost_gnn-0.3.1/src/frost/runtime/worker/_batch_convergence.py +103 -0
  135. frost_gnn-0.3.1/src/frost/runtime/worker/_benchmark_observer.py +271 -0
  136. frost_gnn-0.3.1/src/frost/runtime/worker/_checkpoint.py +71 -0
  137. frost_gnn-0.3.1/src/frost/runtime/worker/_config.py +58 -0
  138. frost_gnn-0.3.1/src/frost/runtime/worker/_direct_writeback.py +182 -0
  139. frost_gnn-0.3.1/src/frost/runtime/worker/_direct_writeback_sync.py +56 -0
  140. frost_gnn-0.3.1/src/frost/runtime/worker/_dynamic_assembler.py +319 -0
  141. frost_gnn-0.3.1/src/frost/runtime/worker/_dynamic_state.py +698 -0
  142. frost_gnn-0.3.1/src/frost/runtime/worker/_engine.py +424 -0
  143. frost_gnn-0.3.1/src/frost/runtime/worker/_feature_staging.py +342 -0
  144. frost_gnn-0.3.1/src/frost/runtime/worker/_gpu_resident_writeback.py +162 -0
  145. frost_gnn-0.3.1/src/frost/runtime/worker/_h2d_credit.py +139 -0
  146. frost_gnn-0.3.1/src/frost/runtime/worker/_host_to_device.py +253 -0
  147. frost_gnn-0.3.1/src/frost/runtime/worker/_pipeline.py +308 -0
  148. frost_gnn-0.3.1/src/frost/runtime/worker/_receiver.py +185 -0
  149. frost_gnn-0.3.1/src/frost/runtime/worker/_split_benchmark_observer.py +152 -0
  150. frost_gnn-0.3.1/src/frost/runtime/worker/_state_coordinator.py +384 -0
  151. frost_gnn-0.3.1/src/frost/runtime/worker/_static_assembler.py +388 -0
  152. frost_gnn-0.3.1/src/frost/runtime/worker/_telemetry.py +95 -0
  153. frost_gnn-0.3.1/src/frost/runtime/worker/_tensor_lifetime.py +52 -0
  154. frost_gnn-0.3.1/src/frost/runtime/worker/_trainer.py +458 -0
  155. frost_gnn-0.3.1/src/frost/runtime/worker/mailbox_router.py +39 -0
  156. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/__init__.py +0 -0
  157. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/_p2p_comm.py +199 -0
  158. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/_task_handlers.py +313 -0
  159. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/_writer.py +335 -0
  160. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/utils/__init__.py +0 -0
  161. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/utils/_interaction_ops.py +79 -0
  162. frost_gnn-0.3.1/src/frost/runtime/worker/p2p_syncer/utils/_p2p_task_maker.py +103 -0
  163. frost_gnn-0.3.1/src/frost/runtime/worker/types.py +35 -0
  164. frost_gnn-0.3.1/src/frost/sampling/ngh_sampler.py +285 -0
  165. frost_gnn-0.3.1/src/frost/sampling/protocols.py +26 -0
  166. frost_gnn-0.3.1/src/frost/seq_sampler/__init__.py +96 -0
  167. frost_gnn-0.3.1/src/frost/seq_sampler/_core.pyi +224 -0
  168. frost_gnn-0.3.1/src/frost/seq_sampler/planner_ownership.hpp +315 -0
  169. frost_gnn-0.3.1/src/frost/seq_sampler/sampler_base.h +212 -0
  170. frost_gnn-0.3.1/src/frost/seq_sampler/sampler_common.h +75 -0
  171. frost_gnn-0.3.1/src/frost/seq_sampler/sampler_seq.hpp +251 -0
  172. frost_gnn-0.3.1/src/frost/seq_sampler/seq_sampler_main.cpp +67 -0
  173. frost_gnn-0.3.1/src/frost/shm/__init__.py +0 -0
  174. frost_gnn-0.3.1/src/frost/shm/_core.py +401 -0
  175. frost_gnn-0.3.1/src/frost/shm/control_plane.py +161 -0
  176. frost_gnn-0.3.1/src/frost/shm/dataset_mgr.py +707 -0
  177. frost_gnn-0.3.1/src/frost/shm/dynamic_state_sync.py +374 -0
  178. frost_gnn-0.3.1/src/frost/shm/mem_mgr.py +238 -0
  179. frost_gnn-0.3.1/src/frost/shm/ring_buf.py +300 -0
  180. frost_gnn-0.3.1/src/frost/tpnet/LICENSE +21 -0
  181. frost_gnn-0.3.1/src/frost/tpnet/NOTICE.md +45 -0
  182. frost_gnn-0.3.1/src/frost/tpnet/__init__.py +2 -0
  183. frost_gnn-0.3.1/src/frost/tpnet/config.py +228 -0
  184. frost_gnn-0.3.1/src/frost/tpnet/data.py +326 -0
  185. frost_gnn-0.3.1/src/frost/tpnet/layers.py +107 -0
  186. frost_gnn-0.3.1/src/frost/tpnet/model.py +610 -0
  187. frost_gnn-0.3.1/src/frost/tpnet/sampling.py +747 -0
  188. frost_gnn-0.3.1/src/frost/tpnet/train.py +771 -0
  189. frost_gnn-0.3.1/src/frost/tpnet/training.py +63 -0
  190. frost_gnn-0.3.1/src/frost/utils/__init__.py +0 -0
  191. frost_gnn-0.3.1/src/frost/utils/_logger.py +34 -0
  192. frost_gnn-0.3.1/src/frost/utils/dtypes.py +35 -0
  193. frost_gnn-0.3.1/src/frost/utils/tensor_set.py +128 -0
  194. frost_gnn-0.3.1/src/frost/utils/utils.py +24 -0
  195. frost_gnn-0.3.1/tests/fixtures/tpnet/README.md +45 -0
  196. frost_gnn-0.3.1/tests/fixtures/tpnet/classic.json +1781 -0
  197. frost_gnn-0.3.1/tests/fixtures/tpnet/tgb.json +1934 -0
  198. frost_gnn-0.3.1/tests/test_access_plan_analyzer.py +168 -0
  199. frost_gnn-0.3.1/tests/test_affinity.py +389 -0
  200. frost_gnn-0.3.1/tests/test_architecture_boundaries.py +40 -0
  201. frost_gnn-0.3.1/tests/test_batch_balance.py +35 -0
  202. frost_gnn-0.3.1/tests/test_batch_convergence.py +36 -0
  203. frost_gnn-0.3.1/tests/test_control_plane.py +165 -0
  204. frost_gnn-0.3.1/tests/test_data_catalog.py +61 -0
  205. frost_gnn-0.3.1/tests/test_data_ops_determinism.py +196 -0
  206. frost_gnn-0.3.1/tests/test_data_paths.py +82 -0
  207. frost_gnn-0.3.1/tests/test_data_preparation.py +249 -0
  208. frost_gnn-0.3.1/tests/test_dataset_mgr.py +424 -0
  209. frost_gnn-0.3.1/tests/test_direct_dynamic_writeback.py +270 -0
  210. frost_gnn-0.3.1/tests/test_direct_writeback_sync.py +86 -0
  211. frost_gnn-0.3.1/tests/test_dispatch.py +48 -0
  212. frost_gnn-0.3.1/tests/test_dtypes.py +24 -0
  213. frost_gnn-0.3.1/tests/test_dynamic_planner_debug_dump.py +103 -0
  214. frost_gnn-0.3.1/tests/test_dynamic_planner_routing.py +242 -0
  215. frost_gnn-0.3.1/tests/test_dynamic_state_policy.py +93 -0
  216. frost_gnn-0.3.1/tests/test_dynamic_state_sync_runtime.py +368 -0
  217. frost_gnn-0.3.1/tests/test_early_stopping.py +17 -0
  218. frost_gnn-0.3.1/tests/test_encoders.py +513 -0
  219. frost_gnn-0.3.1/tests/test_eval_metric_selection.py +34 -0
  220. frost_gnn-0.3.1/tests/test_eval_sampler_reset.py +101 -0
  221. frost_gnn-0.3.1/tests/test_feature_staging.py +121 -0
  222. frost_gnn-0.3.1/tests/test_gpu_topology.py +89 -0
  223. frost_gnn-0.3.1/tests/test_graph.py +252 -0
  224. frost_gnn-0.3.1/tests/test_host_macro_metadata.py +100 -0
  225. frost_gnn-0.3.1/tests/test_host_protocols.py +6 -0
  226. frost_gnn-0.3.1/tests/test_host_sampler_inputs.py +78 -0
  227. frost_gnn-0.3.1/tests/test_interaction_ops.py +44 -0
  228. frost_gnn-0.3.1/tests/test_launcher.py +118 -0
  229. frost_gnn-0.3.1/tests/test_mailbox_router.py +27 -0
  230. frost_gnn-0.3.1/tests/test_mem_mgr.py +229 -0
  231. frost_gnn-0.3.1/tests/test_mem_updater.py +38 -0
  232. frost_gnn-0.3.1/tests/test_meta.py +44 -0
  233. frost_gnn-0.3.1/tests/test_model_configs.py +95 -0
  234. frost_gnn-0.3.1/tests/test_model_contracts.py +66 -0
  235. frost_gnn-0.3.1/tests/test_models.py +345 -0
  236. frost_gnn-0.3.1/tests/test_neg_sampler.py +315 -0
  237. frost_gnn-0.3.1/tests/test_ngh_sampler.py +214 -0
  238. frost_gnn-0.3.1/tests/test_p2p_comm.py +50 -0
  239. frost_gnn-0.3.1/tests/test_p2p_task_maker.py +82 -0
  240. frost_gnn-0.3.1/tests/test_plan.py +189 -0
  241. frost_gnn-0.3.1/tests/test_plan_router.py +80 -0
  242. frost_gnn-0.3.1/tests/test_prediction_cli.py +397 -0
  243. frost_gnn-0.3.1/tests/test_pypi_release.py +109 -0
  244. frost_gnn-0.3.1/tests/test_receiver_control_plane.py +53 -0
  245. frost_gnn-0.3.1/tests/test_repository_hygiene.py +138 -0
  246. frost_gnn-0.3.1/tests/test_result.py +34 -0
  247. frost_gnn-0.3.1/tests/test_ring_buf.py +134 -0
  248. frost_gnn-0.3.1/tests/test_run_stable_mode.py +47 -0
  249. frost_gnn-0.3.1/tests/test_sampled_layout.py +99 -0
  250. frost_gnn-0.3.1/tests/test_seq_sampler.py +178 -0
  251. frost_gnn-0.3.1/tests/test_shared_dynamic_state_sync.py +373 -0
  252. frost_gnn-0.3.1/tests/test_shm_numa.py +82 -0
  253. frost_gnn-0.3.1/tests/test_single_gpu_resident.py +71 -0
  254. frost_gnn-0.3.1/tests/test_state_coordinator.py +393 -0
  255. frost_gnn-0.3.1/tests/test_static_assembler.py +283 -0
  256. frost_gnn-0.3.1/tests/test_static_planner_timing.py +136 -0
  257. frost_gnn-0.3.1/tests/test_temporal_attention.py +256 -0
  258. frost_gnn-0.3.1/tests/test_tensor_lifetime.py +114 -0
  259. frost_gnn-0.3.1/tests/test_tensor_set.py +101 -0
  260. frost_gnn-0.3.1/tests/test_tgb_download.py +121 -0
  261. frost_gnn-0.3.1/tests/test_tgb_eval.py +204 -0
  262. frost_gnn-0.3.1/tests/test_tgb_sampler_config.py +165 -0
  263. frost_gnn-0.3.1/tests/test_tpnet_backend.py +428 -0
  264. frost_gnn-0.3.1/tests/test_tpnet_train.py +210 -0
  265. frost_gnn-0.3.1/tests/test_train_only_epoch.py +129 -0
  266. frost_gnn-0.3.1/tests/test_worker_benchmark_observer.py +76 -0
  267. frost_gnn-0.3.1/tests/test_worker_epoch_reset.py +217 -0
  268. frost_gnn-0.3.1/tests/test_worker_grad_context.py +61 -0
  269. frost_gnn-0.3.1/tests/test_worker_pipeline_init.py +57 -0
  270. frost_gnn-0.3.1/tests/test_worker_shm_sync.py +412 -0
  271. frost_gnn-0.3.1/tests/test_worker_split_benchmark_observer.py +119 -0
  272. frost_gnn-0.3.1/tests/test_writer_empty_writeback.py +96 -0
  273. frost_gnn-0.3.1/uv.lock +1285 -0
@@ -0,0 +1,5 @@
1
+ # Copy this file to .env.local and replace the example paths.
2
+ # .env.local is loaded automatically.
3
+ # Prepared data: https://huggingface.co/datasets/dttutty/frost_dataset
4
+ FROST_DATA_ROOT=/path/to/frost_dataset
5
+ TGB_DATA_ROOT=/path/to/tgb
@@ -0,0 +1,128 @@
1
+ """Build public distributions from a clean commit without a second README source."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import hashlib
7
+ import io
8
+ import json
9
+ from pathlib import Path
10
+ import re
11
+ import shutil
12
+ import subprocess
13
+ import tarfile
14
+ import tempfile
15
+ import tomllib
16
+
17
+
18
+ PUBLIC_INSTALL = """## 📦 Install
19
+
20
+ On Linux x86-64 (glibc 2.31+) with Python 3.12:
21
+
22
+ ```bash
23
+ python -m pip install frost_gnn
24
+ ```
25
+
26
+ The Python import is `frost`; the command is `frost`. The CSV trainer runs on CPU
27
+ unless you select a GPU. To use CPU-only PyTorch, install `torch==2.9.0` from
28
+ `https://download.pytorch.org/whl/cpu` before installing FROST. CUDA execution
29
+ requires a compatible NVIDIA driver.
30
+
31
+ If upgrading from the old FROST 0.1.0 distribution, first run
32
+ `python -m pip uninstall frost` to avoid overlapping package files.
33
+
34
+ """
35
+
36
+
37
+ def public_readme(source: str) -> str:
38
+ """Reuse user documentation, stripping repository-only artwork and links."""
39
+ intro = source.index("A multi-GPU framework")
40
+ result = "# FROST\n\n" + source[intro:]
41
+ result, count = re.subn(
42
+ r"^## 📦 Install\n.*?(?=^## )",
43
+ PUBLIC_INSTALL,
44
+ result,
45
+ count=1,
46
+ flags=re.MULTILINE | re.DOTALL,
47
+ )
48
+ if count != 1:
49
+ raise ValueError("Expected exactly one installation section in README.md.")
50
+
51
+ def link(match):
52
+ label, target = match.groups()
53
+ if "://" in target or target.startswith(("#", "mailto:")):
54
+ return match.group(0)
55
+ return f"{label} (`{target.split('#')[0]}` in the source distribution)"
56
+
57
+ result = re.sub(r"\[([^\[\]]+)\]\(([^\s)]+)\)", link, result)
58
+ if "github.com/dttutty/frost_dev" in result:
59
+ raise ValueError(
60
+ "The public package description still contains private repository links."
61
+ )
62
+ return result
63
+
64
+
65
+ def build(output: Path) -> None:
66
+ repo = Path(__file__).resolve().parents[2]
67
+ subprocess.run(["git", "diff", "--exit-code", "HEAD", "--"], cwd=repo, check=True)
68
+ commit = subprocess.check_output(
69
+ ["git", "rev-parse", "HEAD"], cwd=repo, text=True
70
+ ).strip()
71
+ output = output.resolve()
72
+ output.mkdir(parents=True, exist_ok=False)
73
+ raw, assets = output / "raw", output / "assets"
74
+ assets.mkdir()
75
+ archive = subprocess.check_output(["git", "archive", "HEAD"], cwd=repo)
76
+ with tempfile.TemporaryDirectory(prefix="frost-pypi-source-") as temp:
77
+ stage = Path(temp)
78
+ with tarfile.open(fileobj=io.BytesIO(archive)) as source:
79
+ source.extractall(stage, filter="data")
80
+ readme = stage / "README.md"
81
+ readme.write_text(public_readme(readme.read_text()))
82
+ metadata = tomllib.loads((stage / "pyproject.toml").read_text())["project"]
83
+ subprocess.run(["uv", "build", "--out-dir", str(raw)], cwd=stage, check=True)
84
+ wheels, sdists = list(raw.glob("*.whl")), list(raw.glob("*.tar.gz"))
85
+ if len(wheels) != 1 or len(sdists) != 1:
86
+ raise ValueError("Expected one wheel and one source distribution.")
87
+ subprocess.run(
88
+ [
89
+ "uvx",
90
+ "--with",
91
+ "patchelf",
92
+ "auditwheel",
93
+ "repair",
94
+ "--plat",
95
+ "manylinux_2_31_x86_64",
96
+ "--wheel-dir",
97
+ str(assets),
98
+ str(wheels[0]),
99
+ ],
100
+ check=True,
101
+ )
102
+ shutil.copy2(sdists[0], assets / sdists[0].name)
103
+ subprocess.run(
104
+ ["uvx", "twine", "check", *map(str, sorted(assets.iterdir()))], check=True
105
+ )
106
+ info = {
107
+ "distribution": metadata["name"],
108
+ "version": metadata["version"],
109
+ "source_commit": commit,
110
+ "platform": "manylinux_2_31_x86_64",
111
+ "python": "3.12",
112
+ "readme": "Generated from README.md by build_pypi_release.py",
113
+ }
114
+ (assets / "BUILD_INFO.json").write_text(json.dumps(info, indent=2) + "\n")
115
+ manifest = "".join(
116
+ f"{hashlib.sha256(p.read_bytes()).hexdigest()} {p.name}\n"
117
+ for p in sorted(assets.iterdir())
118
+ )
119
+ (assets / "SHA256SUMS").write_text(manifest)
120
+ print(f"Release artifacts: {assets}")
121
+
122
+
123
+ if __name__ == "__main__":
124
+ parser = argparse.ArgumentParser(description=__doc__)
125
+ parser.add_argument(
126
+ "--output", type=Path, required=True, help="New build directory."
127
+ )
128
+ build(parser.parse_args().output)
@@ -0,0 +1,40 @@
1
+ """Update the README badge from a real coverage.py JSON report."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ import re
10
+
11
+
12
+ def main() -> None:
13
+ parser = argparse.ArgumentParser(description=__doc__)
14
+ parser.add_argument("report", type=Path)
15
+ args = parser.parse_args()
16
+ totals = json.loads(args.report.read_text())["totals"]
17
+ total = int(totals["num_statements"])
18
+ covered = int(totals["covered_lines"])
19
+ if not 0 <= covered <= total or total == 0:
20
+ raise ValueError("Coverage report must contain a nonempty Python source measurement")
21
+ percent = 100 * covered / total
22
+ color = "brightgreen" if percent >= 80 else "yellowgreen" if percent >= 60 else "orange"
23
+ repository = os.environ.get("GITHUB_REPOSITORY", "dttutty/frost_dev")
24
+ badge = (
25
+ f"[![CPU coverage](https://img.shields.io/badge/CPU%20coverage-"
26
+ f"{percent:.1f}%25-{color})]"
27
+ f"(https://github.com/{repository}/actions/workflows/coverage.yml)"
28
+ )
29
+ readme = Path(__file__).resolve().parents[2] / "README.md"
30
+ updated, count = re.subn(
31
+ r"^\[!\[CPU coverage\].*$", badge, readme.read_text(), flags=re.MULTILINE
32
+ )
33
+ if count != 1:
34
+ raise ValueError(f"Expected one CPU coverage badge in README, found {count}")
35
+ readme.write_text(updated)
36
+ print(f"CPU Python line coverage: {covered}/{total} = {percent:.1f}%")
37
+
38
+
39
+ if __name__ == "__main__":
40
+ main()
@@ -0,0 +1,95 @@
1
+ """Verify private release assets before copying public distributions to dist/."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from email.parser import BytesParser
7
+ import hashlib
8
+ import json
9
+ from pathlib import Path
10
+ import re
11
+ import shutil
12
+ import tarfile
13
+ import zipfile
14
+
15
+
16
+ def verify(source: Path, output: Path, tag: str, commit: str) -> None:
17
+ if not re.fullmatch(r"v\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?", tag):
18
+ raise ValueError("Expected a version tag such as v0.3.1.")
19
+ version = tag[1:]
20
+ hashes = {}
21
+ for line in (source / "SHA256SUMS").read_text().splitlines():
22
+ digest, name = line.split(" ", 1)
23
+ if (
24
+ not re.fullmatch(r"[a-f0-9]{64}", digest)
25
+ or Path(name).name != name
26
+ or name in hashes
27
+ ):
28
+ raise ValueError("Invalid checksum manifest entry.")
29
+ hashes[name] = digest
30
+ files = {p.name for p in source.iterdir() if p.is_file()} - {"SHA256SUMS"}
31
+ if files != hashes.keys():
32
+ raise ValueError("Checksum manifest must cover every release asset.")
33
+ for name, digest in hashes.items():
34
+ if hashlib.sha256((source / name).read_bytes()).hexdigest() != digest:
35
+ raise ValueError(f"Checksum mismatch: {name}")
36
+ info = json.loads((source / "BUILD_INFO.json").read_text())
37
+ if info["version"] != version or info["source_commit"] != commit:
38
+ raise ValueError("Release version or source commit does not match the tag.")
39
+ distributions = sorted(
40
+ p for p in source.iterdir() if p.suffix == ".whl" or p.name.endswith(".tar.gz")
41
+ )
42
+ if (
43
+ not any(p.suffix == ".whl" for p in distributions)
44
+ or sum(p.name.endswith(".tar.gz") for p in distributions) != 1
45
+ ):
46
+ raise ValueError("Expected at least one wheel and one source distribution.")
47
+ for path in distributions:
48
+ if path.suffix == ".whl":
49
+ with zipfile.ZipFile(path) as wheel:
50
+ names = [
51
+ n for n in wheel.namelist() if n.endswith(".dist-info/METADATA")
52
+ ]
53
+ if len(names) != 1:
54
+ raise ValueError("Expected one wheel metadata record.")
55
+ metadata = wheel.read(names[0])
56
+ else:
57
+ with tarfile.open(path) as sdist:
58
+ names = [
59
+ m
60
+ for m in sdist.getmembers()
61
+ if m.name.count("/") == 1 and m.name.endswith("/PKG-INFO")
62
+ ]
63
+ if len(names) != 1:
64
+ raise ValueError("Expected one source metadata record.")
65
+ stream = sdist.extractfile(names[0])
66
+ if stream is None:
67
+ raise ValueError("Missing source metadata.")
68
+ metadata = stream.read()
69
+ record = BytesParser().parsebytes(metadata)
70
+ name = re.sub(r"[-_.]+", "-", record["Name"]).lower()
71
+ if name != "frost-gnn" or record["Version"] != version:
72
+ raise ValueError(f"Wrong distribution name/version: {path.name}")
73
+ if (
74
+ b"github.com/dttutty/frost_dev" in metadata
75
+ or b"not available yet" in metadata
76
+ ):
77
+ raise ValueError(
78
+ "Distribution description is not ready for public installation."
79
+ )
80
+ output.mkdir(parents=True, exist_ok=False)
81
+ for path in distributions:
82
+ shutil.copy2(path, output / path.name)
83
+ print(
84
+ f"Verified {len(distributions)} frost-gnn {version} distributions from {commit}."
85
+ )
86
+
87
+
88
+ if __name__ == "__main__":
89
+ parser = argparse.ArgumentParser(description=__doc__)
90
+ parser.add_argument("--source", type=Path, required=True)
91
+ parser.add_argument("--output", type=Path, required=True)
92
+ parser.add_argument("--tag", required=True)
93
+ parser.add_argument("--commit", required=True)
94
+ args = parser.parse_args()
95
+ verify(args.source, args.output, args.tag, args.commit)
@@ -0,0 +1,88 @@
1
+ name: CPU coverage
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths-ignore: [README.md]
7
+ pull_request:
8
+ paths-ignore: [README.md]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: cpu-coverage-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ tests:
20
+ runs-on: ubuntu-22.04
21
+ timeout-minutes: 20
22
+ env:
23
+ CUDA_VISIBLE_DEVICES: ""
24
+ OMP_NUM_THREADS: "2"
25
+ MKL_NUM_THREADS: "2"
26
+ CMAKE_BUILD_PARALLEL_LEVEL: "2"
27
+ steps:
28
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
29
+ - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
30
+ with:
31
+ python-version: "3.12"
32
+ enable-cache: true
33
+ - name: Install CPU test environment
34
+ run: |
35
+ uv venv --python 3.12
36
+ uv pip install torch==2.9.0 --index-url https://download.pytorch.org/whl/cpu
37
+ uv pip install --no-sources -e . pytest pytest-cov
38
+ - name: Measure Python line coverage
39
+ run: >-
40
+ uv run --no-sync pytest tests/ -q
41
+ --cov=src/frost --cov-report=term:skip-covered
42
+ --cov-report=json:coverage.json --cov-report=html:htmlcov
43
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
44
+ if: always()
45
+ with:
46
+ name: cpu-coverage
47
+ path: |
48
+ coverage.json
49
+ htmlcov/
50
+ retention-days: 14
51
+ if-no-files-found: warn
52
+
53
+ badge:
54
+ needs: tests
55
+ if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
56
+ runs-on: ubuntu-22.04
57
+ timeout-minutes: 5
58
+ permissions:
59
+ contents: write
60
+ steps:
61
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
62
+ with:
63
+ ref: ${{ github.sha }}
64
+ - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
65
+ with:
66
+ python-version: "3.12"
67
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
68
+ with:
69
+ name: cpu-coverage
70
+ path: ${{ runner.temp }}/cpu-coverage
71
+ - name: Update measured badge on the tested revision
72
+ env:
73
+ COVERAGE_REPORT: ${{ runner.temp }}/cpu-coverage/coverage.json
74
+ run: |
75
+ uv run --no-project --python 3.12 python .github/scripts/update_coverage_badge.py "$COVERAGE_REPORT"
76
+ if git diff --quiet -- README.md; then
77
+ exit 0
78
+ fi
79
+ git fetch origin main
80
+ if [ "$(git rev-parse origin/main)" != "$GITHUB_SHA" ]; then
81
+ echo "main advanced; a newer run will update the badge."
82
+ exit 0
83
+ fi
84
+ git config user.name 'github-actions[bot]'
85
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
86
+ git add README.md
87
+ git commit -m 'docs: update CPU coverage badge'
88
+ git push origin HEAD:main
@@ -0,0 +1,71 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ release_tag:
7
+ description: 'Verified GitHub Release tag, e.g. v0.3.1'
8
+ required: true
9
+ type: string
10
+ dry_run:
11
+ description: 'Validate artifacts without publishing'
12
+ required: true
13
+ default: true
14
+ type: boolean
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ concurrency:
20
+ group: pypi-publication
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ prepare:
25
+ if: github.ref == 'refs/heads/main'
26
+ runs-on: ubuntu-22.04
27
+ timeout-minutes: 10
28
+ steps:
29
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
30
+ with:
31
+ fetch-depth: 0
32
+ - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
33
+ with:
34
+ python-version: '3.12'
35
+ - name: Download and verify release assets
36
+ env:
37
+ GH_TOKEN: ${{ github.token }}
38
+ RELEASE_TAG: ${{ inputs.release_tag }}
39
+ run: |
40
+ [[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+((a|b|rc)[0-9]+)?$ ]]
41
+ gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --dir release-assets
42
+ release_commit=$(git rev-parse --verify "${RELEASE_TAG}^{commit}")
43
+ uv run --no-project --python 3.12 python .github/scripts/verify_pypi_release.py \
44
+ --source release-assets --output dist --tag "$RELEASE_TAG" --commit "$release_commit"
45
+ uvx twine check dist/*
46
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
47
+ with:
48
+ name: pypi-distributions
49
+ path: dist/
50
+ if-no-files-found: error
51
+ retention-days: 7
52
+
53
+ publish:
54
+ needs: prepare
55
+ if: github.ref == 'refs/heads/main' && !inputs.dry_run
56
+ runs-on: ubuntu-22.04
57
+ timeout-minutes: 10
58
+ environment:
59
+ name: pypi
60
+ url: https://pypi.org/project/frost-gnn/
61
+ permissions:
62
+ id-token: write
63
+ steps:
64
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
65
+ with:
66
+ name: pypi-distributions
67
+ path: dist/
68
+ - name: Publish verified distributions
69
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
70
+ with:
71
+ packages-dir: dist/
@@ -0,0 +1,109 @@
1
+ # Data & model artifacts
2
+ /models/
3
+ checkpoints/
4
+ data/
5
+ !src/frost/data/
6
+ !src/frost/data/*.py
7
+ datasets/
8
+ *.pt
9
+ *.pth
10
+ *.pkl
11
+ *.h5
12
+ *.ckpt
13
+ *.gz
14
+ *.zip
15
+
16
+ # Build
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+ *.so
34
+
35
+ # Python
36
+ __pycache__/
37
+ *.pyc
38
+ *.pyo
39
+ *.pyd
40
+ .Python
41
+ .venv/
42
+ env/
43
+ venv/
44
+ ENV/
45
+ .pytest_cache/
46
+ .mypy_cache/
47
+
48
+ # Logs & outputs
49
+ outputs/
50
+ logs/
51
+ artifacts/
52
+ /results/
53
+ *.log
54
+
55
+ # Experiment
56
+ experiment_logs*
57
+ result.json
58
+ _tgmem_out/
59
+ wandb/
60
+ .wandb/
61
+ tensorboard/
62
+ runs/
63
+ slurm/
64
+
65
+ # IDE & OS
66
+ .idea/
67
+ .claude/
68
+ *.swp
69
+ *.swo
70
+ *~
71
+ .DS_Store
72
+ .ipynb_checkpoints/
73
+ *.ipynb
74
+
75
+ # Paper repositories are maintained outside the code repository.
76
+ /overleaf/
77
+ /revision/
78
+
79
+ # Misc
80
+ .env.local
81
+ .trash/
82
+ .ruff_cache/
83
+ hydralance/
84
+ temp/
85
+ traces/
86
+
87
+ DATA/DGB*
88
+ DATA/.temp
89
+ DATA/TGL*
90
+ TGL_DATA/
91
+ /DATA
92
+ tmp/
93
+ *.tar
94
+ *.tar.gz
95
+
96
+ # Generated experiment artifacts
97
+ seed_sweeps/**
98
+ seed_sweeps_old/**
99
+
100
+ download_data.sh
101
+ *.txt
102
+ *.tsv
103
+
104
+ # Local reproduction tools and compatibility navigation (not part of the library).
105
+ /benchmarks
106
+ /docs/artifact_organization.md
107
+ /docs/camera_ready_sources.md
108
+ /.vscode/launch.json
109
+ /.vscode/tasks.json
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,119 @@
1
+ {
2
+ "git.detectSubmodules": true,
3
+ "git.detectSubmodulesLimit": 30,
4
+ "git.autoRepositoryDetection": true,
5
+ "scm.alwaysShowRepositories": true,
6
+ "scm.repositories.selectionMode": "multiple",
7
+
8
+ "ruff.nativeServer": "auto",
9
+ "python.languageServer": "Pylance",
10
+ "python.analysis.typeCheckingMode": "basic",
11
+ "python.analysis.diagnosticMode": "openFilesOnly",
12
+ "python.analysis.indexing": false,
13
+ "python.analysis.exclude": [
14
+ "**/.*",
15
+ "**/.git",
16
+ "**/__pycache__",
17
+ "**/.ruff_cache",
18
+ "**/node_modules",
19
+ "**/logs",
20
+ "**/checkpoints",
21
+ "**/wandb",
22
+ "**/data",
23
+ "**/outputs",
24
+ "**/build",
25
+ "**/dist",
26
+ "**/.venv",
27
+ "**/third_party"
28
+ ],
29
+ "python.analysis.extraPaths": [
30
+ "./src"
31
+ ],
32
+ "python.autoComplete.extraPaths": [
33
+ "./src"
34
+ ],
35
+
36
+ "[python]": {
37
+ "editor.defaultFormatter": "charliermarsh.ruff",
38
+ "editor.formatOnSave": true,
39
+ "editor.codeActionsOnSave": {
40
+ "source.fixAll.ruff": "explicit",
41
+ "source.organizeImports.ruff": "explicit"
42
+ }
43
+ },
44
+
45
+ "files.watcherExclude": {
46
+ "**/.git/**": true,
47
+ "**/__pycache__/**": true,
48
+ "**/.ruff_cache/**": true,
49
+ "**/node_modules/**": true,
50
+ "**/logs/**": true,
51
+ "**/checkpoints/**": true,
52
+ "**/wandb/**": true,
53
+ "**/data/**": true,
54
+ "**/outputs/**": true,
55
+ "**/build/**": true,
56
+ "**/dist/**": true,
57
+ "**/.venv/**": true,
58
+ "**/third_party/**": true
59
+ },
60
+ "files.exclude": {
61
+ "**/.git": true,
62
+ "**/.svn": true,
63
+ "**/.hg": true,
64
+ "**/.DS_Store": true,
65
+ "**/Thumbs.db": true,
66
+ "**/.venv": true,
67
+ "**/build": true,
68
+ "**/dist": true,
69
+ "hydralance": true
70
+ },
71
+
72
+ "cmake.configureSettings": {
73
+ "Python_EXECUTABLE": "${workspaceFolder}/.venv/bin/python",
74
+ "nanobind_DIR": "${workspaceFolder}/.venv/lib/python3.12/site-packages/nanobind/cmake"
75
+ },
76
+
77
+ "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
78
+ "python-envs.defaultEnvManager": "ms-python.python:venv",
79
+ "python-envs.defaultPackageManager": "ms-python.python:pip",
80
+
81
+ "latex-workshop.latex.autoBuild.run": "onSave",
82
+ "latex-workshop.latex.recipe.default": "first",
83
+ "latex-workshop.latex.recipes": [
84
+ {
85
+ "name": "latexmk",
86
+ "tools": ["latexmk"]
87
+ }
88
+ ],
89
+ "latex-workshop.latex.tools": [
90
+ {
91
+ "name": "latexmk",
92
+ "command": "latexmk",
93
+ "args": [
94
+ "-pdf",
95
+ "-synctex=1",
96
+ "-interaction=nonstopmode",
97
+ "-file-line-error",
98
+ "-outdir=%OUTDIR%",
99
+ "%DOC%"
100
+ ],
101
+ "env": {
102
+ "PATH": "${env:PATH}"
103
+ }
104
+ }
105
+ ],
106
+ "latex-workshop.latex.clean.command": "latexmk",
107
+
108
+ "hydralance.excludePatterns": [
109
+ ".venv/**",
110
+ "outputs/**",
111
+ "third_party/**"
112
+ ],
113
+ "hydralance.hideHelperFolder": true,
114
+
115
+ "workbench.editorAssociations": {
116
+ "README.md": "vscode.markdown.preview.editor",
117
+ "**/README.md": "vscode.markdown.preview.editor"
118
+ }
119
+ }