Skip to content

test(nni): pin IW score returned by nni_search to independent recompute#245

Merged
ms609 merged 1 commit into
cpp-searchfrom
claude/test-nni-iw-regression
May 19, 2026
Merged

test(nni): pin IW score returned by nni_search to independent recompute#245
ms609 merged 1 commit into
cpp-searchfrom
claude/test-nni-iw-regression

Conversation

@ms609
Copy link
Copy Markdown
Owner

@ms609 ms609 commented May 19, 2026

Summary

Closes the test coverage gap that allowed the IW NNI rescore bug (3df9088) to be committed without detection. The existing IW NNI test only asserted result$score >= 0 and topology shape — it never cross-checked that the returned score matched the IW score of the returned topology.

Pre-fix, nni_search did new_score = best_score + integer_delta unconditionally, mixing a float IW score with an integer EW step count. The returned result$score was the accumulated garbage value. The existing test would still pass because the assertions were weak.

Changes

  • Added tests/testthat/test-ts-nni-iw-rescore.R with 4 test cases and 20 assertions:
    • Binary characters under concavity=10 with independent IW score recompute via ts_fitch_score and TreeLength
    • Multistate (0–3) characters under IW
    • Sweep across concavity values (3, 10, 100) to verify fix is general
    • Binary+NA (inapplicable tokens) under IW

All tests pass with the fix in place; the pattern (assert result$score == ts_score(rt, ds, concavity=10)) would have caught the pre-fix bug because integer EW deltas don't translate to IW score changes.

Notes

Profile parsimony isn't directly testable here because ts_nni_search's Rcpp bridge doesn't forward infoAmounts. However, the IW path exercises the same compute_weighted_score dispatch that the fix unified for both modes, so this closes the C++ entry-point gap.

Closes the coverage gap that let the IW NNI rescore bug (3df9088) ship.
The existing IW NNI test (test-ts-spr-nni-opt.R) only asserted
result$score >= 0 and topology shape — it never cross-checked that the
returned score matched the IW score of the returned topology.  Pre-fix,
nni_search did new_score = best_score + integer_delta unconditionally,
so under IW the returned score was the accumulated wrong float and the
existing assertions still passed.

New file mirrors the EW pattern from "NNI: 15-tip random dataset"
(result$score == ts_score(rt, ds)) but under concavity=10, sweeps a few
concavities, and exercises multistate + NA cases.  Profile parsimony
isn't reached because ts_nni_search's Rcpp bridge doesn't forward
infoAmounts; the IW path through compute_weighted_score is the same
codepath the fix unified, so this catches both modes at the C++ level.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ms609 ms609 changed the base branch from main to cpp-search May 19, 2026 13:55
@ms609 ms609 merged commit f281356 into cpp-search May 19, 2026
8 of 10 checks passed
@ms609 ms609 deleted the claude/test-nni-iw-regression branch May 19, 2026 14:03
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