Repository navigation
Conversation
benchmarks/plate_solve.py times compute_wcs on synthetic star fields made from a known TAN WCS (missing and spurious detections, position noise) and checks each solution against the true WCS. Results can be saved and compared between runs. benchmarks/complexity.py measures how hashes, candidate pairs, solve time and cost per tested pair grow with the number of stars, and plots them on log-log axes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Testing candidate asterism pairs cost ~25 us per pair, almost all NumPy call overhead on tiny arrays. The number of pairs grows as ~n^8 (quads) and ~n^6 (triangles), so when min_match is not reached every pair is tested and solves can take 10-20 s with 20 detections and 40 catalog stars. - find_transform and compute_wcs get a `backend` argument: "numba", "numpy" or "auto" (default: numba when installed). The numba loop costs ~0.5 us per pair. numba is an optional dependency (`twirl[numba]`) and is only imported when used. - Candidate pairs are built as NumPy arrays instead of a Python list. - quads.reorder uses take_along_axis instead of a Python loop (same output). Both backends return the same transform: tests compare them on simulated fields, and on 357 SPECULOOS images run through astra the solutions are identical to the previous code, while the slowest solve drops from 19.3 s to 0.6 s (quads) and from 4.0 s to 0.26 s (triangles). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit de40792.
backend="auto" checked only that numba was installed, so a numba that cannot be imported (e.g. with an unsupported NumPy version) made every call fail. "auto" now tries the import and falls back to the NumPy loop; an explicit backend="numba" still raises. Also run the backend tests that do not need numba without it, add a test for the fallback, mention the first-call compile in the docstring, and make the README speed-up claim match the benchmarks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add a Python 3.13 / astropy 7.0 job that uninstalls numba, so the NumPy backend most users get is tested. Run pytest with `uv run --no-sync`: `uv run` re-synced the environment to uv.lock, which undid the astropy version pin (CI tested astropy 7.2.2 in every job) and would reinstall numba. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
astropy 6.1 and 7.0 use np.in1d, which NumPy 2.4 removed, so they fail to import with the NumPy locked for Python >= 3.11. This only shows now that the astropy pin is no longer undone by `uv run`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add optional numba backend to
find_transformSummary
This PR makes plate solving much faster when many asterism pairs must be tested. A new
backendargument onfind_transformandcompute_wcsselects a numba-compiled loop. numba is optional. Without it, twirl uses the current NumPy loop and gives the same results.Why
Testing one candidate asterism pair costs about 25 µs. Almost all of that is NumPy call overhead on tiny arrays (
lstsqon 3–4 points, a 20×40 distance array). The number of pairs grows as about n⁸ for quads and n⁶ for triangles. Whenmin_matchis not reached,find_transformtests every pair. With 20 detections and 40 catalog stars, one solve then takes 10–20 s.On 357 real SPECULOOS images, run through the astra pointing pipeline, 114 images (32%) took more than 1 s with the current code, and the slowest took 19 s.
Changes
find_transform(..., backend="auto")andcompute_wcs(..., backend="auto"):"numba","numpy", or"auto"(numba when it is installed). The default is"auto", so existing calls need no change.twirl/_numba.py: the compiled pair loop (least-squares affine fit, projection, cross-match count, early stop). numba is imported only when this backend is used, soimport twirldoes not get slower.quads.reorderusesnp.take_along_axis, not a Python loop. The output is identical, and this is about 2× faster on 40 stars.pyproject.toml: new optional extratwirl[numba], and numba is added to thedevgroup so CI tests both backends.uv.lockis updated (no existing pins changed).pip install "twirl[numba]".benchmarks/:plate_solve.pytimescompute_wcson synthetic fields and checks each solution against the true WCS.complexity.pyplots how the cost grows with the number of stars. Both have a--backendoption.Results
Synthetic benchmark (
benchmarks/plate_solve.py, median per case):When early stop happens, the fixed cost of
compute_wcs(about 5 ms) dominates, so the gain is small there.357 SPECULOOS images, 20 detections and 40 Gaia stars each:
asterism=3, min_match=0.5), before → afterThe solutions are identical to the current code on all 357 images, for both asterisms.
Measured on an Apple M2 Max with 8 parallel workers.
Compatibility
numpy<2.6), and it usually supports a new Python version some months after its release.Testing
tests/test_backends.py(50 cases):reordermatches the old loop exactlycompute_wcsgives the same WCS with both backendsValueErrorNotes
main.