pypulseqpp 0.0.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 (372) hide show
  1. pypulseqpp-0.0.1/.gitattributes +11 -0
  2. pypulseqpp-0.0.1/.github/ISSUE_TEMPLATE/bug_report.yml +42 -0
  3. pypulseqpp-0.0.1/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. pypulseqpp-0.0.1/.github/ISSUE_TEMPLATE/feature_request.yml +31 -0
  5. pypulseqpp-0.0.1/.github/PULL_REQUEST_TEMPLATE.md +24 -0
  6. pypulseqpp-0.0.1/.github/dependabot.yml +36 -0
  7. pypulseqpp-0.0.1/.github/workflows/codeql.yml +33 -0
  8. pypulseqpp-0.0.1/.github/workflows/docs.yml +131 -0
  9. pypulseqpp-0.0.1/.github/workflows/style.yml +42 -0
  10. pypulseqpp-0.0.1/.github/workflows/tags-release.yml +58 -0
  11. pypulseqpp-0.0.1/.github/workflows/test-ci.yml +74 -0
  12. pypulseqpp-0.0.1/.github/workflows/viewer-release.yml +30 -0
  13. pypulseqpp-0.0.1/.github/workflows/viewer-wheels.yml +104 -0
  14. pypulseqpp-0.0.1/.github/workflows/wheels.yml +70 -0
  15. pypulseqpp-0.0.1/.gitignore +31 -0
  16. pypulseqpp-0.0.1/.gitmodules +6 -0
  17. pypulseqpp-0.0.1/.pre-commit-config.yaml +32 -0
  18. pypulseqpp-0.0.1/AGENTS.md +301 -0
  19. pypulseqpp-0.0.1/CLAUDE.md +302 -0
  20. pypulseqpp-0.0.1/CMakeLists.txt +78 -0
  21. pypulseqpp-0.0.1/CODE_OF_CONDUCT.md +42 -0
  22. pypulseqpp-0.0.1/CONTRIBUTING.md +101 -0
  23. pypulseqpp-0.0.1/GEMINI.md +302 -0
  24. pypulseqpp-0.0.1/LICENSE +21 -0
  25. pypulseqpp-0.0.1/LICENSES/SigPy-BSD-3-Clause.txt +13 -0
  26. pypulseqpp-0.0.1/LICENSES/pocketfft-BSD-3-Clause.txt +41 -0
  27. pypulseqpp-0.0.1/LICENSES/safe_pns_prediction-BSD-3-Clause.txt +29 -0
  28. pypulseqpp-0.0.1/NEXT.md +89 -0
  29. pypulseqpp-0.0.1/PKG-INFO +128 -0
  30. pypulseqpp-0.0.1/README.md +79 -0
  31. pypulseqpp-0.0.1/SECURITY.md +33 -0
  32. pypulseqpp-0.0.1/benchmarks/throughput.py +76 -0
  33. pypulseqpp-0.0.1/codecov.yml +24 -0
  34. pypulseqpp-0.0.1/docs/Makefile +14 -0
  35. pypulseqpp-0.0.1/docs/_templates/autosummary/base.rst +5 -0
  36. pypulseqpp-0.0.1/docs/_templates/autosummary/class.rst +20 -0
  37. pypulseqpp-0.0.1/docs/_templates/autosummary/function.rst +5 -0
  38. pypulseqpp-0.0.1/docs/_templates/autosummary/module.rst +41 -0
  39. pypulseqpp-0.0.1/docs/api/cli.md +19 -0
  40. pypulseqpp-0.0.1/docs/api/events.md +52 -0
  41. pypulseqpp-0.0.1/docs/api/gradients.md +43 -0
  42. pypulseqpp-0.0.1/docs/api/index.md +65 -0
  43. pypulseqpp-0.0.1/docs/api/modules.md +119 -0
  44. pypulseqpp-0.0.1/docs/api/plotting.md +28 -0
  45. pypulseqpp-0.0.1/docs/api/rf.md +52 -0
  46. pypulseqpp-0.0.1/docs/api/safety.md +58 -0
  47. pypulseqpp-0.0.1/docs/api/sampling.md +53 -0
  48. pypulseqpp-0.0.1/docs/api/sequence.md +59 -0
  49. pypulseqpp-0.0.1/docs/api/timing.md +21 -0
  50. pypulseqpp-0.0.1/docs/api/trajectories.md +23 -0
  51. pypulseqpp-0.0.1/docs/conf.py +61 -0
  52. pypulseqpp-0.0.1/docs/developer_guide.md +1 -0
  53. pypulseqpp-0.0.1/docs/examples/index.md +1 -0
  54. pypulseqpp-0.0.1/docs/index.md +36 -0
  55. pypulseqpp-0.0.1/docs/misc/code_of_conduct.md +1 -0
  56. pypulseqpp-0.0.1/docs/misc/contributors.md +1 -0
  57. pypulseqpp-0.0.1/docs/misc/license.md +1 -0
  58. pypulseqpp-0.0.1/docs/misc/related.md +1 -0
  59. pypulseqpp-0.0.1/docs/user_guide.md +1 -0
  60. pypulseqpp-0.0.1/examples/sequence/__init__.py +20 -0
  61. pypulseqpp-0.0.1/examples/sequence/gre2D_sequence.py +466 -0
  62. pypulseqpp-0.0.1/external/MRArbGrad/.gitignore +13 -0
  63. pypulseqpp-0.0.1/external/MRArbGrad/CHANGELOG +44 -0
  64. pypulseqpp-0.0.1/external/MRArbGrad/LICENSE +7 -0
  65. pypulseqpp-0.0.1/external/MRArbGrad/MANIFEST.in +1 -0
  66. pypulseqpp-0.0.1/external/MRArbGrad/README.md +34 -0
  67. pypulseqpp-0.0.1/external/MRArbGrad/copy.bash +16 -0
  68. pypulseqpp-0.0.1/external/MRArbGrad/example/example_BuiltInTraj.py +105 -0
  69. pypulseqpp-0.0.1/external/MRArbGrad/example/example_ExternalFunction2D.py +80 -0
  70. pypulseqpp-0.0.1/external/MRArbGrad/example/example_ExternalSamples2D.py +84 -0
  71. pypulseqpp-0.0.1/external/MRArbGrad/example/example_ExternalSamples3D.py +72 -0
  72. pypulseqpp-0.0.1/external/MRArbGrad/example/requirements.txt +1 -0
  73. pypulseqpp-0.0.1/external/MRArbGrad/install.bash +9 -0
  74. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/Function.py +570 -0
  75. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/Utility.py +330 -0
  76. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/__init__.py +6 -0
  77. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/mag/Mag.cpp +446 -0
  78. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/mag/Mag.h +197 -0
  79. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/main.cpp +871 -0
  80. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/Cones.h +141 -0
  81. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/MrTraj.h +414 -0
  82. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/MrTraj_2D.h +63 -0
  83. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/Rosette.h +105 -0
  84. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/Seiffert.h +198 -0
  85. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/Shell3d.h +107 -0
  86. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/Spiral.h +60 -0
  87. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/TrajFunc.h +90 -0
  88. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/VDSpiral.h +113 -0
  89. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/traj/Yarnball.h +146 -0
  90. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/Intp.h +90 -0
  91. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/LinIntp.h +71 -0
  92. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/SplineIntp.h +134 -0
  93. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/global.cpp +1 -0
  94. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/global.h +68 -0
  95. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/v3.cpp +450 -0
  96. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/ext/utility/v3.h +72 -0
  97. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/trajfunc/__init__.py +1 -0
  98. pypulseqpp-0.0.1/external/MRArbGrad/mrarbgrad_src/trajfunc/main.py +38 -0
  99. pypulseqpp-0.0.1/external/MRArbGrad/push.bash +3 -0
  100. pypulseqpp-0.0.1/external/MRArbGrad/pyproject.toml +20 -0
  101. pypulseqpp-0.0.1/external/MRArbGrad/requirements.txt +3 -0
  102. pypulseqpp-0.0.1/external/MRArbGrad/setup.py +62 -0
  103. pypulseqpp-0.0.1/external/MRArbGrad/upload.bash +3 -0
  104. pypulseqpp-0.0.1/external/MRArbGrad/upload_twine.bash +3 -0
  105. pypulseqpp-0.0.1/external/NOTICE.md +44 -0
  106. pypulseqpp-0.0.1/pyproject.toml +171 -0
  107. pypulseqpp-0.0.1/scripts/build_docs.sh +56 -0
  108. pypulseqpp-0.0.1/scripts/format_and_lint.sh +21 -0
  109. pypulseqpp-0.0.1/scripts/sync_agent_docs.sh +29 -0
  110. pypulseqpp-0.0.1/setup.py +5 -0
  111. pypulseqpp-0.0.1/src/cpp/bindings/arbgrad.cpp +232 -0
  112. pypulseqpp-0.0.1/src/cpp/bindings/arbgrad.h +10 -0
  113. pypulseqpp-0.0.1/src/cpp/bindings/module.cpp +1826 -0
  114. pypulseqpp-0.0.1/src/cpp/bindings/ptx.cpp +792 -0
  115. pypulseqpp-0.0.1/src/cpp/bindings/ptx.h +10 -0
  116. pypulseqpp-0.0.1/src/cpp/bindings/pulseqpp_decode.h +357 -0
  117. pypulseqpp-0.0.1/src/cpp/bindings/pulseqpp_events.h +1178 -0
  118. pypulseqpp-0.0.1/src/cpp/bindings/pulseqpp_eventtypes.cpp +1392 -0
  119. pypulseqpp-0.0.1/src/cpp/bindings/pulseqpp_eventtypes.h +349 -0
  120. pypulseqpp-0.0.1/src/cpp/bindings/sampling.cpp +427 -0
  121. pypulseqpp-0.0.1/src/cpp/bindings/sampling.h +10 -0
  122. pypulseqpp-0.0.1/src/cpp/bindings/sim.cpp +194 -0
  123. pypulseqpp-0.0.1/src/cpp/bindings/sim.h +10 -0
  124. pypulseqpp-0.0.1/src/cpp/bindings/slr.cpp +375 -0
  125. pypulseqpp-0.0.1/src/cpp/bindings/slr.h +10 -0
  126. pypulseqpp-0.0.1/src/cpp/pulseq/analysis.cpp +377 -0
  127. pypulseqpp-0.0.1/src/cpp/pulseq/analysis.hpp +97 -0
  128. pypulseqpp-0.0.1/src/cpp/pulseq/binary.hpp +87 -0
  129. pypulseqpp-0.0.1/src/cpp/pulseq/channels.hpp +41 -0
  130. pypulseqpp-0.0.1/src/cpp/pulseq/corners.cpp +232 -0
  131. pypulseqpp-0.0.1/src/cpp/pulseq/corners.hpp +85 -0
  132. pypulseqpp-0.0.1/src/cpp/pulseq/dedup.cpp +798 -0
  133. pypulseqpp-0.0.1/src/cpp/pulseq/events.hpp +262 -0
  134. pypulseqpp-0.0.1/src/cpp/pulseq/fov.cpp +1031 -0
  135. pypulseqpp-0.0.1/src/cpp/pulseq/fov.hpp +124 -0
  136. pypulseqpp-0.0.1/src/cpp/pulseq/kspace.cpp +612 -0
  137. pypulseqpp-0.0.1/src/cpp/pulseq/kspace.hpp +84 -0
  138. pypulseqpp-0.0.1/src/cpp/pulseq/md5.cpp +138 -0
  139. pypulseqpp-0.0.1/src/cpp/pulseq/md5.hpp +26 -0
  140. pypulseqpp-0.0.1/src/cpp/pulseq/parsed.hpp +87 -0
  141. pypulseqpp-0.0.1/src/cpp/pulseq/pns.cpp +175 -0
  142. pypulseqpp-0.0.1/src/cpp/pulseq/pns.hpp +92 -0
  143. pypulseqpp-0.0.1/src/cpp/pulseq/raster.cpp +132 -0
  144. pypulseqpp-0.0.1/src/cpp/pulseq/raster.hpp +94 -0
  145. pypulseqpp-0.0.1/src/cpp/pulseq/read.cpp +1058 -0
  146. pypulseqpp-0.0.1/src/cpp/pulseq/read.hpp +32 -0
  147. pypulseqpp-0.0.1/src/cpp/pulseq/read_binary.cpp +455 -0
  148. pypulseqpp-0.0.1/src/cpp/pulseq/resonance.cpp +225 -0
  149. pypulseqpp-0.0.1/src/cpp/pulseq/resonance.hpp +90 -0
  150. pypulseqpp-0.0.1/src/cpp/pulseq/rf_safety.cpp +394 -0
  151. pypulseqpp-0.0.1/src/cpp/pulseq/rf_safety.hpp +102 -0
  152. pypulseqpp-0.0.1/src/cpp/pulseq/rfft.cpp +195 -0
  153. pypulseqpp-0.0.1/src/cpp/pulseq/rfft.hpp +59 -0
  154. pypulseqpp-0.0.1/src/cpp/pulseq/safety.cpp +603 -0
  155. pypulseqpp-0.0.1/src/cpp/pulseq/safety.hpp +126 -0
  156. pypulseqpp-0.0.1/src/cpp/pulseq/sequence.cpp +1107 -0
  157. pypulseqpp-0.0.1/src/cpp/pulseq/sequence.hpp +1722 -0
  158. pypulseqpp-0.0.1/src/cpp/pulseq/shape.cpp +349 -0
  159. pypulseqpp-0.0.1/src/cpp/pulseq/shape.hpp +48 -0
  160. pypulseqpp-0.0.1/src/cpp/pulseq/timing.cpp +685 -0
  161. pypulseqpp-0.0.1/src/cpp/pulseq/timing.hpp +105 -0
  162. pypulseqpp-0.0.1/src/cpp/pulseq/types.hpp +133 -0
  163. pypulseqpp-0.0.1/src/cpp/pulseq/waveforms.cpp +482 -0
  164. pypulseqpp-0.0.1/src/cpp/pulseq/waveforms.hpp +105 -0
  165. pypulseqpp-0.0.1/src/cpp/pulseq/write.hpp +74 -0
  166. pypulseqpp-0.0.1/src/cpp/pulseq/write_binary.cpp +510 -0
  167. pypulseqpp-0.0.1/src/cpp/pulseq/write_text.cpp +1262 -0
  168. pypulseqpp-0.0.1/src/cpp/third_party/pocketfft_hdronly.h +3853 -0
  169. pypulseqpp-0.0.1/src/pypulseqpp/__init__.py +298 -0
  170. pypulseqpp-0.0.1/src/pypulseqpp/_accelerators.py +67 -0
  171. pypulseqpp-0.0.1/src/pypulseqpp/_adiabatic.py +328 -0
  172. pypulseqpp-0.0.1/src/pypulseqpp/_angles.py +316 -0
  173. pypulseqpp-0.0.1/src/pypulseqpp/_arbgrad/__init__.py +224 -0
  174. pypulseqpp-0.0.1/src/pypulseqpp/_b1_pulses.py +397 -0
  175. pypulseqpp-0.0.1/src/pypulseqpp/_band_phases.py +103 -0
  176. pypulseqpp-0.0.1/src/pypulseqpp/_block_to_events.py +74 -0
  177. pypulseqpp-0.0.1/src/pypulseqpp/_calc_rf_bandwidth.py +167 -0
  178. pypulseqpp-0.0.1/src/pypulseqpp/_calc_rf_power.py +68 -0
  179. pypulseqpp-0.0.1/src/pypulseqpp/_check_timing.py +220 -0
  180. pypulseqpp-0.0.1/src/pypulseqpp/_epi.py +170 -0
  181. pypulseqpp-0.0.1/src/pypulseqpp/_events.py +431 -0
  182. pypulseqpp-0.0.1/src/pypulseqpp/_gradients.py +441 -0
  183. pypulseqpp-0.0.1/src/pypulseqpp/_kspace.py +164 -0
  184. pypulseqpp-0.0.1/src/pypulseqpp/_make_hexagon_gradient_area.py +408 -0
  185. pypulseqpp-0.0.1/src/pypulseqpp/_make_label.py +57 -0
  186. pypulseqpp-0.0.1/src/pypulseqpp/_make_rf_shim.py +41 -0
  187. pypulseqpp-0.0.1/src/pypulseqpp/_make_rotation.py +165 -0
  188. pypulseqpp-0.0.1/src/pypulseqpp/_masks.py +875 -0
  189. pypulseqpp-0.0.1/src/pypulseqpp/_opts.py +202 -0
  190. pypulseqpp-0.0.1/src/pypulseqpp/_ordering.py +121 -0
  191. pypulseqpp-0.0.1/src/pypulseqpp/_ptx.py +477 -0
  192. pypulseqpp-0.0.1/src/pypulseqpp/_report.py +349 -0
  193. pypulseqpp-0.0.1/src/pypulseqpp/_results.py +147 -0
  194. pypulseqpp-0.0.1/src/pypulseqpp/_rf_pulses.py +1006 -0
  195. pypulseqpp-0.0.1/src/pypulseqpp/_rotate_3d.py +163 -0
  196. pypulseqpp-0.0.1/src/pypulseqpp/_sampling.py +85 -0
  197. pypulseqpp-0.0.1/src/pypulseqpp/_schedules.py +158 -0
  198. pypulseqpp-0.0.1/src/pypulseqpp/_sequence.py +1454 -0
  199. pypulseqpp-0.0.1/src/pypulseqpp/_sim_rf.py +230 -0
  200. pypulseqpp-0.0.1/src/pypulseqpp/_slice_encoding.py +364 -0
  201. pypulseqpp-0.0.1/src/pypulseqpp/_slr.py +700 -0
  202. pypulseqpp-0.0.1/src/pypulseqpp/_timing.py +170 -0
  203. pypulseqpp-0.0.1/src/pypulseqpp/_traj_to_grad.py +121 -0
  204. pypulseqpp-0.0.1/src/pypulseqpp/_trajectories.py +279 -0
  205. pypulseqpp-0.0.1/src/pypulseqpp/_transform_fov.py +264 -0
  206. pypulseqpp-0.0.1/src/pypulseqpp/_waveforms.py +346 -0
  207. pypulseqpp-0.0.1/src/pypulseqpp/cli/__init__.py +237 -0
  208. pypulseqpp-0.0.1/src/pypulseqpp/plot/__init__.py +23 -0
  209. pypulseqpp-0.0.1/src/pypulseqpp/plot/_kspace.py +206 -0
  210. pypulseqpp-0.0.1/src/pypulseqpp/plot/_paper.py +211 -0
  211. pypulseqpp-0.0.1/src/pypulseqpp/plot/_rf.py +386 -0
  212. pypulseqpp-0.0.1/src/pypulseqpp/plot/_seqeyes.py +433 -0
  213. pypulseqpp-0.0.1/src/pypulseqpp/plot/_style.py +68 -0
  214. pypulseqpp-0.0.1/src/pypulseqpp/safety/__init__.py +172 -0
  215. pypulseqpp-0.0.1/src/pypulseqpp/safety/_physical.py +22 -0
  216. pypulseqpp-0.0.1/src/pypulseqpp/safety/_pns.py +142 -0
  217. pypulseqpp-0.0.1/src/pypulseqpp/safety/_resonance.py +296 -0
  218. pypulseqpp-0.0.1/src/pypulseqpp/safety/_sar.py +382 -0
  219. pypulseqpp-0.0.1/src/pypulseqpp/sequences/__init__.py +162 -0
  220. pypulseqpp-0.0.1/src/pypulseqpp/sequences/_module.py +290 -0
  221. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/__init__.py +24 -0
  222. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/_base.py +55 -0
  223. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/multiband.py +304 -0
  224. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/nonselective.py +226 -0
  225. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/selective.py +273 -0
  226. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/spatial2d.py +115 -0
  227. pypulseqpp-0.0.1/src/pypulseqpp/sequences/excitation/spectral.py +233 -0
  228. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/__init__.py +26 -0
  229. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/_common.py +19 -0
  230. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/diffusion.py +245 -0
  231. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/fatsat.py +229 -0
  232. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/inversion.py +106 -0
  233. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/saturation.py +382 -0
  234. pypulseqpp-0.0.1/src/pypulseqpp/sequences/preparation/t2prep.py +222 -0
  235. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/__init__.py +48 -0
  236. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/_common.py +183 -0
  237. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/_trajectories.py +841 -0
  238. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/bssfp.py +358 -0
  239. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/epi.py +684 -0
  240. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/fse.py +519 -0
  241. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/line.py +510 -0
  242. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/navigator.py +226 -0
  243. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/noncartesian.py +1067 -0
  244. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/propeller.py +208 -0
  245. pypulseqpp-0.0.1/src/pypulseqpp/sequences/readout/zte.py +361 -0
  246. pypulseqpp-0.0.1/tests/conftest.py +42 -0
  247. pypulseqpp-0.0.1/tests/convert.py +142 -0
  248. pypulseqpp-0.0.1/tests/cpp/sanitize.cpp +265 -0
  249. pypulseqpp-0.0.1/tests/extended.py +127 -0
  250. pypulseqpp-0.0.1/tests/reference.py +430 -0
  251. pypulseqpp-0.0.1/tests/requirements.txt +12 -0
  252. pypulseqpp-0.0.1/tests/seq/NOTICE.md +16 -0
  253. pypulseqpp-0.0.1/tests/seq/seq1.seq +68 -0
  254. pypulseqpp-0.0.1/tests/seq/seq1_m.seq +70 -0
  255. pypulseqpp-0.0.1/tests/seq/seq2.seq +73 -0
  256. pypulseqpp-0.0.1/tests/seq/seq2_m.seq +75 -0
  257. pypulseqpp-0.0.1/tests/seq/seq3.seq +136 -0
  258. pypulseqpp-0.0.1/tests/seq/seq3_m.seq +138 -0
  259. pypulseqpp-0.0.1/tests/seq/seq4.seq +154 -0
  260. pypulseqpp-0.0.1/tests/seq/seq4_m.seq +156 -0
  261. pypulseqpp-0.0.1/tests/seq/seq5.seq +1315 -0
  262. pypulseqpp-0.0.1/tests/seq/seq6.seq +147 -0
  263. pypulseqpp-0.0.1/tests/seq/seq_adc_only.seq +33 -0
  264. pypulseqpp-0.0.1/tests/seq/seq_ext_only.seq +45 -0
  265. pypulseqpp-0.0.1/tests/seq/seq_make_block_pulses.seq +79 -0
  266. pypulseqpp-0.0.1/tests/seq/seq_make_block_pulses_m.seq +75 -0
  267. pypulseqpp-0.0.1/tests/seq/seq_make_gauss_pulses.seq +9055 -0
  268. pypulseqpp-0.0.1/tests/seq/seq_make_gauss_pulses_m.seq +5083 -0
  269. pypulseqpp-0.0.1/tests/seq/seq_make_radial.seq +92 -0
  270. pypulseqpp-0.0.1/tests/seq/seq_make_sinc_pulses.seq +9124 -0
  271. pypulseqpp-0.0.1/tests/seq/seq_make_sinc_pulses_m.seq +10107 -0
  272. pypulseqpp-0.0.1/tests/seq/seq_trap_only.seq +33 -0
  273. pypulseqpp-0.0.1/tests/seq/simple_mprage120.seq +3666 -0
  274. pypulseqpp-0.0.1/tests/seq/simple_mprage130.seq +3706 -0
  275. pypulseqpp-0.0.1/tests/seq/simple_mprage131.seq +3706 -0
  276. pypulseqpp-0.0.1/tests/seq/simple_mprage140.seq +3687 -0
  277. pypulseqpp-0.0.1/tests/seq/simple_mprage141.seq +3687 -0
  278. pypulseqpp-0.0.1/tests/seq/simple_mprage142.seq +3687 -0
  279. pypulseqpp-0.0.1/tests/seq/simple_mprage150.seq +3688 -0
  280. pypulseqpp-0.0.1/tests/seq/write_epi.seq +3466 -0
  281. pypulseqpp-0.0.1/tests/seq/write_epi_label.seq +8768 -0
  282. pypulseqpp-0.0.1/tests/seq/write_epi_se.seq +3227 -0
  283. pypulseqpp-0.0.1/tests/seq/write_epi_se_rs.seq +10341 -0
  284. pypulseqpp-0.0.1/tests/seq/write_gre.seq +3504 -0
  285. pypulseqpp-0.0.1/tests/seq/write_gre_label.seq +3527 -0
  286. pypulseqpp-0.0.1/tests/seq/write_gre_label_softdelay.seq +3666 -0
  287. pypulseqpp-0.0.1/tests/seq/write_haste.seq +4911 -0
  288. pypulseqpp-0.0.1/tests/seq/write_mprage.seq +8268 -0
  289. pypulseqpp-0.0.1/tests/seq/write_radial_gre.seq +4698 -0
  290. pypulseqpp-0.0.1/tests/seq/write_tse.seq +5096 -0
  291. pypulseqpp-0.0.1/tests/seq/write_ute.seq +1429 -0
  292. pypulseqpp-0.0.1/tests/test_absolute_trajectory.py +129 -0
  293. pypulseqpp-0.0.1/tests/test_adiabatic.py +137 -0
  294. pypulseqpp-0.0.1/tests/test_arbgrad_spiral.py +111 -0
  295. pypulseqpp-0.0.1/tests/test_b1_and_recursive_pulses.py +160 -0
  296. pypulseqpp-0.0.1/tests/test_binary.py +214 -0
  297. pypulseqpp-0.0.1/tests/test_binary_signature.py +110 -0
  298. pypulseqpp-0.0.1/tests/test_block.py +167 -0
  299. pypulseqpp-0.0.1/tests/test_block_k_origins.py +190 -0
  300. pypulseqpp-0.0.1/tests/test_block_to_events.py +68 -0
  301. pypulseqpp-0.0.1/tests/test_calc_rf_bandwidth.py +133 -0
  302. pypulseqpp-0.0.1/tests/test_check_timing.py +610 -0
  303. pypulseqpp-0.0.1/tests/test_corpus.py +291 -0
  304. pypulseqpp-0.0.1/tests/test_design_excitation.py +273 -0
  305. pypulseqpp-0.0.1/tests/test_design_excitation_selective_pulses.py +422 -0
  306. pypulseqpp-0.0.1/tests/test_design_fatsat.py +308 -0
  307. pypulseqpp-0.0.1/tests/test_design_module.py +270 -0
  308. pypulseqpp-0.0.1/tests/test_design_preparation.py +427 -0
  309. pypulseqpp-0.0.1/tests/test_design_promoted.py +188 -0
  310. pypulseqpp-0.0.1/tests/test_design_readout_bssfp.py +438 -0
  311. pypulseqpp-0.0.1/tests/test_design_readout_epi.py +501 -0
  312. pypulseqpp-0.0.1/tests/test_design_readout_fse.py +552 -0
  313. pypulseqpp-0.0.1/tests/test_design_readout_line.py +408 -0
  314. pypulseqpp-0.0.1/tests/test_design_readout_navigator.py +133 -0
  315. pypulseqpp-0.0.1/tests/test_design_readout_noncartesian.py +650 -0
  316. pypulseqpp-0.0.1/tests/test_design_readout_propeller.py +233 -0
  317. pypulseqpp-0.0.1/tests/test_design_readout_wave.py +232 -0
  318. pypulseqpp-0.0.1/tests/test_design_readout_zte.py +420 -0
  319. pypulseqpp-0.0.1/tests/test_design_refocusing.py +211 -0
  320. pypulseqpp-0.0.1/tests/test_design_slice_rephasing.py +282 -0
  321. pypulseqpp-0.0.1/tests/test_docstrings.py +49 -0
  322. pypulseqpp-0.0.1/tests/test_evaluate_labels.py +162 -0
  323. pypulseqpp-0.0.1/tests/test_examples.py +227 -0
  324. pypulseqpp-0.0.1/tests/test_facade.py +983 -0
  325. pypulseqpp-0.0.1/tests/test_fov_shift.py +274 -0
  326. pypulseqpp-0.0.1/tests/test_get_block.py +192 -0
  327. pypulseqpp-0.0.1/tests/test_gradient_axes.py +245 -0
  328. pypulseqpp-0.0.1/tests/test_interoperability.py +85 -0
  329. pypulseqpp-0.0.1/tests/test_kspace.py +455 -0
  330. pypulseqpp-0.0.1/tests/test_labels.py +84 -0
  331. pypulseqpp-0.0.1/tests/test_make_rotation.py +108 -0
  332. pypulseqpp-0.0.1/tests/test_matlab_parity.py +289 -0
  333. pypulseqpp-0.0.1/tests/test_mech_resonance.py +422 -0
  334. pypulseqpp-0.0.1/tests/test_opts.py +161 -0
  335. pypulseqpp-0.0.1/tests/test_opts_cap_system.py +79 -0
  336. pypulseqpp-0.0.1/tests/test_paper_plot.py +190 -0
  337. pypulseqpp-0.0.1/tests/test_parity.py +80 -0
  338. pypulseqpp-0.0.1/tests/test_plot.py +357 -0
  339. pypulseqpp-0.0.1/tests/test_plotting.py +311 -0
  340. pypulseqpp-0.0.1/tests/test_pns.py +292 -0
  341. pypulseqpp-0.0.1/tests/test_promoted_columns.py +107 -0
  342. pypulseqpp-0.0.1/tests/test_ptx.py +136 -0
  343. pypulseqpp-0.0.1/tests/test_ptx_design.py +258 -0
  344. pypulseqpp-0.0.1/tests/test_ptx_kernels.py +184 -0
  345. pypulseqpp-0.0.1/tests/test_pypulseq_base_factories.py +365 -0
  346. pypulseqpp-0.0.1/tests/test_pypulseq_epi_order.py +221 -0
  347. pypulseqpp-0.0.1/tests/test_pypulseq_simulate.py +146 -0
  348. pypulseqpp-0.0.1/tests/test_read.py +201 -0
  349. pypulseqpp-0.0.1/tests/test_repetition.py +352 -0
  350. pypulseqpp-0.0.1/tests/test_report.py +334 -0
  351. pypulseqpp-0.0.1/tests/test_rf_power.py +145 -0
  352. pypulseqpp-0.0.1/tests/test_rf_shim.py +87 -0
  353. pypulseqpp-0.0.1/tests/test_rotation_convention.py +103 -0
  354. pypulseqpp-0.0.1/tests/test_rotation_extension.py +133 -0
  355. pypulseqpp-0.0.1/tests/test_safety.py +546 -0
  356. pypulseqpp-0.0.1/tests/test_sampling_angles.py +36 -0
  357. pypulseqpp-0.0.1/tests/test_sampling_cartesian.py +125 -0
  358. pypulseqpp-0.0.1/tests/test_sampling_orderings.py +98 -0
  359. pypulseqpp-0.0.1/tests/test_sampling_poisson.py +106 -0
  360. pypulseqpp-0.0.1/tests/test_sar.py +350 -0
  361. pypulseqpp-0.0.1/tests/test_sequence.py +209 -0
  362. pypulseqpp-0.0.1/tests/test_shape.py +149 -0
  363. pypulseqpp-0.0.1/tests/test_sim_bloch.py +166 -0
  364. pypulseqpp-0.0.1/tests/test_slice_encoding.py +144 -0
  365. pypulseqpp-0.0.1/tests/test_slr_design.py +256 -0
  366. pypulseqpp-0.0.1/tests/test_smoke.py +8 -0
  367. pypulseqpp-0.0.1/tests/test_soft_delays.py +203 -0
  368. pypulseqpp-0.0.1/tests/test_structure.py +480 -0
  369. pypulseqpp-0.0.1/tests/test_transform_fov.py +925 -0
  370. pypulseqpp-0.0.1/tests/test_v141.py +129 -0
  371. pypulseqpp-0.0.1/tests/test_version.py +58 -0
  372. pypulseqpp-0.0.1/tests/test_waveforms.py +315 -0
@@ -0,0 +1,11 @@
1
+ * text=auto eol=lf
2
+
3
+ *.png binary
4
+ *.jpg binary
5
+ *.npy binary
6
+ *.npz binary
7
+ *.mat binary
8
+ *.h5 binary
9
+
10
+ # Generated: keep them out of diffs and out of the language statistics.
11
+ *.ipynb linguist-documentation=true
@@ -0,0 +1,42 @@
1
+ name: Bug report
2
+ description: Something behaves differently from what it says it does.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: textarea
6
+ id: what-happened
7
+ attributes:
8
+ label: What happened
9
+ description: What did you expect, and what did you get instead?
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: reproduce
15
+ attributes:
16
+ label: Smallest reproduction
17
+ description: >
18
+ Code we can run. Prefer synthetic arrays over a data file; if the shape
19
+ of real data matters, give the shapes and dtypes.
20
+ render: python
21
+ validations:
22
+ required: true
23
+
24
+ - type: textarea
25
+ id: environment
26
+ attributes:
27
+ label: Environment
28
+ description: >
29
+ Output of `python -c "import pypulseqpp, sys, torch;
30
+ print(pypulseqpp.__version__, sys.version, torch.__version__,
31
+ torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'cpu')"`
32
+ render: shell
33
+ validations:
34
+ required: true
35
+
36
+ - type: dropdown
37
+ id: device
38
+ attributes:
39
+ label: Where it happens
40
+ options: ["CPU only", "CUDA only", "Both"]
41
+ validations:
42
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Question or idea
4
+ url: https://github.com/pulserver/pypulseqpp/discussions
5
+ about: Ask about usage, or propose something before it is a bug report.
6
+ - name: Security vulnerability
7
+ url: https://github.com/pulserver/pypulseqpp/security/advisories/new
8
+ about: Report privately. Please do not open a public issue.
@@ -0,0 +1,31 @@
1
+ name: Feature request
2
+ description: Something this package should be able to do and cannot.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: The problem
9
+ description: What are you trying to do, and where does it stop?
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: proposal
15
+ attributes:
16
+ label: What you have in mind
17
+ description: >
18
+ A sketch of the API, if you have one. If there is a paper or another
19
+ implementation that does this, name it — that is usually the fastest
20
+ way to agree on what is wanted.
21
+ validations:
22
+ required: true
23
+
24
+ - type: textarea
25
+ id: layer
26
+ attributes:
27
+ label: Which layer this belongs to
28
+ description: >
29
+ This package is one of a family with a strict layering (`mrutils` at the
30
+ base, `deepmr` at the top). If the feature needs something from a
31
+ sibling package, say which — that usually means it belongs elsewhere.
@@ -0,0 +1,24 @@
1
+ ## What this changes
2
+
3
+ <!-- One or two sentences. What behaviour is different afterwards? -->
4
+
5
+ ## Why
6
+
7
+ <!-- The problem this solves. Link the issue if there is one. -->
8
+
9
+ ## How it was verified
10
+
11
+ <!-- The commands you ran and what they said. Paste the output, don't
12
+ summarise it. "Tests pass" is not verification. -->
13
+
14
+ ```
15
+ ```
16
+
17
+ ## Checklist
18
+
19
+ - [ ] `bash scripts/format_and_lint.sh` is clean
20
+ - [ ] `pytest -q` passes, and new behaviour has a test whose name states the
21
+ invariant it protects
22
+ - [ ] Anything numerical is exercised on CPU and CUDA, or the CUDA leg is
23
+ marked `@pytest.mark.cuda` and skips cleanly
24
+ - [ ] No comment or docstring describes the code's history
@@ -0,0 +1,36 @@
1
+ version: 2
2
+ updates:
3
+ # The Actions in .github/workflows pin third-party code that runs with
4
+ # repository credentials, so these are the updates worth having weekly.
5
+ - package-ecosystem: github-actions
6
+ directory: "/"
7
+ schedule:
8
+ interval: weekly
9
+ day: monday
10
+ labels: ["dependencies", "ci"]
11
+ commit-message:
12
+ prefix: "ci"
13
+ groups:
14
+ actions:
15
+ patterns: ["*"]
16
+
17
+ - package-ecosystem: pip
18
+ directory: "/"
19
+ schedule:
20
+ interval: weekly
21
+ day: monday
22
+ open-pull-requests-limit: 5
23
+ labels: ["dependencies", "python"]
24
+ commit-message:
25
+ prefix: "deps"
26
+ groups:
27
+ # The reference implementation moves the parity fixtures with it, so its
28
+ # bumps arrive alone and get a full run of their own.
29
+ reference:
30
+ patterns: ["pypulseq"]
31
+ numerical:
32
+ patterns: ["numpy"]
33
+ build:
34
+ patterns: ["scikit-build-core", "pybind11", "setuptools_scm"]
35
+ dev-tooling:
36
+ patterns: ["pytest*", "coverage", "ruff", "pre-commit", "build"]
@@ -0,0 +1,33 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ analyze:
11
+ name: Analyze Python
12
+ runs-on: ubuntu-latest
13
+ # Code scanning on a private repository needs GitHub Advanced Security,
14
+ # and uploading results without it is a hard error rather than a warning.
15
+ # The job stands down while the repository is private and starts working
16
+ # on its own once it is made public.
17
+ if: github.event.repository.visibility == 'public'
18
+ permissions:
19
+ security-events: write
20
+ packages: read
21
+ actions: read
22
+ contents: read
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ with:
26
+ submodules: recursive # the solver in external/ is compiled in
27
+ - uses: github/codeql-action/init@v4
28
+ with:
29
+ languages: python
30
+ build-mode: none
31
+ - uses: github/codeql-action/analyze@v4
32
+ with:
33
+ category: "/language:python"
@@ -0,0 +1,131 @@
1
+ name: Docs
2
+
3
+ # Builds the documentation and publishes it to GitHub Pages, which serves the
4
+ # gh-pages branch. The site holds one directory per version: main publishes
5
+ # `latest`, a release tag publishes `vX.Y.Z` and copies it to `stable`, and the
6
+ # root redirects to whichever of the two a reader should see. Pull requests
7
+ # build without publishing, so a documentation warning fails review rather than
8
+ # the site.
9
+ #
10
+ # The branch is the site's archive. GitHub Pages can be deployed from an
11
+ # artifact instead, but a deployment replaces the whole site, so publishing one
12
+ # version would drop the others.
13
+
14
+ on:
15
+ push:
16
+ branches: [main]
17
+ tags: ["v[0-9]+.[0-9]+.[0-9]+"]
18
+ pull_request:
19
+ workflow_dispatch:
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ # Every publish commits to the same branch, so main, the tags and a manual run
25
+ # share one group and wait for each other rather than racing to push. A pull
26
+ # request publishes nothing and gets a group of its own.
27
+ concurrency:
28
+ group: docs-${{ github.event.pull_request.number || 'publish' }}
29
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
30
+
31
+ jobs:
32
+ docs:
33
+ runs-on: ubuntu-latest
34
+ permissions:
35
+ contents: write # the publish step pushes the built site to gh-pages
36
+ steps:
37
+ - uses: actions/checkout@v7
38
+ with:
39
+ submodules: recursive # the solver in external/ is compiled in
40
+ fetch-depth: 0 # setuptools_scm reads the tags
41
+
42
+ - uses: actions/setup-python@v7
43
+ with:
44
+ python-version: "3.12"
45
+ cache: pip
46
+
47
+ - name: Name this version
48
+ id: version
49
+ # A release is built as `stable`, the URL a reader should land on, and
50
+ # archived under its tag as well; the canonical link in both copies
51
+ # points at `stable`, so a search result leads to the current release
52
+ # rather than to whichever version was indexed first.
53
+ run: |
54
+ if [ "$GITHUB_REF_TYPE" = tag ]; then
55
+ echo "dir=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
56
+ echo "canonical=stable" >> "$GITHUB_OUTPUT"
57
+ else
58
+ echo "dir=latest" >> "$GITHUB_OUTPUT"
59
+ echo "canonical=latest" >> "$GITHUB_OUTPUT"
60
+ fi
61
+
62
+ - name: Install
63
+ # Into the CMake build directory build_docs.sh uses, so the build it
64
+ # makes for Sphinx reuses these objects rather than compiling twice.
65
+ run: |
66
+ python -m pip install --upgrade pip
67
+ python -m pip install -Cbuild-dir="$PWD/docs/build/cmake" '.[doc]'
68
+
69
+ - name: Build
70
+ env:
71
+ PYPULSEQPP_DOCS_VERSION: ${{ steps.version.outputs.canonical }}
72
+ run: bash scripts/build_docs.sh
73
+
74
+ - name: Check that the site is only the site
75
+ # Sphinx caches parsed documents in the output directory unless given
76
+ # `-d`, and the cache is far larger than the pages. Publishing commits
77
+ # whatever is there to a branch that is never pruned, so a build that
78
+ # leaves it behind has to fail here rather than on main.
79
+ run: |
80
+ if [ -e docs/build/html/.doctrees ]; then
81
+ echo "::error::The doctree cache is inside the built site and would be published." >&2
82
+ exit 1
83
+ fi
84
+
85
+ - name: Publish
86
+ if: github.event_name != 'pull_request'
87
+ env:
88
+ DIR: ${{ steps.version.outputs.dir }}
89
+ GH_TOKEN: ${{ github.token }}
90
+ run: |
91
+ site="$RUNNER_TEMP/pages"
92
+ remote="https://x-access-token:$GH_TOKEN@github.com/$GITHUB_REPOSITORY"
93
+ # The branch does not exist before the first publish.
94
+ if ! git clone --depth 1 --branch gh-pages "$remote" "$site"; then
95
+ git init -b gh-pages "$site"
96
+ git -C "$site" remote add origin "$remote"
97
+ fi
98
+
99
+ # Only this version's directory is replaced; the others stay as the
100
+ # release that built them left them.
101
+ rm -rf "${site:?}/$DIR"
102
+ cp -r docs/build/html "$site/$DIR"
103
+
104
+ # `stable` is the newest release, so a patch tagged after a later
105
+ # release archives itself without taking `stable` backwards.
106
+ if [ "$GITHUB_REF_TYPE" = tag ]; then
107
+ newest=$(ls "$site" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
108
+ if [ "$newest" = "$DIR" ]; then
109
+ rm -rf "$site/stable"
110
+ cp -r "$site/$DIR" "$site/stable"
111
+ else
112
+ echo "$newest is newer than $DIR; leaving stable alone."
113
+ fi
114
+ fi
115
+
116
+ # The root goes to the current release, or to main's pages until
117
+ # there is one.
118
+ target=latest
119
+ [ -d "$site/stable" ] && target=stable
120
+ printf '<!doctype html>\n<meta http-equiv="refresh" content="0; url=%s/">\n' \
121
+ "$target" > "$site/index.html"
122
+ # Pages runs the branch through Jekyll unless told not to, and Jekyll
123
+ # drops the _static and _sources directories Sphinx writes.
124
+ touch "$site/.nojekyll"
125
+
126
+ git -C "$site" add -A
127
+ git -C "$site" -c user.name='github-actions[bot]' \
128
+ -c user.email='41898282+github-actions[bot]@users.noreply.github.com' \
129
+ commit -m "Documentation for $DIR from $GITHUB_SHA" \
130
+ || { echo "No documentation change to publish."; exit 0; }
131
+ git -C "$site" push origin gh-pages
@@ -0,0 +1,42 @@
1
+ name: Style
2
+
3
+ # Runs the same script pre-commit runs, in --check mode: nothing is rewritten
4
+ # and any file that would change fails the job.
5
+
6
+ on:
7
+ push:
8
+ pull_request:
9
+ workflow_dispatch:
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ format-and-lint:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+ with:
21
+ submodules: recursive # the solver in external/ is compiled in
22
+ - uses: actions/setup-python@v7
23
+ with:
24
+ python-version: "3.11"
25
+
26
+ # The linter only, taken from the same `dev` extra a contributor's
27
+ # pre-commit installs, so both apply one ruff. Installing the extra whole
28
+ # would download the runtime for a job that never imports it.
29
+ - name: Install ruff
30
+ run: |
31
+ python -m pip install --upgrade pip
32
+ python - <<'PY' > "$RUNNER_TEMP/lint-requirements.txt"
33
+ import tomllib
34
+
35
+ with open("pyproject.toml", "rb") as pyproject:
36
+ dev = tomllib.load(pyproject)["project"]["optional-dependencies"]["dev"]
37
+ print("\n".join(line for line in dev if line.startswith("ruff")))
38
+ PY
39
+ python -m pip install -r "$RUNNER_TEMP/lint-requirements.txt"
40
+
41
+ - name: Format and lint (check only)
42
+ run: bash scripts/format_and_lint.sh --check
@@ -0,0 +1,58 @@
1
+ name: Release
2
+
3
+ # Publishes a tagged version: the wheels and sdist from the reusable Wheels
4
+ # workflow, then PyPI and a signed GitHub release. The version comes from the
5
+ # tag via setuptools_scm.
6
+
7
+ on:
8
+ push:
9
+ tags:
10
+ - "v[0-9]+.[0-9]+.[0-9]+"
11
+
12
+ jobs:
13
+ build:
14
+ uses: ./.github/workflows/wheels.yml
15
+ with:
16
+ cibw-build: "cp310-* cp311-* cp312-* cp313-*"
17
+
18
+ publish-to-pypi:
19
+ needs: [build]
20
+ runs-on: ubuntu-latest
21
+ environment:
22
+ name: pypi
23
+ url: https://pypi.org/p/pypulseqpp
24
+ permissions:
25
+ id-token: write # trusted publishing (OIDC)
26
+ steps:
27
+ - uses: actions/download-artifact@v8
28
+ with:
29
+ pattern: "{wheels-*,sdist}"
30
+ path: dist
31
+ merge-multiple: true
32
+ - uses: pypa/gh-action-pypi-publish@release/v1
33
+
34
+ github-release:
35
+ needs: [publish-to-pypi]
36
+ runs-on: ubuntu-latest
37
+ permissions:
38
+ contents: write # create the release
39
+ id-token: write # sigstore
40
+ steps:
41
+ - uses: actions/download-artifact@v8
42
+ with:
43
+ pattern: "{wheels-*,sdist}"
44
+ path: dist
45
+ merge-multiple: true
46
+ - uses: sigstore/gh-action-sigstore-python@v3.5.0
47
+ with:
48
+ inputs: |
49
+ ./dist/*.tar.gz
50
+ ./dist/*.whl
51
+ - env:
52
+ GITHUB_TOKEN: ${{ github.token }}
53
+ run: |
54
+ gh release create '${{ github.ref_name }}' \
55
+ --repo '${{ github.repository }}' \
56
+ --title '${{ github.ref_name }}' \
57
+ --generate-notes
58
+ gh release upload '${{ github.ref_name }}' dist/** --repo '${{ github.repository }}'
@@ -0,0 +1,74 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ # Every platform a wheel ships for: the compiled core is the package,
20
+ # so a platform that is not tested is not supported.
21
+ os: [ubuntu-latest, macos-14, windows-latest]
22
+ # The floor and the ceiling of the supported band: an intermediate
23
+ # release that fails on neither is not a plausible failure.
24
+ python-version: ["3.10", "3.13"]
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ with:
28
+ submodules: recursive # the solver in external/ is compiled in
29
+ fetch-depth: 0 # setuptools_scm reads the tags
30
+
31
+ - uses: actions/setup-python@v7
32
+ with:
33
+ python-version: ${{ matrix.python-version }}
34
+ cache: pip
35
+
36
+ - name: Install
37
+ run: |
38
+ python -m pip install --upgrade pip
39
+ python -m pip install .[dev] -r tests/requirements.txt
40
+
41
+ - name: Run the tests
42
+ run: pytest -q --cov --cov-report=xml
43
+
44
+ - uses: codecov/codecov-action@v7
45
+ if: matrix.os == 'ubuntu-latest'
46
+ with:
47
+ fail_ci_if_error: false
48
+ token: ${{ secrets.CODECOV_TOKEN }}
49
+ slug: pulserver/pypulseqpp
50
+
51
+ sanitize:
52
+ name: sanitizers
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - uses: actions/checkout@v7
56
+ with:
57
+ submodules: recursive # the solver in external/ is compiled in
58
+
59
+ # A read past the end of an array is not a wrong answer, it is whatever
60
+ # was there -- so the tests above catch it only when the memory it lands
61
+ # on differs enough to change a number. One such bug reported the same
62
+ # waveform on five of those six jobs and a different one on the sixth,
63
+ # and gave two different answers within a single process. This is the
64
+ # job that would have named it on the first run.
65
+ - name: Build the core with the sanitizers on
66
+ run: |
67
+ g++ -std=c++17 -g -O1 \
68
+ -fsanitize=address,undefined -fno-sanitize-recover=all \
69
+ -fno-omit-frame-pointer \
70
+ -I src/cpp -o sanitize \
71
+ tests/cpp/sanitize.cpp src/cpp/pulseq/*.cpp
72
+
73
+ - name: Read, write and expand every sequence in the corpus
74
+ run: ./sanitize tests/seq/*.seq
@@ -0,0 +1,30 @@
1
+ name: Viewer release
2
+
3
+ # Publishes pypulseqpp-seqeyes, the GPL viewer package in viewer/, from a tag
4
+ # of its own: it is versioned after the SeqEyes release it builds, not after
5
+ # pypulseqpp.
6
+
7
+ on:
8
+ push:
9
+ tags:
10
+ - "viewer-v[0-9]+.[0-9]+.[0-9]+*"
11
+
12
+ jobs:
13
+ build:
14
+ uses: ./.github/workflows/viewer-wheels.yml
15
+
16
+ publish-to-pypi:
17
+ needs: [build]
18
+ runs-on: ubuntu-latest
19
+ environment:
20
+ name: pypi-viewer
21
+ url: https://pypi.org/p/pypulseqpp-seqeyes
22
+ permissions:
23
+ id-token: write # trusted publishing (OIDC)
24
+ steps:
25
+ - uses: actions/download-artifact@v8
26
+ with:
27
+ pattern: "{viewer-wheels-*,viewer-sdist}"
28
+ path: dist
29
+ merge-multiple: true
30
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,104 @@
1
+ name: Viewer wheels
2
+
3
+ # SeqEyes, built from viewer/ as the pypulseqpp-seqeyes wheels that
4
+ # `pip install 'pypulseqpp[plot]'` installs. Reusable: the viewer release
5
+ # publishes what this builds, and any change under viewer/ runs it.
6
+
7
+ on:
8
+ workflow_call:
9
+ workflow_dispatch:
10
+ pull_request:
11
+ paths: ["viewer/**", ".gitmodules", ".github/workflows/viewer-*.yml"]
12
+ push:
13
+ branches: [main]
14
+ paths: ["viewer/**", ".gitmodules", ".github/workflows/viewer-*.yml"]
15
+
16
+ jobs:
17
+ wheels:
18
+ name: ${{ matrix.os }}
19
+ runs-on: ${{ matrix.os }}
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ # The platforms pypulseqpp ships for. Each build job also installs the
24
+ # wheel beside PySide6 and draws a sequence with it: see
25
+ # viewer/tests/smoke.py and [tool.cibuildwheel] in viewer/pyproject.toml.
26
+ os: [ubuntu-latest, macos-14, windows-latest]
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ with:
30
+ submodules: recursive # SeqEyes is the submodule in viewer/seqeyes
31
+
32
+ - uses: pypa/cibuildwheel@v4.2.0
33
+ with:
34
+ package-dir: viewer
35
+
36
+ - uses: actions/upload-artifact@v7
37
+ with:
38
+ name: viewer-wheels-${{ matrix.os }}
39
+ path: wheelhouse/*.whl
40
+ if-no-files-found: error
41
+
42
+ plot:
43
+ # pypulseqpp's own plot tests, against the wheel just built: the test that
44
+ # opens SeqEyes skips when there is none, so here it is made to run.
45
+ name: plot / ${{ matrix.os }}
46
+ needs: wheels
47
+ runs-on: ${{ matrix.os }}
48
+ strategy:
49
+ fail-fast: false
50
+ matrix:
51
+ os: [ubuntu-latest, macos-14, windows-latest]
52
+ env:
53
+ QT_QPA_PLATFORM: offscreen
54
+ steps:
55
+ - uses: actions/checkout@v7
56
+ with:
57
+ submodules: recursive
58
+ fetch-depth: 0 # setuptools_scm reads the tags
59
+
60
+ - uses: actions/setup-python@v7
61
+ with:
62
+ python-version: "3.12"
63
+
64
+ - uses: actions/download-artifact@v8
65
+ with:
66
+ name: viewer-wheels-${{ matrix.os }}
67
+ path: viewer-wheel
68
+
69
+ # PySide6's Qt draws with the system's display libraries, which every
70
+ # desktop has and the runner image does not all of. These are what
71
+ # drawing offscreen needs; viewer/README.md lists them too.
72
+ - name: System libraries
73
+ if: runner.os == 'Linux'
74
+ run: |
75
+ sudo apt-get update
76
+ sudo apt-get install -y libegl1 libgl1 libx11-6 libdbus-1-3 \
77
+ libfontconfig1 libfreetype6 libglib2.0-0 libxkbcommon0
78
+
79
+ - name: Install
80
+ shell: bash
81
+ run: |
82
+ python -m pip install --upgrade pip
83
+ python -m pip install ".[dev]" -r tests/requirements.txt viewer-wheel/*.whl
84
+
85
+ - name: Test
86
+ run: python -m pytest tests/test_plot.py -ra
87
+
88
+ sdist:
89
+ runs-on: ubuntu-latest
90
+ steps:
91
+ - uses: actions/checkout@v7
92
+ with:
93
+ submodules: recursive
94
+ - uses: actions/setup-python@v7
95
+ with:
96
+ python-version: "3.12"
97
+ - run: |
98
+ python -m pip install --upgrade pip build
99
+ python -m build --sdist viewer --outdir dist
100
+ - uses: actions/upload-artifact@v7
101
+ with:
102
+ name: viewer-sdist
103
+ path: dist/*.tar.gz
104
+ if-no-files-found: error