Makefile¶
This repository uses a Makefile as a simple command launcher for common tasks.
Think of it as a “one command per workflow” shortcut (similar to npm run … in JS).
It wraps these tools:
uv: creates
.venvand installs dependenciesruff: formatting + linting
mypy: type checking
pytest: tests (fast / slow / perf)
Sphinx: builds documentation (HTML + optional PDF)
Note
Windows detail: Make recipes run under cmd.exe (SHELL := cmd.exe in the Makefile).
You can still invoke make … from PowerShell, but the recipe lines execute in cmd.exe.
A few targets intentionally call PowerShell (mainly experiment loops) because it is more robust there.
Quick start¶
First-time setup (all platforms)¶
make install-dev
This will:
check that
uvexistscheck you have a recent enough Python (default minimum is 3.14)
create
.venvif missinginstall dependencies (runtime + dev tools)
Day-to-day workflow (most common)¶
When you changed code and want quick confidence:
make fmt
make pytest
When you want “as close to CI as possible”:
make final
Documentation (HTML)¶
make docs-html
If you don’t have LaTeX installed, prefer docs-html over docs (see “Docs” section below).
How to think about the Make targets (junior-friendly)¶
The targets fall into two big buckets:
1) Quality checks (keep code correct and consistent)¶
Formatting: makes code look consistent (indentation, line breaks, etc.)
Linting: catches common mistakes and style issues
Typing: catches type errors before runtime
Testing: ensures behaviour stays correct
2) Productive targets (produce outputs)¶
run experiments and produce files under
out/…sync experiment outputs into
docs/…(for the gallery/docs)build documentation
If you are unsure which command to run: use make final.
Dependency groups (pyproject.toml)¶
uv installs dependencies from your pyproject.toml. This project uses “extras”:
default: runtime dependencies (needed to run the package)
dev: developer tools (ruff, mypy, pytest, …)
docs: documentation tools (sphinx, theme, myst, bibtex, …)
What the Makefile uses¶
Most developer commands run via:
uv run --extra dev ...
Documentation build runs via:
uv run --extra docs ...
Why docs-deps uses --all-extras¶
make docs-deps runs:
uv sync --all-extras
because the docs pipeline also runs a small helper under the dev extra before invoking Sphinx.
Virtual environment behavior¶
Where is the venv?¶
The venv is always created at:
.venv/
Minimum Python version¶
The Makefile checks PYTHON_MIN (default is 3.14). If your Python is older, make python-check fails.
Why UV_LINK_MODE=copy exists¶
On Windows, hardlinks can fail or warn on multi-drive setups (e.g. repo on D: but cache on C:).
So the Makefile defaults to:
UV_LINK_MODE=copy
You can override it, but the default is chosen to reduce Windows pain.
Common “reset” if your venv is broken¶
make clean-venv
make install-dev
Formatting (ruff)¶
Formatting is purely about how code looks, not what it does.
Targets¶
Target |
What it does |
When to use it |
|---|---|---|
|
formats selected repo paths |
normal formatting |
|
checks formatting only (no changes) |
CI-like check |
|
“broad auto-fix”: ruff fixes + formats everything |
when you want the repo cleaned up quickly |
Typical usage¶
Before committing:
make fmt
If CI says “formatting changed”:
make format
Linting (ruff)¶
Linting looks for potential bugs and bad patterns, for example:
unused imports
variables shadowing names
common correctness issues ruff knows how to detect
Targets¶
Target |
What it does |
Does it edit files? |
|---|---|---|
|
ruff check-only |
no |
|
ruff with auto-fix |
yes |
Typical usage¶
Use
make lintwhen you only want to see problems.Use
make lint-fixwhen you want ruff to fix safe issues automatically.
Typing (mypy)¶
Typing checks help catch issues like:
calling functions with wrong argument types
returning the wrong types
forgetting to handle
None
Target¶
make mypy
It runs:
mypy mathxlab tests experiments
Common tip for juniors¶
If mypy errors look scary, start with the first error in the output. Many later errors are “follow-up noise” caused by an earlier wrong type.
Tests (pytest): fast / slow / perf¶
This repo separates tests using pytest markers:
fast tests:
not slow and not perfslow tests:
slow and not perfperf tests:
perf
Markers are set in tests like:
import pytest
@pytest.mark.slow
def test_big_case():
...
Coverage and threshold¶
Tests collect coverage for the library packages (not for experiment scripts). Coverage fails the target if it drops below 80%.
Stable temp paths¶
pytest is invoked with stable temp directories:
temp_pytest_cachetemp_pytest
They are cleaned by make clean.
pytest (fast tests)¶
make pytest
What it does:
runs only fast tests (
not slow and not perf)runs with coverage
fails if coverage < 80%
This is the main “developer loop” test target.
pytest-xdist (fast tests in parallel)¶
make pytest-xdist
Runs fast tests using xdist:
-n auto --dist=load
Use this when the test suite gets bigger and you want faster local feedback.
pytest-slow (two-phase: fast then slow)¶
make pytest-slow
What it does (important detail):
deletes
.coverageruns fast tests first in best-effort mode (failures in this phase do not fail the target — this is intentional in the Makefile)
runs slow tests with:
xdist by default (
PYTEST_XDIST_SLOW=-n auto --dist=load)--cov-appendto combine coverage from both phasescoverage threshold (80%)
Warning
If you want “fast tests must pass”, run make pytest separately.
make pytest-slow is designed to always get through the slow suite even if fast tests are currently failing.
Performance tests (pytest-perf): what they are and how to use them¶
Performance tests are still pytest tests, but marked with @pytest.mark.perf.
They exist to catch accidental slowdowns (e.g. a function becomes 10× slower).
Why the Makefile forces thread counts to 1¶
Math/scientific libraries sometimes use multiple CPU threads automatically (BLAS/OpenMP/etc.). That makes timings noisy and hard to compare.
So perf targets set:
OMP_NUM_THREADS=1MKL_NUM_THREADS=1OPENBLAS_NUM_THREADS=1NUMEXPR_NUM_THREADS=1
This makes timings more reproducible across machines and runs.
pytest-perf (run perf-marked tests)¶
make pytest-perf
Runs only:
-m "perf"
and shows progress output.
Use this when:
you changed a performance-sensitive function
you want to ensure you didn’t introduce an obvious regression
pytest-perf-baseline (update accepted baseline numbers)¶
make pytest-perf-baseline
Same as pytest-perf, but also passes:
--perf-update-baseline
Use this only when:
you intentionally changed performance (e.g. algorithm changed)
and you want to accept the new timings as the baseline
Warning
Baseline updates should be done on a reasonably idle machine. If you run baseline updates while your system is busy, you may “bake in” slow/noisy numbers.
Performance suite (non-pytest): snapshots and comparisons¶
In addition to pytest-perf, the Makefile also provides a small “perf runner” pipeline:
perf (dev snapshot)¶
make perf
Runs:
python mathxlab/tools/run_perf.py --mode dev --overwrite
This is typically used during development to write/update a performance snapshot.
perf-release (release snapshot)¶
make perf-release
Runs:
python mathxlab/tools/run_perf.py --mode release --overwrite
This is usually for “release-ish” measurements (more stable, fewer surprises).
perf-compare (compare two snapshots)¶
make perf-compare A=v0.1.0 B=v0.2.0
Runs:
python mathxlab/tools/compare_perf.py --a … --b …
Use this when you want a readable report of “what got faster/slower” between two saved snapshots.
Tip
If you’re not sure what valid values for A and B are, run make perf once and look at the output written by the script.
It usually prints the snapshot identifiers/paths it created.
Run logs and experiment runner¶
Experiments are Python modules like:
mathxlab/experiments/e001.pymathxlab/experiments/e002.py…
Run one experiment (recommended: via Make)¶
make run EXP=e001
This:
creates
out/e001/(if missing)creates
out/e001/logs/writes a log file:
out/e001/logs/run_e001.log
runs the experiment with:
python -m mathxlab.experiments.e001 --out out/e001 -v
Forward CLI arguments¶
make run EXP=e001 ARGS="--seed 123 --n 200000"
Note
On Windows, always quote ARGS="..." if it contains spaces.
Run all experiments¶
make out
This finds all mathxlab/experiments/e???.py files and runs them sequentially.
Snapshots: syncing experiment outputs into docs¶
After experiments run, you typically want to publish their outputs into docs/…
so the gallery/docs can include them.
snapshots¶
make snapshots
Runs a sync helper that copies the “published artifacts” from:
out/...
into:
docs/...
You can limit what is synced:
make snapshots IDS="e001 e002 e010"
Or redirect roots:
make snapshots OUT_ROOT=out DOCS_ROOT=docs
Documentation builds: HTML and optional PDF¶
docs-html (recommended)¶
make docs-html
What it does:
installs docs dependencies (
make docs-deps)runs the snapshot sync helper (so docs see the latest experiment artifacts)
builds Sphinx HTML into
docs/_build/html
Important: it uses Sphinx -W, so warnings become errors (this is intentional for CI cleanliness).
docs-pdf (optional; requires LaTeX)¶
make docs-pdf
Requires a working LaTeX toolchain (including latexmk).
docs¶
make docs
Runs the full chain:
status → tags-check → docs-html → docs-pdf
Note
If you don’t have LaTeX installed locally, use make docs-html.
Target overview (what each target does)¶
Targets are defined in the repository Makefile.
This table explains what each one is for.
Target |
Purpose |
|---|---|
|
Remove caches/build artifacts (docs build, mypy/pytest/ruff caches, temp dirs, …). |
|
Remove the virtual environment |
|
Build docs: |
|
Remove |
|
Install/update dependencies needed for docs builds ( |
|
Build Sphinx HTML into |
|
Build PDF docs (optional): requires LaTeX toolchain + |
|
Full chain: |
|
Strict chain: |
|
Broad auto-fix helper: |
|
Apply formatting ( |
|
Check formatting only ( |
|
Print the complete list of targets and short descriptions. |
|
Editable install: |
|
Install default dependencies ( |
|
Install default + dev dependencies ( |
|
Install default + docs dependencies ( |
|
Ruff lint (check-only): |
|
Ruff lint with auto-fix: |
|
Type-check: |
|
Run all experiments sequentially ( |
|
Run perf suite in “dev” mode (writes snapshot). |
|
Compare two perf snapshots ( |
|
Run perf suite in “release” mode (writes snapshot). |
|
Run fast tests ( |
|
Run perf-marked tests ( |
|
Same as |
|
Run fast then slow tests with coverage aggregation. |
|
Run fast tests with xdist ( |
|
Verify Python >= |
|
Run a single experiment: |
|
Sync |
|
Update |
|
Validate docs tags against |
|
Verify |
|
Create |
|
Remove and recreate |
Variables you can override¶
Make lets you pass variables like:
make <target> NAME=value
Common variables:
Variable |
Meaning |
Example |
|---|---|---|
|
experiment id (used by |
|
|
forwarded CLI args for experiments |
|
|
snapshot input root |
|
|
snapshot docs root |
|
|
experiment id filter for snapshots |
|
|
perf snapshot identifiers for |
|
|
extra xdist args for fast tests |
|
|
xdist args for slow tests (default: |
|
|
minimum Python version |
|
|
uv link mode (default: |
|
Troubleshooting¶
make uv-check fails (“uv is not installed”)¶
Install uv and ensure it is on PATH, then rerun:
make uv-check
make install-dev
make python-check fails (Python too old / not found)¶
Reset sequence:
make clean-venv
make install-dev
docs-html fails with include marker errors¶
This happens when docs/development.md uses include with start-after / end-before
and the marker headings are missing or renamed.
This file intentionally contains these headings exactly:
## Dependency groups (pyproject.toml)## Run logs and experiment runner## Target overview (what each target does)
If you rename them, the include markers in other docs may break.
Windows quoting problems for ARGS¶
If ARGS contains spaces, always quote:
make run EXP=e001 ARGS="--seed 1 --n 200000"
If you need nested quotes, prefer single quotes inside double quotes:
make run EXP=e001 ARGS="--title 'My Title With Spaces'"