blochsim 0.0.9__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 (398) hide show
  1. blochsim-0.0.9/.gitattributes +42 -0
  2. blochsim-0.0.9/.github/DISCUSSION_TEMPLATE/ideas.yml +19 -0
  3. blochsim-0.0.9/.github/DISCUSSION_TEMPLATE/q-a.yml +24 -0
  4. blochsim-0.0.9/.github/ISSUE_TEMPLATE/bug_report.yml +69 -0
  5. blochsim-0.0.9/.github/ISSUE_TEMPLATE/config.yml +8 -0
  6. blochsim-0.0.9/.github/ISSUE_TEMPLATE/feature_request.yml +41 -0
  7. blochsim-0.0.9/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  8. blochsim-0.0.9/.github/SECURITY.md +39 -0
  9. blochsim-0.0.9/.github/copilot-instructions.md +1 -0
  10. blochsim-0.0.9/.github/dependabot.yml +27 -0
  11. blochsim-0.0.9/.github/skills/add-a-sequence/SKILL.md +79 -0
  12. blochsim-0.0.9/.github/skills/build-and-test/SKILL.md +95 -0
  13. blochsim-0.0.9/.github/skills/build-the-docs/SKILL.md +125 -0
  14. blochsim-0.0.9/.github/skills/cut-a-release/SKILL.md +53 -0
  15. blochsim-0.0.9/.github/workflows/codeql.yml +54 -0
  16. blochsim-0.0.9/.github/workflows/docs.yml +148 -0
  17. blochsim-0.0.9/.github/workflows/lint.yml +31 -0
  18. blochsim-0.0.9/.github/workflows/test.yml +89 -0
  19. blochsim-0.0.9/.github/workflows/wheels.yml +109 -0
  20. blochsim-0.0.9/.gitignore +63 -0
  21. blochsim-0.0.9/.pre-commit-config.yaml +40 -0
  22. blochsim-0.0.9/AGENTS.md +1 -0
  23. blochsim-0.0.9/CHANGELOG.md +1294 -0
  24. blochsim-0.0.9/CLAUDE.md +219 -0
  25. blochsim-0.0.9/CMakeLists.txt +58 -0
  26. blochsim-0.0.9/LICENSE.txt +21 -0
  27. blochsim-0.0.9/PKG-INFO +168 -0
  28. blochsim-0.0.9/README.md +106 -0
  29. blochsim-0.0.9/benchmarks/README.md +281 -0
  30. blochsim-0.0.9/benchmarks/_common.py +181 -0
  31. blochsim-0.0.9/benchmarks/anatomy.py +318 -0
  32. blochsim-0.0.9/benchmarks/bench_blochsim.py +116 -0
  33. blochsim-0.0.9/benchmarks/bench_epgpy.py +95 -0
  34. blochsim-0.0.9/benchmarks/bench_sycomore.py +115 -0
  35. blochsim-0.0.9/benchmarks/figures/convergence.png +0 -0
  36. blochsim-0.0.9/benchmarks/figures/derivatives.png +0 -0
  37. blochsim-0.0.9/benchmarks/figures/memory.png +0 -0
  38. blochsim-0.0.9/benchmarks/figures/throughput.png +0 -0
  39. blochsim-0.0.9/benchmarks/julia/Manifest.toml +1206 -0
  40. blochsim-0.0.9/benchmarks/julia/Project.toml +28 -0
  41. blochsim-0.0.9/benchmarks/julia/bench_blochsimulators.jl +173 -0
  42. blochsim-0.0.9/benchmarks/julia/bench_koma.jl +169 -0
  43. blochsim-0.0.9/benchmarks/julia/common.jl +139 -0
  44. blochsim-0.0.9/benchmarks/make_figures.py +392 -0
  45. blochsim-0.0.9/benchmarks/results/anatomy.txt +89 -0
  46. blochsim-0.0.9/benchmarks/results/blochsim-forward-1-cpu-t4.json +36 -0
  47. blochsim-0.0.9/benchmarks/results/blochsim-forward-1-cuda.json +48 -0
  48. blochsim-0.0.9/benchmarks/results/blochsim-forward-10-cpu-t4.json +36 -0
  49. blochsim-0.0.9/benchmarks/results/blochsim-forward-10-cuda.json +48 -0
  50. blochsim-0.0.9/benchmarks/results/blochsim-forward-100-cpu-t4.json +36 -0
  51. blochsim-0.0.9/benchmarks/results/blochsim-forward-100-cuda.json +48 -0
  52. blochsim-0.0.9/benchmarks/results/blochsim-forward-1000-cpu-t1.json +36 -0
  53. blochsim-0.0.9/benchmarks/results/blochsim-forward-1000-cpu-t4.json +36 -0
  54. blochsim-0.0.9/benchmarks/results/blochsim-forward-1000-cuda.json +48 -0
  55. blochsim-0.0.9/benchmarks/results/blochsim-forward-10000-cpu-t1.json +36 -0
  56. blochsim-0.0.9/benchmarks/results/blochsim-forward-10000-cpu-t4.json +36 -0
  57. blochsim-0.0.9/benchmarks/results/blochsim-forward-10000-cuda.json +48 -0
  58. blochsim-0.0.9/benchmarks/results/blochsim-forward-100000-cpu-t1.json +36 -0
  59. blochsim-0.0.9/benchmarks/results/blochsim-forward-100000-cpu-t4.json +36 -0
  60. blochsim-0.0.9/benchmarks/results/blochsim-forward-100000-cuda.json +48 -0
  61. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-1-cpu-t4.json +36 -0
  62. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-1-cuda.json +48 -0
  63. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-10-cpu-t4.json +36 -0
  64. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-10-cuda.json +48 -0
  65. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-100-cpu-t4.json +36 -0
  66. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-100-cuda.json +48 -0
  67. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-1000-cpu-t4.json +36 -0
  68. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-1000-cuda.json +48 -0
  69. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-10000-cpu-t4.json +36 -0
  70. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-10000-cuda.json +48 -0
  71. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-100000-cpu-t4.json +36 -0
  72. blochsim-0.0.9/benchmarks/results/blochsim-jacobian-100000-cuda.json +48 -0
  73. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-1-cpu-t4.json +36 -0
  74. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-1-cuda.json +48 -0
  75. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-10-cpu-t4.json +36 -0
  76. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-10-cuda.json +48 -0
  77. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-100-cpu-t4.json +36 -0
  78. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-100-cuda.json +48 -0
  79. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-1000-cpu-t4.json +36 -0
  80. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-1000-cuda.json +48 -0
  81. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-10000-cpu-t4.json +36 -0
  82. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-10000-cuda.json +48 -0
  83. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-100000-cpu-t4.json +36 -0
  84. blochsim-0.0.9/benchmarks/results/blochsim-jacobian1-100000-cuda.json +48 -0
  85. blochsim-0.0.9/benchmarks/results/blochsimulators-complex-1000-cpu-t4.json +23 -0
  86. blochsim-0.0.9/benchmarks/results/blochsimulators-complex-1000-cuda.json +23 -0
  87. blochsim-0.0.9/benchmarks/results/blochsimulators-complex-10000-cpu-t4.json +23 -0
  88. blochsim-0.0.9/benchmarks/results/blochsimulators-complex-10000-cuda.json +23 -0
  89. blochsim-0.0.9/benchmarks/results/blochsimulators-complex-100000-cpu-t4.json +23 -0
  90. blochsim-0.0.9/benchmarks/results/blochsimulators-complex-100000-cuda.json +48 -0
  91. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-1-cpu-t4.json +23 -0
  92. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-1-cuda.json +23 -0
  93. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-10-cpu-t4.json +23 -0
  94. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-10-cuda.json +23 -0
  95. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-100-cpu-t4.json +23 -0
  96. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-100-cuda.json +48 -0
  97. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-1000-cpu-t1.json +23 -0
  98. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-1000-cpu-t4.json +23 -0
  99. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-1000-cuda.json +23 -0
  100. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-10000-cpu-t1.json +23 -0
  101. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-10000-cpu-t4.json +23 -0
  102. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-10000-cuda.json +23 -0
  103. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-100000-cpu-t1.json +23 -0
  104. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-100000-cpu-t4.json +23 -0
  105. blochsim-0.0.9/benchmarks/results/blochsimulators-forward-100000-cuda.json +23 -0
  106. blochsim-0.0.9/benchmarks/results/blochsimulators-jacobian-1000-cpu-t4.json +23 -0
  107. blochsim-0.0.9/benchmarks/results/blochsimulators-jacobian-1000-cuda.json +48 -0
  108. blochsim-0.0.9/benchmarks/results/blochsimulators-jacobian-10000-cpu-t4.json +23 -0
  109. blochsim-0.0.9/benchmarks/results/blochsimulators-jacobian-10000-cuda.json +48 -0
  110. blochsim-0.0.9/benchmarks/results/blochsimulators-jacobian-100000-cpu-t4.json +23 -0
  111. blochsim-0.0.9/benchmarks/results/blochsimulators-jacobian-100000-cuda.json +48 -0
  112. blochsim-0.0.9/benchmarks/results/epgpy-forward-1.json +36 -0
  113. blochsim-0.0.9/benchmarks/results/epgpy-forward-10.json +36 -0
  114. blochsim-0.0.9/benchmarks/results/epgpy-forward-100.json +36 -0
  115. blochsim-0.0.9/benchmarks/results/epgpy-forward-1000.json +36 -0
  116. blochsim-0.0.9/benchmarks/results/epgpy-forward-10000.json +36 -0
  117. blochsim-0.0.9/benchmarks/results/epgpy-jacobian-1.json +36 -0
  118. blochsim-0.0.9/benchmarks/results/epgpy-jacobian-10.json +36 -0
  119. blochsim-0.0.9/benchmarks/results/epgpy-jacobian-100.json +36 -0
  120. blochsim-0.0.9/benchmarks/results/epgpy-jacobian-1000.json +36 -0
  121. blochsim-0.0.9/benchmarks/results/epgpy-jacobian-10000.json +36 -0
  122. blochsim-0.0.9/benchmarks/results/koma-forward-1-s64-cpu-t4.json +23 -0
  123. blochsim-0.0.9/benchmarks/results/koma-forward-1-s64-cuda.json +23 -0
  124. blochsim-0.0.9/benchmarks/results/koma-forward-10-s64-cpu-t4.json +23 -0
  125. blochsim-0.0.9/benchmarks/results/koma-forward-10-s64-cuda.json +23 -0
  126. blochsim-0.0.9/benchmarks/results/koma-forward-100-s64-cpu-t4.json +23 -0
  127. blochsim-0.0.9/benchmarks/results/koma-forward-100-s64-cuda.json +48 -0
  128. blochsim-0.0.9/benchmarks/results/koma-forward-1000-s64-cpu-t4.json +23 -0
  129. blochsim-0.0.9/benchmarks/results/koma-forward-1000-s64-cuda.json +48 -0
  130. blochsim-0.0.9/benchmarks/results/sycomore-forward-1.json +35 -0
  131. blochsim-0.0.9/benchmarks/results/sycomore-forward-10.json +35 -0
  132. blochsim-0.0.9/benchmarks/results/sycomore-forward-100.json +35 -0
  133. blochsim-0.0.9/benchmarks/results/sycomore-forward-1000.json +35 -0
  134. blochsim-0.0.9/benchmarks/results/sycomore-forward-10000.json +35 -0
  135. blochsim-0.0.9/benchmarks/results/table.md +142 -0
  136. blochsim-0.0.9/benchmarks/results/validation.json +208 -0
  137. blochsim-0.0.9/benchmarks/results/validation.txt +193 -0
  138. blochsim-0.0.9/benchmarks/run_all.py +319 -0
  139. blochsim-0.0.9/benchmarks/setup.sh +154 -0
  140. blochsim-0.0.9/benchmarks/summarize.py +58 -0
  141. blochsim-0.0.9/benchmarks/validate.py +277 -0
  142. blochsim-0.0.9/docs/Makefile +20 -0
  143. blochsim-0.0.9/docs/_static/blochsim-logo-dark.svg +19 -0
  144. blochsim-0.0.9/docs/_static/blochsim-logo.svg +19 -0
  145. blochsim-0.0.9/docs/_static/blochsim-mark-dark.svg +18 -0
  146. blochsim-0.0.9/docs/_static/blochsim-mark.svg +18 -0
  147. blochsim-0.0.9/docs/_static/custom.css +33 -0
  148. blochsim-0.0.9/docs/_templates/autosummary/base.rst +5 -0
  149. blochsim-0.0.9/docs/_templates/autosummary/class.rst +21 -0
  150. blochsim-0.0.9/docs/_templates/autosummary/function.rst +10 -0
  151. blochsim-0.0.9/docs/_templates/autosummary/module.rst +64 -0
  152. blochsim-0.0.9/docs/api/estimators.md +121 -0
  153. blochsim-0.0.9/docs/api/execution.md +63 -0
  154. blochsim-0.0.9/docs/api/index.md +49 -0
  155. blochsim-0.0.9/docs/api/model.md +143 -0
  156. blochsim-0.0.9/docs/api/optim.md +26 -0
  157. blochsim-0.0.9/docs/api/recon.md +79 -0
  158. blochsim-0.0.9/docs/api/sequence.md +94 -0
  159. blochsim-0.0.9/docs/api/simulators.md +92 -0
  160. blochsim-0.0.9/docs/binder/apt.txt +3 -0
  161. blochsim-0.0.9/docs/binder/environment.yml +10 -0
  162. blochsim-0.0.9/docs/conf.py +437 -0
  163. blochsim-0.0.9/docs/developer_guide.md +335 -0
  164. blochsim-0.0.9/docs/explanation_figures.py +1406 -0
  165. blochsim-0.0.9/docs/explanations/description.md +202 -0
  166. blochsim-0.0.9/docs/explanations/epg.md +364 -0
  167. blochsim-0.0.9/docs/explanations/implementation.md +326 -0
  168. blochsim-0.0.9/docs/explanations/index.md +28 -0
  169. blochsim-0.0.9/docs/figure_style.py +57 -0
  170. blochsim-0.0.9/docs/index.md +16 -0
  171. blochsim-0.0.9/docs/misc/code_of_conduct.md +128 -0
  172. blochsim-0.0.9/docs/misc/contributors.md +9 -0
  173. blochsim-0.0.9/docs/misc/index.md +13 -0
  174. blochsim-0.0.9/docs/misc/license.md +4 -0
  175. blochsim-0.0.9/docs/misc/related.md +125 -0
  176. blochsim-0.0.9/docs/sphinx_add_colab_link.py +171 -0
  177. blochsim-0.0.9/docs/user_guide.md +269 -0
  178. blochsim-0.0.9/examples/01-framework/01-getting-started.py +594 -0
  179. blochsim-0.0.9/examples/01-framework/02-expanded-physics.py +614 -0
  180. blochsim-0.0.9/examples/01-framework/03-writing-a-simulator.py +424 -0
  181. blochsim-0.0.9/examples/01-framework/04-custom-operator.py +384 -0
  182. blochsim-0.0.9/examples/01-framework/README.rst +16 -0
  183. blochsim-0.0.9/examples/01-framework/data/slr90.npz +0 -0
  184. blochsim-0.0.9/examples/01-framework/fse.seq +1252 -0
  185. blochsim-0.0.9/examples/02-parameter-inference/01-dictionary-matching.py +605 -0
  186. blochsim-0.0.9/examples/02-parameter-inference/02-lookup-table.py +568 -0
  187. blochsim-0.0.9/examples/02-parameter-inference/03-nonlinear-least-squares.py +547 -0
  188. blochsim-0.0.9/examples/02-parameter-inference/04-perk.py +637 -0
  189. blochsim-0.0.9/examples/02-parameter-inference/README.rst +15 -0
  190. blochsim-0.0.9/examples/03-sequence-optimization/01-echo-train-design.py +830 -0
  191. blochsim-0.0.9/examples/03-sequence-optimization/02-joint-relaxometry.py +613 -0
  192. blochsim-0.0.9/examples/03-sequence-optimization/03-rf-pulse-design.py +170 -0
  193. blochsim-0.0.9/examples/03-sequence-optimization/README.rst +19 -0
  194. blochsim-0.0.9/examples/04-model-based-imaging/01-linear-subspace.py +508 -0
  195. blochsim-0.0.9/examples/04-model-based-imaging/02-nonlinear-inversion.py +545 -0
  196. blochsim-0.0.9/examples/04-model-based-imaging/README.rst +17 -0
  197. blochsim-0.0.9/examples/05-misc/01-synthetic-data.py +597 -0
  198. blochsim-0.0.9/examples/05-misc/README.rst +4 -0
  199. blochsim-0.0.9/examples/05-misc/data/PD/IXI002-Guys-0828-PD.nii.gz +0 -0
  200. blochsim-0.0.9/examples/05-misc/data/T1/IXI002-Guys-0828-T1.nii.gz +0 -0
  201. blochsim-0.0.9/examples/05-misc/data/T2/IXI002-Guys-0828-T2.nii.gz +0 -0
  202. blochsim-0.0.9/examples/README.rst +2 -0
  203. blochsim-0.0.9/examples/_gallery_header.md +49 -0
  204. blochsim-0.0.9/pyproject.toml +239 -0
  205. blochsim-0.0.9/scripts/build_docs.sh +63 -0
  206. blochsim-0.0.9/scripts/check_wheel.py +57 -0
  207. blochsim-0.0.9/scripts/make_artwork.py +161 -0
  208. blochsim-0.0.9/scripts/prune_triton_cache.py +142 -0
  209. blochsim-0.0.9/scripts/publish_docs.py +143 -0
  210. blochsim-0.0.9/setup.py +10 -0
  211. blochsim-0.0.9/src/blochsim/__init__.py +30 -0
  212. blochsim-0.0.9/src/blochsim/_bounds.py +207 -0
  213. blochsim-0.0.9/src/blochsim/_epg_cpu.cpp +11790 -0
  214. blochsim-0.0.9/src/blochsim/_execution.py +519 -0
  215. blochsim-0.0.9/src/blochsim/_functional/__init__.py +37 -0
  216. blochsim-0.0.9/src/blochsim/_functional/_bssfp.py +87 -0
  217. blochsim-0.0.9/src/blochsim/_functional/_fse.py +99 -0
  218. blochsim-0.0.9/src/blochsim/_functional/_mp2rage.py +86 -0
  219. blochsim-0.0.9/src/blochsim/_functional/_mpnrage.py +87 -0
  220. blochsim-0.0.9/src/blochsim/_functional/_mprage.py +86 -0
  221. blochsim-0.0.9/src/blochsim/_functional/_mrf.py +96 -0
  222. blochsim-0.0.9/src/blochsim/_functional/_run.py +49 -0
  223. blochsim-0.0.9/src/blochsim/_functional/_spgr.py +82 -0
  224. blochsim-0.0.9/src/blochsim/_ifunc.hpp +39 -0
  225. blochsim-0.0.9/src/blochsim/_perk_cpu.cpp +401 -0
  226. blochsim-0.0.9/src/blochsim/_subspace.py +129 -0
  227. blochsim-0.0.9/src/blochsim/_threads.hpp +169 -0
  228. blochsim-0.0.9/src/blochsim/_version.py +24 -0
  229. blochsim-0.0.9/src/blochsim/estimators/__init__.py +37 -0
  230. blochsim-0.0.9/src/blochsim/estimators/_dictionary.py +460 -0
  231. blochsim-0.0.9/src/blochsim/estimators/_grouped.py +251 -0
  232. blochsim-0.0.9/src/blochsim/estimators/_lookup.py +291 -0
  233. blochsim-0.0.9/src/blochsim/estimators/_mapping.py +648 -0
  234. blochsim-0.0.9/src/blochsim/estimators/_nlls.py +327 -0
  235. blochsim-0.0.9/src/blochsim/estimators/_perk.py +915 -0
  236. blochsim-0.0.9/src/blochsim/estimators/_perk_native.py +87 -0
  237. blochsim-0.0.9/src/blochsim/estimators/_perk_triton.py +332 -0
  238. blochsim-0.0.9/src/blochsim/model/__init__.py +30 -0
  239. blochsim-0.0.9/src/blochsim/model/_binding.py +386 -0
  240. blochsim-0.0.9/src/blochsim/model/_signal.py +320 -0
  241. blochsim-0.0.9/src/blochsim/model/_state_machine.py +920 -0
  242. blochsim-0.0.9/src/blochsim/optim/__init__.py +16 -0
  243. blochsim-0.0.9/src/blochsim/optim/_design.py +287 -0
  244. blochsim-0.0.9/src/blochsim/recon/__init__.py +34 -0
  245. blochsim-0.0.9/src/blochsim/recon/_gauss_newton.py +793 -0
  246. blochsim-0.0.9/src/blochsim/recon/_operator.py +542 -0
  247. blochsim-0.0.9/src/blochsim/sequence/__init__.py +86 -0
  248. blochsim-0.0.9/src/blochsim/sequence/_accelerators.py +4136 -0
  249. blochsim-0.0.9/src/blochsim/sequence/_array.py +202 -0
  250. blochsim-0.0.9/src/blochsim/sequence/_builders.py +278 -0
  251. blochsim-0.0.9/src/blochsim/sequence/_calibration.py +48 -0
  252. blochsim-0.0.9/src/blochsim/sequence/_description.py +981 -0
  253. blochsim-0.0.9/src/blochsim/sequence/_epg_triton.py +18718 -0
  254. blochsim-0.0.9/src/blochsim/sequence/_fixed_point.py +180 -0
  255. blochsim-0.0.9/src/blochsim/sequence/_lineshape.py +283 -0
  256. blochsim-0.0.9/src/blochsim/sequence/_mrd.py +173 -0
  257. blochsim-0.0.9/src/blochsim/sequence/_operators.py +629 -0
  258. blochsim-0.0.9/src/blochsim/sequence/_parameters.py +559 -0
  259. blochsim-0.0.9/src/blochsim/sequence/_pools.py +293 -0
  260. blochsim-0.0.9/src/blochsim/sequence/_pools_triton.py +2226 -0
  261. blochsim-0.0.9/src/blochsim/sequence/_pulseq.py +430 -0
  262. blochsim-0.0.9/src/blochsim/sequence/_settling.py +85 -0
  263. blochsim-0.0.9/src/blochsim/sequence/_simulation.py +819 -0
  264. blochsim-0.0.9/src/blochsim/sequence/_transition.py +822 -0
  265. blochsim-0.0.9/src/blochsim/sequence/_transmit.py +203 -0
  266. blochsim-0.0.9/src/blochsim/simulators/__init__.py +61 -0
  267. blochsim-0.0.9/src/blochsim/simulators/_contrast.py +39 -0
  268. blochsim-0.0.9/src/blochsim/simulators/_pulses.py +158 -0
  269. blochsim-0.0.9/src/blochsim/simulators/asl.py +143 -0
  270. blochsim-0.0.9/src/blochsim/simulators/bssfp.py +113 -0
  271. blochsim-0.0.9/src/blochsim/simulators/cest.py +239 -0
  272. blochsim-0.0.9/src/blochsim/simulators/diffusion.py +103 -0
  273. blochsim-0.0.9/src/blochsim/simulators/flash.py +187 -0
  274. blochsim-0.0.9/src/blochsim/simulators/fmssfp.py +141 -0
  275. blochsim-0.0.9/src/blochsim/simulators/fse.py +213 -0
  276. blochsim-0.0.9/src/blochsim/simulators/gradient_echo.py +277 -0
  277. blochsim-0.0.9/src/blochsim/simulators/hsfp.py +106 -0
  278. blochsim-0.0.9/src/blochsim/simulators/looklocker.py +302 -0
  279. blochsim-0.0.9/src/blochsim/simulators/mp2rage.py +274 -0
  280. blochsim-0.0.9/src/blochsim/simulators/mpnrage.py +90 -0
  281. blochsim-0.0.9/src/blochsim/simulators/mprage.py +130 -0
  282. blochsim-0.0.9/src/blochsim/simulators/mrf.py +84 -0
  283. blochsim-0.0.9/src/blochsim/simulators/relaxometry.py +194 -0
  284. blochsim-0.0.9/src/blochsim/simulators/spgr.py +104 -0
  285. blochsim-0.0.9/src/blochsim/simulators/spin_echo.py +96 -0
  286. blochsim-0.0.9/src/blochsim/simulators/stimulated_echo.py +102 -0
  287. blochsim-0.0.9/src/blochsim/simulators/truefisp.py +169 -0
  288. blochsim-0.0.9/src/blochsim/utils/__init__.py +8 -0
  289. blochsim-0.0.9/src/blochsim/utils/_b1rms.py +45 -0
  290. blochsim-0.0.9/tests/conftest.py +17 -0
  291. blochsim-0.0.9/tests/epg/test_adc.py +109 -0
  292. blochsim-0.0.9/tests/epg/test_diffusion.py +89 -0
  293. blochsim-0.0.9/tests/epg/test_flow.py +137 -0
  294. blochsim-0.0.9/tests/epg/test_relaxation.py +237 -0
  295. blochsim-0.0.9/tests/epg/test_rf.py +382 -0
  296. blochsim-0.0.9/tests/epg/test_shift.py +144 -0
  297. blochsim-0.0.9/tests/epg/test_spoil.py +79 -0
  298. blochsim-0.0.9/tests/epg/test_states.py +61 -0
  299. blochsim-0.0.9/tests/epg/test_three_pool.py +538 -0
  300. blochsim-0.0.9/tests/epg/test_two_pool.py +379 -0
  301. blochsim-0.0.9/tests/epg/test_two_pool_transverse.py +437 -0
  302. blochsim-0.0.9/tests/estimators/test_dictionary.py +99 -0
  303. blochsim-0.0.9/tests/estimators/test_grouped.py +325 -0
  304. blochsim-0.0.9/tests/estimators/test_lookup.py +228 -0
  305. blochsim-0.0.9/tests/estimators/test_mapping.py +209 -0
  306. blochsim-0.0.9/tests/estimators/test_mapping_comparison.py +47 -0
  307. blochsim-0.0.9/tests/estimators/test_nlls.py +353 -0
  308. blochsim-0.0.9/tests/estimators/test_perk.py +294 -0
  309. blochsim-0.0.9/tests/estimators/test_perk_kernel.py +207 -0
  310. blochsim-0.0.9/tests/estimators/test_streaming.py +253 -0
  311. blochsim-0.0.9/tests/estimators/test_subspace.py +98 -0
  312. blochsim-0.0.9/tests/estimators/test_uncertainty.py +380 -0
  313. blochsim-0.0.9/tests/functionals/test_bssfp_func.py +57 -0
  314. blochsim-0.0.9/tests/functionals/test_fse_func.py +61 -0
  315. blochsim-0.0.9/tests/functionals/test_mp2rage_func.py +54 -0
  316. blochsim-0.0.9/tests/functionals/test_mp2rage_reference.py +303 -0
  317. blochsim-0.0.9/tests/functionals/test_mpnrage_func.py +27 -0
  318. blochsim-0.0.9/tests/functionals/test_mprage_func.py +41 -0
  319. blochsim-0.0.9/tests/functionals/test_mrf_func.py +62 -0
  320. blochsim-0.0.9/tests/functionals/test_spgr_func.py +59 -0
  321. blochsim-0.0.9/tests/model/test_array_backends.py +186 -0
  322. blochsim-0.0.9/tests/model/test_jacobian_contract.py +104 -0
  323. blochsim-0.0.9/tests/model/test_model_physics.py +144 -0
  324. blochsim-0.0.9/tests/model/test_signal_model.py +245 -0
  325. blochsim-0.0.9/tests/model/test_state_machine.py +300 -0
  326. blochsim-0.0.9/tests/optim/test_binding.py +469 -0
  327. blochsim-0.0.9/tests/optim/test_design.py +254 -0
  328. blochsim-0.0.9/tests/recon/test_constraints.py +232 -0
  329. blochsim-0.0.9/tests/recon/test_deepinv.py +136 -0
  330. blochsim-0.0.9/tests/recon/test_gauss_newton.py +429 -0
  331. blochsim-0.0.9/tests/recon/test_operator.py +308 -0
  332. blochsim-0.0.9/tests/recon/test_subspace_route.py +141 -0
  333. blochsim-0.0.9/tests/sequence/test_against_epgpy.py +137 -0
  334. blochsim-0.0.9/tests/sequence/test_both_pools.py +1696 -0
  335. blochsim-0.0.9/tests/sequence/test_bound_pool.py +1721 -0
  336. blochsim-0.0.9/tests/sequence/test_calibration.py +44 -0
  337. blochsim-0.0.9/tests/sequence/test_cuda_parity.py +628 -0
  338. blochsim-0.0.9/tests/sequence/test_description.py +204 -0
  339. blochsim-0.0.9/tests/sequence/test_description_helper.py +109 -0
  340. blochsim-0.0.9/tests/sequence/test_diffusion_kernels.py +376 -0
  341. blochsim-0.0.9/tests/sequence/test_distribute.py +240 -0
  342. blochsim-0.0.9/tests/sequence/test_dynamic_transmit.py +2064 -0
  343. blochsim-0.0.9/tests/sequence/test_engine_coverage.py +176 -0
  344. blochsim-0.0.9/tests/sequence/test_exact_profile.py +396 -0
  345. blochsim-0.0.9/tests/sequence/test_exchange.py +1418 -0
  346. blochsim-0.0.9/tests/sequence/test_execution.py +301 -0
  347. blochsim-0.0.9/tests/sequence/test_feature_gates.py +503 -0
  348. blochsim-0.0.9/tests/sequence/test_features.py +163 -0
  349. blochsim-0.0.9/tests/sequence/test_first_order_adjoint.py +212 -0
  350. blochsim-0.0.9/tests/sequence/test_fixed_point.py +155 -0
  351. blochsim-0.0.9/tests/sequence/test_flow_kernels.py +378 -0
  352. blochsim-0.0.9/tests/sequence/test_gradient_operators.py +150 -0
  353. blochsim-0.0.9/tests/sequence/test_host_feature_mask.py +269 -0
  354. blochsim-0.0.9/tests/sequence/test_interpreted.py +101 -0
  355. blochsim-0.0.9/tests/sequence/test_lineshape.py +222 -0
  356. blochsim-0.0.9/tests/sequence/test_many_pools.py +760 -0
  357. blochsim-0.0.9/tests/sequence/test_many_pools_interpreted.py +79 -0
  358. blochsim-0.0.9/tests/sequence/test_mrd_reader.py +203 -0
  359. blochsim-0.0.9/tests/sequence/test_offload.py +823 -0
  360. blochsim-0.0.9/tests/sequence/test_operators.py +283 -0
  361. blochsim-0.0.9/tests/sequence/test_parameters.py +211 -0
  362. blochsim-0.0.9/tests/sequence/test_pulseq_reader.py +234 -0
  363. blochsim-0.0.9/tests/sequence/test_real_subspace.py +1212 -0
  364. blochsim-0.0.9/tests/sequence/test_rf_definition.py +188 -0
  365. blochsim-0.0.9/tests/sequence/test_settling.py +171 -0
  366. blochsim-0.0.9/tests/sequence/test_simulation.py +275 -0
  367. blochsim-0.0.9/tests/sequence/test_slice_table.py +619 -0
  368. blochsim-0.0.9/tests/sequence/test_ssfp_readouts.py +152 -0
  369. blochsim-0.0.9/tests/sequence/test_static_spread.py +354 -0
  370. blochsim-0.0.9/tests/sequence/test_steady_state.py +146 -0
  371. blochsim-0.0.9/tests/sequence/test_subspace_streams.py +149 -0
  372. blochsim-0.0.9/tests/sequence/test_train_batching.py +405 -0
  373. blochsim-0.0.9/tests/sequence/test_transition.py +477 -0
  374. blochsim-0.0.9/tests/sequence/test_transmit_array.py +987 -0
  375. blochsim-0.0.9/tests/sequence/test_unrefocused_time.py +262 -0
  376. blochsim-0.0.9/tests/sequence/test_vjp.py +492 -0
  377. blochsim-0.0.9/tests/sequence/test_washout_kernels.py +498 -0
  378. blochsim-0.0.9/tests/sequence/test_worker_pool.py +105 -0
  379. blochsim-0.0.9/tests/simulators/test_closed_forms.py +317 -0
  380. blochsim-0.0.9/tests/simulators/test_epg_trains.py +251 -0
  381. blochsim-0.0.9/tests/simulators/test_sampled_pulses.py +359 -0
  382. blochsim-0.0.9/tests/utils/__init__.py +1 -0
  383. blochsim-0.0.9/tests/utils/epg/__init__.py +37 -0
  384. blochsim-0.0.9/tests/utils/epg/_adc.py +81 -0
  385. blochsim-0.0.9/tests/utils/epg/_adiabatic_inversion.py +31 -0
  386. blochsim-0.0.9/tests/utils/epg/_diffusion.py +99 -0
  387. blochsim-0.0.9/tests/utils/epg/_flow.py +188 -0
  388. blochsim-0.0.9/tests/utils/epg/_longitudinal_relaxation.py +156 -0
  389. blochsim-0.0.9/tests/utils/epg/_rf_pulse.py +717 -0
  390. blochsim-0.0.9/tests/utils/epg/_shift.py +40 -0
  391. blochsim-0.0.9/tests/utils/epg/_spoil.py +25 -0
  392. blochsim-0.0.9/tests/utils/epg/_states_matrix.py +58 -0
  393. blochsim-0.0.9/tests/utils/epg/_transverse_relaxation.py +142 -0
  394. blochsim-0.0.9/tests/utils/epg/_utils.py +24 -0
  395. blochsim-0.0.9/tests/utils/exchange.py +47 -0
  396. blochsim-0.0.9/tests/utils/interpreted.py +979 -0
  397. blochsim-0.0.9/tests/utils/interpreted_pools.py +221 -0
  398. blochsim-0.0.9/tests/utils/packed_reference.py +812 -0
@@ -0,0 +1,42 @@
1
+ # Normalise line endings in the repository; check out native on Windows.
2
+ * text=auto
3
+
4
+ # Text, whatever the platform.
5
+ *.py text diff=python
6
+ *.pyi text diff=python
7
+ *.cpp text diff=cpp
8
+ *.hpp text diff=cpp
9
+ *.md text
10
+ *.rst text
11
+ *.txt text
12
+ *.toml text
13
+ *.cfg text
14
+ *.yml text
15
+ *.yaml text
16
+ *.json text
17
+ *.ipynb text
18
+ CMakeLists.txt text
19
+ *.cmake text
20
+
21
+ # Shell scripts must keep LF even when checked out on Windows, so a WSL or
22
+ # Git Bash run of scripts/ does not fail on carriage returns.
23
+ *.sh text eol=lf
24
+
25
+ # Binary, never diffed or line-ending converted.
26
+ *.png binary
27
+ *.jpg binary
28
+ *.jpeg binary
29
+ *.gif binary
30
+ *.svg binary
31
+ *.pdf binary
32
+ *.npy binary
33
+ *.npz binary
34
+ *.so binary
35
+ *.pyd binary
36
+ *.dylib binary
37
+ *.dll binary
38
+
39
+ # What the repository's language bar reports: the package, not the pages that
40
+ # describe it. Affects GitHub's statistics only, nothing that is built.
41
+ docs/** linguist-documentation
42
+ examples/** linguist-documentation
@@ -0,0 +1,19 @@
1
+ labels: ["idea"]
2
+ body:
3
+ - type: textarea
4
+ id: idea
5
+ attributes:
6
+ label: The idea
7
+ description: |
8
+ A model, an interface or a direction you think BlochSim should take,
9
+ before it is concrete enough to be a feature request.
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: context
15
+ attributes:
16
+ label: Where it comes from
17
+ description: |
18
+ The experiment that prompted it, and any paper or implementation that
19
+ already does something like it.
@@ -0,0 +1,24 @@
1
+ labels: ["question"]
2
+ body:
3
+ - type: textarea
4
+ id: question
5
+ attributes:
6
+ label: What you are trying to do
7
+ description: |
8
+ The sequence or the analysis, and where you got stuck. If a signal
9
+ looks wrong, say what you expected it to look like and why.
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: code
15
+ attributes:
16
+ label: What you have so far
17
+ description: The call you are making, if there is one.
18
+ render: python
19
+
20
+ - type: input
21
+ id: version
22
+ attributes:
23
+ label: BlochSim version
24
+ placeholder: "0.4.1, or the commit you are on"
@@ -0,0 +1,69 @@
1
+ name: Bug report
2
+ description: A simulation that raises, hangs, or returns a signal you can show is wrong.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Questions about how to use BlochSim, and results you are unsure about,
9
+ belong in [Discussions](https://github.com/pulserver/blochsim/discussions).
10
+ This form is for behaviour you can pin down.
11
+
12
+ - type: textarea
13
+ id: what-happens
14
+ attributes:
15
+ label: What happens
16
+ description: What you ran, and what it did instead of what you expected.
17
+ placeholder: |
18
+ A 48-echo FSE train at 60 degree refocusing returns NaN from the
19
+ second echo on, on CPU, for T2 = 0.
20
+ validations:
21
+ required: true
22
+
23
+ - type: textarea
24
+ id: reproduce
25
+ attributes:
26
+ label: Code that reproduces it
27
+ description: |
28
+ The shortest script that shows the problem, with the tissue and the
29
+ protocol it was run on. Paste it whole rather than describing it -- a
30
+ sequence is enough numbers that a description leaves the run ambiguous.
31
+ render: python
32
+ validations:
33
+ required: true
34
+
35
+ - type: textarea
36
+ id: traceback
37
+ attributes:
38
+ label: Traceback or output
39
+ description: The full traceback, or the numbers that are wrong beside the ones you expected.
40
+ render: shell
41
+
42
+ - type: textarea
43
+ id: environment
44
+ attributes:
45
+ label: Environment
46
+ description: |
47
+ Paste the output of
48
+
49
+ ```sh
50
+ python -c "from importlib.metadata import version; print('blochsim', version('blochsim'))"
51
+ python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.get_device_name() if torch.cuda.is_available() else 'cpu')"
52
+ python -VV
53
+ ```
54
+ render: shell
55
+ validations:
56
+ required: true
57
+
58
+ - type: dropdown
59
+ id: where
60
+ attributes:
61
+ label: Where it runs
62
+ description: Which backends you have seen it on.
63
+ multiple: true
64
+ options:
65
+ - CPU
66
+ - CUDA (Triton kernels)
67
+ - Both
68
+ validations:
69
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Question, or a result you are unsure about
4
+ url: https://github.com/pulserver/blochsim/discussions
5
+ about: Ask how to model a sequence, or whether a signal you got is expected.
6
+ - name: Security report
7
+ url: https://github.com/pulserver/blochsim/security/policy
8
+ about: Report a vulnerability privately rather than in a public issue.
@@ -0,0 +1,41 @@
1
+ name: Feature request
2
+ description: A sequence, a physical effect, an estimator or an interface BlochSim should carry.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What you are trying to simulate or fit
9
+ description: |
10
+ The acquisition or the analysis, in the vocabulary of the experiment
11
+ rather than of the API. What the sequence plays, which properties you
12
+ need out, and what stops you today.
13
+ validations:
14
+ required: true
15
+
16
+ - type: textarea
17
+ id: proposal
18
+ attributes:
19
+ label: What you would like BlochSim to do
20
+ description: |
21
+ If you already know the shape of it -- an operator, a tissue property,
22
+ a keyword on an existing simulator -- sketch the call you would like
23
+ to write.
24
+ render: python
25
+ validations:
26
+ required: true
27
+
28
+ - type: textarea
29
+ id: reference
30
+ attributes:
31
+ label: Reference
32
+ description: |
33
+ The paper the model comes from, if there is one, and the figure or
34
+ equation the implementation would have to reproduce.
35
+
36
+ - type: checkboxes
37
+ id: contribution
38
+ attributes:
39
+ label: Contribution
40
+ options:
41
+ - label: I would be willing to open a pull request for this.
@@ -0,0 +1,28 @@
1
+ ## What this changes
2
+
3
+ <!-- What the code does now that it did not do before, in one or two
4
+ sentences. Link the issue or discussion it comes from, if there is one. -->
5
+
6
+ ## How it was checked
7
+
8
+ <!-- The command you ran and what it printed: a test that fails before the
9
+ change and passes after it, an analytic case the new physics reproduces,
10
+ a benchmark. "Tests pass" on its own does not say which ones. -->
11
+
12
+ ```
13
+ pytest tests/
14
+ ```
15
+
16
+ ## Checklist
17
+
18
+ - [ ] `pytest tests/` passes locally, and a new test covers what changed.
19
+ - [ ] `pre-commit run --all-files` is clean -- the `ruff format` and
20
+ `ruff check` that CI's Lint job runs.
21
+ - [ ] Public functions and classes carry a numpydoc docstring: one line of
22
+ what, then Parameters, Returns, Raises.
23
+ - [ ] Comments and docstrings describe the code as it is now -- no
24
+ "previously", no "this replaces", no naming of a bug that is fixed.
25
+ - [ ] A change to what the kernels compute is in both of them, and arrives
26
+ with a test that pins it against something outside BlochSim.
27
+ - [ ] The documentation is updated where the change is visible to a caller,
28
+ and `bash scripts/build_docs.sh` still builds.
@@ -0,0 +1,39 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are applied to the latest release on PyPI and to `main`.
6
+ Older releases are not patched.
7
+
8
+ ## Reporting a vulnerability
9
+
10
+ Report privately, not as a public issue:
11
+
12
+ - open a draft advisory through
13
+ [Security -> Report a vulnerability](https://github.com/pulserver/blochsim/security/advisories/new),
14
+ which is the preferred route; or
15
+ - email **matteo.cencini@gmail.com** if you cannot use GitHub.
16
+
17
+ Please include what an attacker can do, the version and platform you saw it
18
+ on, and a minimal reproduction. You can expect an acknowledgement within a
19
+ week, an assessment within two, and credit in the advisory unless you ask
20
+ otherwise. Please give us a chance to release a fix before disclosing
21
+ publicly.
22
+
23
+ ## Scope
24
+
25
+ BlochSim executes the code you give it: a sequence layout, a cost function and
26
+ a signal model are all Python that runs in your process, so a malicious
27
+ *script* is out of scope in the same way it is for NumPy or PyTorch.
28
+
29
+ In scope is anything that turns *data* into execution or into memory
30
+ corruption -- a sequence description, a pulse waveform, a phantom or a
31
+ dictionary read from a file, or values passed to the simulator, reaching the
32
+ C++ and Triton kernels. Those kernels index raw pointers, so an out-of-bounds
33
+ read or write reachable from ordinary arguments is a vulnerability and not
34
+ merely a bug.
35
+
36
+ ## Not a vulnerability
37
+
38
+ A simulation that returns wrong physics, diverges, or raises is a
39
+ [bug report](https://github.com/pulserver/blochsim/issues/new/choose).
@@ -0,0 +1 @@
1
+ ../CLAUDE.md
@@ -0,0 +1,27 @@
1
+ version: 2
2
+ updates:
3
+ # The actions the workflows pin. A pinned action that is never updated is a
4
+ # pinned action that stops working.
5
+ - package-ecosystem: github-actions
6
+ directory: /
7
+ schedule:
8
+ interval: weekly
9
+ day: monday
10
+ commit-message:
11
+ prefix: "ci"
12
+ groups:
13
+ actions:
14
+ patterns: ["*"]
15
+
16
+ # The dependency floors in pyproject.toml, and the toolchain the extras pull.
17
+ - package-ecosystem: pip
18
+ directory: /
19
+ schedule:
20
+ interval: weekly
21
+ day: monday
22
+ commit-message:
23
+ prefix: "deps"
24
+ groups:
25
+ python:
26
+ patterns: ["*"]
27
+ update-types: [minor, patch]
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: add-a-sequence
3
+ description: Write a new simulator, signal model, or sequence operator in BlochSim. Use when asked to add a sequence, a readout module, a physical effect, or a tissue property.
4
+ ---
5
+
6
+ # Add a sequence to BlochSim
7
+
8
+ **There is one base class, `Simulator`, written in one of two ways.** Nothing
9
+ downstream ever asks which way.
10
+
11
+ ## A signal that has to be played
12
+
13
+ Implement `layout()`: a train of pulses whose magnetization state carries from
14
+ one event to the next, which is almost every quantitative sequence.
15
+
16
+ 1. Set `model` to a `SpinPhysics` — what a voxel holds, so which tissue
17
+ properties are exposed and which terms the kernels carry, and what each kind
18
+ of event does to it.
19
+ 2. Implement `layout()`, returning the operators of one repetition in the order
20
+ they are played.
21
+
22
+ The extended phase-graph engine, the derivative, the device placement and the
23
+ memory policy all follow, and none of them is yours to write. The two pieces
24
+ are separate so either can change without the other: giving an MRF timing a
25
+ selective excitation, or a refocused train a readout that spoils rather than
26
+ winds, is an assignment rather than a new model.
27
+
28
+ `EventOperators` names the operator each kind of event plays. Four are supplied
29
+ and a `SpinPhysics` picks one: `SPOILED` and `UNBALANCED` for gradient-spoiled
30
+ and gradient-echo trains, `BALANCED` for a fully refocused steady state,
31
+ `REFOCUSED` for a spin-echo train.
32
+
33
+ ## A signal with a closed form
34
+
35
+ Implement `evaluate()` instead — a mono-exponential decay, an
36
+ inversion-recovery curve, an Ernst steady state. There is nothing to play and
37
+ no state to carry, so there is no `layout` and the `SpinPhysics` carries only
38
+ the property declaration. `SPGRSimulator` is written this way; `MP2RAGESimulator`
39
+ carries both, the closed form a lookup table is built from and the layout a
40
+ description arriving from a scanner is compared against.
41
+
42
+ ## An operator of your own
43
+
44
+ An operator is a Python function that returns events. `Excitation`,
45
+ `Refocusing`, `Readout`, `Dephase`, `Spoil`, `Delay` and the readout modules
46
+ built from them speak exactly the vocabulary the kernels implement, so a module
47
+ you write — a T2 preparation, an MT saturation, a shaped pulse — reaches the
48
+ fused kernels with no change to them.
49
+
50
+ An event is a wait, an RF pulse, or an ADC. **Gradients are not events**: each
51
+ event declares its own role (`CRUSH_BEFORE`, `CRUSH_AFTER`, `SHIFT_AFTER`,
52
+ `SPOIL_AFTER`), because a Pulseq file has no gradient *use* field and a crusher
53
+ cannot be told from a phase encode by looking at waveforms. What an operator
54
+ cannot say is how much a gradient dephases: a description carries one crusher
55
+ moment for the whole sequence, and dephasing is quantized to whole
56
+ configuration orders.
57
+
58
+ ## Adding a physical effect
59
+
60
+ A tissue property has an **identity** — the value at which it has no effect —
61
+ and a run reads which properties were passed to decide which terms the kernel
62
+ evaluates. Adding an effect is one line in a model's property declaration, plus
63
+ the term itself in **both** kernels.
64
+
65
+ The shared parameter ABI is `src/blochsim/sequence/_parameters.py`, read by the
66
+ Python dispatch, the C++ extension and the Triton kernels. A parameter added
67
+ there is added in all three or in none.
68
+
69
+ ## What the change has to arrive with
70
+
71
+ A test that pins the new physics against something **outside** BlochSim: a
72
+ closed form, a published figure, or an isochromat summation written out in the
73
+ test itself. Comparing BlochSim to BlochSim proves the two agree, which was
74
+ never in doubt. Put it in `tests/epg/` and state the invariant in the module
75
+ docstring.
76
+
77
+ Units are public at the edges and internal underneath: a caller writes
78
+ milliseconds, degrees and Hz; a description timestamps in microseconds and
79
+ carries radians. Convert at the boundary and name the unit in the identifier.
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: build-and-test
3
+ description: Compile the C++ kernels and run the BlochSim suite, including the Triton paths on a machine with no GPU. Use when asked to build, install, test, or reproduce a failure in blochsim.
4
+ ---
5
+
6
+ # Build and test BlochSim
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ pip install -e ".[dev]" ; echo "exit: $?"
12
+ ```
13
+
14
+ `dev` is `test` + `doc` + `examples` plus ruff, mypy and pre-commit. For a test
15
+ run alone, `pip install -e ".[test]"` is enough and much smaller.
16
+
17
+ An editable install puts the Python sources on the path, so an edit under
18
+ `src/blochsim` takes effect on the next import. The two C++ extensions are
19
+ compiled artifacts and do not: re-run the install after editing any `.cpp`.
20
+
21
+ **Read the exit status, not the output.** A failed compile leaves the
22
+ previously built `.so` importable, so the suite runs green against a kernel
23
+ that no longer corresponds to the source. Confirm the extension is the one you
24
+ just built:
25
+
26
+ ```sh
27
+ python -c "import blochsim._epg_cpu as k; print(k.__file__)"
28
+ ```
29
+
30
+ ## Test
31
+
32
+ ```sh
33
+ pytest tests/ # everything, with coverage
34
+ pytest tests/epg/ # one area
35
+ pytest tests/epg/test_shift.py -k inversion
36
+ pytest tests/ -n auto # across cores
37
+ ```
38
+
39
+ `tests/` mirrors the source. `epg/` pins the state machine operator by operator
40
+ against closed forms — the shift, the RF rotation, relaxation, diffusion, flow,
41
+ spoiling, the two-pool and three-pool longitudinal steps — while `sequence/`,
42
+ `model/`, `estimators/`, `recon/` and `optim/` cover the layers above.
43
+
44
+ ## The Triton paths, without a GPU
45
+
46
+ The tests carrying the `interpreted` marker are deselected by `addopts`. They
47
+ run a Triton kernel through Triton's CPU interpreter — no card, no compile,
48
+ about a minute each — and are how the GPU plumbing is verified anywhere:
49
+
50
+ ```sh
51
+ pytest tests/ -m interpreted
52
+ TRITON_INTERPRET=1 python your_script.py # the same trick, by hand
53
+ ```
54
+
55
+ Triton has no wheel for macOS or Windows. On those platforms the default
56
+ deselection is not an optimisation, it is the only thing that runs.
57
+
58
+ ## Before you time anything
59
+
60
+ Kernel compiles dominate a cold GPU run, not the arithmetic. A suite that takes
61
+ minutes on a card is mostly Triton compiling one specialization per feature
62
+ combination it meets; the second run of the same suite is a different number
63
+ entirely. Run the whole suite at natural boundaries rather than after every
64
+ edit.
65
+
66
+ The cache keys on the *source* of `_epg_triton.py`, not on what it means, so a
67
+ formatting pass or a deleted dead local costs the same full recompile as a new
68
+ `tl.constexpr`. If the suite suddenly takes an hour where it took minutes,
69
+ check whether that file changed before looking for a performance regression.
70
+
71
+ ## Style
72
+
73
+ ```sh
74
+ pre-commit install # once per clone; nothing runs on commit until you do
75
+ pre-commit run --all-files # the whole tree, which is what CI's Lint job runs
76
+ pre-commit run --files path/to/one.py
77
+ ```
78
+
79
+ `ruff format`, then `ruff check --fix`, plus whitespace and file hygiene. There
80
+ is no black and no isort, and `.pre-commit-config.yaml` pins the ruff version
81
+ so a local run and CI agree on it.
82
+
83
+ A hook that rewrites a file fails the commit and leaves the fix in the working
84
+ tree: read it, `git add` it, commit again.
85
+
86
+ To get a commit through without them — a work-in-progress commit, or a merge
87
+ you did not write:
88
+
89
+ ```sh
90
+ SKIP=ruff-check git commit -m "..." # one hook
91
+ git commit --no-verify -m "..." # all of them
92
+ ```
93
+
94
+ CI runs the same hooks over the whole tree on every branch, so either one
95
+ defers the failure rather than avoiding it.
@@ -0,0 +1,125 @@
1
+ ---
2
+ name: build-the-docs
3
+ description: Build the BlochSim documentation, add a figure to an explanation page, or add a gallery example. Use when asked about docs, Sphinx, the gallery, MyST, or Read the Docs for blochsim.
4
+ ---
5
+
6
+ # Build the BlochSim documentation
7
+
8
+ ```sh
9
+ bash scripts/build_docs.sh # incremental
10
+ bash scripts/build_docs.sh --clean # re-execute every example
11
+ PYTHON_BIN=~/envs/blochsim/bin/python bash scripts/build_docs.sh
12
+ python -m http.server --directory docs/build/html 8000
13
+ ```
14
+
15
+ The script checks that the interpreter it is given can import BlochSim and the
16
+ documentation extensions, then builds into `docs/build/html`. Two things make
17
+ it slower than a plain Sphinx run and are the point of it: **sphinx-gallery
18
+ executes the examples**, and **the explanation pages' figures are re-rendered**
19
+ by `docs/explanation_figures.py` with the BlochSim in your working tree.
20
+
21
+ An example is executed when the interpreter can import everything it imports:
22
+ an environment with the `dev` or `examples` extra runs the whole gallery, one
23
+ with `doc` alone runs what needs nothing but BlochSim. A page already carrying
24
+ output executed from the source as it stands is reused rather than re-run, and
25
+ the build names each example it did not run and where its output came from. To
26
+ build the pages without running any of them, pass `-D plot_gallery=0`.
27
+
28
+ ## Where the published pages are built
29
+
30
+ `.github/workflows/docs.yml` builds them, and GitHub Pages serves them from
31
+ the `gh-pages` branch at <https://pulserver.github.io/blochsim/>.
32
+
33
+ Its **HTML** job runs on every branch and pull request with the `doc` extra:
34
+ every page is written and the examples needing nothing but BlochSim are
35
+ executed, in a few minutes, and the tree is uploaded as an artifact to read.
36
+
37
+ Its **Site** job runs on `main`, on a `v*.*.*` tag and on demand. It installs
38
+ the `examples` extra, so every notebook is executed, and hands what
39
+ Sphinx wrote to `scripts/publish_docs.py`, which places it in the site:
40
+
41
+ site/latest/ the development branch
42
+ site/v1.2.3/ one directory per release, kept as it was published
43
+ site/index.html a page sending a reader to the newest release
44
+ site/versions.json the list the switcher in the sidebar reads
45
+
46
+ `BLOCHSIM_DOCS_VERSION` is what a build calls itself; it names the directory,
47
+ the switcher's mark and the Binder path prefix, and defaults to `latest`.
48
+
49
+ ## Markdown, and the two places it is not
50
+
51
+ Pages are **MyST Markdown**. Two file sets stay reStructuredText because the
52
+ tooling requires it — converting either breaks the build:
53
+
54
+ - `examples/**/README.rst` — sphinx-gallery concatenates a gallery header
55
+ verbatim into a generated `index.rst`.
56
+ - `docs/_templates/autosummary/*.rst` — `sphinx.ext.autosummary` writes stubs
57
+ with a hardcoded `.rst` suffix and finds its directives by regex over raw
58
+ source lines.
59
+
60
+ That regex is also why the API pages hold `autosummary` and `currentmodule`
61
+ inside `` ```{eval-rst} `` blocks rather than in MyST directive syntax: written
62
+ as `` ```{autosummary} `` the stub generator never sees them and every linked
63
+ page 404s.
64
+
65
+ ## Add a figure to an explanation page
66
+
67
+ Write a function in `docs/explanation_figures.py` that returns a Matplotlib
68
+ figure, register it in the `FIGURES` mapping at the bottom, and reference the
69
+ file it writes:
70
+
71
+ ````markdown
72
+ ```{figure} /generated/figures/your_figure.png
73
+ :width: 100%
74
+ :alt: One sentence a screen reader can use.
75
+
76
+ The caption, which is where the reader is told what to look at.
77
+ ```
78
+ ````
79
+
80
+ Figures are simulated at build time rather than checked in, so one cannot
81
+ outlive the behaviour it shows.
82
+
83
+ ## Add a gallery example
84
+
85
+ Drop a script into the right `examples/` section. The numeric prefix orders it
86
+ within the section, and the module docstring — an reStructuredText title block
87
+ — becomes the page's introduction. What it imports decides where it is
88
+ executed and where it is published as source.
89
+
90
+ ## Writing
91
+
92
+ The audience is MR scientists: pulse sequences and physics, not software
93
+ architecture. Describe what BlochSim does and why that is right on its own
94
+ terms. These pages are not a changelog — never justify a design by describing
95
+ the design it replaced.
96
+
97
+
98
+ ## Documentation architecture
99
+
100
+ Keep the public sidebar shallow and in this order:
101
+
102
+ 1. User Guide
103
+ 2. Developer Guide
104
+ 3. Explanations
105
+ 4. Examples
106
+ 5. API
107
+ 6. Miscellaneous
108
+
109
+ The first four Framework examples are the Course. They teach the public
110
+ abstraction in order; the remaining example sections are standalone Tours.
111
+
112
+ For a new sequence, teach {class}`blochsim.model.Simulator` as the extension
113
+ point. A state-machine simulator has two complementary inputs: its class-level
114
+ handlers interpret commands from an incoming Pulseq/MRD description, and
115
+ `layout()` constructs the same sequence offline. Lower-level
116
+ `SequenceDescription`, `SpinPhysics` and operator machinery are important
117
+ reference/developer concepts, not competing user-facing sequence APIs.
118
+
119
+ Functional `*_sim` calls are conveniences around shipped simulators. Do not
120
+ present them as the abstraction a user implements.
121
+
122
+ Figures must remain readable at final documentation width in both light and
123
+ dark mode. Use `docs/figure_style.py` for common typography, transparent
124
+ backgrounds and foreground colors; examples set only figure-specific geometry
125
+ and scientific encodings.
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: cut-a-release
3
+ description: Publish a BlochSim release to PyPI, and diagnose a wheel build. Use when asked about releasing, versioning, cibuildwheel, abi3 wheels, or the packaging setup.
4
+ ---
5
+
6
+ # Cut a BlochSim release
7
+
8
+ The version comes from the git tag through setuptools-scm; nothing in the tree
9
+ states it. To release:
10
+
11
+ ```sh
12
+ git tag -a v0.1.0 -m "v0.1.0"
13
+ git push origin v0.1.0
14
+ ```
15
+
16
+ The `Wheels` workflow then builds the sdist and every wheel, tests each one,
17
+ and publishes to PyPI through trusted publishing — no token is stored
18
+ anywhere. The `pypi` environment on the repository gates it.
19
+
20
+ `workflow_dispatch` builds and tests everything without publishing, which is
21
+ how to check a packaging change before tagging. So does any pull request that
22
+ touches `pyproject.toml`, `CMakeLists.txt`, the `.cpp` sources, or the workflow.
23
+
24
+ ## What gets built, and why there is so little of it
25
+
26
+ Both kernels are plain CPython extensions against the **stable ABI** from 3.10
27
+ on. They call no PyTorch API and link no PyTorch library. So:
28
+
29
+ - one `cp310-abi3` wheel per platform serves every supported interpreter —
30
+ there is no per-minor-version build to repeat;
31
+ - the wheel is a couple of megabytes rather than the size of libtorch, and
32
+ needs no `auditwheel`/`delocate` repair beyond manylinux compliance.
33
+
34
+ A `#include <torch/...>` in either `.cpp` ends all of that: the wheel would
35
+ become per-version, per-torch-version, and hundreds of megabytes.
36
+ `scripts/check_wheel.py` guards the size and is what cibuildwheel tests every
37
+ wheel with — it loads each kernel by file path, so it needs no PyTorch.
38
+
39
+ Platforms: manylinux x86_64 and aarch64 on native runners, macOS arm64 with the
40
+ Intel wheel cross-compiled beside it, Windows AMD64.
41
+
42
+ ## The build itself
43
+
44
+ `scikit-build-core` drives `CMakeLists.txt`. `setup.py` is a shim for tools
45
+ that still shell out to it and configures nothing — do not put build logic
46
+ there. `[tool.scikit-build]` in `pyproject.toml` sets `wheel.py-api = "cp310"`,
47
+ which is what produces the abi3 tag, and excludes the `.cpp` and `.hpp` sources
48
+ from the wheel.
49
+
50
+ CMake 3.26 or newer is required, for `python_add_library(... USE_SABI ...)`.
51
+ scikit-build-core installs it as a build-time wheel when the system CMake is
52
+ older, so a stale system CMake is not a problem — unless you build with
53
+ `--no-build-isolation`, where it is.