Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/requirements.txt text eol=lf
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ jobs:
- run: pip install -e ".[docs,dev]"
- name: Install PyKale (the tutorials import kale at execution time)
# Only this job executes tutorials, so only it needs kale. Matches the notebook install cell
# (_shared/gallery/notebook_first_cell.py), which installs the same source on Colab.
# (source/_shared/gallery/notebook_first_cell.py), which installs the same source on Colab.
run: pip install "git+https://github.com/pykale/pykale.git"
- name: Install runnable tutorials' extra dependencies
run: |
Expand Down
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ htmlcov/
Thumbs.db

# Generated tutorial thumbnails (deterministic, rebuilt by scripts/generate_thumbnails.py)
_static/thumbnails/
source/_static/thumbnails/

# Generated homepage partials
_generated/
_static/tutorials.json
# copied at setup from _shared/assets/logos
_static/pykale_logo.png
source/_static/tutorials.json
# copied at setup from source/_shared/assets/logos
source/_static/pykale_logo.png

# Internal working note, kept local (not published)
docs/PYTORCH_ALIGNMENT.md
17 changes: 8 additions & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Hook versions are pinned to match pykale/.pre-commit-config.yaml so a contributor moving between
# the two repos gets identical formatting decisions.
# Hook versions are pinned for reproducible formatting and a working local setup.
repos:
- repo: https://github.com/datarootsio/databooks
rev: 1.3.7
Expand All @@ -19,7 +18,7 @@ repos:
- id: mixed-line-ending
- id: trailing-whitespace
- id: debug-statements
exclude: ^tutorials/
exclude: ^(others|protein|cancer|cardiac|materials)/
- id: check-yaml
- id: requirements-txt-fixer

Expand All @@ -30,27 +29,27 @@ repos:
additional_dependencies: [flake8-print, "importlib-metadata<5.0.0"]
args: [--config=setup.cfg]
# Migrated example code is kept byte-identical to upstream pykale/examples, which is itself
# excluded from flake8 there. Only tutorial.py, authored here, is linted.
exclude: ^tutorials/(?!.*tutorial\.py$).*
# excluded from flake8 there. Only plot_<slug>.py, authored here, is linted.
exclude: ^(others|protein|cancer|cardiac|materials)/(?!.*plot_[^/]+\.py$).*

- repo: https://github.com/psf/black
rev: 23.11.0
hooks:
- id: black
language_version: python3
additional_dependencies: ['click==8.0.4']
exclude: ^tutorials/(?!.*tutorial\.py$).*
exclude: ^(others|protein|cancer|cardiac|materials)/(?!.*plot_[^/]+\.py$).*

- repo: https://github.com/pycqa/isort
rev: 5.11.2
rev: 5.13.2
hooks:
- id: isort
args: [--settings-path, ./pyproject.toml]
exclude: ^tutorials/(?!.*tutorial\.py$).*
exclude: ^(others|protein|cancer|cardiac|materials)/(?!.*plot_[^/]+\.py$).*

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.0
hooks:
- id: mypy
files: ^(scripts|_shared)/
files: ^(scripts|source/_shared)/
additional_dependencies: [pydantic>=2.9, types-PyYAML]
18 changes: 9 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ pre-commit install

1. **Pick the folder.** The path fixes the domain and the task, so this decision is the important one. Domain buckets are `others`, `protein`, `cancer`, `cardiac`, `materials`. Use `others` when the tutorial demonstrates a method rather than an application. If the tutorial is one of a related set, nest it in a group folder as `others/image_classification/` does.

2. **Copy the templates.**
2. **Copy the templates.** For example, to add `others/my_tutorial`:

```bash
mkdir -p <domain>/<slug>
cp _shared/templates/metadata.template.yml <domain>/<slug>/metadata.yml
cp _shared/templates/README.template.md <domain>/<slug>/README.md
cp _shared/templates/tutorial.py.template <domain>/<slug>/tutorial.py
mkdir -p others/my_tutorial
cp source/_shared/templates/metadata.template.yml others/my_tutorial/metadata.yml
cp source/_shared/templates/README.template.md others/my_tutorial/README.md
cp source/_shared/templates/plot.py.template others/my_tutorial/plot_my_tutorial.py
```

3. **Register it** in `tutorial_catalog.yml` under the right bucket, and in the group's `tutorials:` list if it belongs to one. Order within a list is the order it appears in the gallery, so put the gentlest introduction first.

4. **Fill in `metadata.yml`.** Every field is validated. Derive `kale_api` from the imports the code actually makes, not from prose — upstream READMEs have drifted from their code more than once.

5. **Write `tutorial.py`.** See the conventions below.
5. **Write `plot_<slug>.py`** (replace `<slug>` with the folder name). See the conventions below.

6. **Check it.**

Expand All @@ -33,7 +33,7 @@ pre-commit install
make html-noplot
```

## Conventions for `tutorial.py`
## Conventions for `plot_<slug>.py`

Sphinx-gallery turns this file into an HTML page, a notebook and a downloadable script. It is the only file in the folder that the doc build executes.

Expand All @@ -42,7 +42,7 @@ Sphinx-gallery turns this file into an HTML page, a notebook and a downloadable
- **Make the first plot the best one.** It becomes the gallery thumbnail.
- **Show the mechanism, not just the score.** A metric says whether it worked; a plot of what the model learned says why.
- **Set the seed** with `kale.utils.seed.set_seed`, which covers cuDNN as well as Python, NumPy and torch.
- Sections are `# %%` blocks with an rST heading. Only `tutorial.py` is linted and formatted; migrated example code is deliberately left byte-identical to upstream.
- Sections are `# %%` blocks with an rST heading. The authored `plot_<slug>.py` narrative is linted and formatted; migrated example code is deliberately left byte-identical to upstream.

## Migrated examples

Expand All @@ -52,7 +52,7 @@ Files copied from `pykale/examples` are kept unmodified so that upstream fixes c
python scripts/migrate_examples.py --pykale-root ../pykale --dry-run
```

The sync never overwrites `tutorial.py`, `metadata.yml`, `README.md` or `requirements.txt`. Where a copied file genuinely must change — an import that assumed the `examples` package, for instance — record it in `POST_MIGRATION_PATCHES` in `scripts/migrate_examples.py` rather than editing the file directly, so a re-sync cannot quietly undo it.
The sync never overwrites `plot_<slug>.py`, `metadata.yml`, `README.md` or `requirements.txt`. Where a copied file genuinely must change — an import that assumed the `examples` package, for instance — record it in `POST_MIGRATION_PATCHES` in `scripts/migrate_examples.py` rather than editing the file directly, so a re-sync cannot quietly undo it.

## Data

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ PYTHON ?= python
help:
@echo "install install docs + dev dependencies"
@echo "validate check catalog, metadata, schema and thumbnails agree (run before pushing)"
@echo "schema regenerate _shared/metadata/tutorial.schema.json"
@echo "export regenerate the landing-page + _static tutorials.json"
@echo "schema regenerate source/_shared/metadata/tutorial.schema.json"
@echo "export regenerate the landing-page + source/_static tutorials.json"
@echo "pages regenerate the per-domain pages"
@echo "thumbnails regenerate the placeholder SVG thumbnails"
@echo "homepage regenerate the homepage/list-page partials"
Expand Down
50 changes: 33 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# PyKale Tutorials

Runnable tutorials for [PyKale](https://github.com/pykale/pykale), published as a [sphinx-gallery](https://sphinx-gallery.github.io/) site.
[![Docs build](https://github.com/pykale/tutorials/actions/workflows/docs.yml/badge.svg)](https://github.com/pykale/tutorials/actions/workflows/docs.yml)
[![Live site](https://img.shields.io/website?url=https%3A%2F%2Fpykale.github.io%2Ftutorials%2F&label=tutorials%20site&up_message=online)](https://pykale.github.io/tutorials/)
[![PyKale on PyPI](https://img.shields.io/pypi/v/pykale?label=pykale)](https://pypi.org/project/pykale/)
[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

Each tutorial is a self-contained folder holding two things: a narrated `tutorial.py` that runs in minutes and explains the method, and the full `main.py` experiment it was drawn from. Read the first, run the second when you want real numbers.
Runnable tutorials for [PyKale](https://github.com/pykale/pykale), published at **[pykale.github.io/tutorials](https://pykale.github.io/tutorials/)** as a [sphinx-gallery](https://sphinx-gallery.github.io/) site.

Each tutorial is a self-contained folder holding two things: a narrated `plot_<slug>.py` that runs in minutes and explains the method, and the full `main.py` experiment it was drawn from. Read the first, run the second when you want real numbers.

## Layout

Expand All @@ -23,23 +30,25 @@ Supporting directories:

| Path | What it holds |
|---|---|
| `_shared/` | Gallery config, metadata schema, contribution templates, launch-badge builders, assets |
| `source/` | Build infrastructure, kept out of the root: Sphinx `_static` assets, `_templates`, shared gallery/launch code and metadata schema (`_shared`), and the news `_data` |
| `pages/` | Standalone content pages: getting started, all tutorials, learning paths, tutorial status, and about. `index.rst` remains at the root as Sphinx's entry page. |
| `scripts/` | Migration, validation, schema generation, landing-page export |
| `tutorial_catalog.yml` | Source of truth: which tutorials exist and in what order |

## Running a tutorial

```bash
pip install -r requirements.txt
cd others/image_classification/digit_domain_adaptation
pip install pykale
pip install -r requirements.txt
python main.py --cfg configs/MN2UP-DANN.yaml --devices 1
```

Every tutorial's README states its datasets, hardware needs and expected runtime. Datasets are never committed here — they download on demand or come from [pykale/data](https://github.com/pykale/data).

## Building the site

The tasks are available three ways. The **direct Python commands work everywhere** — use these if in doubt (Windows has no `make` by default):
Install the site dependencies and validate the catalogue:

```bash
pip install -e ".[docs,dev]"
Expand All @@ -48,20 +57,27 @@ pip install -e ".[docs,dev]"
python scripts/generate_schema.py --check
python scripts/validate_catalog.py

# fast build, no tutorial execution (what CI runs on PRs)
python scripts/generate_bucket_pages.py
python -m sphinx -b html -W --keep-going . _build/html # (bash: prefix TUTORIALS_NOPLOT=1)

# full build, executes every runnable tutorial.py
python scripts/generate_bucket_pages.py
python -m sphinx -b html -W --keep-going . _build/html
```

Shortcuts wrapping exactly those commands:
For a build without tutorial execution, use the platform shortcut. Both run the thumbnail,
homepage, and domain-page generators before Sphinx:

- **Linux/macOS/CI:** `make validate`, `make html-noplot`, `make html` (needs GNU `make`).
- **Windows:** `.\make.bat validate`, `.\make.bat html-noplot`, `.\make.bat html` (PowerShell/cmd).
From Git Bash, call it as `cmd //c ".\make.bat validate"`, or just run the Python commands above.
From Git Bash, call it as `cmd //c ".\make.bat html-noplot"`.

To run the equivalent commands directly, generate the source files first:

```bash
python scripts/generate_thumbnails.py
python scripts/generate_homepage.py
python scripts/generate_bucket_pages.py
```

Then run `TUTORIALS_NOPLOT=1 python -m sphinx -b html -W --keep-going . _build/html`
in bash, or set `$env:TUTORIALS_NOPLOT='1'` before the Sphinx command in PowerShell.
For a full build that executes eligible `plot_<slug>.py` narratives, unset that variable and run
the same Sphinx command after installing PyKale and each runnable tutorial's requirements.

Open `_build/html/index.html` when the build finishes.

Expand All @@ -71,19 +87,19 @@ Three layers, cheapest first — mirroring how PyTorch's tutorials CI works, min

1. **`validate` + unit tests** (every PR) — catalog/metadata/schema agree; the gallery filter, migration and generators are unit-tested. Fast, no runtime deps.
2. **`html-noplot`** (every PR) — renders every tutorial from source but executes none. Catches structural and parse errors without running anything.
3. **Nightly executing build** (`nightly-exec` job) — actually *runs* the CPU-runnable, non-draft tutorials with their real dependencies, catching runtime breakage (a wrong `kale` call, a shape mismatch) that the parse-only layers miss. GPU-required tutorials are rendered but never executed, matching PyTorch's curated do-not-run list. The set that runs is defined once (`TutorialMetadata.is_runnable`) and shared by the build filter and `scripts/list_runnable.py`.
3. **Manually triggered executing build** (`nightly-exec` job) — runs the CPU-runnable, non-draft tutorials with their real dependencies, catching runtime breakage (a wrong `kale` call, a shape mismatch) that the parse-only layers miss. Its nightly schedule is currently disabled. GPU-required tutorials are rendered but never executed. The set that runs is defined once (`TutorialMetadata.is_runnable`) and shared by the build filter and `scripts/list_runnable.py`.

## Status

All 16 tutorials are migrated and runnable. Most `tutorial.py` narratives are still stubs, marked `draft: true` in their metadata and badged in the gallery — the underlying example is complete either way. Writing those narratives is the current work.
All 16 tutorials are migrated. Most `plot_<slug>.py` narratives are still stubs, marked `draft: true` in their metadata and badged in the gallery — the underlying example is complete either way. Writing those narratives is the current work. Notebook launch actions remain disabled until generated notebooks are published.

## Relationship to the main library

These tutorials were migrated from `examples/` in the [pykale](https://github.com/pykale/pykale) repository; each tutorial's `metadata.yml` records the folder it came from. The library itself is documented at [pykale.readthedocs.io](https://pykale.readthedocs.io).

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md). New tutorials start from the templates in `_shared/templates/`.
See [CONTRIBUTING.md](CONTRIBUTING.md). New tutorials start from the templates in `source/_shared/templates/`.

## License

Expand Down
10 changes: 5 additions & 5 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Roadmap

**Phase 1 — scaffold and migrate. _Done._**
All 16 examples migrated from `pykale/examples`, `_shared/` infrastructure and build scripts in place, one complete narrative (`others/image_classification/digit_domain_adaptation`). Local repository only.
All 16 examples migrated from `pykale/examples`, shared infrastructure and build scripts in place, one complete narrative (`others/image_classification/digit_domain_adaptation`).

**Phase 2 — publish.**
Create `pykale/tutorials`, push, enable Actions, set branch protection, and decide the docs host (ReadTheDocs project vs GitHub Pages). That decision fills in the deploy step `.github/workflows/docs.yml` deliberately leaves blank.
**Phase 2 — publish the site. _Done._**
The repository is at `pykale/tutorials`. The docs workflow builds the site and deploys pushes to `main` to GitHub Pages at `https://pykale.github.io/tutorials/`.

Also required here: **Colab and Binder launch URLs do not resolve yet.** Both point at `notebooks/<domain>/<slug>/tutorial.ipynb`, on the assumption that CI commits sphinx-gallery's generated notebooks to `notebooks/` on main. Sphinx-gallery writes them under `_gallery/`, which is gitignored, and Colab cannot open a notebook from the docs site — only from GitHub, Drive or a gist. Until that CI step exists, every Colab and Binder badge is a 404.
**Notebook launch actions remain pending.** Colab and Binder URLs expect `notebooks/<domain>/<slug>/plot_<slug>.ipynb` on `main`, but Sphinx-Gallery generates notebooks under the gitignored `_gallery/` directory. The site keeps notebook download and Colab actions disabled until notebooks are published and verified. Do not add launch badges to the README before that.

**Phase 3 — the remaining 15 narratives.**
The bulk of the work. Small PRs, one bucket at a time, starting with `others/image_classification` since its four tutorials share a theme. Each landing PR clears that tutorial's `draft: true`. `make validate --strict-drafts` passes when the phase is complete.
The bulk of the work. Small PRs, one bucket at a time, starting with `others/image_classification` since its four tutorials share a theme. Each landing PR clears that tutorial's `draft: true`. `python scripts/validate_catalog.py --strict-drafts` passes when the phase is complete. The executing CI job can be run manually; its nightly schedule is currently disabled.

**Phase 4 — landing page.**
Blocked on a framework decision. The data contract is already fixed and exported; see `../landing-page/README.md`.
Expand Down
2 changes: 1 addition & 1 deletion cancer/multiomics_cancer_classification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Integrate mRNA expression, DNA methylation and miRNA expression with per-omics graph convolutional networks, fusing their predictions through a view-correlation discovery network.

> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
> **Note:** the narrated `plot_multiomics_cancer_classification.py` in this folder is still a stub. The example below
> is complete and runnable.

## 1. Description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
# ``_shared/templates/tutorial.py.template``.
# ``source/_shared/templates/plot.py.template``.

print("See main.py in this folder for the runnable example.")
2 changes: 1 addition & 1 deletion cardiac/cardiac_mri_diagnosis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Diagnose pulmonary arterial hypertension from short-axis cardiac MRI. MPCA keeps the tensor structure of the image stack intact, and the learned weights map back onto the anatomy.

> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
> **Note:** the narrated `plot_cardiac_mri_diagnosis.py` in this folder is still a stub. The example below
> is complete and runnable.

## 1. Description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
# ``_shared/templates/tutorial.py.template``.
# ``source/_shared/templates/plot.py.template``.

print("See main.py in this folder for the runnable example.")
2 changes: 1 addition & 1 deletion cardiac/landmark_uncertainty_estimation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Quantile Binning sorts landmark predictions into bins of increasing uncertainty with estimated error bounds, so the least reliable predictions can be filtered out before they are acted on.

> **Note:** the narrated `tutorial.py` in this folder is still a stub. The example below
> **Note:** the narrated `plot_landmark_uncertainty_estimation.py` in this folder is still a stub. The example below
> is complete and runnable.

## 1. Description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
# Setup
# -----
# Placeholder so the gallery builds. Replace this file using
# ``_shared/templates/tutorial.py.template``.
# ``source/_shared/templates/plot.py.template``.

print("See main.py in this folder for the runnable example.")
Loading
Loading