diff --git a/.gitignore b/.gitignore index 34c0f55..bbdd9e4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,8 @@ env/ .DS_Store Thumbs.db +# Generated demo plots +demos_py/*.png + # MATLAB *.asv diff --git a/README_PYTHON.md b/README_PYTHON.md index d927e56..97f60ce 100644 --- a/README_PYTHON.md +++ b/README_PYTHON.md @@ -104,7 +104,7 @@ The `CITATION.cff` at the repository root provides machine-readable metadata. ## How the Python implementation differs from MATLAB - **Numerical kernel**: same algorithm; SciPy LAPACK calls (QR, SVD, eigendecomposition) replace MATLAB's `mldivide` / `eig` / `svd`. -- **Bug fixes baked in**: the linear-indexing bug at `DCEBE_estimateBAT.m:50` (default search interval used only signal 1's bounds) is fixed in the canonical Python `default_interval`. See [reports/06-dcebe-bug-fixes.md](../reports/06-dcebe-bug-fixes.md). +- **Bug fixes baked in**: the linear-indexing bug at `DCEBE_estimateBAT.m:50` (default search interval used only signal 1's bounds) is fixed in the canonical Python `default_interval`. - **Default optimiser**: `scipy.optimize.minimize(method="L-BFGS-B")`. The MATLAB reference uses `fminunc` quasi-Newton with central finite differences; the SciPy version uses forward FD with a slightly enlarged step (`eps=1e-4`) and a sub-grid jitter on integer-`t` starting points to bridge the kink the spline-matrix structure has there. - **`factorize=True` path**: ported as `dcebe._factorize.gcv_score_factorize` but **not exposed** through `estimate_bat`; the MATLAB source itself notes the QR path is better-conditioned. @@ -117,4 +117,4 @@ The `CITATION.cff` at the repository root provides machine-readable metadata. - exact match on `k_opt` - `rtol(beta_opt) = 0.2` (the GCV objective is flat in beta near the optimum) -See [reports/07-dcebe-port-plan.md](../reports/07-dcebe-port-plan.md) for the full porting plan and `tests_py/test_matlab_parity.py` for the parity assertions. +See `tests_py/test_matlab_parity.py` for the parity assertions. diff --git a/demos_py/demo_common_output.png b/demos_py/demo_common_output.png deleted file mode 100644 index 633d8e2..0000000 Binary files a/demos_py/demo_common_output.png and /dev/null differ diff --git a/demos_py/demo_output.png b/demos_py/demo_output.png deleted file mode 100644 index 613e97b..0000000 Binary files a/demos_py/demo_output.png and /dev/null differ diff --git a/docs/PRE_PYPI_CHECKLIST.md b/docs/PRE_PYPI_CHECKLIST.md deleted file mode 100644 index 66cb257..0000000 --- a/docs/PRE_PYPI_CHECKLIST.md +++ /dev/null @@ -1,107 +0,0 @@ -# Pre-PyPI publication checklist - -Hand-off list for taking `dcebe` from the current branch state to a published `pip install dcebe` package. - -The agent (Claude) **does not** open PRs, push branches, edit `.github/`, or upload to PyPI — these all require the maintainer's hand on the wheel. The items below are framed as concrete maintainer actions. - -## 1. Get the bug-fix and port branches reviewed and merged - -The port branch `claude/dcebe-port-2026-05` is based on `claude/dcebe-prep-2026-05`. The latter is a prerequisite (it carries the search-interval bug fix that the Python's `default_interval` mirrors). - -Recommended order: - -1. `git push -u origin claude/dcebe-prep-2026-05` and open a PR against `master`. Merge. -2. `git push -u origin claude/dcebe-port-2026-05` and open a PR against `master`. Merge. - -Both branches are local-only at the time of this checklist's writing. - -## 2. Wire up CI (one-time) - -Copy the two templates into `.github/workflows/`: - -```bash -mkdir -p .github/workflows -cp docs/ci_templates/ci.yml .github/workflows/ci.yml -cp docs/ci_templates/release.yml .github/workflows/release.yml -git add .github/workflows -git commit -m "Add CI workflows" -``` - -Then push and verify the CI badge turns green on the next PR. - -## 3. Configure PyPI OIDC trusted publishing (one-time) - -The `release.yml` template uses OIDC instead of a PyPI API token. Setup: - -1. Log in to https://pypi.org and visit *Account → Publishing*. -2. Add a new "pending publisher": - - PyPI Project Name: `dcebe` - - Owner: `mstorath` - - Repository name: `DCEBE` - - Workflow name: `release.yml` - - Environment name: `pypi` -3. In GitHub: *Settings → Environments → New environment* → name `pypi` (no protection rules required, but optional reviewer approval recommended). - -Once configured, the `Release` workflow will publish on any `v*.*.*` tag without an API token. - -## 4. Reserve the `dcebe` name on PyPI (optional but recommended) - -To prevent name-squatting, push a placeholder `0.1.0.dev0` build to TestPyPI before the real release: - -```bash -# From a clean checkout, with a `release.yml.dryrun` variant pointing at -# https://test.pypi.org/legacy/, or via twine directly: -python -m build -twine upload --repository-url https://test.pypi.org/legacy/ dist/* -``` - -Then verify with `pip install -i https://test.pypi.org/simple/ dcebe` in a fresh venv. - -## 5. Tag and push v0.1.0 - -Once steps 1–3 are done and CI is green: - -```bash -git tag -a v0.1.0 -m "DCEBE Python v0.1.0 — initial release" -git push origin v0.1.0 -``` - -The `Release` workflow will pick up the tag, build sdist + wheel, and publish to PyPI. - -## 6. Smoke-test the public install - -In a fresh venv: - -```bash -pip install dcebe -python -c "from dcebe import estimate_bat; print(estimate_bat.__doc__[:200])" -python -c "import dcebe; print(dcebe.__version__)" -``` - -If these succeed, the publication path is healthy. - -## 7. Update README.md to mention the Python package - -Currently `README.md` (the MATLAB-facing one) doesn't mention `dcebe` on PyPI. Add a one-line pointer near the top: - -> **Python users**: `pip install dcebe` — see [README_PYTHON.md](README_PYTHON.md). - -## What the agent left in place - -- `pyproject.toml` with hatchling backend, `dcebe` package name, version `0.1.0.dev0`, dependencies pinned to NumPy ≥ 1.23 and SciPy ≥ 1.10, classifiers for cp3.9–cp3.13. -- `python/dcebe/{__init__,_spline,_search,_barriers,_gcv,_factorize,_solve}.py` — full Python port. -- `tests_py/` — 81 unit tests (1 skipped placeholder), including 4 MATLAB parity tests against `matlab_fixtures/`. -- `matlab_fixtures/fixture_{small,explicit_si,common}.mat` — committed reference fixtures. -- `scripts/gen_fixtures.m` — regen tool for the fixtures. -- `demos_py/{demo,demo_common,demo_in_vivo}.py` — runnable demonstrations. -- `README_PYTHON.md` — package-level docs. -- `CITATION.cff` (added in `claude/dcebe-prep-2026-05`). -- `docs/ci_templates/{ci,release}.yml` — copy-and-paste CI. -- `docs/PRE_PYPI_CHECKLIST.md` — this file. - -## What the agent deliberately left out - -- Any file under `.github/` (CLAUDE.md forbids editing CI workflows directly). -- Any push to `origin` (CLAUDE.md requires the maintainer to push). -- Any PyPI upload, TestPyPI upload, or git tag (deliberate — irreversible, requires owner judgement). -- Performance optimisation (pure-Python is functionally correct but per-call overhead is higher than MATLAB; see the comment in `_solve.py` about `eps` and the demo runtime caveat. Future work: vectorise the per-(t, beta) inner loop in `gcv_score_qr`, or factor the matrix once per `t` and reuse across `beta`). diff --git a/docs/ci_templates/ci.yml b/docs/ci_templates/ci.yml deleted file mode 100644 index 63bba2f..0000000 --- a/docs/ci_templates/ci.yml +++ /dev/null @@ -1,36 +0,0 @@ -# Template — copy to `.github/workflows/ci.yml` to enable. -# -# Pure-Python pytest matrix for the dcebe package. The agent does not -# touch .github/ directly per CLAUDE.md; copy this file into place -# manually and commit on a separate branch (no agent push). - -name: CI - -on: - push: - branches: [master, main] - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: pip - - - name: Install package and test deps - run: | - python -m pip install --upgrade pip - pip install -e .[test] - - - name: Run pytest (skip MATLAB parity if fixtures absent) - run: pytest tests_py/ -v diff --git a/docs/ci_templates/release.yml b/docs/ci_templates/release.yml deleted file mode 100644 index 1957966..0000000 --- a/docs/ci_templates/release.yml +++ /dev/null @@ -1,58 +0,0 @@ -# Template — copy to `.github/workflows/release.yml` to enable. -# -# Tag-gated PyPI publication via OIDC trusted publishing. No PyPI API -# token required after one-time configuration on PyPI side. Triggers -# only on annotated tags matching `v*.*.*`. - -name: Release - -on: - push: - tags: ["v*.*.*"] - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install build backend - run: | - python -m pip install --upgrade pip - pip install build - - - name: Build sdist and wheel - run: python -m build - - - uses: actions/upload-artifact@v4 - with: - name: dist - path: dist/ - - publish: - needs: build - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/dcebe - permissions: - id-token: write # required for OIDC trusted publishing - steps: - - uses: actions/download-artifact@v4 - with: - name: dist - path: dist/ - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - # No `password:` argument — OIDC trusted publisher (configure - # at https://pypi.org/manage/account/publishing/ before the - # first release).