cs-dmri 0.1.0__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 (137) hide show
  1. cs_dmri-0.1.0/.github/workflows/ci.yml +116 -0
  2. cs_dmri-0.1.0/.github/workflows/release.yml +313 -0
  3. cs_dmri-0.1.0/.gitignore +36 -0
  4. cs_dmri-0.1.0/.readthedocs.yaml +15 -0
  5. cs_dmri-0.1.0/Cargo.lock +1897 -0
  6. cs_dmri-0.1.0/Cargo.toml +90 -0
  7. cs_dmri-0.1.0/LICENSE-APACHE +209 -0
  8. cs_dmri-0.1.0/LICENSE-DIPY +48 -0
  9. cs_dmri-0.1.0/LICENSE-MIT +29 -0
  10. cs_dmri-0.1.0/LICENSE-MRTRIX +408 -0
  11. cs_dmri-0.1.0/PKG-INFO +65 -0
  12. cs_dmri-0.1.0/README.md +75 -0
  13. cs_dmri-0.1.0/build.rs +62 -0
  14. cs_dmri-0.1.0/cs_dmri/__init__.py +58 -0
  15. cs_dmri-0.1.0/cs_dmri/_frame.py +34 -0
  16. cs_dmri-0.1.0/cs_dmri/_inputs.py +48 -0
  17. cs_dmri-0.1.0/cs_dmri/_spatial.py +94 -0
  18. cs_dmri-0.1.0/cs_dmri/dti.py +97 -0
  19. cs_dmri-0.1.0/cs_dmri/dwi.py +184 -0
  20. cs_dmri-0.1.0/cs_dmri/gradients.py +107 -0
  21. cs_dmri-0.1.0/cs_dmri/multitissue.py +248 -0
  22. cs_dmri-0.1.0/cs_dmri/qc.py +253 -0
  23. cs_dmri-0.1.0/cs_dmri/shore.py +258 -0
  24. cs_dmri-0.1.0/docs/_static/.gitkeep +0 -0
  25. cs_dmri-0.1.0/docs/api/python.md +77 -0
  26. cs_dmri-0.1.0/docs/api/rust.md +20 -0
  27. cs_dmri-0.1.0/docs/changes.md +16 -0
  28. cs_dmri-0.1.0/docs/cli/cs-dti.md +102 -0
  29. cs_dmri-0.1.0/docs/cli/cs-fit.md +170 -0
  30. cs_dmri-0.1.0/docs/cli/cs-mtnorm.md +47 -0
  31. cs_dmri-0.1.0/docs/cli/cs-odf.md +115 -0
  32. cs_dmri-0.1.0/docs/cli/cs-qc.md +26 -0
  33. cs_dmri-0.1.0/docs/cli/cs-response.md +141 -0
  34. cs_dmri-0.1.0/docs/cli/cs-ss3t-full.md +175 -0
  35. cs_dmri-0.1.0/docs/cli/cs-ss3t.md +131 -0
  36. cs_dmri-0.1.0/docs/cli/cs-synth.md +63 -0
  37. cs_dmri-0.1.0/docs/cli/index.md +29 -0
  38. cs_dmri-0.1.0/docs/conf.py +55 -0
  39. cs_dmri-0.1.0/docs/development.md +52 -0
  40. cs_dmri-0.1.0/docs/index.md +59 -0
  41. cs_dmri-0.1.0/docs/installation.md +58 -0
  42. cs_dmri-0.1.0/docs/license.md +18 -0
  43. cs_dmri-0.1.0/docs/quickstart.md +56 -0
  44. cs_dmri-0.1.0/docs/references.md +58 -0
  45. cs_dmri-0.1.0/docs/requirements.txt +5 -0
  46. cs_dmri-0.1.0/docs/tools/make_cli_pages.py +52 -0
  47. cs_dmri-0.1.0/docs/user/conventions.md +58 -0
  48. cs_dmri-0.1.0/docs/user/dti.md +40 -0
  49. cs_dmri-0.1.0/docs/user/export.md +81 -0
  50. cs_dmri-0.1.0/docs/user/multitissue.md +73 -0
  51. cs_dmri-0.1.0/docs/user/qc.md +137 -0
  52. cs_dmri-0.1.0/docs/user/shore.md +103 -0
  53. cs_dmri-0.1.0/pyproject.toml +49 -0
  54. cs_dmri-0.1.0/python/.cargo/config.toml +14 -0
  55. cs_dmri-0.1.0/python/Cargo.toml +27 -0
  56. cs_dmri-0.1.0/python/README.md +31 -0
  57. cs_dmri-0.1.0/python/src/lib.rs +399 -0
  58. cs_dmri-0.1.0/python/src/models.rs +708 -0
  59. cs_dmri-0.1.0/python/tests/_dipy_reference.py +45 -0
  60. cs_dmri-0.1.0/python/tests/conftest.py +27 -0
  61. cs_dmri-0.1.0/python/tests/test_cli_parity.py +83 -0
  62. cs_dmri-0.1.0/python/tests/test_dwi_dti.py +77 -0
  63. cs_dmri-0.1.0/python/tests/test_models.py +254 -0
  64. cs_dmri-0.1.0/python/tests/test_qc.py +101 -0
  65. cs_dmri-0.1.0/scripts/bench_fit_options.py +794 -0
  66. cs_dmri-0.1.0/scripts/bench_plots.R +191 -0
  67. cs_dmri-0.1.0/scripts/generate_lasso_fixtures.py +190 -0
  68. cs_dmri-0.1.0/scripts/gold_compare.Rmd +490 -0
  69. cs_dmri-0.1.0/scripts/gold_standard.sh +301 -0
  70. cs_dmri-0.1.0/scripts/microstructure_math.md +329 -0
  71. cs_dmri-0.1.0/scripts/validate_microstructure.py +532 -0
  72. cs_dmri-0.1.0/src/basis/mod.rs +47 -0
  73. cs_dmri-0.1.0/src/basis/shore.rs +274 -0
  74. cs_dmri-0.1.0/src/bin/cs-dti.rs +259 -0
  75. cs_dmri-0.1.0/src/bin/cs-fit.rs +535 -0
  76. cs_dmri-0.1.0/src/bin/cs-mtnorm.rs +226 -0
  77. cs_dmri-0.1.0/src/bin/cs-odf.rs +343 -0
  78. cs_dmri-0.1.0/src/bin/cs-qc.rs +178 -0
  79. cs_dmri-0.1.0/src/bin/cs-response.rs +321 -0
  80. cs_dmri-0.1.0/src/bin/cs-ss3t-full.rs +491 -0
  81. cs_dmri-0.1.0/src/bin/cs-ss3t.rs +346 -0
  82. cs_dmri-0.1.0/src/bin/cs-synth.rs +221 -0
  83. cs_dmri-0.1.0/src/dti.rs +579 -0
  84. cs_dmri-0.1.0/src/fit.rs +774 -0
  85. cs_dmri-0.1.0/src/graddev.rs +271 -0
  86. cs_dmri-0.1.0/src/io/coeffs.rs +186 -0
  87. cs_dmri-0.1.0/src/io/dwi.rs +377 -0
  88. cs_dmri-0.1.0/src/io/microstructure.rs +676 -0
  89. cs_dmri-0.1.0/src/io/mod.rs +282 -0
  90. cs_dmri-0.1.0/src/io/odx_out.rs +1743 -0
  91. cs_dmri-0.1.0/src/io/output.rs +538 -0
  92. cs_dmri-0.1.0/src/io/provenance.rs +182 -0
  93. cs_dmri-0.1.0/src/lib.rs +156 -0
  94. cs_dmri-0.1.0/src/math.rs +247 -0
  95. cs_dmri-0.1.0/src/multitissue/dhollander.rs +1174 -0
  96. cs_dmri-0.1.0/src/multitissue/forward.rs +247 -0
  97. cs_dmri-0.1.0/src/multitissue/mod.rs +33 -0
  98. cs_dmri-0.1.0/src/multitissue/mtnormalise.rs +839 -0
  99. cs_dmri-0.1.0/src/multitissue/pipeline.rs +141 -0
  100. cs_dmri-0.1.0/src/multitissue/response.rs +252 -0
  101. cs_dmri-0.1.0/src/multitissue/response_estimation.rs +644 -0
  102. cs_dmri-0.1.0/src/multitissue/sidecar.rs +71 -0
  103. cs_dmri-0.1.0/src/multitissue/ss3t.rs +768 -0
  104. cs_dmri-0.1.0/src/multitissue/volume.rs +179 -0
  105. cs_dmri-0.1.0/src/odf.rs +269 -0
  106. cs_dmri-0.1.0/src/progress.rs +181 -0
  107. cs_dmri-0.1.0/src/qc.rs +1106 -0
  108. cs_dmri-0.1.0/src/qspace.rs +369 -0
  109. cs_dmri-0.1.0/src/scalars/microstructure.rs +362 -0
  110. cs_dmri-0.1.0/src/scalars/mod.rs +8 -0
  111. cs_dmri-0.1.0/src/sh.rs +84 -0
  112. cs_dmri-0.1.0/src/solver/alpha.rs +449 -0
  113. cs_dmri-0.1.0/src/solver/csd.rs +274 -0
  114. cs_dmri-0.1.0/src/solver/fista.rs +231 -0
  115. cs_dmri-0.1.0/src/solver/icls.rs +738 -0
  116. cs_dmri-0.1.0/src/solver/mod.rs +51 -0
  117. cs_dmri-0.1.0/src/solver/shore_icls.rs +200 -0
  118. cs_dmri-0.1.0/src/solver/tikhonov.rs +106 -0
  119. cs_dmri-0.1.0/src/synth.rs +84 -0
  120. cs_dmri-0.1.0/src/voxel_loop.rs +194 -0
  121. cs_dmri-0.1.0/tests/alpha_selection.rs +347 -0
  122. cs_dmri-0.1.0/tests/common/mod.rs +254 -0
  123. cs_dmri-0.1.0/tests/data/lasso_fixtures/ill_conditioned.bin +0 -0
  124. cs_dmri-0.1.0/tests/data/lasso_fixtures/manifest.json +10 -0
  125. cs_dmri-0.1.0/tests/data/lasso_fixtures/medium_dense.bin +0 -0
  126. cs_dmri-0.1.0/tests/data/lasso_fixtures/near_alpha_max.bin +0 -0
  127. cs_dmri-0.1.0/tests/data/lasso_fixtures/nonneg_medium.bin +0 -0
  128. cs_dmri-0.1.0/tests/data/lasso_fixtures/nonneg_small.bin +0 -0
  129. cs_dmri-0.1.0/tests/data/lasso_fixtures/small_dense.bin +0 -0
  130. cs_dmri-0.1.0/tests/data/lasso_fixtures/underdetermined.bin +0 -0
  131. cs_dmri-0.1.0/tests/data/lasso_fixtures/very_low_alpha.bin +0 -0
  132. cs_dmri-0.1.0/tests/optimization_kkt.rs +235 -0
  133. cs_dmri-0.1.0/tests/optimization_limits.rs +157 -0
  134. cs_dmri-0.1.0/tests/optimization_recovery.rs +165 -0
  135. cs_dmri-0.1.0/tests/optimization_sklearn.rs +205 -0
  136. cs_dmri-0.1.0/tests/roundtrip.rs +152 -0
  137. cs_dmri-0.1.0/tests/scalars.rs +235 -0
@@ -0,0 +1,116 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ # Cancel in-flight runs when a new commit is pushed to the same PR/branch.
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ env:
15
+ CARGO_TERM_COLOR: always
16
+ RUST_BACKTRACE: 1
17
+
18
+ jobs:
19
+ rust-tests:
20
+ name: Rust tests (${{ matrix.os }})
21
+ runs-on: ${{ matrix.os }}
22
+ strategy:
23
+ fail-fast: false
24
+ matrix:
25
+ os: [ubuntu-latest, macos-latest, windows-latest]
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: dtolnay/rust-toolchain@stable
29
+ - uses: Swatinem/rust-cache@v2
30
+
31
+ # Build only the cs_dmri crate here. cs_dmri_py is a Python cdylib
32
+ # whose link step needs `-undefined dynamic_lookup` on macOS
33
+ # (configured in python/.cargo/config.toml, only visible when cargo
34
+ # runs from python/). The cdylib itself is built in the python-tests
35
+ # job via maturin; `cargo check` here still catches Rust errors.
36
+ - name: cargo build (cs_dmri)
37
+ run: cargo build -p cs_dmri --all-targets
38
+
39
+ - name: cargo check (cs_dmri_py)
40
+ run: cargo check -p cs_dmri_py
41
+
42
+ - name: cargo test (cs_dmri)
43
+ run: cargo test -p cs_dmri
44
+
45
+ python-tests:
46
+ name: Python tests (${{ matrix.os }} / py${{ matrix.python-version }})
47
+ runs-on: ${{ matrix.os }}
48
+ strategy:
49
+ fail-fast: false
50
+ matrix:
51
+ os: [ubuntu-latest, macos-latest]
52
+ python-version: ['3.10', '3.12']
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - uses: dtolnay/rust-toolchain@stable
56
+ - uses: Swatinem/rust-cache@v2
57
+ - uses: actions/setup-python@v5
58
+ with:
59
+ python-version: ${{ matrix.python-version }}
60
+ cache: pip
61
+
62
+ # `maturin develop` requires an active virtualenv (it refuses to
63
+ # install into the system Python). Create one inside python/ and
64
+ # activate it for the remainder of the job by exporting VIRTUAL_ENV
65
+ # and prepending its bin/ directory to PATH.
66
+ - name: Create + activate virtualenv
67
+ working-directory: python
68
+ run: |
69
+ python -m venv .venv
70
+ echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
71
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
72
+
73
+ - name: Install build + test deps
74
+ working-directory: python
75
+ run: |
76
+ python -m pip install --upgrade pip
77
+ python -m pip install maturin pytest numpy nibabel dipy
78
+
79
+ - name: Build + install (maturin develop)
80
+ working-directory: python
81
+ run: maturin develop --release
82
+
83
+ # tests/test_cli_parity.py skips here: it needs the qsiprep test data.
84
+ - name: Run Python tests
85
+ working-directory: python
86
+ run: pytest tests/ -v
87
+
88
+ docs:
89
+ name: Documentation
90
+ runs-on: ubuntu-latest
91
+ steps:
92
+ - uses: actions/checkout@v4
93
+ - uses: actions/setup-python@v5
94
+ with:
95
+ python-version: '3.12'
96
+ cache: pip
97
+ # Read the Docs builds without the compiled extension (autodoc mocks
98
+ # it), so this job does the same.
99
+ - name: Install documentation dependencies
100
+ run: python -m pip install -r docs/requirements.txt
101
+ - name: Build documentation
102
+ run: sphinx-build -W --keep-going -b html docs docs/_build/html
103
+
104
+ cli-reference:
105
+ name: Command-line reference is current
106
+ runs-on: ubuntu-latest
107
+ steps:
108
+ - uses: actions/checkout@v4
109
+ - uses: dtolnay/rust-toolchain@stable
110
+ - uses: Swatinem/rust-cache@v2
111
+ - name: Build the tools
112
+ run: cargo build --release --bins
113
+ - name: Regenerate docs/cli from --help
114
+ run: python docs/tools/make_cli_pages.py
115
+ - name: Check for differences
116
+ run: git diff --exit-code docs/cli
@@ -0,0 +1,313 @@
1
+ name: Release wheels + PyPI
2
+
3
+ # Trigger on bare semver tags (e.g. 0.1.0, 0.2.0rc1) or manual dispatch.
4
+ on:
5
+ push:
6
+ tags:
7
+ - '[0-9]+.[0-9]+.[0-9]+*'
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ env:
14
+ CARGO_TERM_COLOR: always
15
+
16
+ jobs:
17
+ # ─── Tag / version sanity check ────────────────────────────────────────────
18
+ #
19
+ # Refuses to build if the pushed tag (`X.Y.Z`) doesn't match the version
20
+ # in [workspace.package] of Cargo.toml. Catches "I tagged but forgot to
21
+ # bump" before we waste 4× wheel build minutes on a wrong-version release.
22
+ # All build jobs `needs: verify-version`, so a mismatch fails the whole
23
+ # workflow at the first step.
24
+ verify-version:
25
+ name: Verify tag matches Cargo.toml
26
+ runs-on: ubuntu-latest
27
+ outputs:
28
+ version: ${{ steps.check.outputs.version }}
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - id: check
32
+ run: |
33
+ set -euo pipefail
34
+ # Extract the version from the [workspace.package] section.
35
+ PKG_VERSION=$(awk '
36
+ /^\[workspace\.package\]/ { in_section = 1; next }
37
+ /^\[/ { in_section = 0 }
38
+ in_section && /^version[[:space:]]*=/ {
39
+ gsub(/^version[[:space:]]*=[[:space:]]*"|"[[:space:]]*$/, "")
40
+ print
41
+ exit
42
+ }
43
+ ' Cargo.toml)
44
+ echo "Cargo.toml: $PKG_VERSION"
45
+ echo "version=$PKG_VERSION" >> "$GITHUB_OUTPUT"
46
+ # Only enforce tag<->version match when triggered by a tag push.
47
+ # workflow_dispatch and re-runs without a tag fall through.
48
+ if [[ "$GITHUB_REF" != refs/tags/* ]]; then
49
+ echo "Not a tag push; skipping tag<->version match."
50
+ exit 0
51
+ fi
52
+ TAG_VERSION="${GITHUB_REF#refs/tags/}"
53
+ echo "Tag: $TAG_VERSION"
54
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
55
+ echo "::error::Tag $TAG_VERSION does not match [workspace.package].version = \"$PKG_VERSION\". Bump Cargo.toml or retag."
56
+ exit 1
57
+ fi
58
+
59
+ # ─── Linux wheels ───────────────────────────────────────────────────────────
60
+ linux:
61
+ name: Linux ${{ matrix.target }} (${{ matrix.libc }})
62
+ runs-on: ubuntu-latest
63
+ needs: [verify-version]
64
+ strategy:
65
+ fail-fast: false
66
+ matrix:
67
+ include:
68
+ - target: x86_64
69
+ libc: manylinux
70
+ - target: aarch64
71
+ libc: manylinux
72
+ - target: x86_64
73
+ libc: musllinux
74
+ - target: aarch64
75
+ libc: musllinux
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+ - uses: PyO3/maturin-action@v1
79
+ # cs_dmri itself has no C dependencies, but odx-rs builds HDF5 from
80
+ # source (hdf5-metno, static), which needs CMake >= 3.26. Set the
81
+ # CMAKE env var so the cmake-rs crate uses our pip-installed cmake;
82
+ # before-script-linux populates /opt/_pipcmake/bin/cmake. This block
83
+ # is odx-rs's, kept in step with it.
84
+ env:
85
+ CMAKE: /opt/_pipcmake/bin/cmake
86
+ with:
87
+ target: ${{ matrix.target }}
88
+ # abi3-py39 wheels work for all CPython >= 3.9.
89
+ args: --release --out dist
90
+ working-directory: python
91
+ # `2_28` (AlmaLinux 9 / glibc 2.28) instead of the older `auto`
92
+ # (= `2014` = manylinux_2_17, CentOS 7 / glibc 2.17). The 2014
93
+ # cross image ships Python 3.6, which maturin rejects (needs
94
+ # >=3.7) and which is below our abi3-py39 baseline. 2_28 has
95
+ # Python 3.9+ baked in. The compat trade-off is glibc 2.28+
96
+ # required at install time — i.e. RHEL/AlmaLinux 8+, Debian 10+,
97
+ # Ubuntu 18.10+ (essentially anything from the last ~6 years).
98
+ manylinux: ${{ matrix.libc == 'manylinux' && '2_28' || 'musllinux_1_2' }}
99
+ before-script-linux: |
100
+ # hdf5-metno-src builds HDF5 from source via cmake; it requires
101
+ # CMake >= 3.26. Both manylinux_2_17 (AlmaLinux yum cmake 3.20)
102
+ # and musllinux_1_2 (Alpine apk cmake 3.22) ship too-old
103
+ # versions, and cross-compile images from messense (used for
104
+ # aarch64) don't have /opt/python/ at all. Install a recent
105
+ # cmake via pip from whatever Python we can find, and pin its
106
+ # path via the CMAKE env var.
107
+ #
108
+ # `set -ex` prints every command so failures are diagnosable
109
+ # from the workflow log without re-running.
110
+ set -ex
111
+
112
+ uname -a
113
+ cat /etc/os-release || true
114
+ ls -d /opt/python 2>&1 || echo "(no /opt/python — likely a cross-compile image)"
115
+
116
+ # Install minimal build tools + a system Python if needed.
117
+ # The cross-compile images have package managers but often no
118
+ # Python preinstalled, so add it here.
119
+ if command -v apk >/dev/null 2>&1; then
120
+ apk update
121
+ apk add --no-cache make gcc g++ musl-dev linux-headers perl bash python3 py3-pip
122
+ elif command -v dnf >/dev/null 2>&1; then
123
+ dnf install -y make gcc gcc-c++ perl python3 python3-pip
124
+ elif command -v yum >/dev/null 2>&1; then
125
+ yum install -y make gcc gcc-c++ perl python3 python3-pip
126
+ elif command -v apt-get >/dev/null 2>&1; then
127
+ apt-get update
128
+ apt-get install -y make gcc g++ perl python3 python3-pip
129
+ fi
130
+
131
+ # Resolve a pip we can use. Prefer the PyPA-style /opt/python
132
+ # bundled CPython on native images; on *cross-compile* images
133
+ # (messense/*-cross), /opt/python contains TARGET-arch binaries
134
+ # that can't execute on the build host (kernel returns ENOEXEC,
135
+ # bash falls back to interpreting the pip script and chokes on
136
+ # Python syntax). Probe each candidate by running --version
137
+ # before committing to it.
138
+ PIP_CMD=""
139
+ PYBIN=""
140
+ for cand in /opt/python/cp310-cp310/bin /opt/python/cp311-cp311/bin \
141
+ /opt/python/cp312-cp312/bin /opt/python/cp39-cp39/bin \
142
+ /opt/python/cp313-cp313/bin; do
143
+ if [ -d "$cand" ] && "$cand/python" --version >/dev/null 2>&1; then
144
+ PYBIN="$cand"
145
+ PIP_CMD="$cand/pip"
146
+ break
147
+ fi
148
+ done
149
+ if [ -z "$PIP_CMD" ]; then
150
+ if command -v pip3 >/dev/null 2>&1; then
151
+ PIP_CMD="pip3"
152
+ elif command -v python3 >/dev/null 2>&1; then
153
+ python3 -m ensurepip --default-pip 2>/dev/null || true
154
+ PIP_CMD="python3 -m pip"
155
+ else
156
+ echo "::error::No Python/pip found in build container"
157
+ exit 1
158
+ fi
159
+ fi
160
+ echo "Using PIP_CMD='$PIP_CMD' (PYBIN='${PYBIN:-system}')"
161
+
162
+ # If we found a /opt/python/cp* Python, make sure it shadows
163
+ # any older system python3 on PATH. maturin invokes `python3`
164
+ # from PATH after this script runs, and we've seen older
165
+ # cross-compile images leave a too-old `/usr/bin/python3` in
166
+ # place (e.g., 3.6 on the legacy manylinux2014 cross image).
167
+ if [ -n "$PYBIN" ]; then
168
+ ln -sf "$PYBIN/python" /usr/local/bin/python3
169
+ ln -sf "$PYBIN/python" /usr/local/bin/python
170
+ fi
171
+
172
+ # Install cmake>=3.26 from PyPI. `--break-system-packages` is
173
+ # for PEP 668-marked Pythons (some distros); harmless on others.
174
+ $PIP_CMD install --no-cache-dir --upgrade --break-system-packages "cmake>=3.26" 2>/dev/null \
175
+ || $PIP_CMD install --no-cache-dir --upgrade "cmake>=3.26"
176
+
177
+ # Find where the pip-installed cmake landed. With /opt/python
178
+ # it's in $PYBIN/cmake; with system pip it's wherever pip puts
179
+ # console scripts — discoverable via `command -v cmake` after
180
+ # install (PATH is updated by the install).
181
+ INSTALLED_CMAKE=""
182
+ if [ -n "$PYBIN" ] && [ -x "$PYBIN/cmake" ]; then
183
+ INSTALLED_CMAKE="$PYBIN/cmake"
184
+ elif command -v cmake >/dev/null 2>&1; then
185
+ INSTALLED_CMAKE=$(command -v cmake)
186
+ else
187
+ # Last-ditch: ask pip where it put the script.
188
+ INSTALLED_CMAKE=$($PIP_CMD show -f cmake 2>/dev/null | awk '/Location:/ {loc=$2} /bin\/cmake/ {print loc"/"$1}' | head -1)
189
+ # That gives a path relative to site-packages; resolve to abs.
190
+ if [ -n "$INSTALLED_CMAKE" ]; then
191
+ INSTALLED_CMAKE=$(realpath "$INSTALLED_CMAKE" 2>/dev/null || echo "$INSTALLED_CMAKE")
192
+ fi
193
+ fi
194
+ if [ -z "$INSTALLED_CMAKE" ] || [ ! -x "$INSTALLED_CMAKE" ]; then
195
+ echo "::error::Could not locate pip-installed cmake binary"
196
+ $PIP_CMD show -f cmake || true
197
+ exit 1
198
+ fi
199
+ echo "Pip-installed cmake at: $INSTALLED_CMAKE"
200
+
201
+ # Pin the path to match the CMAKE env var set on the step.
202
+ mkdir -p /opt/_pipcmake/bin
203
+ ln -sf "$INSTALLED_CMAKE" /opt/_pipcmake/bin/cmake
204
+
205
+ # Shadow common system locations as a PATH fallback.
206
+ for target in /usr/local/bin/cmake /usr/bin/cmake; do
207
+ parent=$(dirname "$target")
208
+ [ -d "$parent" ] && ln -sf "$INSTALLED_CMAKE" "$target" || true
209
+ done
210
+
211
+ # Diagnostic: confirm the right cmake is found via every route.
212
+ echo "--- cmake versions:"
213
+ "$INSTALLED_CMAKE" --version
214
+ /opt/_pipcmake/bin/cmake --version
215
+ cmake --version
216
+ - uses: actions/upload-artifact@v4
217
+ with:
218
+ name: wheels-linux-${{ matrix.target }}-${{ matrix.libc }}
219
+ path: python/dist
220
+
221
+ # ─── macOS wheels (x86_64 + Apple Silicon) ─────────────────────────────────
222
+ #
223
+ # Runner mapping:
224
+ # - aarch64 → macos-14 (Apple Silicon, free)
225
+ # - x86_64 → macos-15-intel (Intel x86_64 on macOS 15; GitHub's
226
+ # recommended migration path for x86_64
227
+ # builds, supported until August 2027).
228
+ #
229
+ # The older `macos-13` runner pool is queue-pressured; `macos-15-intel`
230
+ # is the current Intel target with much better availability. There's no
231
+ # `macos-15-intel-large` etc. — Intel hardware is being retired, so
232
+ # macos-15-intel is the long tail.
233
+ macos:
234
+ name: macOS ${{ matrix.target }}
235
+ runs-on: ${{ matrix.target == 'aarch64' && 'macos-14' || 'macos-15-intel' }}
236
+ needs: [verify-version]
237
+ strategy:
238
+ fail-fast: false
239
+ matrix:
240
+ target: [x86_64, aarch64]
241
+ steps:
242
+ - uses: actions/checkout@v4
243
+ - uses: PyO3/maturin-action@v1
244
+ with:
245
+ target: ${{ matrix.target }}
246
+ args: --release --out dist
247
+ working-directory: python
248
+ - uses: actions/upload-artifact@v4
249
+ with:
250
+ name: wheels-macos-${{ matrix.target }}
251
+ path: python/dist
252
+
253
+ # ─── Windows wheels ────────────────────────────────────────────────────────
254
+ windows:
255
+ name: Windows ${{ matrix.target }}
256
+ runs-on: windows-latest
257
+ needs: [verify-version]
258
+ strategy:
259
+ fail-fast: false
260
+ matrix:
261
+ target: [x64]
262
+ steps:
263
+ - uses: actions/checkout@v4
264
+ - uses: PyO3/maturin-action@v1
265
+ with:
266
+ target: ${{ matrix.target }}
267
+ args: --release --out dist
268
+ working-directory: python
269
+ - uses: actions/upload-artifact@v4
270
+ with:
271
+ name: wheels-windows-${{ matrix.target }}
272
+ path: python/dist
273
+
274
+ # ─── Source distribution ───────────────────────────────────────────────────
275
+ sdist:
276
+ name: sdist
277
+ runs-on: ubuntu-latest
278
+ needs: [verify-version]
279
+ steps:
280
+ - uses: actions/checkout@v4
281
+ - uses: PyO3/maturin-action@v1
282
+ with:
283
+ command: sdist
284
+ args: --out dist
285
+ working-directory: python
286
+ - uses: actions/upload-artifact@v4
287
+ with:
288
+ name: sdist
289
+ path: python/dist
290
+
291
+ publish:
292
+ name: Publish to PyPI
293
+ needs: [linux, macos, windows, sdist]
294
+ runs-on: ubuntu-latest
295
+ if: startsWith(github.ref, 'refs/tags/')
296
+ environment:
297
+ name: pypi
298
+ url: https://pypi.org/p/cs_dmri
299
+ permissions:
300
+ id-token: write
301
+ steps:
302
+ - uses: actions/download-artifact@v4
303
+ with:
304
+ path: dist
305
+ merge-multiple: true
306
+ - name: Inspect artifacts
307
+ run: ls -la dist/
308
+ - uses: pypa/gh-action-pypi-publish@release/v1
309
+ with:
310
+ packages-dir: dist
311
+ # Switch to test.pypi.org for dry-runs by uncommenting:
312
+ # repository-url: https://test.pypi.org/legacy/
313
+ skip-existing: true
@@ -0,0 +1,36 @@
1
+ # Generated by Cargo
2
+ # will have compiled files and executables
3
+ debug
4
+ target
5
+
6
+ # These are backup files generated by rustfmt
7
+ **/*.rs.bk
8
+
9
+ # MSVC Windows builds of rustc generate these, which store debugging information
10
+ *.pdb
11
+
12
+ # Generated by cargo mutants
13
+ # Contains mutation testing data
14
+ **/mutants.out*/
15
+
16
+ # rustc will dump stack traces when hitting an internal compiler error to PWD
17
+ rustc-ice-*.txt
18
+ .DS_Store
19
+ # RustRover
20
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
21
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
22
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
23
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
24
+ #.idea/
25
+
26
+ # Reference papers kept alongside the project. Never commit these — they are
27
+ # third-party copyrighted PDFs, not source.
28
+ literature/
29
+
30
+ # Python bindings
31
+ python/cs_dmri/_cs_dmri*.so
32
+ python/cs_dmri/_cs_dmri*.pyd
33
+ python/.venv/
34
+ __pycache__/
35
+ *.egg-info/
36
+ docs/_build/
@@ -0,0 +1,15 @@
1
+ # Read the Docs configuration: https://docs.readthedocs.io/en/stable/config-file/v2.html
2
+ version: 2
3
+
4
+ build:
5
+ os: ubuntu-24.04
6
+ tools:
7
+ python: "3.12"
8
+
9
+ sphinx:
10
+ configuration: docs/conf.py
11
+ fail_on_warning: true
12
+
13
+ python:
14
+ install:
15
+ - requirements: docs/requirements.txt