Skip to content

TLS: survey-scale fast path (batch phase-binned kernel + exact refinement)#68

Merged
johnh2o2 merged 6 commits into
v1.0-fixesfrom
feature/tls-fast-survey
Jul 7, 2026
Merged

TLS: survey-scale fast path (batch phase-binned kernel + exact refinement)#68
johnh2o2 merged 6 commits into
v1.0-fixesfrom
feature/tls-fast-survey

Conversation

@johnh2o2

@johnh2o2 johnh2o2 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

What

Rewrites Transit Least Squares for survey-scale throughput (N ≫ 1 light curves). The new batch API tls_search_batch() and kernel tls_fast.cu become the default for tls_search / tls_search_gpu / tls_transit (opt out with use_fast=False). The legacy per-point kernel is untouched and retained.

The old kernel did two full O(ndata) passes per (duration, t0) trial and capped light curves at ~3,500 points. Both limits are gone.

Results (end-to-end survey wall time, 100% injected-transit recovery in every regime)

Regime (ndata, periods) A5000 Ada V100
TESS FFI (1.3K, 2.5K) 1.25 ms/LC (~800 LC/s) 3.05 1.42
K2 90-d (4.3K, 9.7K) 3.1 6.4 3.9
TESS 2-min (19.7K, 2.5K) 2.8 5.1 3.1
TESS 1-yr (17K, 42K) 18.4 27.3 16.5
Kepler 4-yr (65K, 172K) 168 198 146

Fidelity — not lossy for detection (measured)

The key question: does the coarse epoch grid + refinement sacrifice detectability? Measured with scripts/tls_fidelity_experiment.py — same injected light curves, same Ofir grid, cuvarbase (default t0=3 and reference-matched t0=33) vs the real transitleastsquares, with SDE recomputed by the identical statistic on both methods' χ² spectra (their SR→SDE normalizations differ):

Signal cuvarbase t0=3 (default) cuvarbase t0=33 (matched) recovery
tess-ffi depth 0.005 (strong) 0.99× ref 1.03× 12/12
tess-ffi depth 0.002 (marginal) 0.97× ref 1.01× 10/10
k2 depth 0.004 (narrow) 0.98× ref 1.01× 6/6

Default is within 1–3% of the reference SDE (safe direction — slight under-report), matched t0=33 within 1%, 100% recovery including marginal and narrow transits. SDE is a period-space contrast that normalizes out epoch-grid density, so the coarse grid trades reported t0/parameter precision (restored by the refinement pass) for speed, not detectability. Matched fidelity costs a measured 6–15× (Kepler-4yr 8.4× → 1.48 s/LC).

Speed & cost

Throughput is the market-independent invariant: apples-to-apples on the same machine (same LCs/grid, single GPU vs all CPU cores), ~1,000–3,000× faster than the reference at matched SDE fidelity (more at the default grid; ratio is CPU-dependent). Versus GTLS (the only other GPU TLS, CuPy/RTX 4090, arXiv:2607.00348), a Kepler-class config runs ~22× (matched) to ~190× (default) faster on a cheaper A5000.

Dollar figures are pricing-dependent and were not the headline — an earlier revision over-pinned them by pairing a spot-price GPU with an AWS on-demand CPU. analysis/TLS_COST_ANALYSIS.md has the honest, caveated breakdown (hundreds-to-thousands× cheaper per LC than CPU TLS under any reasonable pricing).

How

  • Phase-binned batch kernel: one block per (light curve, period), fold once into shared-memory bins, scan trials against bin-averaged integrated-template tables (S1=∫T, S2=∫T²) with closed-form chi² (chi2_0 − num²/den). Trial cost independent of ndata.
  • Period banding: grid split by required bin count so long-period searches don't pay the finest band's cost.
  • Float-float fold: ~1e-8 phase error at 4-year baselines with no 1/64-rate double math on consumer GPUs.
  • Exact top-K refinement kernel re-fits the best candidate periods on a finer local (duration, t0) grid. Refinement sharpens reported parameters only — SDE/FAP come from the uniform coarse spectrum so the statistic's scale stays consistent (refining only the peak would inflate SDE → false positives).
  • Cancellation-free score output; chi² reconstructed in float64 host-side.
  • Support fixes: SDE median-detrend window capped at 91 (reference convention; minutes → 0.1 s at 190k periods); duration_grid_keplerian vectorized (1.1 s → 40 ms); threaded per-LC stats; 64-bit offsets; input validation (qmax<1, power-of-two block_size, non-negative refine_top_k).

Validation

  • 68/68 GPU tests on RTX 4000 Ada (sm_89), including golden cross-validation vs the reference transitleastsquares package; core suite green on A5000 (sm_86) and V100 (sm_70).
  • Fidelity experiment above (SDE parity) + raw numbers in benchmarks/results/tls_survey_jul2026/.
  • 25-agent adversarial review; 19 confirmed findings fixed or explicitly deferred.
  • Block-size heuristic swept and tuned per compute capability across all three GPU families (no single-GPU over-fit).

Caveats

  • Module stays EXPERIMENTAL. Detection SDE is at parity with the reference (above), but that is three regimes, not a full injection–recovery completeness campaign across a (period, depth, ndata) grid (item D3) — the sign-off needed before dropping the flag.
  • Small unrelated infra fix: scripts/setup-remote.sh now honors RUNPOD_REMOTE_DIR (some pods have broken /workspace volumes).

🤖 Generated with Claude Code

johnh2o2 and others added 2 commits July 6, 2026 13:58
… refinement)

Rewrite Transit Least Squares for survey-scale throughput. The legacy
kernel did two full O(ndata) passes per (duration, t0) trial and capped
light curves at ~3,500 points; the new default (`use_fast=True`) removes
both limits and processes a whole survey chunk in one launch.

Architecture (cuvarbase/kernels/tls_fast.cu + tls_search_batch):
- One block per (light curve, period); fold once into shared-memory
  phase bins, then scan every trial against bin-averaged integrated
  template tables (S1=int T, S2=int T^2) with a closed-form chi2
  (chi2_0 - num^2/den). Trial cost is independent of ndata; no shared-
  memory cap on light-curve length.
- Period grid split into bin-count bands so long-period searches don't
  pay the finest band's per-trial cost.
- Exact float-float ("double-single") fold: ~1e-8 phase error at
  4-year baselines with no 1/64-rate double math on consumer GPUs.
- Cancellation-free score output (num^2/den); chi2 reconstructed in
  float64 host-side against a float64 chi2_0.
- Second exact kernel re-fits the top-K candidate periods per light
  curve on a finer local (duration, t0) grid. Refinement sharpens the
  reported parameters only; the SDE/FAP statistics are computed from
  the uniform coarse spectrum so the detection statistic's scale stays
  consistent with the legacy kernel.

Support fixes: SDE median-detrend window capped at 91 (reference TLS
convention) instead of a pathological nperiods/10 window (minutes ->
~0.1 s at 190k periods); duration_grid_keplerian vectorized (1.1 s ->
40 ms at 190k periods); per-light-curve statistics run on a thread pool;
64-bit batch offsets; qmax<1, power-of-two block_size, and non-negative
refine_top_k validated with clear errors.

Measured end-to-end (scripts/benchmark_tls_survey.py, 100% injected-
transit recovery in every regime; RTX A5000): TESS FFI 1.2 ms/LC
(~800 LC/s), K2 3.1 ms, TESS 2-min 2.8 ms, TESS 1-yr 18 ms, Kepler
4-yr (65k pts, 172k periods) 0.17 s/LC vs ~522 s for reference TLS on a
16-core CPU. Full suite 68/68 on RTX 4000 Ada (incl. golden tests vs the
reference transitleastsquares package); core suite green on A5000 and
V100. Block-size heuristic swept and tuned per compute capability
(sm_70/86/89). See analysis/TLS_COST_ANALYSIS.md for the GPU-vs-CPU and
GPU-vs-GTLS cost comparison.

The legacy per-point kernel is retained behind use_fast=False; the
module stays EXPERIMENTAL pending an injection-recovery validation
campaign at reference-matched epoch fidelity (item D3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEGJJrPcrvkJGeMAEryRPG
…timing

Adds an apples-to-apples fidelity experiment answering whether the
coarse-epoch-grid + refinement fast path sacrifices detectability.

scripts/tls_fidelity_experiment.py runs identical injected light curves
through cuvarbase (t0_oversample=3 default AND 33 reference-matched) and
the reference transitleastsquares package on one shared Ofir grid, and
recomputes SDE with the IDENTICAL statistic on both methods' chi2
spectra (their SR->SDE normalizations differ, so the statistic is held
fixed and only spectrum fidelity varies).

Result (RTX A5000): the default coarse grid is within 1-3% of the
reference SDE (0.97-0.99x) with 100% recovery, including a marginal
near-threshold depth and a narrow transit; matched t0=33 closes it to
within 1% (1.01-1.03x). SDE is a period-space contrast that is largely
insensitive to epoch-grid density, so the coarse grid trades reported
t0/parameter precision (restored by refinement) for speed, not
detectability. scripts/tls_matched_timing.py measures the matched-
fidelity cost: 6-15x over default (Kepler-4yr 8.4x -> 1.48 s/LC).

Rewrites analysis/TLS_COST_ANALYSIS.md honestly: throughput is the
market-independent invariant (thousands x vs CPU; ~22-190x vs the GTLS
CuPy GPU-TLS, arXiv:2607.00348); the earlier dollar ratios over-pinned
the multiplier by comparing a spot-price GPU against an AWS on-demand
CPU. Raw fidelity numbers in benchmarks/results/tls_survey_jul2026/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEGJJrPcrvkJGeMAEryRPG
@johnh2o2

johnh2o2 commented Jul 6, 2026

Copy link
Copy Markdown
Owner Author

Follow-up: measured fidelity (is the coarse-grid + refinement lossy?)

Pushed c4d10ff — an apples-to-apples fidelity experiment (scripts/tls_fidelity_experiment.py). It runs the same injected light curves through cuvarbase (default t0_oversample=3 and reference-matched 33) and the reference transitleastsquares on one shared Ofir grid, and recomputes SDE with the identical statistic on both methods' χ² spectra (their SR→SDE normalizations differ, so the statistic is held fixed and only spectrum fidelity varies).

The fast path is not lossy in detectability. On the identical SDE statistic (RTX A5000):

Signal cuvarbase t0=3 (default) cuvarbase t0=33 (matched) reference recovery
tess-ffi depth 0.005 (strong) 0.99× 1.03× 14.61 12/12
tess-ffi depth 0.002 (marginal) 0.97× 1.01× 12.37 10/10
k2 depth 0.004 (narrow) 0.98× 1.01× 25.82 6/6

Default is within 1–3% of the reference SDE (safe direction — slight under-report); matched t0=33 is within 1%. SDE is a period-space contrast that normalizes out epoch-grid density, so the coarse grid trades reported t0/parameter precision (restored by refinement) for speed, not detectability. Matched fidelity costs a measured 6–15× (Kepler-4yr 8.4× → 1.48 s/LC).

Also corrected the earlier cost claims in analysis/TLS_COST_ANALYSIS.md: the six-figure "cheaper" ratios over-pinned the multiplier by pairing a spot-price GPU with an AWS on-demand CPU. Throughput is the market-independent invariant (thousands× vs CPU at matched SDE fidelity; ~22–190× vs GTLS, arXiv:2607.00348, on cheaper hardware).

johnh2o2 and others added 4 commits July 6, 2026 22:44
…l SDE)

Reproduces arXiv:2607.00348 Fig.7 on one RTX A5000 with a matched search:
identical Ofir period grid, identical per-period duration window, matched
epoch density (t0_oversample=8 == GTLS skip=8), and one injected transit fed
to every method. One identical SDE routine scores every spectrum.

cuvarbase-TLS-matched is 30 -> 171x faster than GTLS-skip8 across 200->2000 d
(growing with baseline; GTLS scales ~N^2.5 from per-batch kernel-launch
overhead, cuvarbase ~linear), at 1-3% SDE parity and 100% recovery. It is also
23-40x faster than the paper's own RTX-4090 GTLS numbers (immune to the
A5000-vs-4090 question). The paper's Fig.7 used GTLS skip=8, not full-scan.

Documents the paper's BLS-comparison caveat: its Kunimoto qmin=2e-4/noverlap=3
config forces up to 5000 phase bins (sub-cadence durations for 30-min data),
cheap for GTLS's cumsum but a 25x penalty for cuvarbase-BLS's per-duration
re-binning, and noverlap=3 bypasses the fused kernel. A sensible BLS config
(qmin=2e-3, fused noverlap=2) is faster than both TLS and GTLS; our improved
batched BLS at the paper's exact config is ~23x faster than their 121.1 s.

Adds analysis/GTLS_COMPARISON.md + the reproduced figure, the benchmark under
scripts/gtls_benchmark/ (BLS curves require feature/bls-survey-speed), and the
raw July-2026 A5000 result JSONs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Answers the single-light-curve / cold-start question. On a fresh A5000, one
LC, fresh process, kernel JIT compile INCLUDED, on-disk kernel cache cleared
(true first-run case), full launch-to-answer wall time:

  baseline   cuvarbase-TLS-matched   GTLS-skip8   cold ratio
  200 d      4.1 s                   10.7 s       2.6x
  500 d      4.5 s                   27.8 s       6.1x
  1000 d     4.8 s                   83.8 s       17x
  1500 d     5.6 s                   191.0 s      34x

cuvarbase's cold cost is a ~fixed ~3-4s kernel compile that barely grows with
baseline; GTLS's is its exploding search, so the ratio grows 2.6x -> 34x. From
the 2nd star onward (pycuda/cupy disk cache warm) cuvarbase drops to ~0.5-2s and
the ratio snaps back toward the warm 30-171x; GTLS recompiles + re-searches every
call. SDE parity holds cold too.

Adds analysis/GTLS_COMPARISON.md section 2b, the raw cold data, and the harness
(scripts/gtls_benchmark/cold_shot.py + cold_driver.sh).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…numbers

- Add TLS to the survey-scale performance story: 30-170x faster than GTLS (the
  only other GPU TLS) at 1-3% SDE parity, 23-40x vs GTLS's own 4090 numbers,
  thousands-x vs CPU transitleastsquares (links analysis/GTLS_COMPARISON.md).
- Update the experimental TLS description to the validated fast path
  (tls_search_batch): arbitrary ndata, SDE parity with reference + GTLS,
  still flagged experimental pending full injection-recovery.
- Correct the BLS "vs previous release" framing: the v1.0 kernel is inherited
  from 0.2.6 essentially unchanged; the survey-speed campaign (PR #66) makes it
  2.9-9.2x faster (kernel) / 2.0-12.7x (end-to-end), plus 34x from kernel
  caching in a naive loop. (Replaces a misleading "~1x" characterization.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#68 review)

_finish_lc's coarse-parameter fallback (taken when a light curve's top-K exact
refinements all return the failure sentinel while the coarse scan still has
valid periods) reads t0_h/dur_h/depth_h, but those were fetched only under
`return_arrays or not K`. In the default batch path (refine_top_k=50 -> K>0,
return_arrays=False) they were never bound, so reaching the fallback raised
UnboundLocalError and aborted the whole batch. Surfaced by the adversarial PR
review. Fetch the coarse arrays whenever the fallback can fire (any LC with all
refined scores <= 0), still skipping the D2H on the common path. Adds a
deterministic regression test that forces every refinement to the sentinel and
asserts the default batch falls back instead of crashing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johnh2o2 johnh2o2 merged commit 72f3663 into v1.0-fixes Jul 7, 2026
12 checks passed
@astrobatty astrobatty deleted the feature/tls-fast-survey branch July 10, 2026 10:58
johnh2o2 added a commit that referenced this pull request Jul 11, 2026
Adversarial review of the fast-path library surface found no
correctness defects (analysis/tls-audit-jul2026.md has the verified
invariants and the full findings table). Applied here:

- Remove the stale import-time "EXPERIMENTAL, not for science use"
  warning from cuvarbase.tls: the fast path is golden-tested against
  the reference package and ships as a v1.0 feature; the warning also
  pointed users at an internal analysis/ document
- Delete dead _auto_nbins() (superseded by the inline per-period
  banding in tls_search_batch)
- tls_search_gpu: document use_fast/refine_top_k/refine_oversample/
  nbins; correct the flux docstring (no path normalizes y) and note
  the legacy path's float32-fold BJD limitation; warn when the
  never-used `durations` parameter is passed
- _preprocess_batch: explicit int32 guard for pathological per-LC
  point counts
- Tests: banded-vs-single-band parity (GPU; exercises the NBINS
  band split + period_map scatter, the one intricate path the suite
  only covered implicitly), SDE median-kernel cap/rounding/short-
  series behavior (CPU), int32 guard + durations warning (CPU)
- bls.py: fix five invalid \chi/\omega escape sequences in docstrings

CPU suite: 253 passed / 543 GPU-skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
johnh2o2 added a commit that referenced this pull request Jul 11, 2026
First GPU validation of the #66+#67+#68 union (each PR had only been
pod-validated on its own branch). RTX A5000, pycuda 2026.1, numpy 2.4.6,
with batman + cufinufft + the reference transitleastsquares installed:

- Full suite: 796 passed, 0 skipped, 0 failed (17:14). An initial run
  showed 2 skips - the TLS golden tests, because the reference package
  was missing from the pod; with it installed they run and pass. Gate
  environments must install transitleastsquares (recorded in SUMMARY)
- scripts/check_release_gate.py: 14/14 ALL CHECKS PASSED
- Re-measured + archived the matched-fidelity timings the claims audit
  flagged as unarchived: tess-yr 12.8x (25.3 -> 325.2 ms/LC), kepler-4yr
  8.1x (188.3 -> 1520.5 ms/LC), 100% recovery at both fidelities
  (benchmarks/results/tls_survey_jul2026/matched_timing_a5000_jul2026.txt);
  published range tightened ~5-15x -> ~5-13x, Kepler matched cost ~$114/M,
  TESS-yr ~$24/M now measured (CHANGELOG, release notes,
  TLS_COST_ANALYSIS, provenance README updated)
- Release notes: GPU test count updated to the gate's 796/0-skip
- Docs built on the pod with all 14 GPU-rendered figures, zero warnings
  (after fixing a docstring rst error in utils.py and letting the pod
  sync include docs/source/logo.png) - staged as the local orphan branch
  gh-pages-staging (fab3a5e), to be pushed on release day
- Archive: analysis/v1.0-release-gate-jul2026/ (suite/gate/timing logs,
  environment record, SUMMARY.md). Pod terminated, API-verified

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant