You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found while centralising the annotation score columns of the text exports in #785. That PR deliberately does not touch this, because this one changes candidate ranking rather than formatting.
The guard cannot fail any more
MsdialCore/Algorithm/Annotation/MsReferenceScorer.csCalculateScore builds the total score from a list of terms, and gates the MS/MS term on the dot products being non-negative:
MsScanMatching.GetWeightedDotProduct and its siblings return -1 when there is nothing to compare, so that second guard was written to skip the MS/MS term for a candidate with no product-ion spectrum.
Since the squared-metrics rename in #589 (4b39b845e, released as 5.5.250625) those are derived properties:
publicfloatWeightedDotProduct{
get =>(float)Math.Sqrt(Math.Max(SquaredWeightedDotProduct,0f));
set =>SquaredWeightedDotProduct=value*value;}
Math.Max(..., 0f) means the getters can never return a negative number, so the guard is now unconditionally true. MatchedPeaksPercentage has no such clamp and is still -1, so msmsScore is negative and gets added anyway.
Observable effect
On console_fastlc_demo (7 SCIEX WIFF LC-MS/MS negative files, the demo's own method.txt), a precursor-only row reads:
20230406_feces_1_NEG.mdpeak peak 2
Name no MS2: FA 5:0
m/z similarity 1.00
Total score -0.143
Before #589 the MS/MS term would have been skipped and Total score would have come from mass similarity alone, so roughly 1.0, times 0.9 when the reference has no InChIKey. It is now a negative number for every no MS2: candidate.
Why this matters beyond cosmetics
Total score participates in ranking. MsScanMatchResultContainer.Representative picks the argmax of ResultOrder, and the tiered annotation work depends on an actual MS/MS match outranking a precursor-only candidate. Pushing every precursor-only candidate to a negative total may currently produce the desired ordering by accident, but it is not a designed ranking rule, and it also means the number exported as Total score is not interpretable for those rows.
Suggested direction
#785 adds MsScanMatchResult.IsSpectrumComparisonPerformed, which reads the sentinel from the raw Squared* fields where it survives and also excludes Unknown and TextDB results. That gives this guard a correct expression:
Keep today's ranking effect but express it deliberately, for example by ordering precursor-only candidates below every spectrum-matched candidate rather than by letting a -1 leak into an arithmetic mean.
Either way it needs a demo comparison of representative selection, not just of the exported number, plus a check of MsScanMatchResultEvaluator and the tiered-annotation priority rules. MsRefSearchParameterBase.TotalScoreCutoff also exists but is not applied by MsScanMatchResultEvaluator, which is worth confirming in the same pass.
Also affected by the same clamp
GetSpectralEntropySimilarity returns -1 under the same condition and is stored unclamped in MsScanMatchResult.SpectralEntropy, and EnhancedDotProduct is assigned (float)Math.Sqrt(sqenhancedDotProduct), so it becomes NaN when the underlying value is -1. Neither column is currently kept in any concrete accessor's header, so nothing exports them today, but they are wrong in memory and would surface if a header ever included them.
Found while centralising the annotation score columns of the text exports in #785. That PR deliberately does not touch this, because this one changes candidate ranking rather than formatting.
The guard cannot fail any more
MsdialCore/Algorithm/Annotation/MsReferenceScorer.csCalculateScorebuilds the total score from a list of terms, and gates the MS/MS term on the dot products being non-negative:MsScanMatching.GetWeightedDotProductand its siblings return-1when there is nothing to compare, so that second guard was written to skip the MS/MS term for a candidate with no product-ion spectrum.Since the squared-metrics rename in #589 (
4b39b845e, released as 5.5.250625) those are derived properties:Math.Max(..., 0f)means the getters can never return a negative number, so the guard is now unconditionally true.MatchedPeaksPercentagehas no such clamp and is still-1, somsmsScoreis negative and gets added anyway.Observable effect
On
console_fastlc_demo(7 SCIEX WIFF LC-MS/MS negative files, the demo's ownmethod.txt), a precursor-only row reads:Before #589 the MS/MS term would have been skipped and
Total scorewould have come from mass similarity alone, so roughly 1.0, times 0.9 when the reference has no InChIKey. It is now a negative number for everyno MS2:candidate.Why this matters beyond cosmetics
Total scoreparticipates in ranking.MsScanMatchResultContainer.Representativepicks the argmax ofResultOrder, and the tiered annotation work depends on an actual MS/MS match outranking a precursor-only candidate. Pushing every precursor-only candidate to a negative total may currently produce the desired ordering by accident, but it is not a designed ranking rule, and it also means the number exported asTotal scoreis not interpretable for those rows.Suggested direction
#785addsMsScanMatchResult.IsSpectrumComparisonPerformed, which reads the sentinel from the rawSquared*fields where it survives and also excludesUnknownandTextDBresults. That gives this guard a correct expression:Whether that is the right fix depends on which behaviour is intended, which is why it is not in #785:
-1leak into an arithmetic mean.Either way it needs a demo comparison of representative selection, not just of the exported number, plus a check of
MsScanMatchResultEvaluatorand the tiered-annotation priority rules.MsRefSearchParameterBase.TotalScoreCutoffalso exists but is not applied byMsScanMatchResultEvaluator, which is worth confirming in the same pass.Also affected by the same clamp
GetSpectralEntropySimilarityreturns-1under the same condition and is stored unclamped inMsScanMatchResult.SpectralEntropy, andEnhancedDotProductis assigned(float)Math.Sqrt(sqenhancedDotProduct), so it becomesNaNwhen the underlying value is-1. Neither column is currently kept in any concrete accessor's header, so nothing exports them today, but they are wrong in memory and would surface if a header ever included them.🤖 Generated with Claude Code