Repository navigation
A protein with no scoreable truth is not a false positive - #26
Merged
Merged
Conversation
The PK kernel restricted coverage to eligible rows and left every mass summing over all of them. A protein whose TOI ground truth was entirely already known in t0 has nothing left to score, so normalize() drops it from every denominator, and its predictions still landed in `fp`. Every one of those predictions is a false positive by construction: there is no term it could have emitted that would have counted. Micro precision was therefore charged for proteins the evaluation was not measuring. Restricting `pred_totals` to eligible rows is the whole fix. `tp_totals` is restricted with it, which is a no-op under the current invariant (a column can only be a TP when it is in TOI and not excluded, so an ineligible row already contributes zero) and is done so the two totals share one population rather than resting on that invariant. Measured on the fixture this file already carried: FP at the low threshold goes from 4 to 2 and every other column is unchanged, n=2, tp=2, fn=5, macro precision 1, macro recall 5/6. The new test pins all six so a later widening of the restriction cannot pass unnoticed. The docstring of test_pk_precision_recall_unchanged_by_fix said the fix touched only the `n` column. That was true of the coverage fix and is no longer true, so it now names which columns each restriction moves and why the rest cannot. Direction and size on the campaign: this RAISES PK micro precision, so published PK figures were conservative rather than optimistic. On the 2026-08-30 run the excluded mass is 3,385 of 2,648,460 in BPO and 576 of 214,491 in MFO, from ten and eight proteins, order 1e-4 on a base of 0.15. The macro columns the external board reports do not move at all. The dense fallback compute_confusion_matrix_exclude has neither this restriction nor the earlier coverage one. It is gated behind CAFAEVAL_SPARSE=0 for A/B comparison, is not reachable with a per_protein_sink, and has produced no published number, so it is left alone here and flagged rather than changed.
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.
What was wrong
compute_confusion_matrix_exclude_sparserestricted coverage to eligible rows and left every mass summing over all of them:A protein whose TOI ground truth was entirely already known in t0 has nothing left to score.
normalize()drops it from every denominator, and its predictions still landed infp. Every one of them is a false positive by construction, because no term it could emit would have counted. Micro precision was charged for proteins the evaluation was not measuring.The fix
Restrict
pred_totalsto eligible rows.tp_totalsis restricted with it: a no-op under the current invariant, since a column can only be a TP when it is in TOI and not excluded, so an ineligible row already contributes zero. It is included so the two totals share one population instead of resting on that invariant.Measured, on the fixture this repo already carried
tests/test_pk_coverage_bug.py::_make_pk_scenariohas exactly the case: protein 1 is ineligible and predicts terms 2 and 3, neither excluded for it, neither in its truth.Only
fpmoves. The new test pins all six so a later widening of the restriction cannot pass unnoticed.Direction and size, for the record
This raises PK micro precision, so published PK figures were conservative and not optimistic. On the 2026-08-30 campaign run the excluded mass is 3,385 of 2,648,460 in BPO (0.128 %) and 576 of 214,491 in MFO (0.269 %), from ten and eight proteins respectively. Order 1e-4 on a base of 0.15.
The macro columns the external board reports do not move at all, since an ineligible protein already contributed zero to both.
It does change stored
f_micro_wfigures, which is why this was raised as a decision rather than applied as a patch.Out of scope, flagged not changed
The dense fallback
compute_confusion_matrix_excludehas neither this restriction nor the earlier coverage one. It is gated behindCAFAEVAL_SPARSE=0for A/B comparison, raisesNotImplementedErrorwhen aper_protein_sinkis requested, and has produced no published number. Bringing it into line would change what the A/B measures, so it is left alone and named here.Downstream
PROTEA's
_run_cafa_grid_artifact.pyrefuses to write a namespace while this defect stands, andtests/test_per_protein_grid_producer.py:1306is a canary that fails the day this lands. Both need updating in the same migration, leaf first: this merges, PROTEA repins the resolved commit, then the guard and the canary follow.Checks
Full suite before and after on
origin/main: identical, no failures. With this branch: 36 passed, 6 xfailed, 0 failed.ruff check .clean.