Skip to content

TST: Add direct C-extension tests for BLS periodogram - #20151

Draft
ReemHamraz wants to merge 4 commits into
astropy:mainfrom
ReemHamraz:test-bls-extension
Draft

TST: Add direct C-extension tests for BLS periodogram#20151
ReemHamraz wants to merge 4 commits into
astropy:mainfrom
ReemHamraz:test-bls-extension

Conversation

@ReemHamraz

Copy link
Copy Markdown
Contributor

Hey everyone! This PR tackles the BLS extension testing for my GSoC project.

Instead of routing through the high-level BoxLeastSquares API, this suite hits the Cython/C-engine (bls_impl and run_bls) directly to ensure the math and memory allocations hold up in complete isolation.

Key additions:

  • Fed strictly-typed np.float64 memory buffers directly into the C-boundary.
  • Built a SyntheticTransit fixture to mathematically verify the C-engine recovers the correct period, depth, and duration from a planted signal. (Note: I relaxed the rtol for the depth assertion to 5e-2 to account for expected discrete phase binning drift across the C-level grid).
  • Added isolated tests for the C-level early-exit trapdoors (Flags 1 and 2) to ensure they safely cross the boundary and raise Python ValueErrors as expected.

cc: @neutrinoceros

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.


from astropy.timeseries.periodograms.bls._impl import bls_impl

_F64Array = npt.NDArray[np.float64]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this annotation will become unnecessary in the future but let's just be consistent with the existing ones for now.

Suggested change
_F64Array = npt.NDArray[np.float64]
_F64Array: TypeAlias = npt.NDArray[np.float64]

true_depth = 0.5

t = np.linspace(0, 10, 1000, dtype=np.float64)
y = np.ones_like(t, dtype=np.float64)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dtype will implicitly match t's

Suggested change
y = np.ones_like(t, dtype=np.float64)
y = np.ones_like(t)

0, # obj_flag
)

assert type(results) is tuple

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this particular case I think we'd rather make the type check a little looser to make room for a potential change using a NamedTuple

Suggested change
assert type(results) is tuple
assert isinstance(results, tuple)

Comment on lines +71 to +74
for array_out in results:
assert type(array_out) is np.ndarray
assert array_out.dtype == np.float64
assert array_out.shape == periods.shape

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems good but probably insufficient. I would expect at least a couple numerical checks in addition to these.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay on it

expected_err_msg: str,
) -> None:
"""Test C-level early-exit flags."""
with pytest.raises(ValueError) as exc_info:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason not to use pytest.raises's match argument here ? The expected message also seems invariant so let's not overengineer it yet and avoid parametrizing on it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeahh understandable, I should've done exactly that

0,
)

out_objective, out_depth, _, out_duration, _, _, _ = results

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have unused return values be named (use _ as a prefix instead of the whole name) to make the test more readable in isolation. It'd also be good to have a comment or two explaining why they are not checked.

best_idx = np.argmax(out_objective)

np.testing.assert_allclose(
periods[best_idx], perfect_transit.true_period, rtol=1e-2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these relative tolerances seem a bit underwhelming. Is there a technical reason why we expect low accuracy here ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 1e-2 tolerance was originally a sort of bandage for two things: first, my synthetic data only had 1,000 points with an oversample=10 rate, and second my np.linspace(1.5, 2.5, 100) didn't actually include the exact period of 2.0..... hence why I decided to go for rtol=1e-2 but I fixed that in the latest commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants