Skip to content

perf: rotation-matrix grid transform + one Grid2D per VectorYX2D (numpy deflections phase 3) - #519

Merged
Jammy2211 merged 2 commits into
mainfrom
feature/numpy-deflections-p3
Sep 3, 2026
Merged

Jammy2211 merged 2 commits into
mainfrom
feature/numpy-deflections-p3

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Phase 3 of the numpy-deflections-cpu epic (PyAutoGalaxy#598) — the shared-geometry lever every @transform-decorated profile pays once per call.

  • geometry_util.transform_grid_2d_to_reference_frame builds the clockwise rotation from one scalar cos/sin of the angle and applies it as two multiply-adds per coordinate ([y cos a − x sin a, x cos a + y sin a]), instead of the polar form's per-pixel sqrt + arctan2 + sin + cos. Algebraically identical (max abs difference 2e-15 on a 15k-point grid); 1.42 ms → 0.21 ms on that grid (~7×).
  • VectorYX2D reuses the Grid2D it is handed when it is already paired with the same mask. @to_vector_yx passes the input grid straight through, and the constructor was re-converting its values and rebuilding a second Grid2D on every deflection evaluation. One Grid2D construction per call instead of two; other inputs take the unchanged conversion path.

Net on this box (OMP_NUM_THREADS=1, hst 15,361-point Grid2D): Isothermal.deflections_yx_2d_from 1.95 ms → 0.93 ms with the PyAutoGalaxy hoists from #598 on top. The measurement of record (before/after on hst + euclid for all nine profiles, pins at rtol 1e-6) lands in the autolens_profiling PR of this phase.

The rotation is exact where the polar form left round-off: an on-axis point at angle 0 now gives an x-component of exactly 0.0 where it was ~5e-17. That is what moved one pinned sample in the autolens_profiling stellar cell (relative shift 1.0 on a 1e-17 value) and is re-pinned there with provenance; every other pin held.

Merge order: this PR → PyAutoGalaxy#599 (profiles) → autolens_profiling (numbers).

API Changes

No public API change. transform_grid_2d_to_reference_frame returns the same coordinates; VectorYX2D(values, grid, mask) accepts the same inputs and, when grid is a Grid2D on mask, its .grid is now that object rather than a copy.
See full details below.

Test Plan

  • test_autoarray — 1349 passed, 61 skipped (-n 4); geometry, grids, vectors and decorator tests re-run after each change
  • ruff check / ruff format --check — no new findings in the changed files (both were already format-clean on main)
  • Field-level A/B vs main on the hst grid for Isothermal / PowerLaw / NFW / NFWSph / gNFW: max relative difference ≤ 1e-8
  • autolens_profiling scripts/lens/deflections/{total,dark,stellar}.py hst + euclid: every pin held at rtol 1e-6 except the exact-zero sample above
Full API Changes (for automation & release notes)

Removed

  • none

Added

  • none

Changed

  • autoarray.util.geometry.transform_grid_2d_to_reference_frame(grid_2d, centre, angle, xp) — same signature and result; rotation-matrix evaluation (no per-pixel arctan2/sin/cos).
  • autoarray.VectorYX2D.__init__(values, grid, mask, store_native=False) — when grid is a Grid2D whose .mask is mask and store_native is False, self.grid is grid itself instead of a re-converted copy.

Migration

  • none required

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Re4JLhTCFnGYeez8oEyM6M


Generated by Claude Code

Build the clockwise rotation from one scalar cos/sin of the angle and apply it
as two multiply-adds per coordinate, instead of the polar form's per-pixel
sqrt + arctan2 + sin + cos. Algebraically identical (max abs difference
2e-15 on a 15k-point grid); ~7x cheaper on the numpy path, and every
transform-decorated profile evaluation pays it once per call.

Phase 3 of the numpy-deflections-cpu epic (PyAutoGalaxy#598).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Re4JLhTCFnGYeez8oEyM6M
The @to_vector_yx decorator hands the input Grid2D straight into VectorYX2D,
which re-converted its values and rebuilt a second Grid2D on every
transform-decorated profile evaluation. Reuse the grid when it is already a
Grid2D on the same mask (and native storage is not requested); other inputs
take the existing conversion path. One Grid2D construction per deflection
call instead of two; results unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Re4JLhTCFnGYeez8oEyM6M
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label Sep 3, 2026 — with Claude
@Jammy2211
Jammy2211 merged commit 62feb7e into main Sep 3, 2026
3 checks passed
@Jammy2211
Jammy2211 deleted the feature/numpy-deflections-p3 branch September 3, 2026 13:59
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants