Skip to content

Test NORTA correlation bounds and the doubly truncated normal fit - #35

Merged
lispandfound merged 2 commits into
masterfrom
ai/tests
Oct 22, 2026
Merged

lispandfound merged 2 commits into
masterfrom
ai/tests

Conversation

@claude

@claude claude Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Adds tests/test_norta.py, which tests rupture_generator/sampling/norta.py. Only test files are changed.

What the new tests check

  • The normal marginal is the identity. Uses Hypothesis. NORMAL.apply returns its input unchanged as float64, and integer input comes back as floats. latent_correlation(NORMAL, NORMAL, t) returns t unchanged.
  • Pairing a normal with a non-normal is a linear correction. The normal transform is He_1 alone, so g(rho) = rho * corr(Z, h(Z)). The test computes corr(Z, h(Z)) for a gamma(0.75) marginal with its own scipy.integrate.quad and checks latent_correlation against target / corr at several points. It also checks that swapping the two marginals gives the same answer.
  • Unattainable correlations are refused. A target just past g(1) or g(-1) raises RuptureGeneratorError; both the ceiling and the floor side are tested. A target past the edge by less than NORTA_CORRELATION_SLACK is clipped to latent correlation ±1.
  • An unimplemented family raises ValueError the first time it is transformed. This is the case unknown branch.
  • The doubly truncated normal fit is checked against independent numbers. For several parent scales and maxima:
    • the parent location found by _doubly_truncated_normal_location gives a cut normal with mean 1, computed by direct quadrature of the normal pdf on [0, maximum];
    • _doubly_truncated_normal_spread matches the quadrature coefficient of variation;
    • a narrow parent is left essentially uncut (location 1, spread equal to the scale).
  • The fitted doubly truncated normal behaves correctly through apply. Its output is monotone and stays within [0, maximum], and the transformed standard normal has unit mean and the requested spread (Gauss-Hermite).

Coverage of norta.py

Coverage Lines missed
Before 95% 187, 291, 327, 328, 451, 463, 472
After 99% 291, 463

Both remaining lines are left uncovered on purpose:

  • 291 (_distribution for normal) can't be reached through the public API, because Marginal.apply returns early for the normal marginal.
  • 463 (correlation map not increasing) can't happen: by Price's theorem g never decreases for monotone transforms.

Surviving mutants

_doubly_truncated_normal mutants 1–12 (dropping or garbling a, b, loc or scale) are killed by the new tests. I checked each variant by patching it in through a scratch pytest plugin; every one fails 6–8 of the new doubly truncated normal tests. The existing tests in test_numerics.py (mean 1, support (0, maximum)) look like they should also have caught these. Mutmut's run reported 107 of 221 mutants as timeouts, so its test mapping for this module looks unreliable.

_doubly_truncated_normal_location mutants:

  • Killed:
    • 4–10, 12 pass None or drop an argument. They crash inside SciPy or the root finder.
    • 11 (+ 1.0) never brackets the root, so the search never ends.
    • 13 (- 2.0) moves the cut mean to 2, which the quadrature mean check rejects.
  • Killed by any test that reaches the code: 2 and 3 (invalid="XXignoreXX" / "IGNORE") raise ValueError in np.errstate.
  • Equivalent: 1 (invalid=None) only stops a warning being silenced. With both errstate guards removed, the fits for maxima 1.5, 2, 4.3 and 10 raised no floating-point warnings, so it changes nothing that can be observed.

Issues filed

None. An unknown family string gets past Marginal.__post_init__ and only fails later, with a ValueError (whose message has a typo, "Unimplementhed"). It can only arrive by going against the MarginalFamily type hint, so I didn't file it as a bug.

🤖 Generated with Claude Code

…mal fit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude claude Bot added ai-generated Opened by the scheduled Claude jobs ai-tests AI-written tests that need review labels Oct 21, 2026
@claude
claude Bot requested a review from lispandfound October 21, 2026 14:04
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Benchmarks

7fa9b6e against base 1a72c3e, on INTEL(R) XEON(R) PLATINUM 8573C. Times are medians over the rounds. The median change is 1.3%; changes past 5% are in bold.

Kernels

kernel base head change head IQR rounds padded cells
embed[Mw6.5] 42.4 ms 42.9 ms +1.3% 210 µs 23 448,000 (+0.0%)
circulant_draw[Mw6.5] 4.31 ms 4.38 ms +1.6% 233 µs 174
embed[Mw7.5] 134 ms 136 ms +1.6% 968 µs 8 1,204,224 (+0.0%)
circulant_draw[Mw7.5] 9.88 ms 10.1 ms +1.9% 391 µs 90
eikonal_solve 1.27 s 1.29 s +1.4% 944 µs 5
synthesise_pulses[impulse] 825 µs 831 µs +0.8% 26.5 µs 607
synthesise_pulses[liu] 473 ms 474 ms +0.2% 2.03 ms 5

End to end

Each round is the command line in a fresh interpreter, start-up included. Peak RSS is the largest the child process reached over the rounds.

example base time head time change base peak RSS head peak RSS change
alpine_hope 9.18 s 9.06 s -1.3% 294 MiB 294 MiB -0.1%
beavan 3.46 s 3.48 s +0.4% 232 MiB 232 MiB +0.2%
colombia 8.75 s 8.56 s -2.1% 3,168 MiB 3,168 MiB -0.0%
crustal 1.44 s 1.45 s +1.3% 244 MiB 245 MiB +0.5%
landers 2.75 s 2.81 s +1.9% 385 MiB 385 MiB +0.0%
northridge 1.41 s 1.42 s +0.9% 294 MiB 293 MiB -0.2%
two_faults 2.39 s 2.43 s +1.6% 354 MiB 353 MiB -0.2%
two_faults_segments 1.46 s 1.47 s +1.1% 190 MiB 190 MiB +0.2%
venezuela 16.5 s 16.8 s +1.9% 5,768 MiB 5,766 MiB -0.0%

@lispandfound
lispandfound merged commit 26b166a into master Oct 22, 2026
9 checks passed
@lispandfound
lispandfound deleted the ai/tests branch October 22, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-generated Opened by the scheduled Claude jobs ai-tests AI-written tests that need review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant