Skip to content

Total score adds the MS/MS term for precursor-only candidates because #589 clamped the -1 guard #786

Description

@htsugawa

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.cs CalculateScore builds the total score from a list of terms, and gates the MS/MS term on the dot products being non-negative:

var msmsScore = (
    result.WeightedDotProduct * dotProductFactor +
    result.SimpleDotProduct * dotProductFactor +
    result.ReverseDotProduct * revesrseDotProdFactor +
    result.MatchedPeaksPercentage * presensePercentageFactor) /
    (dotProductFactor + dotProductFactor + revesrseDotProdFactor + presensePercentageFactor);

if (result.AcurateMassSimilarity >= 0 && massFactor > 0)
    scores.Add(result.AcurateMassSimilarity * massFactor);
if (result.WeightedDotProduct >= 0 && result.SimpleDotProduct >= 0 && result.ReverseDotProduct >= 0)
    scores.Add(msmsScore * msmsFactor);

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:

public float WeightedDotProduct {
    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:

if (result.IsSpectrumComparisonPerformed)
    scores.Add(msmsScore * msmsFactor);

Whether that is the right fix depends on which behaviour is intended, which is why it is not in #785:

  1. Restore the pre-Added Raw Dot Product Properties Alongside Renamed Squared Metrics for Spectrum Matching #589 behaviour, where a precursor-only candidate is scored on mass and retention only. This raises those candidates' total scores substantially and will change which candidate wins in some rows.
  2. 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.

🤖 Generated with Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions