|
1 | 1 | # PyAutoLens — Agent Instructions |
2 | 2 |
|
3 | | -**PyAutoLens** is a Python library for strong gravitational lens modeling, built on PyAutoGalaxy. It adds multi-plane ray-tracing via the `Tracer` object and lensing-specific fit/analysis classes. |
| 3 | +Canonical, agent-agnostic instructions for this repo. `CLAUDE.md` imports this |
| 4 | +file; any tool that does not process `@`-imports should read this directly. |
4 | 5 |
|
5 | | -## Setup |
| 6 | +## What this repo is |
6 | 7 |
|
7 | | -```bash |
8 | | -pip install -e ".[dev]" |
9 | | -``` |
| 8 | +**PyAutoLens** (package `autolens`) is the strong gravitational-lensing layer |
| 9 | +built on PyAutoGalaxy. It adds multi-plane ray-tracing via the `Tracer`, and |
| 10 | +lensing-specific `Fit*` / `Analysis*` classes for imaging, interferometer, and |
| 11 | +point-source datasets. |
| 12 | + |
| 13 | +Dependency direction: autolens sits at the top of the stack and may import all |
| 14 | +four layers below it — **autogalaxy**, **autoarray**, **autofit**, and |
| 15 | +**autoconf**. Nothing in the ecosystem imports autolens. |
10 | 16 |
|
11 | | -## Running Tests |
| 17 | +## Related repos |
| 18 | + |
| 19 | +- **Source siblings (all upstream):** PyAutoConf, PyAutoArray, PyAutoFit, |
| 20 | + PyAutoGalaxy. |
| 21 | +- **autolens_workspace** — runnable tutorials/examples (`../autolens_workspace`). |
| 22 | +- **autolens_workspace_test** — integration + JAX/likelihood parity scripts. |
| 23 | +- **autolens_profiling** — performance/profiling harness (`../autolens_profiling`). |
| 24 | +- **HowToLens** — the lecture-style tutorial series (`../HowToLens`). |
| 25 | +- **docs/** — Sphinx source; published to ReadTheDocs. |
| 26 | +- **Science context:** the strong-lensing knowledge wiki at |
| 27 | + `autolens_assistant/wiki/literature/` (concepts, entities, sources) — mass |
| 28 | + models, source reconstruction, degeneracies, substructure, surveys. |
| 29 | + |
| 30 | +## Quick commands |
12 | 31 |
|
13 | 32 | ```bash |
14 | | -python -m pytest test_autolens/ |
15 | | -python -m pytest test_autolens/lens/test_tracer.py |
16 | | -python -m pytest test_autolens/imaging/test_fit_imaging.py -s |
| 33 | +pip install -e ".[dev]" # install with dev/test extras |
| 34 | +python -m pytest test_autolens/ # full test suite |
| 35 | +python -m pytest test_autolens/lens/test_tracer.py # one focused test (add -s for output) |
| 36 | +black autolens/ # formatter (advisory — not gated) |
17 | 37 | ``` |
18 | 38 |
|
19 | | -### Sandboxed / Codex runs |
| 39 | +In a sandboxed / restricted environment, point numba and matplotlib at |
| 40 | +writable caches: |
20 | 41 |
|
21 | 42 | ```bash |
22 | 43 | NUMBA_CACHE_DIR=/tmp/numba_cache MPLCONFIGDIR=/tmp/matplotlib python -m pytest test_autolens/ |
23 | 44 | ``` |
24 | 45 |
|
25 | | -## Key Architecture |
| 46 | +## CI / definition of green |
| 47 | + |
| 48 | +PRs must pass `pytest --cov` on the CI matrix (Python 3.12 **and** 3.13). There |
| 49 | +is no black/ruff/flake8 gate — formatting is advisory. (`requires-python` in |
| 50 | +`pyproject.toml` is `>=3.9`.) |
26 | 51 |
|
27 | | -- **Tracer** (`lens/tracer.py`): groups galaxies by redshift plane, performs multi-plane ray-tracing |
28 | | -- **Fit classes**: `FitImaging`, `FitInterferometer`, `FitPointDataset` — extend autogalaxy equivalents with lensing |
29 | | -- **Analysis classes**: `AnalysisImaging`, `AnalysisInterferometer`, `AnalysisPoint` |
30 | | -- **Namespace**: `al.mp.*` (mass), `al.lp.*` (light), `al.Galaxy`, `al.Tracer` |
| 52 | +## Configuration & defaults |
31 | 53 |
|
32 | | -## Dependencies |
| 54 | +autoconf supplies the packaged defaults under `autolens/config/`. Workspaces |
| 55 | +override them via their own `config/` directory; the test suite pushes a local |
| 56 | +config dir via `conf.instance.push(...)` in `test_autolens/conftest.py`. When a |
| 57 | +change adds a new config key, mirror it into the packaged defaults so |
| 58 | +downstream workspaces inherit it. |
33 | 59 |
|
34 | | -- `autogalaxy` — galaxy morphology, profiles, single-plane fitting |
35 | | -- `autoarray` — data structures, grids, masks, inversions |
36 | | -- `autofit` — non-linear search and model-fitting framework |
| 60 | +## JAX & `xp` |
37 | 61 |
|
38 | | -## Key Rules |
| 62 | +NumPy is the default everywhere; JAX is opt-in and never imported at module |
| 63 | +level. `xp=np` (default) selects NumPy; `xp=jnp` selects JAX (imported locally). |
| 64 | +Thread `xp` through **every** nested call — a missed site silently defaults to |
| 65 | +`xp=np` and fails when a tracer hits an `np.*` op. Two patterns cross the |
| 66 | +`jax.jit` boundary: the `if xp is np:` **guard** for raw `jax.Array` returns |
| 67 | +(the `LensCalc` hessian methods), and **pytree registration** for functions |
| 68 | +returning real wrappers/structured objects — `FitImaging`, `Tracer`, and |
| 69 | +`DatasetModel` register via `register_instance_pytree`, so |
| 70 | +`jax.jit(analysis.fit_from)(instance)` returns a real `FitImaging` with |
| 71 | +`jax.Array` leaves. |
39 | 72 |
|
40 | | -- The `xp` parameter controls NumPy vs JAX: `xp=np` (default) or `xp=jnp` |
41 | | -- Functions inside `jax.jit` must guard autoarray wrapping with `if xp is np:` |
42 | | -- Decorated functions return **raw arrays** — the decorator wraps them |
43 | | -- All files must use Unix line endings (LF) |
44 | | -- Format with `black autolens/` |
| 73 | +**Unit tests are NumPy-only.** A JAX/`xp` change is validated only by the |
| 74 | +parity scripts in `autolens_workspace_test` (`jax.jit` round-trip + |
| 75 | +`fitness._vmap` batch eval) — never by `test_autolens/`. |
| 76 | + |
| 77 | +Full detail lives in PyAutoArray: |
| 78 | +**[`PyAutoArray/docs/agents/jax_and_decorators.md`](../PyAutoArray/docs/agents/jax_and_decorators.md)**. |
| 79 | + |
| 80 | +## Public API |
| 81 | + |
| 82 | +The public surface is defined authoritatively in `autolens/__init__.py` — read |
| 83 | +it rather than trusting a hand-maintained namespace table. Canonical import: |
| 84 | + |
| 85 | +```python |
| 86 | +import autolens as al |
| 87 | +``` |
45 | 88 |
|
46 | | -## Working on Issues |
| 89 | +Profiles re-export from autogalaxy (`al.mp.*`, `al.lp.*`) alongside `al.Galaxy`, |
| 90 | +`al.Tracer`, `al.FitImaging`/`al.AnalysisImaging`, and the point-source classes. |
| 91 | + |
| 92 | +## Key rules / footguns |
| 93 | + |
| 94 | +- Import direction: autolens may use all four upstream packages; nothing |
| 95 | + imports autolens. |
| 96 | +- Grid-decorated profile methods return a **raw array** (the decorator wraps |
| 97 | + it); write `aa.decorators.*` and read coordinates via `grid.array[:, 0]`. |
| 98 | +- All files use Unix line endings (LF, `\n`) — never `\r\n`. |
| 99 | + |
| 100 | +## Working on issues |
47 | 101 |
|
48 | 102 | 1. Read the issue description and any linked plan. |
49 | | -2. Identify affected files and write your changes. |
50 | | -3. Run the full test suite: `python -m pytest test_autolens/` |
51 | | -4. Ensure all tests pass before opening a PR. |
52 | | -5. If changing public API, note the change in your PR description — downstream workspaces may need updates. |
53 | | -## Never rewrite history |
54 | | - |
55 | | -NEVER perform these operations on any repo with a remote: |
56 | | - |
57 | | -- `git init` in a directory already tracked by git |
58 | | -- `rm -rf .git && git init` |
59 | | -- Commit with subject "Initial commit", "Fresh start", "Start fresh", "Reset |
60 | | - for AI workflow", or any equivalent message on a branch with a remote |
61 | | -- `git push --force` to `main` (or any branch tracked as `origin/HEAD`) |
62 | | -- `git filter-repo` / `git filter-branch` on shared branches |
63 | | -- `git rebase -i` rewriting commits already pushed to a shared branch |
64 | | - |
65 | | -If the working tree needs a clean state, the **only** correct sequence is: |
66 | | - |
67 | | - git fetch origin |
68 | | - git reset --hard origin/main |
69 | | - git clean -fd |
70 | | - |
71 | | -This applies equally to humans, local Claude Code, cloud Claude agents, Codex, |
72 | | -and any other agent. The "Initial commit — fresh start for AI workflow" pattern |
73 | | -that appeared independently on origin and local for three workspace repos is |
74 | | -exactly what this rule prevents — it costs ~40 commits of redundant local work |
75 | | -every time it happens. |
| 103 | +2. Identify affected files and make the change. |
| 104 | +3. Run the full suite: `python -m pytest test_autolens/`. |
| 105 | +4. If you changed public API, say so explicitly — the workspaces and |
| 106 | + downstream pipelines may need updates. |
| 107 | +5. Ensure all tests pass before opening a PR. |
| 108 | + |
| 109 | +## Deep dives |
| 110 | + |
| 111 | +- [`PyAutoArray/docs/agents/jax_and_decorators.md`](../PyAutoArray/docs/agents/jax_and_decorators.md) |
| 112 | + — decorator system, `xp` backend pattern, and the `jax.jit` boundary. |
| 113 | + |
| 114 | +## Clean state |
| 115 | + |
| 116 | +Never rewrite history on a repo with a remote (no `git init` over a tracked |
| 117 | +tree, no force-push to `main`, no rebasing pushed shared branches). To reset a |
| 118 | +dirty tree the only correct sequence is: |
| 119 | + |
| 120 | +```bash |
| 121 | +git fetch origin |
| 122 | +git reset --hard origin/main |
| 123 | +git clean -fd |
| 124 | +``` |
0 commit comments